summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/.clang-format2
-rw-r--r--libs/androidfw/Android.bp16
-rw-r--r--libs/androidfw/Android.mk24
-rw-r--r--libs/androidfw/ApkAssets.cpp104
-rw-r--r--libs/androidfw/AssetManager.cpp650
-rw-r--r--libs/androidfw/AssetManager2.cpp576
-rw-r--r--libs/androidfw/AttributeResolution.cpp521
-rw-r--r--libs/androidfw/Chunk.h113
-rw-r--r--libs/androidfw/ChunkIterator.cpp81
-rw-r--r--libs/androidfw/LoadedArsc.cpp572
-rw-r--r--libs/androidfw/LocaleDataTables.cpp1654
-rw-r--r--libs/androidfw/ResourceTypes.cpp124
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h62
-rw-r--r--libs/androidfw/include/androidfw/Asset.h3
-rw-r--r--libs/androidfw/include/androidfw/AssetManager.h96
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h298
-rw-r--r--libs/androidfw/include/androidfw/AttributeFinder.h258
-rw-r--r--libs/androidfw/include/androidfw/AttributeResolution.h62
-rw-r--r--libs/androidfw/include/androidfw/ByteBucketArray.h101
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h83
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h12
-rw-r--r--libs/androidfw/include/androidfw/TypeWrappers.h1
-rw-r--r--libs/androidfw/include/androidfw/Util.h127
-rw-r--r--libs/androidfw/tests/Android.mk39
-rw-r--r--libs/androidfw/tests/ApkAssets_test.cpp34
-rw-r--r--libs/androidfw/tests/AppAsLib_test.cpp56
-rw-r--r--libs/androidfw/tests/AssetManager2_bench.cpp228
-rw-r--r--libs/androidfw/tests/AssetManager2_test.cpp190
-rw-r--r--libs/androidfw/tests/Asset_test.cpp28
-rw-r--r--libs/androidfw/tests/AttributeFinder_test.cpp173
-rw-r--r--libs/androidfw/tests/AttributeResolution_test.cpp209
-rw-r--r--libs/androidfw/tests/BenchMain.cpp31
-rw-r--r--libs/androidfw/tests/ByteBucketArray_test.cpp50
-rw-r--r--libs/androidfw/tests/ConfigLocale_test.cpp46
-rw-r--r--libs/androidfw/tests/Config_test.cpp214
-rw-r--r--libs/androidfw/tests/Idmap_test.cpp162
-rw-r--r--libs/androidfw/tests/LoadedArsc_test.cpp82
-rw-r--r--libs/androidfw/tests/ResTable_test.cpp590
-rw-r--r--libs/androidfw/tests/Split_test.cpp332
-rw-r--r--libs/androidfw/tests/TestHelpers.cpp130
-rw-r--r--libs/androidfw/tests/TestHelpers.h60
-rw-r--r--libs/androidfw/tests/TestMain.cpp28
-rw-r--r--libs/androidfw/tests/Theme_bench.cpp99
-rw-r--r--libs/androidfw/tests/Theme_test.cpp250
-rw-r--r--libs/androidfw/tests/data/.gitignore2
-rw-r--r--libs/androidfw/tests/data/app/R.h34
-rw-r--r--libs/androidfw/tests/data/app/app.apkbin0 -> 1138 bytes
-rw-r--r--libs/androidfw/tests/data/app/app_arsc.h62
-rwxr-xr-xlibs/androidfw/tests/data/app/build7
-rw-r--r--libs/androidfw/tests/data/appaslib/R.h62
-rw-r--r--libs/androidfw/tests/data/appaslib/appaslib.apkbin0 -> 1308 bytes
-rw-r--r--libs/androidfw/tests/data/appaslib/appaslib_arsc.h68
-rw-r--r--libs/androidfw/tests/data/appaslib/appaslib_lib.apkbin0 -> 1308 bytes
-rw-r--r--libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h68
-rwxr-xr-xlibs/androidfw/tests/data/appaslib/build13
-rw-r--r--libs/androidfw/tests/data/basic/AndroidManifest.xml5
-rw-r--r--libs/androidfw/tests/data/basic/R.h88
-rw-r--r--libs/androidfw/tests/data/basic/basic.apkbin0 -> 2959 bytes
-rw-r--r--libs/androidfw/tests/data/basic/basic_arsc.h175
-rw-r--r--libs/androidfw/tests/data/basic/basic_de_fr.apkbin0 -> 1516 bytes
-rw-r--r--libs/androidfw/tests/data/basic/basic_hdpi-v4.apkbin0 -> 1298 bytes
-rw-r--r--libs/androidfw/tests/data/basic/basic_xhdpi-v4.apkbin0 -> 1304 bytes
-rw-r--r--libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apkbin0 -> 1302 bytes
-rwxr-xr-xlibs/androidfw/tests/data/basic/build25
-rw-r--r--libs/androidfw/tests/data/basic/res/values/values.xml24
-rw-r--r--libs/androidfw/tests/data/basic/split_de_fr_arsc.h88
-rw-r--r--libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h69
-rw-r--r--libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h69
-rw-r--r--libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h69
-rw-r--r--libs/androidfw/tests/data/feature/AndroidManifest.xml2
-rwxr-xr-xlibs/androidfw/tests/data/feature/build9
-rw-r--r--libs/androidfw/tests/data/feature/feature.apkbin0 -> 1373 bytes
-rw-r--r--libs/androidfw/tests/data/feature/feature_arsc.h73
-rw-r--r--libs/androidfw/tests/data/feature/res/values/values.xml5
-rw-r--r--libs/androidfw/tests/data/lib/AndroidManifest.xml5
-rw-r--r--libs/androidfw/tests/data/lib/R.h36
-rwxr-xr-xlibs/androidfw/tests/data/lib/build7
-rw-r--r--libs/androidfw/tests/data/lib/lib.apkbin0 -> 1221 bytes
-rw-r--r--libs/androidfw/tests/data/lib/lib_arsc.h68
-rw-r--r--libs/androidfw/tests/data/lib/res/values/values.xml4
-rwxr-xr-xlibs/androidfw/tests/data/overlay/build7
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.apkbin0 -> 1254 bytes
-rw-r--r--libs/androidfw/tests/data/overlay/overlay_arsc.h69
-rw-r--r--libs/androidfw/tests/data/styles/AndroidManifest.xml19
-rw-r--r--libs/androidfw/tests/data/styles/R.h58
-rwxr-xr-xlibs/androidfw/tests/data/styles/build5
-rw-r--r--libs/androidfw/tests/data/styles/res/layout/layout.xml5
-rw-r--r--libs/androidfw/tests/data/styles/res/values/styles.xml62
-rw-r--r--libs/androidfw/tests/data/styles/styles.apkbin0 -> 2422 bytes
-rw-r--r--libs/androidfw/tests/data/system/R.h40
-rwxr-xr-xlibs/androidfw/tests/data/system/build7
-rw-r--r--libs/androidfw/tests/data/system/system.apkbin0 -> 1417 bytes
-rw-r--r--libs/androidfw/tests/data/system/system_arsc.h88
-rw-r--r--libs/hwui/Android.mk161
-rw-r--r--libs/hwui/Animator.cpp2
-rw-r--r--libs/hwui/AssetAtlas.cpp130
-rw-r--r--libs/hwui/AssetAtlas.h174
-rw-r--r--libs/hwui/BakedOpDispatcher.cpp76
-rw-r--r--libs/hwui/BakedOpRenderer.cpp8
-rw-r--r--libs/hwui/BakedOpRenderer.h2
-rw-r--r--libs/hwui/Caches.cpp17
-rw-r--r--libs/hwui/Caches.h22
-rw-r--r--libs/hwui/CanvasState.cpp30
-rw-r--r--libs/hwui/CanvasState.h34
-rw-r--r--libs/hwui/ClipArea.h2
-rw-r--r--libs/hwui/Debug.h3
-rw-r--r--libs/hwui/DeferredDisplayList.cpp686
-rw-r--r--libs/hwui/DeferredDisplayList.h200
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp88
-rw-r--r--libs/hwui/DeferredLayerUpdater.h16
-rw-r--r--libs/hwui/DeviceInfo.cpp7
-rw-r--r--libs/hwui/DeviceInfo.h5
-rw-r--r--libs/hwui/DisplayList.cpp51
-rw-r--r--libs/hwui/DisplayList.h97
-rw-r--r--libs/hwui/DisplayListCanvas.cpp597
-rw-r--r--libs/hwui/DisplayListCanvas.h359
-rw-r--r--libs/hwui/DisplayListOp.h1555
-rw-r--r--libs/hwui/Dither.cpp98
-rw-r--r--libs/hwui/Dither.h56
-rw-r--r--libs/hwui/Extensions.cpp32
-rw-r--r--libs/hwui/Extensions.h4
-rw-r--r--libs/hwui/FloatColor.h21
-rw-r--r--libs/hwui/FontRenderer.cpp37
-rw-r--r--libs/hwui/FontRenderer.h23
-rw-r--r--libs/hwui/FrameBuilder.cpp16
-rw-r--r--libs/hwui/FrameInfoVisualizer.cpp29
-rw-r--r--libs/hwui/FrameInfoVisualizer.h20
-rw-r--r--libs/hwui/GammaFontRenderer.cpp3
-rw-r--r--libs/hwui/GammaFontRenderer.h13
-rw-r--r--libs/hwui/GlLayer.cpp76
-rw-r--r--libs/hwui/GlLayer.h112
-rw-r--r--libs/hwui/Glop.h17
-rw-r--r--libs/hwui/GlopBuilder.cpp143
-rw-r--r--libs/hwui/GlopBuilder.h29
-rw-r--r--libs/hwui/GpuMemoryTracker.cpp8
-rw-r--r--libs/hwui/GpuMemoryTracker.h4
-rw-r--r--libs/hwui/GradientCache.cpp75
-rw-r--r--libs/hwui/GradientCache.h25
-rw-r--r--libs/hwui/IProfileRenderer.h34
-rw-r--r--libs/hwui/Interpolator.cpp33
-rw-r--r--libs/hwui/Interpolator.h11
-rw-r--r--libs/hwui/Layer.cpp256
-rw-r--r--libs/hwui/Layer.h344
-rw-r--r--libs/hwui/LayerBuilder.cpp2
-rw-r--r--libs/hwui/LayerCache.cpp162
-rw-r--r--libs/hwui/LayerCache.h142
-rw-r--r--libs/hwui/LayerRenderer.cpp465
-rw-r--r--libs/hwui/LayerRenderer.h84
-rw-r--r--libs/hwui/NinePatchUtils.h96
-rw-r--r--libs/hwui/OpDumper.cpp1
-rw-r--r--libs/hwui/OpenGLReadback.cpp (renamed from libs/hwui/Readback.cpp)225
-rw-r--r--libs/hwui/OpenGLReadback.h61
-rw-r--r--libs/hwui/OpenGLRenderer.cpp2451
-rw-r--r--libs/hwui/OpenGLRenderer.h785
-rw-r--r--libs/hwui/PatchCache.cpp39
-rw-r--r--libs/hwui/PatchCache.h17
-rw-r--r--libs/hwui/PathCache.cpp127
-rw-r--r--libs/hwui/PathCache.h79
-rw-r--r--libs/hwui/PixelBuffer.cpp12
-rw-r--r--libs/hwui/PixelBuffer.h9
-rw-r--r--libs/hwui/ProfileRenderer.cpp40
-rw-r--r--libs/hwui/ProfileRenderer.h42
-rw-r--r--libs/hwui/Program.h41
-rw-r--r--libs/hwui/ProgramCache.cpp207
-rw-r--r--libs/hwui/ProgramCache.h3
-rw-r--r--libs/hwui/Properties.cpp30
-rw-r--r--libs/hwui/Properties.h32
-rw-r--r--libs/hwui/PropertyValuesHolder.cpp23
-rw-r--r--libs/hwui/Readback.h16
-rw-r--r--libs/hwui/RecordedOp.h38
-rw-r--r--libs/hwui/RecordingCanvas.cpp65
-rw-r--r--libs/hwui/RecordingCanvas.h42
-rw-r--r--libs/hwui/RenderNode.cpp726
-rw-r--r--libs/hwui/RenderNode.h188
-rw-r--r--libs/hwui/RenderProperties.cpp72
-rw-r--r--libs/hwui/RenderProperties.h18
-rw-r--r--libs/hwui/ShadowTessellator.h2
-rw-r--r--libs/hwui/SkiaCanvas.cpp577
-rw-r--r--libs/hwui/SkiaCanvas.h192
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp138
-rw-r--r--libs/hwui/SkiaCanvasProxy.h22
-rw-r--r--libs/hwui/SkiaShader.cpp98
-rw-r--r--libs/hwui/SkiaShader.h17
-rw-r--r--libs/hwui/Snapshot.cpp99
-rw-r--r--libs/hwui/Snapshot.h80
-rw-r--r--libs/hwui/SpotShadow.cpp15
-rw-r--r--libs/hwui/SpotShadow.h1
-rw-r--r--libs/hwui/TessellationCache.cpp16
-rw-r--r--libs/hwui/TessellationCache.h21
-rw-r--r--libs/hwui/Texture.cpp170
-rw-r--r--libs/hwui/Texture.h62
-rw-r--r--libs/hwui/TextureCache.cpp36
-rw-r--r--libs/hwui/TextureCache.h39
-rw-r--r--libs/hwui/TreeInfo.h16
-rw-r--r--libs/hwui/VectorDrawable.cpp62
-rw-r--r--libs/hwui/VectorDrawable.h24
-rw-r--r--libs/hwui/Vertex.h17
-rw-r--r--libs/hwui/VkLayer.cpp40
-rw-r--r--libs/hwui/VkLayer.h76
-rw-r--r--libs/hwui/debug/DefaultGlesDriver.cpp40
-rw-r--r--libs/hwui/debug/DefaultGlesDriver.h35
-rw-r--r--libs/hwui/debug/FatalBaseDriver.cpp40
-rw-r--r--libs/hwui/debug/FatalBaseDriver.h37
-rw-r--r--libs/hwui/debug/GlesDriver.cpp46
-rw-r--r--libs/hwui/debug/GlesDriver.h55
-rw-r--r--libs/hwui/debug/GlesErrorCheckWrapper.cpp74
-rw-r--r--libs/hwui/debug/GlesErrorCheckWrapper.h41
-rw-r--r--libs/hwui/debug/MockGlesDriver.h36
-rw-r--r--libs/hwui/debug/NullGlesDriver.cpp170
-rw-r--r--libs/hwui/debug/NullGlesDriver.h169
-rw-r--r--libs/hwui/debug/ScopedReplaceDriver.h46
-rw-r--r--libs/hwui/debug/gles_decls.in544
-rw-r--r--libs/hwui/debug/gles_redefine.h913
-rw-r--r--libs/hwui/debug/gles_stubs.in1632
-rw-r--r--libs/hwui/debug/gles_undefine.h (renamed from libs/hwui/debug/unwrap_gles.h)5
-rw-r--r--libs/hwui/debug/nullegl.cpp7
-rw-r--r--libs/hwui/debug/nullgles.cpp284
-rw-r--r--libs/hwui/debug/wrap_gles.cpp84
-rw-r--r--libs/hwui/debug/wrap_gles.h917
-rw-r--r--libs/hwui/font/CacheTexture.cpp7
-rw-r--r--libs/hwui/font/CachedGlyphInfo.h12
-rw-r--r--libs/hwui/font/Font.cpp6
-rw-r--r--libs/hwui/font/Font.h4
-rw-r--r--libs/hwui/font/FontUtil.h4
-rw-r--r--libs/hwui/hwui/Bitmap.cpp504
-rw-r--r--libs/hwui/hwui/Bitmap.h148
-rw-r--r--libs/hwui/hwui/Canvas.cpp40
-rw-r--r--libs/hwui/hwui/Canvas.h61
-rw-r--r--libs/hwui/hwui/MinikinSkia.cpp65
-rw-r--r--libs/hwui/hwui/MinikinSkia.h24
-rw-r--r--libs/hwui/hwui/MinikinUtils.cpp39
-rw-r--r--libs/hwui/hwui/MinikinUtils.h20
-rw-r--r--libs/hwui/hwui/Paint.h15
-rw-r--r--libs/hwui/hwui/PaintImpl.cpp13
-rw-r--r--libs/hwui/hwui/Typeface.cpp126
-rw-r--r--libs/hwui/hwui/Typeface.h12
-rw-r--r--libs/hwui/hwui_static_deps.mk1
-rw-r--r--libs/hwui/pipeline/skia/AnimatedDrawables.h90
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.cpp118
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.h59
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp80
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.h49
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp279
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.h155
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp708
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.h80
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp120
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.h160
-rw-r--r--libs/hwui/pipeline/skia/SkiaLayer.h39
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp195
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h58
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp121
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLReadback.h35
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp374
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h132
-rw-r--r--libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp45
-rw-r--r--libs/hwui/pipeline/skia/SkiaProfileRenderer.h42
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp248
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h93
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp160
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.h58
-rw-r--r--libs/hwui/renderstate/Blend.cpp109
-rw-r--r--libs/hwui/renderstate/Blend.h4
-rw-r--r--libs/hwui/renderstate/MeshState.cpp8
-rw-r--r--libs/hwui/renderstate/MeshState.h1
-rw-r--r--libs/hwui/renderstate/OffscreenBufferPool.cpp3
-rw-r--r--libs/hwui/renderstate/RenderState.cpp82
-rw-r--r--libs/hwui/renderstate/RenderState.h10
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp505
-rw-r--r--libs/hwui/renderthread/CanvasContext.h100
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp2
-rw-r--r--libs/hwui/renderthread/EglManager.cpp110
-rw-r--r--libs/hwui/renderthread/EglManager.h33
-rw-r--r--libs/hwui/renderthread/Frame.cpp44
-rw-r--r--libs/hwui/renderthread/Frame.h60
-rw-r--r--libs/hwui/renderthread/IRenderPipeline.h84
-rw-r--r--libs/hwui/renderthread/OpenGLPipeline.cpp266
-rw-r--r--libs/hwui/renderthread/OpenGLPipeline.h74
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp114
-rw-r--r--libs/hwui/renderthread/RenderProxy.h16
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp31
-rw-r--r--libs/hwui/renderthread/RenderThread.h17
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp700
-rw-r--r--libs/hwui/renderthread/VulkanManager.h186
-rw-r--r--libs/hwui/tests/common/BitmapAllocationTestUtils.h76
-rw-r--r--libs/hwui/tests/common/LeakChecker.cpp94
-rw-r--r--libs/hwui/tests/common/LeakChecker.h29
-rw-r--r--libs/hwui/tests/common/TestContext.cpp51
-rw-r--r--libs/hwui/tests/common/TestContext.h18
-rw-r--r--libs/hwui/tests/common/TestListViewSceneBase.cpp77
-rw-r--r--libs/hwui/tests/common/TestListViewSceneBase.h44
-rw-r--r--libs/hwui/tests/common/TestScene.h19
-rw-r--r--libs/hwui/tests/common/TestUtils.cpp120
-rw-r--r--libs/hwui/tests/common/TestUtils.h175
-rw-r--r--libs/hwui/tests/common/scenes/BitmapFillrate.cpp74
-rw-r--r--libs/hwui/tests/common/scenes/BitmapShaders.cpp74
-rw-r--r--libs/hwui/tests/common/scenes/ClippingAnimation.cpp16
-rw-r--r--libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp17
-rw-r--r--libs/hwui/tests/common/scenes/HwBitmap565.cpp49
-rw-r--r--libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp91
-rw-r--r--libs/hwui/tests/common/scenes/HwLayerAnimation.cpp8
-rw-r--r--libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp59
-rw-r--r--libs/hwui/tests/common/scenes/ListViewAnimation.cpp129
-rw-r--r--libs/hwui/tests/common/scenes/OpPropAnimation.cpp6
-rw-r--r--libs/hwui/tests/common/scenes/OvalAnimation.cpp6
-rw-r--r--libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp12
-rw-r--r--libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp63
-rw-r--r--libs/hwui/tests/common/scenes/RecentsAnimation.cpp21
-rw-r--r--libs/hwui/tests/common/scenes/RectGridAnimation.cpp8
-rw-r--r--libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp77
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp12
-rw-r--r--libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp8
-rw-r--r--libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp8
-rw-r--r--libs/hwui/tests/common/scenes/ShapeAnimation.cpp26
-rw-r--r--libs/hwui/tests/common/scenes/TestSceneBase.h7
-rw-r--r--libs/hwui/tests/common/scenes/TextAnimation.cpp6
-rw-r--r--libs/hwui/tests/macrobench/TestSceneRunner.cpp76
-rw-r--r--libs/hwui/tests/macrobench/how_to_run.txt4
-rw-r--r--libs/hwui/tests/macrobench/main.cpp86
-rw-r--r--libs/hwui/tests/microbench/DisplayListCanvasBench.cpp110
-rw-r--r--libs/hwui/tests/microbench/FrameBuilderBench.cpp10
-rw-r--r--libs/hwui/tests/microbench/RenderNodeBench.cpp33
-rw-r--r--libs/hwui/tests/microbench/TaskManagerBench.cpp2
-rwxr-xr-xlibs/hwui/tests/microbench/how_to_run.txt5
-rw-r--r--libs/hwui/tests/microbench/main.cpp18
-rwxr-xr-xlibs/hwui/tests/scripts/prep_buller.sh64
-rwxr-xr-xlibs/hwui/tests/scripts/prep_fugu.sh48
-rwxr-xr-xlibs/hwui/tests/scripts/prep_marlfish.sh45
-rw-r--r--libs/hwui/tests/scripts/prep_ryu.sh31
-rwxr-xr-xlibs/hwui/tests/scripts/prep_volantis.sh10
-rwxr-xr-xlibs/hwui/tests/scripts/stopruntime.sh27
-rw-r--r--libs/hwui/tests/unit/BakedOpDispatcherTests.cpp43
-rw-r--r--libs/hwui/tests/unit/BakedOpRendererTests.cpp2
-rw-r--r--libs/hwui/tests/unit/BitmapTests.cpp44
-rw-r--r--libs/hwui/tests/unit/CanvasContextTests.cpp55
-rw-r--r--libs/hwui/tests/unit/CanvasStateTests.cpp18
-rw-r--r--libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp59
-rw-r--r--libs/hwui/tests/unit/DeviceInfoTests.cpp3
-rw-r--r--libs/hwui/tests/unit/FatalTestCanvas.h143
-rw-r--r--libs/hwui/tests/unit/FontRendererTests.cpp2
-rw-r--r--libs/hwui/tests/unit/FrameBuilderTests.cpp706
-rw-r--r--libs/hwui/tests/unit/GlopBuilderTests.cpp16
-rw-r--r--libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp4
-rw-r--r--libs/hwui/tests/unit/GradientCacheTests.cpp2
-rw-r--r--libs/hwui/tests/unit/LeakCheckTests.cpp8
-rw-r--r--libs/hwui/tests/unit/MeshStateTests.cpp36
-rw-r--r--libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp14
-rw-r--r--libs/hwui/tests/unit/PathInterpolatorTests.cpp110
-rw-r--r--libs/hwui/tests/unit/RecordingCanvasTests.cpp152
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp949
-rw-r--r--libs/hwui/tests/unit/RenderNodeTests.cpp65
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp51
-rw-r--r--libs/hwui/tests/unit/SkiaCanvasTests.cpp4
-rw-r--r--libs/hwui/tests/unit/SkiaDisplayListTests.cpp180
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp347
-rw-r--r--libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp149
-rw-r--r--libs/hwui/tests/unit/TextDropShadowCacheTests.cpp2
-rw-r--r--libs/hwui/tests/unit/main.cpp86
-rw-r--r--libs/hwui/utils/Color.h38
-rw-r--r--libs/hwui/utils/GLUtils.h2
-rw-r--r--libs/hwui/utils/NinePatch.h37
-rw-r--r--libs/hwui/utils/NinePatchImpl.cpp326
-rw-r--r--libs/hwui/utils/PaintUtils.h27
-rw-r--r--libs/hwui/utils/StringUtils.h24
-rw-r--r--libs/hwui/utils/TestWindowContext.cpp25
-rw-r--r--libs/hwui/utils/VectorDrawableUtils.cpp4
-rw-r--r--libs/incident/Android.mk41
-rw-r--r--libs/incident/include/android/os/IncidentReportArgs.h62
-rw-r--r--libs/incident/proto/android/privacy.proto57
-rw-r--r--libs/incident/src/IncidentReportArgs.cpp172
-rw-r--r--libs/input/PointerController.cpp6
-rw-r--r--libs/input/SpriteController.cpp3
-rw-r--r--libs/services/Android.mk43
-rw-r--r--libs/services/include/android/os/DropBoxManager.h94
-rw-r--r--libs/services/src/os/DropBoxManager.cpp199
-rw-r--r--libs/storage/IMountService.cpp2
-rw-r--r--libs/storage/IMountServiceListener.cpp14
-rw-r--r--libs/storage/IMountShutdownObserver.cpp10
-rw-r--r--libs/storage/IObbActionListener.cpp2
-rw-r--r--libs/usb/tests/accessorytest/audio.c2
380 files changed, 25126 insertions, 17765 deletions
diff --git a/libs/androidfw/.clang-format b/libs/androidfw/.clang-format
new file mode 100644
index 000000000000..ee1bee2bc644
--- /dev/null
+++ b/libs/androidfw/.clang-format
@@ -0,0 +1,2 @@
+BasedOnStyle: Google
+ColumnLimit: 100
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 5d12d9507c22..fb898355db92 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -24,9 +24,14 @@ cc_library {
"-Wunreachable-code",
],
srcs: [
+ "ApkAssets.cpp",
"Asset.cpp",
"AssetDir.cpp",
"AssetManager.cpp",
+ "AssetManager2.cpp",
+ "AttributeResolution.cpp",
+ "ChunkIterator.cpp",
+ "LoadedArsc.cpp",
"LocaleData.cpp",
"misc.cpp",
"ObbFile.cpp",
@@ -64,7 +69,16 @@ cc_library {
shared: {
enabled: false,
},
- shared_libs: ["libz-host"],
+ static_libs: [
+ "libziparchive",
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libutils",
+ ],
+ shared_libs: [
+ "libz-host",
+ ],
},
windows: {
enabled: true,
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
new file mode 100644
index 000000000000..68c51effd79d
--- /dev/null
+++ b/libs/androidfw/Android.mk
@@ -0,0 +1,24 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
new file mode 100644
index 000000000000..55f4c3ce6e76
--- /dev/null
+++ b/libs/androidfw/ApkAssets.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include "androidfw/ApkAssets.h"
+
+#include "android-base/logging.h"
+#include "utils/Trace.h"
+#include "ziparchive/zip_archive.h"
+
+#include "androidfw/Asset.h"
+#include "androidfw/Util.h"
+
+namespace android {
+
+std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path) {
+ ATRACE_NAME("ApkAssets::Load");
+ ::ZipArchiveHandle unmanaged_handle;
+ int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
+ if (result != 0) {
+ LOG(ERROR) << ::ErrorCodeString(result);
+ return {};
+ }
+
+ // Wrap the handle in a unique_ptr so it gets automatically closed.
+ std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets());
+ loaded_apk->zip_handle_.reset(unmanaged_handle);
+
+ ::ZipString entry_name("resources.arsc");
+ ::ZipEntry entry;
+ result = ::FindEntry(loaded_apk->zip_handle_.get(), entry_name, &entry);
+ if (result != 0) {
+ LOG(ERROR) << ::ErrorCodeString(result);
+ return {};
+ }
+
+ if (entry.method == kCompressDeflated) {
+ LOG(WARNING) << "resources.arsc is compressed.";
+ }
+
+ loaded_apk->resources_asset_ =
+ loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER);
+ if (loaded_apk->resources_asset_ == nullptr) {
+ return {};
+ }
+
+ loaded_apk->path_ = path;
+ loaded_apk->loaded_arsc_ =
+ LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
+ loaded_apk->resources_asset_->getLength());
+ if (loaded_apk->loaded_arsc_ == nullptr) {
+ return {};
+ }
+ return loaded_apk;
+}
+
+std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode /*mode*/) const {
+ ATRACE_NAME("ApkAssets::Open");
+ CHECK(zip_handle_ != nullptr);
+
+ ::ZipString name(path.c_str());
+ ::ZipEntry entry;
+ int32_t result = ::FindEntry(zip_handle_.get(), name, &entry);
+ if (result != 0) {
+ LOG(ERROR) << "No entry '" << path << "' found in APK.";
+ return {};
+ }
+
+ if (entry.method == kCompressDeflated) {
+ auto compressed_asset = util::make_unique<_CompressedAsset>();
+ if (compressed_asset->openChunk(::GetFileDescriptor(zip_handle_.get()), entry.offset,
+ entry.method, entry.uncompressed_length,
+ entry.compressed_length) != NO_ERROR) {
+ LOG(ERROR) << "Failed to decompress '" << path << "'.";
+ return {};
+ }
+ return std::move(compressed_asset);
+ } else {
+ auto uncompressed_asset = util::make_unique<_FileAsset>();
+ if (uncompressed_asset->openChunk(path.c_str(), ::GetFileDescriptor(zip_handle_.get()),
+ entry.offset, entry.uncompressed_length) != NO_ERROR) {
+ LOG(ERROR) << "Failed to mmap '" << path << "'.";
+ return {};
+ }
+ return std::move(uncompressed_asset);
+ }
+ return {};
+}
+
+} // namespace android
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 80968e51d48c..e0689006d5dd 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -59,12 +59,6 @@ using namespace android;
static const bool kIsDebug = false;
-/*
- * Names for default app, locale, and vendor. We might want to change
- * these to be an actual locale, e.g. always use en-US as the default.
- */
-static const char* kDefaultLocale = "default";
-static const char* kDefaultVendor = "default";
static const char* kAssetsRoot = "assets";
static const char* kAppZipName = NULL; //"classes.jar";
static const char* kSystemAssets = "framework/framework-res.apk";
@@ -80,73 +74,70 @@ const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme";
+const char* AssetManager::OVERLAY_THEME_DIR_PERSIST_PROPERTY = "persist.vendor.overlay.theme";
const char* AssetManager::TARGET_PACKAGE_NAME = "android";
const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
const char* AssetManager::IDMAP_DIR = "/data/resource-cache";
namespace {
- String8 idmapPathForPackagePath(const String8& pkgPath)
- {
- const char* root = getenv("ANDROID_DATA");
- LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
- String8 path(root);
- path.appendPath(kResourceCache);
-
- char buf[256]; // 256 chars should be enough for anyone...
- strncpy(buf, pkgPath.string(), 255);
- buf[255] = '\0';
- char* filename = buf;
- while (*filename && *filename == '/') {
- ++filename;
- }
- char* p = filename;
- while (*p) {
- if (*p == '/') {
- *p = '@';
- }
- ++p;
- }
- path.appendPath(filename);
- path.append("@idmap");
- return path;
+String8 idmapPathForPackagePath(const String8& pkgPath) {
+ const char* root = getenv("ANDROID_DATA");
+ LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
+ String8 path(root);
+ path.appendPath(kResourceCache);
+
+ char buf[256]; // 256 chars should be enough for anyone...
+ strncpy(buf, pkgPath.string(), 255);
+ buf[255] = '\0';
+ char* filename = buf;
+ while (*filename && *filename == '/') {
+ ++filename;
+ }
+ char* p = filename;
+ while (*p) {
+ if (*p == '/') {
+ *p = '@';
+ }
+ ++p;
}
+ path.appendPath(filename);
+ path.append("@idmap");
- /*
- * Like strdup(), but uses C++ "new" operator instead of malloc.
- */
- static char* strdupNew(const char* str)
- {
- char* newStr;
- int len;
+ return path;
+}
+
+/*
+ * Like strdup(), but uses C++ "new" operator instead of malloc.
+ */
+static char* strdupNew(const char* str) {
+ char* newStr;
+ int len;
- if (str == NULL)
- return NULL;
+ if (str == NULL)
+ return NULL;
- len = strlen(str);
- newStr = new char[len+1];
- memcpy(newStr, str, len+1);
+ len = strlen(str);
+ newStr = new char[len+1];
+ memcpy(newStr, str, len+1);
- return newStr;
- }
+ return newStr;
}
+} // namespace
+
/*
* ===========================================================================
* AssetManager
* ===========================================================================
*/
-int32_t AssetManager::getGlobalCount()
-{
+int32_t AssetManager::getGlobalCount() {
return gCount;
}
-AssetManager::AssetManager(CacheMode cacheMode)
- : mLocale(NULL), mVendor(NULL),
- mResources(NULL), mConfig(new ResTable_config),
- mCacheMode(cacheMode), mCacheValid(false)
-{
+AssetManager::AssetManager() :
+ mLocale(NULL), mResources(NULL), mConfig(new ResTable_config) {
int count = android_atomic_inc(&gCount) + 1;
if (kIsDebug) {
ALOGI("Creating AssetManager %p #%d\n", this, count);
@@ -154,8 +145,7 @@ AssetManager::AssetManager(CacheMode cacheMode)
memset(mConfig, 0, sizeof(ResTable_config));
}
-AssetManager::~AssetManager(void)
-{
+AssetManager::~AssetManager() {
int count = android_atomic_dec(&gCount);
if (kIsDebug) {
ALOGI("Destroying AssetManager in %p #%d\n", this, count);
@@ -166,12 +156,10 @@ AssetManager::~AssetManager(void)
// don't have a String class yet, so make sure we clean up
delete[] mLocale;
- delete[] mVendor;
}
bool AssetManager::addAssetPath(
- const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset)
-{
+ const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset) {
AutoMutex _l(mLock);
asset_path ap;
@@ -346,97 +334,16 @@ String8 AssetManager::getAssetPath(const int32_t cookie) const
return String8();
}
-/*
- * Set the current locale. Use NULL to indicate no locale.
- *
- * Close and reopen Zip archives as appropriate, and reset cached
- * information in the locale-specific sections of the tree.
- */
-void AssetManager::setLocale(const char* locale)
-{
- AutoMutex _l(mLock);
- setLocaleLocked(locale);
-}
-
-
-static const char kFilPrefix[] = "fil";
-static const char kTlPrefix[] = "tl";
-
-// The sizes of the prefixes, excluding the 0 suffix.
-// char.
-static const int kFilPrefixLen = sizeof(kFilPrefix) - 1;
-static const int kTlPrefixLen = sizeof(kTlPrefix) - 1;
-
void AssetManager::setLocaleLocked(const char* locale)
{
if (mLocale != NULL) {
- /* previously set, purge cached data */
- purgeFileNameCacheLocked();
- //mZipSet.purgeLocale();
delete[] mLocale;
}
- // If we're attempting to set a locale that starts with "fil",
- // we should convert it to "tl" for backwards compatibility since
- // we've been using "tl" instead of "fil" prior to L.
- //
- // If the resource table already has entries for "fil", we use that
- // instead of attempting a fallback.
- if (strncmp(locale, kFilPrefix, kFilPrefixLen) == 0) {
- Vector<String8> locales;
- ResTable* res = mResources;
- if (res != NULL) {
- res->getLocales(&locales);
- }
- const size_t localesSize = locales.size();
- bool hasFil = false;
- for (size_t i = 0; i < localesSize; ++i) {
- if (locales[i].find(kFilPrefix) == 0) {
- hasFil = true;
- break;
- }
- }
-
-
- if (!hasFil) {
- const size_t newLocaleLen = strlen(locale);
- // This isn't a bug. We really do want mLocale to be 1 byte
- // shorter than locale, because we're replacing "fil-" with
- // "tl-".
- mLocale = new char[newLocaleLen];
- // Copy over "tl".
- memcpy(mLocale, kTlPrefix, kTlPrefixLen);
- // Copy the rest of |locale|, including the terminating '\0'.
- memcpy(mLocale + kTlPrefixLen, locale + kFilPrefixLen,
- newLocaleLen - kFilPrefixLen + 1);
- updateResourceParamsLocked();
- return;
- }
- }
-
mLocale = strdupNew(locale);
updateResourceParamsLocked();
}
-/*
- * Set the current vendor. Use NULL to indicate no vendor.
- *
- * Close and reopen Zip archives as appropriate, and reset cached
- * information in the vendor-specific sections of the tree.
- */
-void AssetManager::setVendor(const char* vendor)
-{
- AutoMutex _l(mLock);
-
- if (mVendor != NULL) {
- /* previously set, purge cached data */
- purgeFileNameCacheLocked();
- //mZipSet.purgeVendor();
- delete[] mVendor;
- }
- mVendor = strdupNew(vendor);
-}
-
void AssetManager::setConfiguration(const ResTable_config& config, const char* locale)
{
AutoMutex _l(mLock);
@@ -461,23 +368,11 @@ void AssetManager::getConfiguration(ResTable_config* outConfig) const
/*
* Open an asset.
*
- * The data could be;
- * - In a file on disk (assetBase + fileName).
- * - In a compressed file on disk (assetBase + fileName.gz).
- * - In a Zip archive, uncompressed or compressed.
+ * The data could be in any asset path. Each asset path could be:
+ * - A directory on disk.
+ * - A Zip archive, uncompressed or compressed.
*
- * It can be in a number of different directories and Zip archives.
- * The search order is:
- * - [appname]
- * - locale + vendor
- * - "default" + vendor
- * - locale + "default"
- * - "default + "default"
- * - "common"
- * - (same as above)
- *
- * To find a particular file, we have to try up to eight paths with
- * all three forms of data.
+ * If the file is in a directory, it could have a .gz suffix, meaning it is compressed.
*
* We should probably reject requests for "illegal" filenames, e.g. those
* with illegal characters or "../" backward relative paths.
@@ -488,10 +383,6 @@ Asset* AssetManager::open(const char* fileName, AccessMode mode)
LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
-
- if (mCacheMode != CACHE_OFF && !mCacheValid)
- loadFileNameCacheLocked();
-
String8 assetName(kAssetsRoot);
assetName.appendPath(fileName);
@@ -516,8 +407,7 @@ Asset* AssetManager::open(const char* fileName, AccessMode mode)
/*
* Open a non-asset file as if it were an asset.
*
- * The "fileName" is the partial path starting from the application
- * name.
+ * The "fileName" is the partial path starting from the application name.
*/
Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie)
{
@@ -525,10 +415,6 @@ Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode, int32_t
LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
-
- if (mCacheMode != CACHE_OFF && !mCacheValid)
- loadFileNameCacheLocked();
-
/*
* For each top-level asset path, search for the asset.
*/
@@ -556,9 +442,6 @@ Asset* AssetManager::openNonAsset(const int32_t cookie, const char* fileName, Ac
LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
- if (mCacheMode != CACHE_OFF && !mCacheValid)
- loadFileNameCacheLocked();
-
if (which < mAssetPaths.size()) {
ALOGV("Looking for non-asset '%s' in '%s'\n", fileName,
mAssetPaths.itemAt(which).path.string());
@@ -590,10 +473,11 @@ FileType AssetManager::getFileType(const char* fileName)
pAsset = open(fileName, Asset::ACCESS_STREAMING);
delete pAsset;
- if (pAsset == NULL)
+ if (pAsset == NULL) {
return kFileTypeNonexistent;
- else
+ } else {
return kFileTypeRegular;
+ }
}
bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const {
@@ -708,10 +592,6 @@ const ResTable* AssetManager::getResTable(bool required) const
LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
}
- if (mCacheMode != CACHE_OFF && !mCacheValid) {
- const_cast<AssetManager*>(this)->loadFileNameCacheLocked();
- }
-
mResources = new ResTable();
updateResourceParamsLocked();
@@ -831,17 +711,7 @@ void AssetManager::getLocales(Vector<String8>* locales, bool includeSystemLocale
{
ResTable* res = mResources;
if (res != NULL) {
- res->getLocales(locales, includeSystemLocales);
- }
-
- const size_t numLocales = locales->size();
- for (size_t i = 0; i < numLocales; ++i) {
- const String8& localeStr = locales->itemAt(i);
- if (localeStr.find(kTlPrefix) == 0) {
- String8 replaced("fil");
- replaced += (localeStr.string() + kTlPrefixLen);
- locales->editItemAt(i) = replaced;
- }
+ res->getLocales(locales, includeSystemLocales, true /* mergeEquivalentLangs */);
}
}
@@ -903,158 +773,6 @@ Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode m
}
/*
- * Open an asset, searching for it in the directory hierarchy for the
- * specified app.
- *
- * Pass in a NULL values for "appName" if the common app directory should
- * be used.
- */
-Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode,
- const asset_path& ap)
-{
- Asset* pAsset = NULL;
-
- /*
- * Try various combinations of locale and vendor.
- */
- if (mLocale != NULL && mVendor != NULL)
- pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor);
- if (pAsset == NULL && mVendor != NULL)
- pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor);
- if (pAsset == NULL && mLocale != NULL)
- pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL);
- if (pAsset == NULL)
- pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL);
-
- return pAsset;
-}
-
-/*
- * Open an asset, searching for it in the directory hierarchy for the
- * specified locale and vendor.
- *
- * We also search in "app.jar".
- *
- * Pass in NULL values for "appName", "locale", and "vendor" if the
- * defaults should be used.
- */
-Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode,
- const asset_path& ap, const char* locale, const char* vendor)
-{
- Asset* pAsset = NULL;
-
- if (ap.type == kFileTypeDirectory) {
- if (mCacheMode == CACHE_OFF) {
- /* look at the filesystem on disk */
- String8 path(createPathNameLocked(ap, locale, vendor));
- path.appendPath(fileName);
-
- String8 excludeName(path);
- excludeName.append(kExcludeExtension);
- if (::getFileType(excludeName.string()) != kFileTypeNonexistent) {
- /* say no more */
- //printf("+++ excluding '%s'\n", (const char*) excludeName);
- return kExcludedAsset;
- }
-
- pAsset = openAssetFromFileLocked(path, mode);
-
- if (pAsset == NULL) {
- /* try again, this time with ".gz" */
- path.append(".gz");
- pAsset = openAssetFromFileLocked(path, mode);
- }
-
- if (pAsset != NULL)
- pAsset->setAssetSource(path);
- } else {
- /* find in cache */
- String8 path(createPathNameLocked(ap, locale, vendor));
- path.appendPath(fileName);
-
- AssetDir::FileInfo tmpInfo;
- bool found = false;
-
- String8 excludeName(path);
- excludeName.append(kExcludeExtension);
-
- if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) {
- /* go no farther */
- //printf("+++ Excluding '%s'\n", (const char*) excludeName);
- return kExcludedAsset;
- }
-
- /*
- * File compression extensions (".gz") don't get stored in the
- * name cache, so we have to try both here.
- */
- if (mCache.indexOf(path) != NAME_NOT_FOUND) {
- found = true;
- pAsset = openAssetFromFileLocked(path, mode);
- if (pAsset == NULL) {
- /* try again, this time with ".gz" */
- path.append(".gz");
- pAsset = openAssetFromFileLocked(path, mode);
- }
- }
-
- if (pAsset != NULL)
- pAsset->setAssetSource(path);
-
- /*
- * Don't continue the search into the Zip files. Our cached info
- * said it was a file on disk; to be consistent with openDir()
- * we want to return the loose asset. If the cached file gets
- * removed, we fail.
- *
- * The alternative is to update our cache when files get deleted,
- * or make some sort of "best effort" promise, but for now I'm
- * taking the hard line.
- */
- if (found) {
- if (pAsset == NULL)
- ALOGD("Expected file not found: '%s'\n", path.string());
- return pAsset;
- }
- }
- }
-
- /*
- * Either it wasn't found on disk or on the cached view of the disk.
- * Dig through the currently-opened set of Zip files. If caching
- * is disabled, the Zip file may get reopened.
- */
- if (pAsset == NULL && ap.type == kFileTypeRegular) {
- String8 path;
-
- path.appendPath((locale != NULL) ? locale : kDefaultLocale);
- path.appendPath((vendor != NULL) ? vendor : kDefaultVendor);
- path.appendPath(fileName);
-
- /* check the appropriate Zip file */
- ZipFileRO* pZip = getZipFileLocked(ap);
- if (pZip != NULL) {
- //printf("GOT zip, checking '%s'\n", (const char*) path);
- ZipEntryRO entry = pZip->findEntryByName(path.string());
- if (entry != NULL) {
- //printf("FOUND in Zip file for %s/%s-%s\n",
- // appName, locale, vendor);
- pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
- pZip->releaseEntry(entry);
- }
- }
-
- if (pAsset != NULL) {
- /* create a "source" name, for debug/display */
- pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()),
- String8(""), String8(fileName)));
- }
- }
-
- return pAsset;
-}
-
-/*
* Create a "source name" for a file from a Zip archive.
*/
String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName,
@@ -1071,18 +789,6 @@ String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName,
}
/*
- * Create a path to a loose asset (asset-base/app/locale/vendor).
- */
-String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale,
- const char* vendor)
-{
- String8 path(ap.path);
- path.appendPath((locale != NULL) ? locale : kDefaultLocale);
- path.appendPath((vendor != NULL) ? vendor : kDefaultVendor);
- return path;
-}
-
-/*
* Create a path to a loose asset (asset-base/app/rootDir).
*/
String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir)
@@ -1095,15 +801,6 @@ String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* roo
/*
* Return a pointer to one of our open Zip archives. Returns NULL if no
* matching Zip file exists.
- *
- * Right now we have 2 possible Zip files (1 each in app/"common").
- *
- * If caching is set to CACHE_OFF, to get the expected behavior we
- * need to reopen the Zip file on every request. That would be silly
- * and expensive, so instead we just check the file modification date.
- *
- * Pass in NULL values for "appName", "locale", and "vendor" if the
- * generics should be used.
*/
ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap)
{
@@ -1188,14 +885,10 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile,
return pAsset;
}
-
-
/*
* Open a directory in the asset namespace.
*
- * An "asset directory" is simply the combination of all files in all
- * locations, with ".gz" stripped for loose files. With app, locale, and
- * vendor defined, we have 8 directories and 2 Zip archives to scan.
+ * An "asset directory" is simply the combination of all asset paths' "assets/" directories.
*
* Pass in "" for the root dir.
*/
@@ -1211,9 +904,6 @@ AssetDir* AssetManager::openDir(const char* dirName)
//printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
- if (mCacheMode != CACHE_OFF && !mCacheValid)
- loadFileNameCacheLocked();
-
pDir = new AssetDir;
/*
@@ -1256,9 +946,7 @@ AssetDir* AssetManager::openDir(const char* dirName)
/*
* Open a directory in the non-asset namespace.
*
- * An "asset directory" is simply the combination of all files in all
- * locations, with ".gz" stripped for loose files. With app, locale, and
- * vendor defined, we have 8 directories and 2 Zip archives to scan.
+ * An "asset directory" is simply the combination of all asset paths' "assets/" directories.
*
* Pass in "" for the root dir.
*/
@@ -1274,9 +962,6 @@ AssetDir* AssetManager::openNonAssetDir(const int32_t cookie, const char* dirNam
//printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
- if (mCacheMode != CACHE_OFF && !mCacheValid)
- loadFileNameCacheLocked();
-
pDir = new AssetDir;
pMergedInfo = new SortedVector<AssetDir::FileInfo>;
@@ -1317,74 +1002,17 @@ AssetDir* AssetManager::openNonAssetDir(const int32_t cookie, const char* dirNam
bool AssetManager::scanAndMergeDirLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
const asset_path& ap, const char* rootDir, const char* dirName)
{
- SortedVector<AssetDir::FileInfo>* pContents;
- String8 path;
-
assert(pMergedInfo != NULL);
- //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName);
+ //printf("scanAndMergeDir: %s %s %s\n", ap.path.string(), rootDir, dirName);
- if (mCacheValid) {
- int i, start, count;
+ String8 path = createPathNameLocked(ap, rootDir);
+ if (dirName[0] != '\0')
+ path.appendPath(dirName);
- pContents = new SortedVector<AssetDir::FileInfo>;
-
- /*
- * Get the basic partial path and find it in the cache. That's
- * the start point for the search.
- */
- path = createPathNameLocked(ap, rootDir);
- if (dirName[0] != '\0')
- path.appendPath(dirName);
-
- start = mCache.indexOf(path);
- if (start == NAME_NOT_FOUND) {
- //printf("+++ not found in cache: dir '%s'\n", (const char*) path);
- delete pContents;
- return false;
- }
-
- /*
- * The match string looks like "common/default/default/foo/bar/".
- * The '/' on the end ensures that we don't match on the directory
- * itself or on ".../foo/barfy/".
- */
- path.append("/");
-
- count = mCache.size();
-
- /*
- * Pick out the stuff in the current dir by examining the pathname.
- * It needs to match the partial pathname prefix, and not have a '/'
- * (fssep) anywhere after the prefix.
- */
- for (i = start+1; i < count; i++) {
- if (mCache[i].getFileName().length() > path.length() &&
- strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0)
- {
- const char* name = mCache[i].getFileName().string();
- // XXX THIS IS BROKEN! Looks like we need to store the full
- // path prefix separately from the file path.
- if (strchr(name + path.length(), '/') == NULL) {
- /* grab it, reducing path to just the filename component */
- AssetDir::FileInfo tmp = mCache[i];
- tmp.setFileName(tmp.getFileName().getPathLeaf());
- pContents->add(tmp);
- }
- } else {
- /* no longer in the dir or its subdirs */
- break;
- }
-
- }
- } else {
- path = createPathNameLocked(ap, rootDir);
- if (dirName[0] != '\0')
- path.appendPath(dirName);
- pContents = scanDirLocked(path);
- if (pContents == NULL)
- return false;
- }
+ SortedVector<AssetDir::FileInfo>* pContents = scanDirLocked(path);
+ if (pContents == NULL)
+ return false;
// if we wanted to do an incremental cache fill, we would do it here
@@ -1711,153 +1339,6 @@ void AssetManager::mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo
#endif
}
-
-/*
- * Load all files into the file name cache. We want to do this across
- * all combinations of { appname, locale, vendor }, performing a recursive
- * directory traversal.
- *
- * This is not the most efficient data structure. Also, gathering the
- * information as we needed it (file-by-file or directory-by-directory)
- * would be faster. However, on the actual device, 99% of the files will
- * live in Zip archives, so this list will be very small. The trouble
- * is that we have to check the "loose" files first, so it's important
- * that we don't beat the filesystem silly looking for files that aren't
- * there.
- *
- * Note on thread safety: this is the only function that causes updates
- * to mCache, and anybody who tries to use it will call here if !mCacheValid,
- * so we need to employ a mutex here.
- */
-void AssetManager::loadFileNameCacheLocked(void)
-{
- assert(!mCacheValid);
- assert(mCache.size() == 0);
-
-#ifdef DO_TIMINGS // need to link against -lrt for this now
- DurationTimer timer;
- timer.start();
-#endif
-
- fncScanLocked(&mCache, "");
-
-#ifdef DO_TIMINGS
- timer.stop();
- ALOGD("Cache scan took %.3fms\n",
- timer.durationUsecs() / 1000.0);
-#endif
-
-#if 0
- int i;
- printf("CACHED FILE LIST (%d entries):\n", mCache.size());
- for (i = 0; i < (int) mCache.size(); i++) {
- printf(" %d: (%d) '%s'\n", i,
- mCache.itemAt(i).getFileType(),
- (const char*) mCache.itemAt(i).getFileName());
- }
-#endif
-
- mCacheValid = true;
-}
-
-/*
- * Scan up to 8 versions of the specified directory.
- */
-void AssetManager::fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
- const char* dirName)
-{
- size_t i = mAssetPaths.size();
- while (i > 0) {
- i--;
- const asset_path& ap = mAssetPaths.itemAt(i);
- fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName);
- if (mLocale != NULL)
- fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName);
- if (mVendor != NULL)
- fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName);
- if (mLocale != NULL && mVendor != NULL)
- fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName);
- }
-}
-
-/*
- * Recursively scan this directory and all subdirs.
- *
- * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE
- * files, and we prepend the extended partial path to the filenames.
- */
-bool AssetManager::fncScanAndMergeDirLocked(
- SortedVector<AssetDir::FileInfo>* pMergedInfo,
- const asset_path& ap, const char* locale, const char* vendor,
- const char* dirName)
-{
- SortedVector<AssetDir::FileInfo>* pContents;
- String8 partialPath;
- String8 fullPath;
-
- // XXX This is broken -- the filename cache needs to hold the base
- // asset path separately from its filename.
-
- partialPath = createPathNameLocked(ap, locale, vendor);
- if (dirName[0] != '\0') {
- partialPath.appendPath(dirName);
- }
-
- fullPath = partialPath;
- pContents = scanDirLocked(fullPath);
- if (pContents == NULL) {
- return false; // directory did not exist
- }
-
- /*
- * Scan all subdirectories of the current dir, merging what we find
- * into "pMergedInfo".
- */
- for (int i = 0; i < (int) pContents->size(); i++) {
- if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) {
- String8 subdir(dirName);
- subdir.appendPath(pContents->itemAt(i).getFileName());
-
- fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string());
- }
- }
-
- /*
- * To be consistent, we want entries for the root directory. If
- * we're the root, add one now.
- */
- if (dirName[0] == '\0') {
- AssetDir::FileInfo tmpInfo;
-
- tmpInfo.set(String8(""), kFileTypeDirectory);
- tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor));
- pContents->add(tmpInfo);
- }
-
- /*
- * We want to prepend the extended partial path to every entry in
- * "pContents". It's the same value for each entry, so this will
- * not change the sorting order of the vector contents.
- */
- for (int i = 0; i < (int) pContents->size(); i++) {
- const AssetDir::FileInfo& info = pContents->itemAt(i);
- pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName()));
- }
-
- mergeInfoLocked(pMergedInfo, pContents);
- delete pContents;
- return true;
-}
-
-/*
- * Trash the cache.
- */
-void AssetManager::purgeFileNameCacheLocked(void)
-{
- mCacheValid = false;
- mCache.clear();
-}
-
/*
* ===========================================================================
* AssetManager::SharedZip
@@ -1991,13 +1472,6 @@ AssetManager::SharedZip::~SharedZip()
*/
/*
- * Constructor.
- */
-AssetManager::ZipSet::ZipSet(void)
-{
-}
-
-/*
* Destructor. Close any open archives.
*/
AssetManager::ZipSet::~ZipSet(void)
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
new file mode 100644
index 000000000000..8d65925a218e
--- /dev/null
+++ b/libs/androidfw/AssetManager2.cpp
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include "androidfw/AssetManager2.h"
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "utils/ByteOrder.h"
+#include "utils/Trace.h"
+
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+namespace android {
+
+AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); }
+
+bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets,
+ bool invalidate_caches) {
+ apk_assets_ = apk_assets;
+ if (invalidate_caches) {
+ InvalidateCaches(static_cast<uint32_t>(-1));
+ }
+ return true;
+}
+
+const std::vector<const ApkAssets*> AssetManager2::GetApkAssets() const { return apk_assets_; }
+
+const ResStringPool* AssetManager2::GetStringPoolForCookie(ApkAssetsCookie cookie) const {
+ if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
+ return nullptr;
+ }
+ return apk_assets_[cookie]->GetLoadedArsc()->GetStringPool();
+}
+
+void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
+ const int diff = configuration_.diff(configuration);
+ configuration_ = configuration;
+
+ if (diff) {
+ InvalidateCaches(static_cast<uint32_t>(diff));
+ }
+}
+
+const ResTable_config& AssetManager2::GetConfiguration() const { return configuration_; }
+
+std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) {
+ const std::string new_path = "assets/" + filename;
+ return OpenNonAsset(new_path, mode);
+}
+
+std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie,
+ Asset::AccessMode mode) {
+ const std::string new_path = "assets/" + filename;
+ return OpenNonAsset(new_path, cookie, mode);
+}
+
+// Search in reverse because that's how we used to do it and we need to preserve behaviour.
+// This is unfortunate, because ClassLoaders delegate to the parent first, so the order
+// is inconsistent for split APKs.
+std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
+ Asset::AccessMode mode,
+ ApkAssetsCookie* out_cookie) {
+ ATRACE_CALL();
+ for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
+ std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
+ if (asset) {
+ if (out_cookie != nullptr) {
+ *out_cookie = i;
+ }
+ return asset;
+ }
+ }
+
+ if (out_cookie != nullptr) {
+ *out_cookie = kInvalidCookie;
+ }
+ return {};
+}
+
+std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
+ ApkAssetsCookie cookie, Asset::AccessMode mode) {
+ ATRACE_CALL();
+ if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
+ return {};
+ }
+ return apk_assets_[cookie]->Open(filename, mode);
+}
+
+ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
+ bool stop_at_first_match, LoadedArsc::Entry* out_entry,
+ ResTable_config* out_selected_config,
+ uint32_t* out_flags) {
+ ATRACE_CALL();
+
+ // Might use this if density_override != 0.
+ ResTable_config density_override_config;
+
+ // Select our configuration or generate a density override configuration.
+ ResTable_config* desired_config = &configuration_;
+ if (density_override != 0 && density_override != configuration_.density) {
+ density_override_config = configuration_;
+ density_override_config.density = density_override;
+ desired_config = &density_override_config;
+ }
+
+ LoadedArsc::Entry best_entry;
+ ResTable_config best_config;
+ int32_t best_index = -1;
+ uint32_t cumulated_flags = 0;
+
+ const size_t apk_asset_count = apk_assets_.size();
+ for (size_t i = 0; i < apk_asset_count; i++) {
+ const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc();
+
+ LoadedArsc::Entry current_entry;
+ ResTable_config current_config;
+ uint32_t flags = 0;
+ if (!loaded_arsc->FindEntry(resid, *desired_config, &current_entry, &current_config, &flags)) {
+ continue;
+ }
+
+ cumulated_flags |= flags;
+
+ if (best_index == -1 || current_config.isBetterThan(best_config, desired_config)) {
+ best_entry = current_entry;
+ best_config = current_config;
+ best_index = static_cast<int32_t>(i);
+ if (stop_at_first_match) {
+ break;
+ }
+ }
+ }
+
+ if (best_index == -1) {
+ return kInvalidCookie;
+ }
+
+ *out_entry = best_entry;
+ *out_selected_config = best_config;
+ *out_flags = cumulated_flags;
+ return best_index;
+}
+
+bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
+ ATRACE_CALL();
+
+ LoadedArsc::Entry entry;
+ ResTable_config config;
+ uint32_t flags = 0u;
+ ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+ true /* stop_at_first_match */, &entry, &config, &flags);
+ if (cookie == kInvalidCookie) {
+ return false;
+ }
+
+ const std::string* package_name =
+ apk_assets_[cookie]->GetLoadedArsc()->GetPackageNameForId(resid);
+ if (package_name == nullptr) {
+ return false;
+ }
+
+ out_name->package = package_name->data();
+ out_name->package_len = package_name->size();
+
+ out_name->type = entry.type_string_ref.string8(&out_name->type_len);
+ out_name->type16 = nullptr;
+ if (out_name->type == nullptr) {
+ out_name->type16 = entry.type_string_ref.string16(&out_name->type_len);
+ if (out_name->type16 == nullptr) {
+ return false;
+ }
+ }
+
+ out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len);
+ out_name->entry16 = nullptr;
+ if (out_name->entry == nullptr) {
+ out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len);
+ if (out_name->entry16 == nullptr) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
+ LoadedArsc::Entry entry;
+ ResTable_config config;
+ ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+ false /* stop_at_first_match */, &entry, &config, out_flags);
+ return cookie != kInvalidCookie;
+}
+
+ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
+ uint16_t density_override, Res_value* out_value,
+ ResTable_config* out_selected_config,
+ uint32_t* out_flags) {
+ ATRACE_CALL();
+
+ LoadedArsc::Entry entry;
+ ResTable_config config;
+ uint32_t flags = 0u;
+ ApkAssetsCookie cookie =
+ FindEntry(resid, density_override, false /* stop_at_first_match */, &entry, &config, &flags);
+ if (cookie == kInvalidCookie) {
+ return kInvalidCookie;
+ }
+
+ if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
+ if (!may_be_bag) {
+ LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
+ }
+ return kInvalidCookie;
+ }
+
+ const Res_value* device_value = reinterpret_cast<const Res_value*>(
+ reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size));
+ out_value->copyFrom_dtoh(*device_value);
+ *out_selected_config = config;
+ *out_flags = flags;
+ return cookie;
+}
+
+const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
+ ATRACE_CALL();
+
+ auto cached_iter = cached_bags_.find(resid);
+ if (cached_iter != cached_bags_.end()) {
+ return cached_iter->second.get();
+ }
+
+ LoadedArsc::Entry entry;
+ ResTable_config config;
+ uint32_t flags = 0u;
+ ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
+ false /* stop_at_first_match */, &entry, &config, &flags);
+ if (cookie == kInvalidCookie) {
+ return nullptr;
+ }
+
+ // Check that the size of the entry header is at least as big as
+ // the desired ResTable_map_entry. Also verify that the entry
+ // was intended to be a map.
+ if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) ||
+ (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
+ // Not a bag, nothing to do.
+ return nullptr;
+ }
+
+ const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry);
+ const ResTable_map* map_entry =
+ reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
+ const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
+
+ const uint32_t parent = dtohl(map->parent.ident);
+ if (parent == 0) {
+ // There is no parent, meaning there is nothing to inherit and we can do a simple
+ // copy of the entries in the map.
+ const size_t entry_count = map_entry_end - map_entry;
+ util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
+ malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
+ ResolvedBag::Entry* new_entry = new_bag->entries;
+ for (; map_entry != map_entry_end; ++map_entry) {
+ new_entry->cookie = cookie;
+ new_entry->value.copyFrom_dtoh(map_entry->value);
+ new_entry->key = dtohl(map_entry->name.ident);
+ new_entry->key_pool = nullptr;
+ new_entry->type_pool = nullptr;
+ ++new_entry;
+ }
+ new_bag->type_spec_flags = flags;
+ new_bag->entry_count = static_cast<uint32_t>(entry_count);
+ ResolvedBag* result = new_bag.get();
+ cached_bags_[resid] = std::move(new_bag);
+ return result;
+ }
+
+ // Get the parent and do a merge of the keys.
+ const ResolvedBag* parent_bag = GetBag(parent);
+ if (parent_bag == nullptr) {
+ // Failed to get the parent that should exist.
+ return nullptr;
+ }
+
+ // Combine flags from the parent and our own bag.
+ flags |= parent_bag->type_spec_flags;
+
+ // Create the max possible entries we can make. Once we construct the bag,
+ // we will realloc to fit to size.
+ const size_t max_count = parent_bag->entry_count + dtohl(map->count);
+ ResolvedBag* new_bag = reinterpret_cast<ResolvedBag*>(
+ malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))));
+ ResolvedBag::Entry* new_entry = new_bag->entries;
+
+ const ResolvedBag::Entry* parent_entry = parent_bag->entries;
+ const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count;
+
+ // The keys are expected to be in sorted order. Merge the two bags.
+ while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
+ const uint32_t child_key = dtohl(map_entry->name.ident);
+ if (child_key <= parent_entry->key) {
+ // Use the child key if it comes before the parent
+ // or is equal to the parent (overrides).
+ new_entry->cookie = cookie;
+ new_entry->value.copyFrom_dtoh(map_entry->value);
+ new_entry->key = child_key;
+ new_entry->key_pool = nullptr;
+ new_entry->type_pool = nullptr;
+ ++map_entry;
+ } else {
+ // Take the parent entry as-is.
+ memcpy(new_entry, parent_entry, sizeof(*new_entry));
+ }
+
+ if (child_key >= parent_entry->key) {
+ // Move to the next parent entry if we used it or it was overridden.
+ ++parent_entry;
+ }
+ // Increment to the next entry to fill.
+ ++new_entry;
+ }
+
+ // Finish the child entries if they exist.
+ while (map_entry != map_entry_end) {
+ new_entry->cookie = cookie;
+ new_entry->value.copyFrom_dtoh(map_entry->value);
+ new_entry->key = dtohl(map_entry->name.ident);
+ new_entry->key_pool = nullptr;
+ new_entry->type_pool = nullptr;
+ ++map_entry;
+ ++new_entry;
+ }
+
+ // Finish the parent entries if they exist.
+ if (parent_entry != parent_entry_end) {
+ // Take the rest of the parent entries as-is.
+ const size_t num_entries_to_copy = parent_entry_end - parent_entry;
+ memcpy(new_entry, parent_entry, num_entries_to_copy * sizeof(*new_entry));
+ new_entry += num_entries_to_copy;
+ }
+
+ // Resize the resulting array to fit.
+ const size_t actual_count = new_entry - new_bag->entries;
+ if (actual_count != max_count) {
+ new_bag = reinterpret_cast<ResolvedBag*>(
+ realloc(new_bag, sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry))));
+ }
+
+ util::unique_cptr<ResolvedBag> final_bag{new_bag};
+ final_bag->type_spec_flags = flags;
+ final_bag->entry_count = static_cast<uint32_t>(actual_count);
+ ResolvedBag* result = final_bag.get();
+ cached_bags_[resid] = std::move(final_bag);
+ return result;
+}
+
+void AssetManager2::InvalidateCaches(uint32_t diff) {
+ if (diff == 0xffffffffu) {
+ // Everything must go.
+ cached_bags_.clear();
+ return;
+ }
+
+ // Be more conservative with what gets purged. Only if the bag has other possible
+ // variations with respect to what changed (diff) should we remove it.
+ for (auto iter = cached_bags_.cbegin(); iter != cached_bags_.cend();) {
+ if (diff & iter->second->type_spec_flags) {
+ iter = cached_bags_.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); }
+
+bool Theme::ApplyStyle(uint32_t resid, bool force) {
+ ATRACE_CALL();
+
+ const ResolvedBag* bag = asset_manager_->GetBag(resid);
+ if (bag == nullptr) {
+ return false;
+ }
+
+ // Merge the flags from this style.
+ type_spec_flags_ |= bag->type_spec_flags;
+
+ // On the first iteration, verify the attribute IDs and
+ // update the entry count in each type.
+ const auto bag_iter_end = end(bag);
+ for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) {
+ const uint32_t attr_resid = bag_iter->key;
+
+ // If the resource ID passed in is not a style, the key can be
+ // some other identifier that is not a resource ID.
+ if (!util::is_valid_resid(attr_resid)) {
+ return false;
+ }
+
+ const uint32_t package_idx = util::get_package_id(attr_resid);
+
+ // The type ID is 1-based, so subtract 1 to get an index.
+ const uint32_t type_idx = util::get_type_id(attr_resid) - 1;
+ const uint32_t entry_idx = util::get_entry_id(attr_resid);
+
+ std::unique_ptr<Package>& package = packages_[package_idx];
+ if (package == nullptr) {
+ package.reset(new Package());
+ }
+
+ util::unique_cptr<Type>& type = package->types[type_idx];
+ if (type == nullptr) {
+ // Set the initial capacity to take up a total amount of 1024 bytes.
+ constexpr uint32_t kInitialCapacity = (1024u - sizeof(Type)) / sizeof(Entry);
+ const uint32_t initial_capacity = std::max(entry_idx, kInitialCapacity);
+ type.reset(
+ reinterpret_cast<Type*>(calloc(sizeof(Type) + (initial_capacity * sizeof(Entry)), 1)));
+ type->entry_capacity = initial_capacity;
+ }
+
+ // Set the entry_count to include this entry. We will populate
+ // and resize the array as necessary in the next pass.
+ if (entry_idx + 1 > type->entry_count) {
+ // Increase the entry count to include this.
+ type->entry_count = entry_idx + 1;
+ }
+ }
+
+ // On the second pass, we will realloc to fit the entry counts
+ // and populate the structures.
+ for (auto bag_iter = begin(bag); bag_iter != bag_iter_end; ++bag_iter) {
+ const uint32_t attr_resid = bag_iter->key;
+ const uint32_t package_idx = util::get_package_id(attr_resid);
+ const uint32_t type_idx = util::get_type_id(attr_resid) - 1;
+ const uint32_t entry_idx = util::get_entry_id(attr_resid);
+ Package* package = packages_[package_idx].get();
+ util::unique_cptr<Type>& type = package->types[type_idx];
+ if (type->entry_count != type->entry_capacity) {
+ // Resize to fit the actual entries that will be included.
+ Type* type_ptr = type.release();
+ type.reset(reinterpret_cast<Type*>(
+ realloc(type_ptr, sizeof(Type) + (type_ptr->entry_count * sizeof(Entry)))));
+ if (type->entry_capacity < type->entry_count) {
+ // Clear the newly allocated memory (which does not get zero initialized).
+ // We need to do this because we |= type_spec_flags.
+ memset(type->entries + type->entry_capacity, 0,
+ sizeof(Entry) * (type->entry_count - type->entry_capacity));
+ }
+ type->entry_capacity = type->entry_count;
+ }
+ Entry& entry = type->entries[entry_idx];
+ if (force || entry.value.dataType == Res_value::TYPE_NULL) {
+ entry.cookie = bag_iter->cookie;
+ entry.type_spec_flags |= bag->type_spec_flags;
+ entry.value = bag_iter->value;
+ }
+ }
+ return true;
+}
+
+ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value,
+ uint32_t* out_flags) const {
+ constexpr const int kMaxIterations = 20;
+
+ uint32_t type_spec_flags = 0u;
+
+ for (int iterations_left = kMaxIterations; iterations_left > 0; iterations_left--) {
+ if (!util::is_valid_resid(resid)) {
+ return kInvalidCookie;
+ }
+
+ const uint32_t package_idx = util::get_package_id(resid);
+
+ // Type ID is 1-based, subtract 1 to get the index.
+ const uint32_t type_idx = util::get_type_id(resid) - 1;
+ const uint32_t entry_idx = util::get_entry_id(resid);
+
+ const Package* package = packages_[package_idx].get();
+ if (package == nullptr) {
+ return kInvalidCookie;
+ }
+
+ const Type* type = package->types[type_idx].get();
+ if (type == nullptr) {
+ return kInvalidCookie;
+ }
+
+ if (entry_idx >= type->entry_count) {
+ return kInvalidCookie;
+ }
+
+ const Entry& entry = type->entries[entry_idx];
+ type_spec_flags |= entry.type_spec_flags;
+
+ switch (entry.value.dataType) {
+ case Res_value::TYPE_ATTRIBUTE:
+ resid = entry.value.data;
+ break;
+
+ case Res_value::TYPE_NULL:
+ return kInvalidCookie;
+
+ default:
+ *out_value = entry.value;
+ if (out_flags != nullptr) {
+ *out_flags = type_spec_flags;
+ }
+ return entry.cookie;
+ }
+ }
+
+ LOG(WARNING) << base::StringPrintf("Too many (%d) attribute references, stopped at: 0x%08x",
+ kMaxIterations, resid);
+ return kInvalidCookie;
+}
+
+void Theme::Clear() {
+ type_spec_flags_ = 0u;
+ for (std::unique_ptr<Package>& package : packages_) {
+ package.reset();
+ }
+}
+
+bool Theme::SetTo(const Theme& o) {
+ if (this == &o) {
+ return true;
+ }
+
+ if (asset_manager_ != o.asset_manager_) {
+ return false;
+ }
+
+ type_spec_flags_ = o.type_spec_flags_;
+
+ for (size_t p = 0; p < arraysize(packages_); p++) {
+ const Package* package = o.packages_[p].get();
+ if (package == nullptr) {
+ packages_[p].reset();
+ continue;
+ }
+
+ for (size_t t = 0; t < arraysize(package->types); t++) {
+ const Type* type = package->types[t].get();
+ if (type == nullptr) {
+ packages_[p]->types[t].reset();
+ continue;
+ }
+
+ const size_t type_alloc_size = sizeof(Type) + (type->entry_capacity * sizeof(Entry));
+ void* copied_data = malloc(type_alloc_size);
+ memcpy(copied_data, type, type_alloc_size);
+ packages_[p]->types[t].reset(reinterpret_cast<Type*>(copied_data));
+ }
+ }
+ return true;
+}
+
+} // namespace android
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
new file mode 100644
index 000000000000..2771ade1dd12
--- /dev/null
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/AttributeResolution.h"
+
+#include <cstdint>
+
+#include <log/log.h>
+
+#include "androidfw/AttributeFinder.h"
+#include "androidfw/ResourceTypes.h"
+
+constexpr bool kDebugStyles = false;
+
+namespace android {
+
+class XmlAttributeFinder
+ : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
+ public:
+ explicit XmlAttributeFinder(const ResXMLParser* parser)
+ : BackTrackingAttributeFinder(
+ 0, parser != nullptr ? parser->getAttributeCount() : 0),
+ parser_(parser) {}
+
+ inline uint32_t GetAttribute(size_t index) const {
+ return parser_->getAttributeNameResID(index);
+ }
+
+ private:
+ const ResXMLParser* parser_;
+};
+
+class BagAttributeFinder
+ : public BackTrackingAttributeFinder<BagAttributeFinder,
+ const ResTable::bag_entry*> {
+ public:
+ BagAttributeFinder(const ResTable::bag_entry* start,
+ const ResTable::bag_entry* end)
+ : BackTrackingAttributeFinder(start, end) {}
+
+ inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const {
+ return entry->map.name.ident;
+ }
+};
+
+bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
+ uint32_t def_style_res, uint32_t* src_values,
+ size_t src_values_length, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values,
+ uint32_t* out_indices) {
+ if (kDebugStyles) {
+ ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme,
+ def_style_attr, def_style_res);
+ }
+
+ const ResTable& res = theme->getResTable();
+ ResTable_config config;
+ Res_value value;
+
+ int indices_idx = 0;
+
+ // Load default style from attribute, if specified...
+ uint32_t def_style_bag_type_set_flags = 0;
+ if (def_style_attr != 0) {
+ Res_value value;
+ if (theme->getAttribute(def_style_attr, &value,
+ &def_style_bag_type_set_flags) >= 0) {
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ def_style_res = value.data;
+ }
+ }
+ }
+
+ // Now lock down the resource object and start pulling stuff from it.
+ res.lock();
+
+ // Retrieve the default style bag, if requested.
+ const ResTable::bag_entry* def_style_start = nullptr;
+ uint32_t def_style_type_set_flags = 0;
+ ssize_t bag_off = def_style_res != 0
+ ? res.getBagLocked(def_style_res, &def_style_start,
+ &def_style_type_set_flags)
+ : -1;
+ def_style_type_set_flags |= def_style_bag_type_set_flags;
+ const ResTable::bag_entry* const def_style_end =
+ def_style_start + (bag_off >= 0 ? bag_off : 0);
+ BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end);
+
+ // Now iterate through all of the attributes that the client has requested,
+ // filling in each with whatever data we can find.
+ for (size_t ii = 0; ii < attrs_length; ii++) {
+ const uint32_t cur_ident = attrs[ii];
+
+ if (kDebugStyles) {
+ ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
+ }
+
+ ssize_t block = -1;
+ uint32_t type_set_flags = 0;
+
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ config.density = 0;
+
+ // Try to find a value for this attribute... we prioritize values
+ // coming from, first XML attributes, then XML style, then default
+ // style, and finally the theme.
+
+ // Retrieve the current input value if available.
+ if (src_values_length > 0 && src_values[ii] != 0) {
+ value.dataType = Res_value::TYPE_ATTRIBUTE;
+ value.data = src_values[ii];
+ if (kDebugStyles) {
+ ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
+ }
+ }
+
+ if (value.dataType == Res_value::TYPE_NULL) {
+ const ResTable::bag_entry* const def_style_entry =
+ def_style_attr_finder.Find(cur_ident);
+ if (def_style_entry != def_style_end) {
+ block = def_style_entry->stringBlock;
+ type_set_flags = def_style_type_set_flags;
+ value = def_style_entry->map.value;
+ if (kDebugStyles) {
+ ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
+ }
+ }
+ }
+
+ uint32_t resid = 0;
+ if (value.dataType != Res_value::TYPE_NULL) {
+ // Take care of resolving the found resource to its final value.
+ ssize_t new_block = theme->resolveAttributeReference(
+ &value, block, &resid, &type_set_flags, &config);
+ if (new_block >= 0) block = new_block;
+ if (kDebugStyles) {
+ ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
+ }
+ } else {
+ // If we still don't have a value for this attribute, try to find
+ // it in the theme!
+ ssize_t new_block =
+ theme->getAttribute(cur_ident, &value, &type_set_flags);
+ if (new_block >= 0) {
+ if (kDebugStyles) {
+ ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
+ }
+ new_block = res.resolveReference(&value, new_block, &resid,
+ &type_set_flags, &config);
+ if (new_block >= 0) block = new_block;
+ if (kDebugStyles) {
+ ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
+ }
+ }
+ }
+
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ if (kDebugStyles) {
+ ALOGI("-> Setting to @null!");
+ }
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ block = -1;
+ }
+
+ if (kDebugStyles) {
+ ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident,
+ value.dataType, value.data);
+ }
+
+ // Write the final value back to Java.
+ out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_DATA] = value.data;
+ out_values[STYLE_ASSET_COOKIE] =
+ block != -1 ? static_cast<uint32_t>(res.getTableCookie(block))
+ : static_cast<uint32_t>(-1);
+ out_values[STYLE_RESOURCE_ID] = resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
+ out_values[STYLE_DENSITY] = config.density;
+
+ if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) {
+ indices_idx++;
+ out_indices[indices_idx] = ii;
+ }
+
+ out_values += STYLE_NUM_ENTRIES;
+ }
+
+ res.unlock();
+
+ if (out_indices != nullptr) {
+ out_indices[0] = indices_idx;
+ }
+ return true;
+}
+
+void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length,
+ uint32_t* out_values, uint32_t* out_indices) {
+ if (kDebugStyles) {
+ ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p",
+ theme, def_style_attr, def_style_res, xml_parser);
+ }
+
+ const ResTable& res = theme->getResTable();
+ ResTable_config config;
+ Res_value value;
+
+ int indices_idx = 0;
+
+ // Load default style from attribute, if specified...
+ uint32_t def_style_bag_type_set_flags = 0;
+ if (def_style_attr != 0) {
+ Res_value value;
+ if (theme->getAttribute(def_style_attr, &value,
+ &def_style_bag_type_set_flags) >= 0) {
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ def_style_res = value.data;
+ }
+ }
+ }
+
+ // Retrieve the style class associated with the current XML tag.
+ int style = 0;
+ uint32_t style_bag_type_set_flags = 0;
+ if (xml_parser != nullptr) {
+ ssize_t idx = xml_parser->indexOfStyle();
+ if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
+ if (value.dataType == value.TYPE_ATTRIBUTE) {
+ if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) <
+ 0) {
+ value.dataType = Res_value::TYPE_NULL;
+ }
+ }
+ if (value.dataType == value.TYPE_REFERENCE) {
+ style = value.data;
+ }
+ }
+ }
+
+ // Now lock down the resource object and start pulling stuff from it.
+ res.lock();
+
+ // Retrieve the default style bag, if requested.
+ const ResTable::bag_entry* def_style_attr_start = nullptr;
+ uint32_t def_style_type_set_flags = 0;
+ ssize_t bag_off = def_style_res != 0
+ ? res.getBagLocked(def_style_res, &def_style_attr_start,
+ &def_style_type_set_flags)
+ : -1;
+ def_style_type_set_flags |= def_style_bag_type_set_flags;
+ const ResTable::bag_entry* const def_style_attr_end =
+ def_style_attr_start + (bag_off >= 0 ? bag_off : 0);
+ BagAttributeFinder def_style_attr_finder(def_style_attr_start,
+ def_style_attr_end);
+
+ // Retrieve the style class bag, if requested.
+ const ResTable::bag_entry* style_attr_start = nullptr;
+ uint32_t style_type_set_flags = 0;
+ bag_off =
+ style != 0
+ ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags)
+ : -1;
+ style_type_set_flags |= style_bag_type_set_flags;
+ const ResTable::bag_entry* const style_attr_end =
+ style_attr_start + (bag_off >= 0 ? bag_off : 0);
+ BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end);
+
+ // Retrieve the XML attributes, if requested.
+ static const ssize_t kXmlBlock = 0x10000000;
+ XmlAttributeFinder xml_attr_finder(xml_parser);
+ const size_t xml_attr_end =
+ xml_parser != nullptr ? xml_parser->getAttributeCount() : 0;
+
+ // Now iterate through all of the attributes that the client has requested,
+ // filling in each with whatever data we can find.
+ for (size_t ii = 0; ii < attrs_length; ii++) {
+ const uint32_t cur_ident = attrs[ii];
+
+ if (kDebugStyles) {
+ ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
+ }
+
+ ssize_t block = kXmlBlock;
+ uint32_t type_set_flags = 0;
+
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ config.density = 0;
+
+ // Try to find a value for this attribute... we prioritize values
+ // coming from, first XML attributes, then XML style, then default
+ // style, and finally the theme.
+
+ // Walk through the xml attributes looking for the requested attribute.
+ const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
+ if (xml_attr_idx != xml_attr_end) {
+ // We found the attribute we were looking for.
+ xml_parser->getAttributeValue(xml_attr_idx, &value);
+ if (kDebugStyles) {
+ ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
+ }
+ }
+
+ if (value.dataType == Res_value::TYPE_NULL) {
+ // Walk through the style class values looking for the requested
+ // attribute.
+ const ResTable::bag_entry* const style_attr_entry =
+ style_attr_finder.Find(cur_ident);
+ if (style_attr_entry != style_attr_end) {
+ // We found the attribute we were looking for.
+ block = style_attr_entry->stringBlock;
+ type_set_flags = style_type_set_flags;
+ value = style_attr_entry->map.value;
+ if (kDebugStyles) {
+ ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
+ }
+ }
+ }
+
+ if (value.dataType == Res_value::TYPE_NULL) {
+ // Walk through the default style values looking for the requested
+ // attribute.
+ const ResTable::bag_entry* const def_style_attr_entry =
+ def_style_attr_finder.Find(cur_ident);
+ if (def_style_attr_entry != def_style_attr_end) {
+ // We found the attribute we were looking for.
+ block = def_style_attr_entry->stringBlock;
+ type_set_flags = style_type_set_flags;
+ value = def_style_attr_entry->map.value;
+ if (kDebugStyles) {
+ ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
+ }
+ }
+ }
+
+ uint32_t resid = 0;
+ if (value.dataType != Res_value::TYPE_NULL) {
+ // Take care of resolving the found resource to its final value.
+ ssize_t new_block = theme->resolveAttributeReference(
+ &value, block, &resid, &type_set_flags, &config);
+ if (new_block >= 0) {
+ block = new_block;
+ }
+
+ if (kDebugStyles) {
+ ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
+ }
+ } else {
+ // If we still don't have a value for this attribute, try to find
+ // it in the theme!
+ ssize_t new_block =
+ theme->getAttribute(cur_ident, &value, &type_set_flags);
+ if (new_block >= 0) {
+ if (kDebugStyles) {
+ ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
+ }
+ new_block = res.resolveReference(&value, new_block, &resid,
+ &type_set_flags, &config);
+ if (new_block >= 0) {
+ block = new_block;
+ }
+
+ if (kDebugStyles) {
+ ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
+ }
+ }
+ }
+
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ if (kDebugStyles) {
+ ALOGI("-> Setting to @null!");
+ }
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ block = kXmlBlock;
+ }
+
+ if (kDebugStyles) {
+ ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident,
+ value.dataType, value.data);
+ }
+
+ // Write the final value back to Java.
+ out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_DATA] = value.data;
+ out_values[STYLE_ASSET_COOKIE] =
+ block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block))
+ : static_cast<uint32_t>(-1);
+ out_values[STYLE_RESOURCE_ID] = resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
+ out_values[STYLE_DENSITY] = config.density;
+
+ if (value.dataType != Res_value::TYPE_NULL) {
+ indices_idx++;
+
+ // out_indices must NOT be nullptr.
+ out_indices[indices_idx] = ii;
+ }
+
+ out_values += STYLE_NUM_ENTRIES;
+ }
+
+ res.unlock();
+
+ // out_indices must NOT be nullptr.
+ out_indices[0] = indices_idx;
+}
+
+bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser,
+ uint32_t* attrs, size_t attrs_length,
+ uint32_t* out_values, uint32_t* out_indices) {
+ ResTable_config config;
+ Res_value value;
+
+ int indices_idx = 0;
+
+ // Now lock down the resource object and start pulling stuff from it.
+ res->lock();
+
+ // Retrieve the XML attributes, if requested.
+ const size_t xml_attr_count = xml_parser->getAttributeCount();
+ size_t ix = 0;
+ uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
+
+ static const ssize_t kXmlBlock = 0x10000000;
+
+ // Now iterate through all of the attributes that the client has requested,
+ // filling in each with whatever data we can find.
+ for (size_t ii = 0; ii < attrs_length; ii++) {
+ const uint32_t cur_ident = attrs[ii];
+ ssize_t block = kXmlBlock;
+ uint32_t type_set_flags = 0;
+
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ config.density = 0;
+
+ // Try to find a value for this attribute...
+ // Skip through XML attributes until the end or the next possible match.
+ while (ix < xml_attr_count && cur_ident > cur_xml_attr) {
+ ix++;
+ cur_xml_attr = xml_parser->getAttributeNameResID(ix);
+ }
+ // Retrieve the current XML attribute if it matches, and step to next.
+ if (ix < xml_attr_count && cur_ident == cur_xml_attr) {
+ xml_parser->getAttributeValue(ix, &value);
+ ix++;
+ cur_xml_attr = xml_parser->getAttributeNameResID(ix);
+ }
+
+ uint32_t resid = 0;
+ if (value.dataType != Res_value::TYPE_NULL) {
+ // Take care of resolving the found resource to its final value.
+ // printf("Resolving attribute reference\n");
+ ssize_t new_block = res->resolveReference(&value, block, &resid,
+ &type_set_flags, &config);
+ if (new_block >= 0) block = new_block;
+ }
+
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ block = kXmlBlock;
+ }
+
+ // Write the final value back to Java.
+ out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_DATA] = value.data;
+ out_values[STYLE_ASSET_COOKIE] =
+ block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block))
+ : static_cast<uint32_t>(-1);
+ out_values[STYLE_RESOURCE_ID] = resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
+ out_values[STYLE_DENSITY] = config.density;
+
+ if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) {
+ indices_idx++;
+ out_indices[indices_idx] = ii;
+ }
+
+ out_values += STYLE_NUM_ENTRIES;
+ }
+
+ res->unlock();
+
+ if (out_indices != nullptr) {
+ out_indices[0] = indices_idx;
+ }
+ return true;
+}
+
+} // namespace android
diff --git a/libs/androidfw/Chunk.h b/libs/androidfw/Chunk.h
new file mode 100644
index 000000000000..e87b94087450
--- /dev/null
+++ b/libs/androidfw/Chunk.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHUNK_H_
+#define CHUNK_H_
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "utils/ByteOrder.h"
+
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+#include "androidfw/ResourceTypes.h"
+
+namespace android {
+
+// Helpful wrapper around a ResChunk_header that provides getter methods
+// that handle endianness conversions and provide access to the data portion
+// of the chunk.
+class Chunk {
+ public:
+ explicit Chunk(const ResChunk_header* chunk) : device_chunk_(chunk) {}
+
+ // Returns the type of the chunk. Caller need not worry about endianness.
+ inline int type() const { return dtohs(device_chunk_->type); }
+
+ // Returns the size of the entire chunk. This can be useful for skipping
+ // over the entire chunk. Caller need not worry about endianness.
+ inline size_t size() const { return dtohl(device_chunk_->size); }
+
+ // Returns the size of the header. Caller need not worry about endianness.
+ inline size_t header_size() const { return dtohs(device_chunk_->headerSize); }
+
+ template <typename T>
+ inline const T* header() const {
+ if (header_size() >= sizeof(T)) {
+ return reinterpret_cast<const T*>(device_chunk_);
+ }
+ return nullptr;
+ }
+
+ inline const void* data_ptr() const {
+ return reinterpret_cast<const uint8_t*>(device_chunk_) + header_size();
+ }
+
+ inline size_t data_size() const { return size() - header_size(); }
+
+ private:
+ const ResChunk_header* device_chunk_;
+};
+
+// Provides a Java style iterator over an array of ResChunk_header's.
+// Validation is performed while iterating.
+// The caller should check if there was an error during chunk validation
+// by calling HadError() and GetLastError() to get the reason for failure.
+// Example:
+//
+// ChunkIterator iter(data_ptr, data_len);
+// while (iter.HasNext()) {
+// const Chunk chunk = iter.Next();
+// ...
+// }
+//
+// if (iter.HadError()) {
+// LOG(ERROR) << iter.GetLastError();
+// }
+//
+class ChunkIterator {
+ public:
+ ChunkIterator(const void* data, size_t len)
+ : next_chunk_(reinterpret_cast<const ResChunk_header*>(data)),
+ len_(len),
+ last_error_(nullptr) {
+ CHECK(next_chunk_ != nullptr) << "data can't be nullptr";
+ VerifyNextChunk();
+ }
+
+ Chunk Next();
+ inline bool HasNext() const { return !HadError() && len_ != 0; };
+ inline bool HadError() const { return last_error_ != nullptr; }
+ inline std::string GetLastError() const { return last_error_; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChunkIterator);
+
+ // Returns false if there was an error.
+ bool VerifyNextChunk();
+
+ const ResChunk_header* next_chunk_;
+ size_t len_;
+ const char* last_error_;
+};
+
+} // namespace android
+
+#endif /* CHUNK_H_ */
diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp
new file mode 100644
index 000000000000..747aa4ad20e6
--- /dev/null
+++ b/libs/androidfw/ChunkIterator.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Chunk.h"
+
+#include "android-base/logging.h"
+
+namespace android {
+
+Chunk ChunkIterator::Next() {
+ CHECK(len_ != 0) << "called Next() after last chunk";
+
+ const ResChunk_header* this_chunk = next_chunk_;
+
+ // We've already checked the values of this_chunk, so safely increment.
+ next_chunk_ = reinterpret_cast<const ResChunk_header*>(
+ reinterpret_cast<const uint8_t*>(this_chunk) + dtohl(this_chunk->size));
+ len_ -= dtohl(this_chunk->size);
+
+ if (len_ != 0) {
+ // Prepare the next chunk.
+ VerifyNextChunk();
+ }
+ return Chunk(this_chunk);
+}
+
+// Returns false if there was an error.
+bool ChunkIterator::VerifyNextChunk() {
+ const uintptr_t header_start = reinterpret_cast<uintptr_t>(next_chunk_);
+
+ // This data must be 4-byte aligned, since we directly
+ // access 32-bit words, which must be aligned on
+ // certain architectures.
+ if (header_start & 0x03) {
+ last_error_ = "header not aligned on 4-byte boundary";
+ return false;
+ }
+
+ if (len_ < sizeof(ResChunk_header)) {
+ last_error_ = "not enough space for header";
+ return false;
+ }
+
+ const size_t header_size = dtohs(next_chunk_->headerSize);
+ const size_t size = dtohl(next_chunk_->size);
+ if (header_size < sizeof(ResChunk_header)) {
+ last_error_ = "header size too small";
+ return false;
+ }
+
+ if (header_size > size) {
+ last_error_ = "header size is larger than entire chunk";
+ return false;
+ }
+
+ if (size > len_) {
+ last_error_ = "chunk size is bigger than given data";
+ return false;
+ }
+
+ if ((size | header_size) & 0x03) {
+ last_error_ = "header sizes are not aligned on 4-byte boundary";
+ return false;
+ }
+ return true;
+}
+
+} // namespace android
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
new file mode 100644
index 000000000000..94d0d4638ba8
--- /dev/null
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_RESOURCES
+
+#include "androidfw/LoadedArsc.h"
+
+#include <cstddef>
+#include <limits>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "utils/ByteOrder.h"
+#include "utils/Trace.h"
+
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+#include "Chunk.h"
+#include "androidfw/ByteBucketArray.h"
+#include "androidfw/Util.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+
+namespace {
+
+// Element of a TypeSpec array. See TypeSpec.
+struct Type {
+ // The configuration for which this type defines entries.
+ // This is already converted to host endianness.
+ ResTable_config configuration;
+
+ // Pointer to the mmapped data where entry definitions are kept.
+ const ResTable_type* type;
+};
+
+// TypeSpec is going to be immediately proceeded by
+// an array of Type structs, all in the same block of memory.
+struct TypeSpec {
+ // Pointer to the mmapped data where flags are kept.
+ // Flags denote whether the resource entry is public
+ // and under which configurations it varies.
+ const ResTable_typeSpec* type_spec;
+
+ // The number of types that follow this struct.
+ // There is a type for each configuration
+ // that entries are defined for.
+ size_t type_count;
+
+ // Trick to easily access a variable number of Type structs
+ // proceeding this struct, and to ensure their alignment.
+ const Type types[0];
+};
+
+// TypeSpecPtr points to the block of memory that holds
+// a TypeSpec struct, followed by an array of Type structs.
+// TypeSpecPtr is a managed pointer that knows how to delete
+// itself.
+using TypeSpecPtr = util::unique_cptr<TypeSpec>;
+
+// Builder that helps accumulate Type structs and then create a single
+// contiguous block of memory to store both the TypeSpec struct and
+// the Type structs.
+class TypeSpecPtrBuilder {
+ public:
+ TypeSpecPtrBuilder(const ResTable_typeSpec* header) : header_(header) {}
+
+ void AddType(const ResTable_type* type) {
+ ResTable_config config;
+ config.copyFromDtoH(type->config);
+ types_.push_back(Type{config, type});
+ }
+
+ TypeSpecPtr Build() {
+ // Check for overflow.
+ if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) {
+ return {};
+ }
+ TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type)));
+ type_spec->type_spec = header_;
+ type_spec->type_count = types_.size();
+ memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type));
+ return TypeSpecPtr(type_spec);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder);
+
+ const ResTable_typeSpec* header_;
+ std::vector<Type> types_;
+};
+
+} // namespace
+
+class LoadedPackage {
+ public:
+ LoadedPackage() = default;
+
+ bool FindEntry(uint8_t type_id, uint16_t entry_id, const ResTable_config& config,
+ LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config,
+ uint32_t* out_flags) const;
+
+ ResStringPool type_string_pool_;
+ ResStringPool key_string_pool_;
+ std::string package_name_;
+ int package_id_ = -1;
+
+ ByteBucketArray<TypeSpecPtr> type_specs_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
+};
+
+bool LoadedPackage::FindEntry(uint8_t type_id, uint16_t entry_id, const ResTable_config& config,
+ LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config,
+ uint32_t* out_flags) const {
+ ATRACE_NAME("LoadedPackage::FindEntry");
+ const TypeSpecPtr& ptr = type_specs_[type_id];
+ if (ptr == nullptr) {
+ return false;
+ }
+
+ // Don't bother checking if the entry ID is larger than
+ // the number of entries.
+ if (entry_id >= dtohl(ptr->type_spec->entryCount)) {
+ return false;
+ }
+
+ const ResTable_config* best_config = nullptr;
+ const ResTable_type* best_type = nullptr;
+ uint32_t best_offset = 0;
+
+ for (uint32_t i = 0; i < ptr->type_count; i++) {
+ const Type* type = &ptr->types[i];
+
+ if (type->configuration.match(config) &&
+ (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) {
+ // The configuration matches and is better than the previous selection.
+ // Find the entry value if it exists for this configuration.
+ size_t entry_count = dtohl(type->type->entryCount);
+ if (entry_id < entry_count) {
+ const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
+ reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize));
+ const uint32_t offset = dtohl(entry_offsets[entry_id]);
+ if (offset != ResTable_type::NO_ENTRY) {
+ // There is an entry for this resource, record it.
+ best_config = &type->configuration;
+ best_type = type->type;
+ best_offset = offset + dtohl(type->type->entriesStart);
+ }
+ }
+ }
+ }
+
+ if (best_type == nullptr) {
+ return false;
+ }
+
+ const uint32_t* flags = reinterpret_cast<const uint32_t*>(ptr->type_spec + 1);
+ *out_flags = dtohl(flags[entry_id]);
+ *out_selected_config = *best_config;
+
+ const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>(
+ reinterpret_cast<const uint8_t*>(best_type) + best_offset);
+ out_entry->entry = best_entry;
+ out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1);
+ out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index));
+ return true;
+}
+
+// The destructor gets generated into arbitrary translation units
+// if left implicit, which causes the compiler to complain about
+// forward declarations and incomplete types.
+LoadedArsc::~LoadedArsc() {}
+
+bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, Entry* out_entry,
+ ResTable_config* out_selected_config, uint32_t* out_flags) const {
+ ATRACE_NAME("LoadedArsc::FindEntry");
+ const uint8_t package_id = util::get_package_id(resid);
+ const uint8_t type_id = util::get_type_id(resid);
+ const uint16_t entry_id = util::get_entry_id(resid);
+
+ if (type_id == 0) {
+ LOG(ERROR) << "Invalid ID 0x" << std::hex << resid << std::dec << ".";
+ return false;
+ }
+
+ for (const auto& loaded_package : packages_) {
+ if (loaded_package->package_id_ == package_id) {
+ return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry,
+ out_selected_config, out_flags);
+ }
+ }
+ return false;
+}
+
+const std::string* LoadedArsc::GetPackageNameForId(uint32_t resid) const {
+ const uint8_t package_id = util::get_package_id(resid);
+ for (const auto& loaded_package : packages_) {
+ if (loaded_package->package_id_ == package_id) {
+ return &loaded_package->package_name_;
+ }
+ }
+ return nullptr;
+}
+
+static bool VerifyType(const Chunk& chunk) {
+ ATRACE_CALL();
+ const ResTable_type* header = chunk.header<ResTable_type>();
+
+ const size_t entry_count = dtohl(header->entryCount);
+ if (entry_count > std::numeric_limits<uint16_t>::max()) {
+ LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_TYPE.";
+ return false;
+ }
+
+ // Make sure that there is enough room for the entry offsets.
+ const size_t offsets_offset = chunk.header_size();
+ const size_t entries_offset = dtohl(header->entriesStart);
+ const size_t offsets_length = sizeof(uint32_t) * entry_count;
+
+ if (offsets_offset + offsets_length > entries_offset) {
+ LOG(ERROR) << "Entry offsets overlap actual entry data.";
+ return false;
+ }
+
+ if (entries_offset > chunk.size()) {
+ LOG(ERROR) << "Entry offsets extend beyond chunk.";
+ return false;
+ }
+
+ if (entries_offset & 0x03) {
+ LOG(ERROR) << "Entries start at unaligned address.";
+ return false;
+ }
+
+ // Check each entry offset.
+ const uint32_t* offsets =
+ reinterpret_cast<const uint32_t*>(reinterpret_cast<const uint8_t*>(header) + offsets_offset);
+ for (size_t i = 0; i < entry_count; i++) {
+ uint32_t offset = dtohl(offsets[i]);
+ if (offset != ResTable_type::NO_ENTRY) {
+ // Check that the offset is aligned.
+ if (offset & 0x03) {
+ LOG(ERROR) << "Entry offset at index " << i << " is not 4-byte aligned.";
+ return false;
+ }
+
+ // Check that the offset doesn't overflow.
+ if (offset > std::numeric_limits<uint32_t>::max() - entries_offset) {
+ // Overflow in offset.
+ LOG(ERROR) << "Entry offset at index " << i << " is too large.";
+ return false;
+ }
+
+ offset += entries_offset;
+ if (offset > chunk.size() - sizeof(ResTable_entry)) {
+ LOG(ERROR) << "Entry offset at index " << i << " is too large. No room for ResTable_entry.";
+ return false;
+ }
+
+ const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
+ reinterpret_cast<const uint8_t*>(header) + offset);
+ const size_t entry_size = dtohs(entry->size);
+ if (entry_size < sizeof(*entry)) {
+ LOG(ERROR) << "ResTable_entry size " << entry_size << " is too small.";
+ return false;
+ }
+
+ // Check the declared entrySize.
+ if (entry_size > chunk.size() || offset > chunk.size() - entry_size) {
+ LOG(ERROR) << "ResTable_entry size " << entry_size << " is too large.";
+ return false;
+ }
+
+ // If this is a map entry, then keep validating.
+ if (entry_size >= sizeof(ResTable_map_entry)) {
+ const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry);
+ const size_t map_entry_count = dtohl(map->count);
+
+ size_t map_entries_start = offset + entry_size;
+ if (map_entries_start & 0x03) {
+ LOG(ERROR) << "Map entries start at unaligned offset.";
+ return false;
+ }
+
+ // Each entry is sizeof(ResTable_map) big.
+ if (map_entry_count > ((chunk.size() - map_entries_start) / sizeof(ResTable_map))) {
+ LOG(ERROR) << "Too many map entries in ResTable_map_entry.";
+ return false;
+ }
+
+ // Great, all the map entries fit!.
+ } else {
+ // There needs to be room for one Res_value struct.
+ if (offset + entry_size > chunk.size() - sizeof(Res_value)) {
+ LOG(ERROR) << "No room for Res_value after ResTable_entry.";
+ return false;
+ }
+
+ const Res_value* value = reinterpret_cast<const Res_value*>(
+ reinterpret_cast<const uint8_t*>(entry) + entry_size);
+ const size_t value_size = dtohs(value->size);
+ if (value_size < sizeof(Res_value)) {
+ LOG(ERROR) << "Res_value is too small.";
+ return false;
+ }
+
+ if (value_size > chunk.size() || offset + entry_size > chunk.size() - value_size) {
+ LOG(ERROR) << "Res_value size is too large.";
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+static bool LoadPackage(const Chunk& chunk, LoadedPackage* loaded_package) {
+ ATRACE_CALL();
+ const ResTable_package* header = chunk.header<ResTable_package>();
+ if (header == nullptr) {
+ LOG(ERROR) << "Chunk RES_TABLE_PACKAGE_TYPE is too small.";
+ return false;
+ }
+
+ loaded_package->package_id_ = dtohl(header->id);
+
+ // A TypeSpec builder. We use this to accumulate the set of Types
+ // available for a TypeSpec, and later build a single, contiguous block
+ // of memory that holds all the Types together with the TypeSpec.
+ std::unique_ptr<TypeSpecPtrBuilder> types_builder;
+
+ // Keep track of the last seen type index. Since type IDs are 1-based,
+ // this records their index, which is 0-based (type ID - 1).
+ uint8_t last_type_idx = 0;
+
+ ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
+ while (iter.HasNext()) {
+ const Chunk child_chunk = iter.Next();
+ switch (child_chunk.type()) {
+ case RES_STRING_POOL_TYPE: {
+ const uintptr_t pool_address =
+ reinterpret_cast<uintptr_t>(child_chunk.header<ResChunk_header>());
+ const uintptr_t header_address = reinterpret_cast<uintptr_t>(header);
+ if (pool_address == header_address + dtohl(header->typeStrings)) {
+ // This string pool is the type string pool.
+ status_t err = loaded_package->type_string_pool_.setTo(
+ child_chunk.header<ResStringPool_header>(), child_chunk.size());
+ if (err != NO_ERROR) {
+ LOG(ERROR) << "Corrupt package type string pool.";
+ return false;
+ }
+ } else if (pool_address == header_address + dtohl(header->keyStrings)) {
+ // This string pool is the key string pool.
+ status_t err = loaded_package->key_string_pool_.setTo(
+ child_chunk.header<ResStringPool_header>(), child_chunk.size());
+ if (err != NO_ERROR) {
+ LOG(ERROR) << "Corrupt package key string pool.";
+ return false;
+ }
+ } else {
+ LOG(WARNING) << "Too many string pool chunks found in package.";
+ }
+ } break;
+
+ case RES_TABLE_TYPE_SPEC_TYPE: {
+ ATRACE_NAME("LoadTableTypeSpec");
+
+ // Starting a new TypeSpec, so finish the old one if there was one.
+ if (types_builder) {
+ TypeSpecPtr type_spec_ptr = types_builder->Build();
+ if (type_spec_ptr == nullptr) {
+ LOG(ERROR) << "Too many type configurations, overflow detected.";
+ return false;
+ }
+
+ loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
+
+ types_builder = {};
+ last_type_idx = 0;
+ }
+
+ const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>();
+ if (type_spec == nullptr) {
+ LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE is too small.";
+ return false;
+ }
+
+ if (type_spec->id == 0) {
+ LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0.";
+ return false;
+ }
+
+ // The data portion of this chunk contains entry_count 32bit entries,
+ // each one representing a set of flags.
+ // Here we only validate that the chunk is well formed.
+ const size_t entry_count = dtohl(type_spec->entryCount);
+
+ // There can only be 2^16 entries in a type, because that is the ID
+ // space for entries (EEEE) in the resource ID 0xPPTTEEEE.
+ if (entry_count > std::numeric_limits<uint16_t>::max()) {
+ LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_SPEC_TYPE: " << entry_count << ".";
+ return false;
+ }
+
+ if (entry_count * sizeof(uint32_t) > chunk.data_size()) {
+ LOG(ERROR) << "Chunk too small to hold entries in RES_TABLE_TYPE_SPEC_TYPE.";
+ return false;
+ }
+
+ last_type_idx = type_spec->id - 1;
+ types_builder = util::make_unique<TypeSpecPtrBuilder>(type_spec);
+ } break;
+
+ case RES_TABLE_TYPE_TYPE: {
+ const ResTable_type* type = child_chunk.header<ResTable_type>();
+ if (type == nullptr) {
+ LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE is too small.";
+ return false;
+ }
+
+ if (type->id == 0) {
+ LOG(ERROR) << "Chunk RES_TABLE_TYPE_TYPE has invalid ID 0.";
+ return false;
+ }
+
+ // Type chunks must be preceded by their TypeSpec chunks.
+ if (!types_builder || type->id - 1 != last_type_idx) {
+ LOG(ERROR) << "Found RES_TABLE_TYPE_TYPE chunk without "
+ "RES_TABLE_TYPE_SPEC_TYPE.";
+ return false;
+ }
+
+ if (!VerifyType(child_chunk)) {
+ return false;
+ }
+
+ types_builder->AddType(type);
+ } break;
+
+ default:
+ LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+ break;
+ }
+ }
+
+ // Finish the last TypeSpec.
+ if (types_builder) {
+ TypeSpecPtr type_spec_ptr = types_builder->Build();
+ if (type_spec_ptr == nullptr) {
+ LOG(ERROR) << "Too many type configurations, overflow detected.";
+ return false;
+ }
+ loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
+ }
+
+ if (iter.HadError()) {
+ LOG(ERROR) << iter.GetLastError();
+ return false;
+ }
+ return true;
+}
+
+bool LoadedArsc::LoadTable(const Chunk& chunk) {
+ ATRACE_CALL();
+ const ResTable_header* header = chunk.header<ResTable_header>();
+ if (header == nullptr) {
+ LOG(ERROR) << "Chunk RES_TABLE_TYPE is too small.";
+ return false;
+ }
+
+ const size_t package_count = dtohl(header->packageCount);
+ size_t packages_seen = 0;
+
+ packages_.reserve(package_count);
+
+ ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
+ while (iter.HasNext()) {
+ const Chunk child_chunk = iter.Next();
+ switch (child_chunk.type()) {
+ case RES_STRING_POOL_TYPE:
+ // Only use the first string pool. Ignore others.
+ if (global_string_pool_.getError() == NO_INIT) {
+ status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(),
+ child_chunk.size());
+ if (err != NO_ERROR) {
+ LOG(ERROR) << "Corrupt string pool.";
+ return false;
+ }
+ } else {
+ LOG(WARNING) << "Multiple string pool chunks found in resource table.";
+ }
+ break;
+
+ case RES_TABLE_PACKAGE_TYPE: {
+ if (packages_seen + 1 > package_count) {
+ LOG(ERROR) << "More package chunks were found than the " << package_count
+ << " declared in the "
+ "header.";
+ return false;
+ }
+ packages_seen++;
+
+ std::unique_ptr<LoadedPackage> loaded_package = util::make_unique<LoadedPackage>();
+ if (!LoadPackage(child_chunk, loaded_package.get())) {
+ return false;
+ }
+ packages_.push_back(std::move(loaded_package));
+ } break;
+
+ default:
+ LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+ break;
+ }
+ }
+
+ if (iter.HadError()) {
+ LOG(ERROR) << iter.GetLastError();
+ return false;
+ }
+ return true;
+}
+
+std::unique_ptr<LoadedArsc> LoadedArsc::Load(const void* data, size_t len) {
+ ATRACE_CALL();
+
+ // Not using make_unique because the constructor is private.
+ std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
+
+ ChunkIterator iter(data, len);
+ while (iter.HasNext()) {
+ const Chunk chunk = iter.Next();
+ switch (chunk.type()) {
+ case RES_TABLE_TYPE:
+ if (!loaded_arsc->LoadTable(chunk)) {
+ return {};
+ }
+ break;
+
+ default:
+ LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+ break;
+ }
+ }
+
+ if (iter.HadError()) {
+ LOG(ERROR) << iter.GetLastError();
+ return {};
+ }
+ return loaded_arsc;
+}
+
+} // namespace android
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index 1ac508525061..7c381efec7ec 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -1,4 +1,4 @@
-// Auto-generated by frameworks/base/tools/localedata/extract_icu_data.py
+// Auto-generated by ./tools/localedata/extract_icu_data.py
const char SCRIPT_CODES[][4] = {
/* 0 */ {'A', 'h', 'o', 'm'},
@@ -39,27 +39,27 @@ const char SCRIPT_CODES[][4] = {
/* 35 */ {'K', 'h', 'm', 'r'},
/* 36 */ {'K', 'n', 'd', 'a'},
/* 37 */ {'K', 'o', 'r', 'e'},
- /* 38 */ {'K', 't', 'h', 'i'},
- /* 39 */ {'L', 'a', 'n', 'a'},
- /* 40 */ {'L', 'a', 'o', 'o'},
- /* 41 */ {'L', 'a', 't', 'n'},
- /* 42 */ {'L', 'e', 'p', 'c'},
- /* 43 */ {'L', 'i', 'n', 'a'},
- /* 44 */ {'L', 'i', 's', 'u'},
- /* 45 */ {'L', 'y', 'c', 'i'},
- /* 46 */ {'L', 'y', 'd', 'i'},
- /* 47 */ {'M', 'a', 'n', 'd'},
- /* 48 */ {'M', 'a', 'n', 'i'},
- /* 49 */ {'M', 'e', 'r', 'c'},
- /* 50 */ {'M', 'l', 'y', 'm'},
- /* 51 */ {'M', 'o', 'n', 'g'},
- /* 52 */ {'M', 'r', 'o', 'o'},
- /* 53 */ {'M', 'y', 'm', 'r'},
- /* 54 */ {'N', 'a', 'r', 'b'},
- /* 55 */ {'N', 'k', 'o', 'o'},
- /* 56 */ {'O', 'g', 'a', 'm'},
- /* 57 */ {'O', 'r', 'k', 'h'},
- /* 58 */ {'O', 'r', 'y', 'a'},
+ /* 38 */ {'L', 'a', 'n', 'a'},
+ /* 39 */ {'L', 'a', 'o', 'o'},
+ /* 40 */ {'L', 'a', 't', 'n'},
+ /* 41 */ {'L', 'e', 'p', 'c'},
+ /* 42 */ {'L', 'i', 'n', 'a'},
+ /* 43 */ {'L', 'i', 's', 'u'},
+ /* 44 */ {'L', 'y', 'c', 'i'},
+ /* 45 */ {'L', 'y', 'd', 'i'},
+ /* 46 */ {'M', 'a', 'n', 'd'},
+ /* 47 */ {'M', 'a', 'n', 'i'},
+ /* 48 */ {'M', 'e', 'r', 'c'},
+ /* 49 */ {'M', 'l', 'y', 'm'},
+ /* 50 */ {'M', 'o', 'n', 'g'},
+ /* 51 */ {'M', 'r', 'o', 'o'},
+ /* 52 */ {'M', 'y', 'm', 'r'},
+ /* 53 */ {'N', 'a', 'r', 'b'},
+ /* 54 */ {'N', 'k', 'o', 'o'},
+ /* 55 */ {'O', 'g', 'a', 'm'},
+ /* 56 */ {'O', 'r', 'k', 'h'},
+ /* 57 */ {'O', 'r', 'y', 'a'},
+ /* 58 */ {'O', 's', 'g', 'e'},
/* 59 */ {'P', 'a', 'u', 'c'},
/* 60 */ {'P', 'h', 'l', 'i'},
/* 61 */ {'P', 'h', 'n', 'x'},
@@ -76,78 +76,147 @@ const char SCRIPT_CODES[][4] = {
/* 72 */ {'T', 'a', 'l', 'e'},
/* 73 */ {'T', 'a', 'l', 'u'},
/* 74 */ {'T', 'a', 'm', 'l'},
- /* 75 */ {'T', 'a', 'v', 't'},
- /* 76 */ {'T', 'e', 'l', 'u'},
- /* 77 */ {'T', 'f', 'n', 'g'},
- /* 78 */ {'T', 'h', 'a', 'a'},
- /* 79 */ {'T', 'h', 'a', 'i'},
- /* 80 */ {'T', 'i', 'b', 't'},
- /* 81 */ {'U', 'g', 'a', 'r'},
- /* 82 */ {'V', 'a', 'i', 'i'},
- /* 83 */ {'X', 'p', 'e', 'o'},
- /* 84 */ {'X', 's', 'u', 'x'},
- /* 85 */ {'Y', 'i', 'i', 'i'},
- /* 86 */ {'~', '~', '~', 'A'},
- /* 87 */ {'~', '~', '~', 'B'},
+ /* 75 */ {'T', 'a', 'n', 'g'},
+ /* 76 */ {'T', 'a', 'v', 't'},
+ /* 77 */ {'T', 'e', 'l', 'u'},
+ /* 78 */ {'T', 'f', 'n', 'g'},
+ /* 79 */ {'T', 'h', 'a', 'a'},
+ /* 80 */ {'T', 'h', 'a', 'i'},
+ /* 81 */ {'T', 'i', 'b', 't'},
+ /* 82 */ {'U', 'g', 'a', 'r'},
+ /* 83 */ {'V', 'a', 'i', 'i'},
+ /* 84 */ {'X', 'p', 'e', 'o'},
+ /* 85 */ {'X', 's', 'u', 'x'},
+ /* 86 */ {'Y', 'i', 'i', 'i'},
+ /* 87 */ {'~', '~', '~', 'A'},
+ /* 88 */ {'~', '~', '~', 'B'},
};
const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
- {0x61610000u, 41u}, // aa -> Latn
+ {0x61610000u, 40u}, // aa -> Latn
+ {0xA0000000u, 40u}, // aai -> Latn
+ {0xA8000000u, 40u}, // aak -> Latn
+ {0xD0000000u, 40u}, // aau -> Latn
{0x61620000u, 15u}, // ab -> Cyrl
- {0xC4200000u, 41u}, // abr -> Latn
- {0x90400000u, 41u}, // ace -> Latn
- {0x9C400000u, 41u}, // ach -> Latn
- {0x80600000u, 41u}, // ada -> Latn
+ {0xA0200000u, 40u}, // abi -> Latn
+ {0xC4200000u, 40u}, // abr -> Latn
+ {0xCC200000u, 40u}, // abt -> Latn
+ {0xE0200000u, 40u}, // aby -> Latn
+ {0x8C400000u, 40u}, // acd -> Latn
+ {0x90400000u, 40u}, // ace -> Latn
+ {0x9C400000u, 40u}, // ach -> Latn
+ {0x80600000u, 40u}, // ada -> Latn
+ {0x90600000u, 40u}, // ade -> Latn
+ {0xA4600000u, 40u}, // adj -> Latn
{0xE0600000u, 15u}, // ady -> Cyrl
+ {0xE4600000u, 40u}, // adz -> Latn
{0x61650000u, 4u}, // ae -> Avst
{0x84800000u, 1u}, // aeb -> Arab
- {0x61660000u, 41u}, // af -> Latn
- {0xC0C00000u, 41u}, // agq -> Latn
+ {0xE0800000u, 40u}, // aey -> Latn
+ {0x61660000u, 40u}, // af -> Latn
+ {0x88C00000u, 40u}, // agc -> Latn
+ {0x8CC00000u, 40u}, // agd -> Latn
+ {0x98C00000u, 40u}, // agg -> Latn
+ {0xB0C00000u, 40u}, // agm -> Latn
+ {0xB8C00000u, 40u}, // ago -> Latn
+ {0xC0C00000u, 40u}, // agq -> Latn
+ {0x80E00000u, 40u}, // aha -> Latn
+ {0xACE00000u, 40u}, // ahl -> Latn
{0xB8E00000u, 0u}, // aho -> Ahom
- {0x616B0000u, 41u}, // ak -> Latn
- {0xA9400000u, 84u}, // akk -> Xsux
- {0xB5600000u, 41u}, // aln -> Latn
+ {0x99200000u, 40u}, // ajg -> Latn
+ {0x616B0000u, 40u}, // ak -> Latn
+ {0xA9400000u, 85u}, // akk -> Xsux
+ {0x81600000u, 40u}, // ala -> Latn
+ {0xA1600000u, 40u}, // ali -> Latn
+ {0xB5600000u, 40u}, // aln -> Latn
{0xCD600000u, 15u}, // alt -> Cyrl
{0x616D0000u, 18u}, // am -> Ethi
- {0xB9800000u, 41u}, // amo -> Latn
- {0xE5C00000u, 41u}, // aoz -> Latn
+ {0xB1800000u, 40u}, // amm -> Latn
+ {0xB5800000u, 40u}, // amn -> Latn
+ {0xB9800000u, 40u}, // amo -> Latn
+ {0xBD800000u, 40u}, // amp -> Latn
+ {0x89A00000u, 40u}, // anc -> Latn
+ {0xA9A00000u, 40u}, // ank -> Latn
+ {0xB5A00000u, 40u}, // ann -> Latn
+ {0xE1A00000u, 40u}, // any -> Latn
+ {0xA5C00000u, 40u}, // aoj -> Latn
+ {0xB1C00000u, 40u}, // aom -> Latn
+ {0xE5C00000u, 40u}, // aoz -> Latn
+ {0x89E00000u, 1u}, // apc -> Arab
+ {0x8DE00000u, 1u}, // apd -> Arab
+ {0x91E00000u, 40u}, // ape -> Latn
+ {0xC5E00000u, 40u}, // apr -> Latn
+ {0xC9E00000u, 40u}, // aps -> Latn
+ {0xE5E00000u, 40u}, // apz -> Latn
{0x61720000u, 1u}, // ar -> Arab
- {0x61725842u, 87u}, // ar-XB -> ~~~B
+ {0x61725842u, 88u}, // ar-XB -> ~~~B
{0x8A200000u, 2u}, // arc -> Armi
- {0xB6200000u, 41u}, // arn -> Latn
- {0xBA200000u, 41u}, // aro -> Latn
+ {0x9E200000u, 40u}, // arh -> Latn
+ {0xB6200000u, 40u}, // arn -> Latn
+ {0xBA200000u, 40u}, // aro -> Latn
{0xC2200000u, 1u}, // arq -> Arab
{0xE2200000u, 1u}, // ary -> Arab
{0xE6200000u, 1u}, // arz -> Arab
{0x61730000u, 7u}, // as -> Beng
- {0x82400000u, 41u}, // asa -> Latn
+ {0x82400000u, 40u}, // asa -> Latn
{0x92400000u, 68u}, // ase -> Sgnw
- {0xCE400000u, 41u}, // ast -> Latn
- {0xA6600000u, 41u}, // atj -> Latn
+ {0x9A400000u, 40u}, // asg -> Latn
+ {0xBA400000u, 40u}, // aso -> Latn
+ {0xCE400000u, 40u}, // ast -> Latn
+ {0x82600000u, 40u}, // ata -> Latn
+ {0x9A600000u, 40u}, // atg -> Latn
+ {0xA6600000u, 40u}, // atj -> Latn
+ {0xE2800000u, 40u}, // auy -> Latn
{0x61760000u, 15u}, // av -> Cyrl
+ {0xAEA00000u, 1u}, // avl -> Arab
+ {0xB6A00000u, 40u}, // avn -> Latn
+ {0xCEA00000u, 40u}, // avt -> Latn
+ {0xD2A00000u, 40u}, // avu -> Latn
{0x82C00000u, 16u}, // awa -> Deva
- {0x61790000u, 41u}, // ay -> Latn
- {0x617A0000u, 41u}, // az -> Latn
+ {0x86C00000u, 40u}, // awb -> Latn
+ {0xBAC00000u, 40u}, // awo -> Latn
+ {0xDEC00000u, 40u}, // awx -> Latn
+ {0x61790000u, 40u}, // ay -> Latn
+ {0x87000000u, 40u}, // ayb -> Latn
+ {0x617A0000u, 40u}, // az -> Latn
{0x617A4951u, 1u}, // az-IQ -> Arab
{0x617A4952u, 1u}, // az-IR -> Arab
{0x617A5255u, 15u}, // az-RU -> Cyrl
{0x62610000u, 15u}, // ba -> Cyrl
{0xAC010000u, 1u}, // bal -> Arab
- {0xB4010000u, 41u}, // ban -> Latn
+ {0xB4010000u, 40u}, // ban -> Latn
{0xBC010000u, 16u}, // bap -> Deva
- {0xC4010000u, 41u}, // bar -> Latn
- {0xC8010000u, 41u}, // bas -> Latn
+ {0xC4010000u, 40u}, // bar -> Latn
+ {0xC8010000u, 40u}, // bas -> Latn
+ {0xD4010000u, 40u}, // bav -> Latn
{0xDC010000u, 5u}, // bax -> Bamu
- {0x88210000u, 41u}, // bbc -> Latn
- {0xA4210000u, 41u}, // bbj -> Latn
- {0xA0410000u, 41u}, // bci -> Latn
+ {0x80210000u, 40u}, // bba -> Latn
+ {0x84210000u, 40u}, // bbb -> Latn
+ {0x88210000u, 40u}, // bbc -> Latn
+ {0x8C210000u, 40u}, // bbd -> Latn
+ {0xA4210000u, 40u}, // bbj -> Latn
+ {0xBC210000u, 40u}, // bbp -> Latn
+ {0xC4210000u, 40u}, // bbr -> Latn
+ {0x94410000u, 40u}, // bcf -> Latn
+ {0x9C410000u, 40u}, // bch -> Latn
+ {0xA0410000u, 40u}, // bci -> Latn
+ {0xB0410000u, 40u}, // bcm -> Latn
+ {0xB4410000u, 40u}, // bcn -> Latn
+ {0xB8410000u, 40u}, // bco -> Latn
+ {0xC0410000u, 18u}, // bcq -> Ethi
+ {0xD0410000u, 40u}, // bcu -> Latn
+ {0x8C610000u, 40u}, // bdd -> Latn
{0x62650000u, 15u}, // be -> Cyrl
+ {0x94810000u, 40u}, // bef -> Latn
+ {0x9C810000u, 40u}, // beh -> Latn
{0xA4810000u, 1u}, // bej -> Arab
- {0xB0810000u, 41u}, // bem -> Latn
- {0xD8810000u, 41u}, // bew -> Latn
- {0xE4810000u, 41u}, // bez -> Latn
- {0x8CA10000u, 41u}, // bfd -> Latn
+ {0xB0810000u, 40u}, // bem -> Latn
+ {0xCC810000u, 40u}, // bet -> Latn
+ {0xD8810000u, 40u}, // bew -> Latn
+ {0xDC810000u, 40u}, // bex -> Latn
+ {0xE4810000u, 40u}, // bez -> Latn
+ {0x8CA10000u, 40u}, // bfd -> Latn
{0xC0A10000u, 74u}, // bfq -> Taml
{0xCCA10000u, 1u}, // bft -> Arab
{0xE0A10000u, 16u}, // bfy -> Deva
@@ -155,663 +224,1202 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
{0x88C10000u, 16u}, // bgc -> Deva
{0xB4C10000u, 1u}, // bgn -> Arab
{0xDCC10000u, 21u}, // bgx -> Grek
- {0x62680000u, 38u}, // bh -> Kthi
{0x84E10000u, 16u}, // bhb -> Deva
+ {0x98E10000u, 40u}, // bhg -> Latn
{0xA0E10000u, 16u}, // bhi -> Deva
- {0xA8E10000u, 41u}, // bhk -> Latn
+ {0xA8E10000u, 40u}, // bhk -> Latn
+ {0xACE10000u, 40u}, // bhl -> Latn
{0xB8E10000u, 16u}, // bho -> Deva
- {0x62690000u, 41u}, // bi -> Latn
- {0xA9010000u, 41u}, // bik -> Latn
- {0xB5010000u, 41u}, // bin -> Latn
+ {0xE0E10000u, 40u}, // bhy -> Latn
+ {0x62690000u, 40u}, // bi -> Latn
+ {0x85010000u, 40u}, // bib -> Latn
+ {0x99010000u, 40u}, // big -> Latn
+ {0xA9010000u, 40u}, // bik -> Latn
+ {0xB1010000u, 40u}, // bim -> Latn
+ {0xB5010000u, 40u}, // bin -> Latn
+ {0xB9010000u, 40u}, // bio -> Latn
+ {0xC1010000u, 40u}, // biq -> Latn
+ {0x9D210000u, 40u}, // bjh -> Latn
+ {0xA1210000u, 18u}, // bji -> Ethi
{0xA5210000u, 16u}, // bjj -> Deva
- {0xB5210000u, 41u}, // bjn -> Latn
- {0xB1410000u, 41u}, // bkm -> Latn
- {0xD1410000u, 41u}, // bku -> Latn
- {0xCD610000u, 75u}, // blt -> Tavt
- {0x626D0000u, 41u}, // bm -> Latn
- {0xC1810000u, 41u}, // bmq -> Latn
+ {0xB5210000u, 40u}, // bjn -> Latn
+ {0xB9210000u, 40u}, // bjo -> Latn
+ {0xC5210000u, 40u}, // bjr -> Latn
+ {0xE5210000u, 40u}, // bjz -> Latn
+ {0x89410000u, 40u}, // bkc -> Latn
+ {0xB1410000u, 40u}, // bkm -> Latn
+ {0xC1410000u, 40u}, // bkq -> Latn
+ {0xD1410000u, 40u}, // bku -> Latn
+ {0xD5410000u, 40u}, // bkv -> Latn
+ {0xCD610000u, 76u}, // blt -> Tavt
+ {0x626D0000u, 40u}, // bm -> Latn
+ {0x9D810000u, 40u}, // bmh -> Latn
+ {0xA9810000u, 40u}, // bmk -> Latn
+ {0xC1810000u, 40u}, // bmq -> Latn
+ {0xD1810000u, 40u}, // bmu -> Latn
{0x626E0000u, 7u}, // bn -> Beng
- {0x626F0000u, 80u}, // bo -> Tibt
+ {0x99A10000u, 40u}, // bng -> Latn
+ {0xB1A10000u, 40u}, // bnm -> Latn
+ {0xBDA10000u, 40u}, // bnp -> Latn
+ {0x626F0000u, 81u}, // bo -> Tibt
+ {0xA5C10000u, 40u}, // boj -> Latn
+ {0xB1C10000u, 40u}, // bom -> Latn
+ {0xB5C10000u, 40u}, // bon -> Latn
{0xE1E10000u, 7u}, // bpy -> Beng
+ {0x8A010000u, 40u}, // bqc -> Latn
{0xA2010000u, 1u}, // bqi -> Arab
- {0xD6010000u, 41u}, // bqv -> Latn
- {0x62720000u, 41u}, // br -> Latn
+ {0xBE010000u, 40u}, // bqp -> Latn
+ {0xD6010000u, 40u}, // bqv -> Latn
+ {0x62720000u, 40u}, // br -> Latn
{0x82210000u, 16u}, // bra -> Deva
{0x9E210000u, 1u}, // brh -> Arab
{0xDE210000u, 16u}, // brx -> Deva
- {0x62730000u, 41u}, // bs -> Latn
+ {0xE6210000u, 40u}, // brz -> Latn
+ {0x62730000u, 40u}, // bs -> Latn
+ {0xA6410000u, 40u}, // bsj -> Latn
{0xC2410000u, 6u}, // bsq -> Bass
- {0xCA410000u, 41u}, // bss -> Latn
- {0xBA610000u, 41u}, // bto -> Latn
+ {0xCA410000u, 40u}, // bss -> Latn
+ {0xCE410000u, 18u}, // bst -> Ethi
+ {0xBA610000u, 40u}, // bto -> Latn
+ {0xCE610000u, 40u}, // btt -> Latn
{0xD6610000u, 16u}, // btv -> Deva
{0x82810000u, 15u}, // bua -> Cyrl
- {0x8A810000u, 41u}, // buc -> Latn
- {0x9A810000u, 41u}, // bug -> Latn
- {0xB2810000u, 41u}, // bum -> Latn
- {0x86A10000u, 41u}, // bvb -> Latn
+ {0x8A810000u, 40u}, // buc -> Latn
+ {0x8E810000u, 40u}, // bud -> Latn
+ {0x9A810000u, 40u}, // bug -> Latn
+ {0xAA810000u, 40u}, // buk -> Latn
+ {0xB2810000u, 40u}, // bum -> Latn
+ {0xBA810000u, 40u}, // buo -> Latn
+ {0xCA810000u, 40u}, // bus -> Latn
+ {0xD2810000u, 40u}, // buu -> Latn
+ {0x86A10000u, 40u}, // bvb -> Latn
+ {0x8EC10000u, 40u}, // bwd -> Latn
+ {0xC6C10000u, 40u}, // bwr -> Latn
+ {0x9EE10000u, 40u}, // bxh -> Latn
+ {0x93010000u, 40u}, // bye -> Latn
{0xB7010000u, 18u}, // byn -> Ethi
- {0xD7010000u, 41u}, // byv -> Latn
- {0x93210000u, 41u}, // bze -> Latn
- {0x63610000u, 41u}, // ca -> Latn
- {0x9C420000u, 41u}, // cch -> Latn
+ {0xC7010000u, 40u}, // byr -> Latn
+ {0xCB010000u, 40u}, // bys -> Latn
+ {0xD7010000u, 40u}, // byv -> Latn
+ {0xDF010000u, 40u}, // byx -> Latn
+ {0x83210000u, 40u}, // bza -> Latn
+ {0x93210000u, 40u}, // bze -> Latn
+ {0x97210000u, 40u}, // bzf -> Latn
+ {0x9F210000u, 40u}, // bzh -> Latn
+ {0xDB210000u, 40u}, // bzw -> Latn
+ {0x63610000u, 40u}, // ca -> Latn
+ {0xB4020000u, 40u}, // can -> Latn
+ {0xA4220000u, 40u}, // cbj -> Latn
+ {0x9C420000u, 40u}, // cch -> Latn
{0xBC420000u, 7u}, // ccp -> Beng
{0x63650000u, 15u}, // ce -> Cyrl
- {0x84820000u, 41u}, // ceb -> Latn
- {0x98C20000u, 41u}, // cgg -> Latn
- {0x63680000u, 41u}, // ch -> Latn
- {0xA8E20000u, 41u}, // chk -> Latn
+ {0x84820000u, 40u}, // ceb -> Latn
+ {0x80A20000u, 40u}, // cfa -> Latn
+ {0x98C20000u, 40u}, // cgg -> Latn
+ {0x63680000u, 40u}, // ch -> Latn
+ {0xA8E20000u, 40u}, // chk -> Latn
{0xB0E20000u, 15u}, // chm -> Cyrl
- {0xB8E20000u, 41u}, // cho -> Latn
- {0xBCE20000u, 41u}, // chp -> Latn
+ {0xB8E20000u, 40u}, // cho -> Latn
+ {0xBCE20000u, 40u}, // chp -> Latn
{0xC4E20000u, 12u}, // chr -> Cher
{0x81220000u, 1u}, // cja -> Arab
{0xB1220000u, 11u}, // cjm -> Cham
+ {0xD5220000u, 40u}, // cjv -> Latn
{0x85420000u, 1u}, // ckb -> Arab
- {0x636F0000u, 41u}, // co -> Latn
+ {0xAD420000u, 40u}, // ckl -> Latn
+ {0xB9420000u, 40u}, // cko -> Latn
+ {0xE1420000u, 40u}, // cky -> Latn
+ {0x81620000u, 40u}, // cla -> Latn
+ {0x91820000u, 40u}, // cme -> Latn
+ {0x636F0000u, 40u}, // co -> Latn
{0xBDC20000u, 13u}, // cop -> Copt
- {0xC9E20000u, 41u}, // cps -> Latn
+ {0xC9E20000u, 40u}, // cps -> Latn
{0x63720000u, 9u}, // cr -> Cans
{0xA6220000u, 9u}, // crj -> Cans
{0xAA220000u, 9u}, // crk -> Cans
{0xAE220000u, 9u}, // crl -> Cans
{0xB2220000u, 9u}, // crm -> Cans
- {0xCA220000u, 41u}, // crs -> Latn
- {0x63730000u, 41u}, // cs -> Latn
- {0x86420000u, 41u}, // csb -> Latn
+ {0xCA220000u, 40u}, // crs -> Latn
+ {0x63730000u, 40u}, // cs -> Latn
+ {0x86420000u, 40u}, // csb -> Latn
{0xDA420000u, 9u}, // csw -> Cans
{0x8E620000u, 59u}, // ctd -> Pauc
{0x63750000u, 15u}, // cu -> Cyrl
{0x63760000u, 15u}, // cv -> Cyrl
- {0x63790000u, 41u}, // cy -> Latn
- {0x64610000u, 41u}, // da -> Latn
- {0xA8030000u, 41u}, // dak -> Latn
+ {0x63790000u, 40u}, // cy -> Latn
+ {0x64610000u, 40u}, // da -> Latn
+ {0x8C030000u, 40u}, // dad -> Latn
+ {0x94030000u, 40u}, // daf -> Latn
+ {0x98030000u, 40u}, // dag -> Latn
+ {0x9C030000u, 40u}, // dah -> Latn
+ {0xA8030000u, 40u}, // dak -> Latn
{0xC4030000u, 15u}, // dar -> Cyrl
- {0xD4030000u, 41u}, // dav -> Latn
+ {0xD4030000u, 40u}, // dav -> Latn
+ {0x8C230000u, 40u}, // dbd -> Latn
+ {0xC0230000u, 40u}, // dbq -> Latn
{0x88430000u, 1u}, // dcc -> Arab
- {0x64650000u, 41u}, // de -> Latn
- {0xB4830000u, 41u}, // den -> Latn
- {0xC4C30000u, 41u}, // dgr -> Latn
- {0x91230000u, 41u}, // dje -> Latn
- {0xA5A30000u, 41u}, // dnj -> Latn
+ {0xB4630000u, 40u}, // ddn -> Latn
+ {0x64650000u, 40u}, // de -> Latn
+ {0x8C830000u, 40u}, // ded -> Latn
+ {0xB4830000u, 40u}, // den -> Latn
+ {0x80C30000u, 40u}, // dga -> Latn
+ {0x9CC30000u, 40u}, // dgh -> Latn
+ {0xA0C30000u, 40u}, // dgi -> Latn
+ {0xACC30000u, 1u}, // dgl -> Arab
+ {0xC4C30000u, 40u}, // dgr -> Latn
+ {0xE4C30000u, 40u}, // dgz -> Latn
+ {0x81030000u, 40u}, // dia -> Latn
+ {0x91230000u, 40u}, // dje -> Latn
+ {0xA5A30000u, 40u}, // dnj -> Latn
+ {0x85C30000u, 40u}, // dob -> Latn
{0xA1C30000u, 1u}, // doi -> Arab
- {0x86430000u, 41u}, // dsb -> Latn
- {0xB2630000u, 41u}, // dtm -> Latn
- {0xBE630000u, 41u}, // dtp -> Latn
- {0x82830000u, 41u}, // dua -> Latn
- {0x64760000u, 78u}, // dv -> Thaa
- {0xBB030000u, 41u}, // dyo -> Latn
- {0xD3030000u, 41u}, // dyu -> Latn
- {0x647A0000u, 80u}, // dz -> Tibt
- {0xD0240000u, 41u}, // ebu -> Latn
- {0x65650000u, 41u}, // ee -> Latn
- {0xA0A40000u, 41u}, // efi -> Latn
- {0xACC40000u, 41u}, // egl -> Latn
+ {0xBDC30000u, 40u}, // dop -> Latn
+ {0xD9C30000u, 40u}, // dow -> Latn
+ {0xA2230000u, 40u}, // dri -> Latn
+ {0xCA230000u, 18u}, // drs -> Ethi
+ {0x86430000u, 40u}, // dsb -> Latn
+ {0xB2630000u, 40u}, // dtm -> Latn
+ {0xBE630000u, 40u}, // dtp -> Latn
+ {0xCA630000u, 40u}, // dts -> Latn
+ {0xE2630000u, 16u}, // dty -> Deva
+ {0x82830000u, 40u}, // dua -> Latn
+ {0x8A830000u, 40u}, // duc -> Latn
+ {0x8E830000u, 40u}, // dud -> Latn
+ {0x9A830000u, 40u}, // dug -> Latn
+ {0x64760000u, 79u}, // dv -> Thaa
+ {0x82A30000u, 40u}, // dva -> Latn
+ {0xDAC30000u, 40u}, // dww -> Latn
+ {0xBB030000u, 40u}, // dyo -> Latn
+ {0xD3030000u, 40u}, // dyu -> Latn
+ {0x647A0000u, 81u}, // dz -> Tibt
+ {0x9B230000u, 40u}, // dzg -> Latn
+ {0xD0240000u, 40u}, // ebu -> Latn
+ {0x65650000u, 40u}, // ee -> Latn
+ {0xA0A40000u, 40u}, // efi -> Latn
+ {0xACC40000u, 40u}, // egl -> Latn
{0xE0C40000u, 17u}, // egy -> Egyp
{0xE1440000u, 32u}, // eky -> Kali
{0x656C0000u, 21u}, // el -> Grek
- {0x656E0000u, 41u}, // en -> Latn
- {0x656E5841u, 86u}, // en-XA -> ~~~A
- {0x656F0000u, 41u}, // eo -> Latn
- {0x65730000u, 41u}, // es -> Latn
- {0xD2440000u, 41u}, // esu -> Latn
- {0x65740000u, 41u}, // et -> Latn
+ {0x81840000u, 40u}, // ema -> Latn
+ {0xA1840000u, 40u}, // emi -> Latn
+ {0x656E0000u, 40u}, // en -> Latn
+ {0x656E5841u, 87u}, // en-XA -> ~~~A
+ {0xB5A40000u, 40u}, // enn -> Latn
+ {0xC1A40000u, 40u}, // enq -> Latn
+ {0x656F0000u, 40u}, // eo -> Latn
+ {0xA2240000u, 40u}, // eri -> Latn
+ {0x65730000u, 40u}, // es -> Latn
+ {0xD2440000u, 40u}, // esu -> Latn
+ {0x65740000u, 40u}, // et -> Latn
+ {0xC6640000u, 40u}, // etr -> Latn
{0xCE640000u, 30u}, // ett -> Ital
- {0x65750000u, 41u}, // eu -> Latn
- {0xBAC40000u, 41u}, // ewo -> Latn
- {0xCEE40000u, 41u}, // ext -> Latn
+ {0xD2640000u, 40u}, // etu -> Latn
+ {0xDE640000u, 40u}, // etx -> Latn
+ {0x65750000u, 40u}, // eu -> Latn
+ {0xBAC40000u, 40u}, // ewo -> Latn
+ {0xCEE40000u, 40u}, // ext -> Latn
{0x66610000u, 1u}, // fa -> Arab
- {0xB4050000u, 41u}, // fan -> Latn
- {0x66660000u, 41u}, // ff -> Latn
- {0xB0A50000u, 41u}, // ffm -> Latn
- {0x66690000u, 41u}, // fi -> Latn
+ {0x80050000u, 40u}, // faa -> Latn
+ {0x84050000u, 40u}, // fab -> Latn
+ {0x98050000u, 40u}, // fag -> Latn
+ {0xA0050000u, 40u}, // fai -> Latn
+ {0xB4050000u, 40u}, // fan -> Latn
+ {0x66660000u, 40u}, // ff -> Latn
+ {0xA0A50000u, 40u}, // ffi -> Latn
+ {0xB0A50000u, 40u}, // ffm -> Latn
+ {0x66690000u, 40u}, // fi -> Latn
{0x81050000u, 1u}, // fia -> Arab
- {0xAD050000u, 41u}, // fil -> Latn
- {0xCD050000u, 41u}, // fit -> Latn
- {0x666A0000u, 41u}, // fj -> Latn
- {0x666F0000u, 41u}, // fo -> Latn
- {0xB5C50000u, 41u}, // fon -> Latn
- {0x66720000u, 41u}, // fr -> Latn
- {0x8A250000u, 41u}, // frc -> Latn
- {0xBE250000u, 41u}, // frp -> Latn
- {0xC6250000u, 41u}, // frr -> Latn
- {0xCA250000u, 41u}, // frs -> Latn
- {0x8E850000u, 41u}, // fud -> Latn
- {0xC2850000u, 41u}, // fuq -> Latn
- {0xC6850000u, 41u}, // fur -> Latn
- {0xD6850000u, 41u}, // fuv -> Latn
- {0xC6A50000u, 41u}, // fvr -> Latn
- {0x66790000u, 41u}, // fy -> Latn
- {0x67610000u, 41u}, // ga -> Latn
- {0x80060000u, 41u}, // gaa -> Latn
- {0x98060000u, 41u}, // gag -> Latn
+ {0xAD050000u, 40u}, // fil -> Latn
+ {0xCD050000u, 40u}, // fit -> Latn
+ {0x666A0000u, 40u}, // fj -> Latn
+ {0xC5650000u, 40u}, // flr -> Latn
+ {0xBD850000u, 40u}, // fmp -> Latn
+ {0x666F0000u, 40u}, // fo -> Latn
+ {0x8DC50000u, 40u}, // fod -> Latn
+ {0xB5C50000u, 40u}, // fon -> Latn
+ {0xC5C50000u, 40u}, // for -> Latn
+ {0x91E50000u, 40u}, // fpe -> Latn
+ {0xCA050000u, 40u}, // fqs -> Latn
+ {0x66720000u, 40u}, // fr -> Latn
+ {0x8A250000u, 40u}, // frc -> Latn
+ {0xBE250000u, 40u}, // frp -> Latn
+ {0xC6250000u, 40u}, // frr -> Latn
+ {0xCA250000u, 40u}, // frs -> Latn
+ {0x86850000u, 1u}, // fub -> Arab
+ {0x8E850000u, 40u}, // fud -> Latn
+ {0x92850000u, 40u}, // fue -> Latn
+ {0x96850000u, 40u}, // fuf -> Latn
+ {0x9E850000u, 40u}, // fuh -> Latn
+ {0xC2850000u, 40u}, // fuq -> Latn
+ {0xC6850000u, 40u}, // fur -> Latn
+ {0xD6850000u, 40u}, // fuv -> Latn
+ {0xE2850000u, 40u}, // fuy -> Latn
+ {0xC6A50000u, 40u}, // fvr -> Latn
+ {0x66790000u, 40u}, // fy -> Latn
+ {0x67610000u, 40u}, // ga -> Latn
+ {0x80060000u, 40u}, // gaa -> Latn
+ {0x94060000u, 40u}, // gaf -> Latn
+ {0x98060000u, 40u}, // gag -> Latn
+ {0x9C060000u, 40u}, // gah -> Latn
+ {0xA4060000u, 40u}, // gaj -> Latn
+ {0xB0060000u, 40u}, // gam -> Latn
{0xB4060000u, 24u}, // gan -> Hans
- {0xE0060000u, 41u}, // gay -> Latn
+ {0xD8060000u, 40u}, // gaw -> Latn
+ {0xE0060000u, 40u}, // gay -> Latn
+ {0x94260000u, 40u}, // gbf -> Latn
{0xB0260000u, 16u}, // gbm -> Deva
+ {0xE0260000u, 40u}, // gby -> Latn
{0xE4260000u, 1u}, // gbz -> Arab
- {0xC4460000u, 41u}, // gcr -> Latn
- {0x67640000u, 41u}, // gd -> Latn
+ {0xC4460000u, 40u}, // gcr -> Latn
+ {0x67640000u, 40u}, // gd -> Latn
+ {0x90660000u, 40u}, // gde -> Latn
+ {0xB4660000u, 40u}, // gdn -> Latn
+ {0xC4660000u, 40u}, // gdr -> Latn
+ {0x84860000u, 40u}, // geb -> Latn
+ {0xA4860000u, 40u}, // gej -> Latn
+ {0xAC860000u, 40u}, // gel -> Latn
{0xE4860000u, 18u}, // gez -> Ethi
+ {0xA8A60000u, 40u}, // gfk -> Latn
{0xB4C60000u, 16u}, // ggn -> Deva
- {0xAD060000u, 41u}, // gil -> Latn
+ {0xC8E60000u, 40u}, // ghs -> Latn
+ {0xAD060000u, 40u}, // gil -> Latn
+ {0xB1060000u, 40u}, // gim -> Latn
{0xA9260000u, 1u}, // gjk -> Arab
+ {0xB5260000u, 40u}, // gjn -> Latn
{0xD1260000u, 1u}, // gju -> Arab
- {0x676C0000u, 41u}, // gl -> Latn
+ {0xB5460000u, 40u}, // gkn -> Latn
+ {0xBD460000u, 40u}, // gkp -> Latn
+ {0x676C0000u, 40u}, // gl -> Latn
{0xA9660000u, 1u}, // glk -> Arab
- {0x676E0000u, 41u}, // gn -> Latn
+ {0xB1860000u, 40u}, // gmm -> Latn
+ {0xD5860000u, 18u}, // gmv -> Ethi
+ {0x676E0000u, 40u}, // gn -> Latn
+ {0x8DA60000u, 40u}, // gnd -> Latn
+ {0x99A60000u, 40u}, // gng -> Latn
+ {0x8DC60000u, 40u}, // god -> Latn
+ {0x95C60000u, 18u}, // gof -> Ethi
+ {0xA1C60000u, 40u}, // goi -> Latn
{0xB1C60000u, 16u}, // gom -> Deva
- {0xB5C60000u, 76u}, // gon -> Telu
- {0xC5C60000u, 41u}, // gor -> Latn
- {0xC9C60000u, 41u}, // gos -> Latn
+ {0xB5C60000u, 77u}, // gon -> Telu
+ {0xC5C60000u, 40u}, // gor -> Latn
+ {0xC9C60000u, 40u}, // gos -> Latn
{0xCDC60000u, 20u}, // got -> Goth
{0x8A260000u, 14u}, // grc -> Cprt
{0xCE260000u, 7u}, // grt -> Beng
- {0xDA460000u, 41u}, // gsw -> Latn
+ {0xDA260000u, 40u}, // grw -> Latn
+ {0xDA460000u, 40u}, // gsw -> Latn
{0x67750000u, 22u}, // gu -> Gujr
- {0x86860000u, 41u}, // gub -> Latn
- {0x8A860000u, 41u}, // guc -> Latn
- {0xC6860000u, 41u}, // gur -> Latn
- {0xE6860000u, 41u}, // guz -> Latn
- {0x67760000u, 41u}, // gv -> Latn
+ {0x86860000u, 40u}, // gub -> Latn
+ {0x8A860000u, 40u}, // guc -> Latn
+ {0x8E860000u, 40u}, // gud -> Latn
+ {0xC6860000u, 40u}, // gur -> Latn
+ {0xDA860000u, 40u}, // guw -> Latn
+ {0xDE860000u, 40u}, // gux -> Latn
+ {0xE6860000u, 40u}, // guz -> Latn
+ {0x67760000u, 40u}, // gv -> Latn
+ {0x96A60000u, 40u}, // gvf -> Latn
{0xC6A60000u, 16u}, // gvr -> Deva
- {0xA2C60000u, 41u}, // gwi -> Latn
- {0x68610000u, 41u}, // ha -> Latn
+ {0xCAA60000u, 40u}, // gvs -> Latn
+ {0x8AC60000u, 1u}, // gwc -> Arab
+ {0xA2C60000u, 40u}, // gwi -> Latn
+ {0xCEC60000u, 1u}, // gwt -> Arab
+ {0xA3060000u, 40u}, // gyi -> Latn
+ {0x68610000u, 40u}, // ha -> Latn
{0x6861434Du, 1u}, // ha-CM -> Arab
{0x68615344u, 1u}, // ha-SD -> Arab
+ {0x98070000u, 40u}, // hag -> Latn
{0xA8070000u, 24u}, // hak -> Hans
- {0xD8070000u, 41u}, // haw -> Latn
+ {0xB0070000u, 40u}, // ham -> Latn
+ {0xD8070000u, 40u}, // haw -> Latn
{0xE4070000u, 1u}, // haz -> Arab
+ {0x84270000u, 40u}, // hbb -> Latn
+ {0xE0670000u, 18u}, // hdy -> Ethi
{0x68650000u, 27u}, // he -> Hebr
+ {0xE0E70000u, 40u}, // hhy -> Latn
{0x68690000u, 16u}, // hi -> Deva
- {0x95070000u, 41u}, // hif -> Latn
- {0xAD070000u, 41u}, // hil -> Latn
+ {0x81070000u, 40u}, // hia -> Latn
+ {0x95070000u, 40u}, // hif -> Latn
+ {0x99070000u, 40u}, // hig -> Latn
+ {0x9D070000u, 40u}, // hih -> Latn
+ {0xAD070000u, 40u}, // hil -> Latn
+ {0x81670000u, 40u}, // hla -> Latn
{0xD1670000u, 28u}, // hlu -> Hluw
{0x8D870000u, 62u}, // hmd -> Plrd
+ {0xCD870000u, 40u}, // hmt -> Latn
{0x8DA70000u, 1u}, // hnd -> Arab
{0x91A70000u, 16u}, // hne -> Deva
{0xA5A70000u, 29u}, // hnj -> Hmng
- {0xB5A70000u, 41u}, // hnn -> Latn
+ {0xB5A70000u, 40u}, // hnn -> Latn
{0xB9A70000u, 1u}, // hno -> Arab
- {0x686F0000u, 41u}, // ho -> Latn
+ {0x686F0000u, 40u}, // ho -> Latn
{0x89C70000u, 16u}, // hoc -> Deva
{0xA5C70000u, 16u}, // hoj -> Deva
- {0x68720000u, 41u}, // hr -> Latn
- {0x86470000u, 41u}, // hsb -> Latn
+ {0xCDC70000u, 40u}, // hot -> Latn
+ {0x68720000u, 40u}, // hr -> Latn
+ {0x86470000u, 40u}, // hsb -> Latn
{0xB6470000u, 24u}, // hsn -> Hans
- {0x68740000u, 41u}, // ht -> Latn
- {0x68750000u, 41u}, // hu -> Latn
+ {0x68740000u, 40u}, // ht -> Latn
+ {0x68750000u, 40u}, // hu -> Latn
+ {0xA2870000u, 40u}, // hui -> Latn
{0x68790000u, 3u}, // hy -> Armn
- {0x687A0000u, 41u}, // hz -> Latn
- {0x69610000u, 41u}, // ia -> Latn
- {0x80280000u, 41u}, // iba -> Latn
- {0x84280000u, 41u}, // ibb -> Latn
- {0x69640000u, 41u}, // id -> Latn
- {0x69670000u, 41u}, // ig -> Latn
- {0x69690000u, 85u}, // ii -> Yiii
- {0x696B0000u, 41u}, // ik -> Latn
- {0xCD480000u, 41u}, // ikt -> Latn
- {0xB9680000u, 41u}, // ilo -> Latn
- {0x696E0000u, 41u}, // in -> Latn
+ {0x687A0000u, 40u}, // hz -> Latn
+ {0x69610000u, 40u}, // ia -> Latn
+ {0xB4080000u, 40u}, // ian -> Latn
+ {0xC4080000u, 40u}, // iar -> Latn
+ {0x80280000u, 40u}, // iba -> Latn
+ {0x84280000u, 40u}, // ibb -> Latn
+ {0xE0280000u, 40u}, // iby -> Latn
+ {0x80480000u, 40u}, // ica -> Latn
+ {0x9C480000u, 40u}, // ich -> Latn
+ {0x69640000u, 40u}, // id -> Latn
+ {0x8C680000u, 40u}, // idd -> Latn
+ {0xA0680000u, 40u}, // idi -> Latn
+ {0xD0680000u, 40u}, // idu -> Latn
+ {0x69670000u, 40u}, // ig -> Latn
+ {0x84C80000u, 40u}, // igb -> Latn
+ {0x90C80000u, 40u}, // ige -> Latn
+ {0x69690000u, 86u}, // ii -> Yiii
+ {0xA5280000u, 40u}, // ijj -> Latn
+ {0x696B0000u, 40u}, // ik -> Latn
+ {0xA9480000u, 40u}, // ikk -> Latn
+ {0xCD480000u, 40u}, // ikt -> Latn
+ {0xD9480000u, 40u}, // ikw -> Latn
+ {0xDD480000u, 40u}, // ikx -> Latn
+ {0xB9680000u, 40u}, // ilo -> Latn
+ {0xB9880000u, 40u}, // imo -> Latn
+ {0x696E0000u, 40u}, // in -> Latn
{0x9DA80000u, 15u}, // inh -> Cyrl
- {0x69730000u, 41u}, // is -> Latn
- {0x69740000u, 41u}, // it -> Latn
+ {0xD1C80000u, 40u}, // iou -> Latn
+ {0xA2280000u, 40u}, // iri -> Latn
+ {0x69730000u, 40u}, // is -> Latn
+ {0x69740000u, 40u}, // it -> Latn
{0x69750000u, 9u}, // iu -> Cans
{0x69770000u, 27u}, // iw -> Hebr
- {0x9F280000u, 41u}, // izh -> Latn
+ {0xB2C80000u, 40u}, // iwm -> Latn
+ {0xCAC80000u, 40u}, // iws -> Latn
+ {0x9F280000u, 40u}, // izh -> Latn
+ {0xA3280000u, 40u}, // izi -> Latn
{0x6A610000u, 31u}, // ja -> Jpan
- {0xB0090000u, 41u}, // jam -> Latn
- {0xB8C90000u, 41u}, // jgo -> Latn
+ {0x84090000u, 40u}, // jab -> Latn
+ {0xB0090000u, 40u}, // jam -> Latn
+ {0xD0290000u, 40u}, // jbu -> Latn
+ {0xB4890000u, 40u}, // jen -> Latn
+ {0xA8C90000u, 40u}, // jgk -> Latn
+ {0xB8C90000u, 40u}, // jgo -> Latn
{0x6A690000u, 27u}, // ji -> Hebr
- {0x89890000u, 41u}, // jmc -> Latn
+ {0x85090000u, 40u}, // jib -> Latn
+ {0x89890000u, 40u}, // jmc -> Latn
{0xAD890000u, 16u}, // jml -> Deva
- {0xCE890000u, 41u}, // jut -> Latn
- {0x6A760000u, 41u}, // jv -> Latn
- {0x6A770000u, 41u}, // jw -> Latn
+ {0x82290000u, 40u}, // jra -> Latn
+ {0xCE890000u, 40u}, // jut -> Latn
+ {0x6A760000u, 40u}, // jv -> Latn
+ {0x6A770000u, 40u}, // jw -> Latn
{0x6B610000u, 19u}, // ka -> Geor
{0x800A0000u, 15u}, // kaa -> Cyrl
- {0x840A0000u, 41u}, // kab -> Latn
- {0x880A0000u, 41u}, // kac -> Latn
- {0xA40A0000u, 41u}, // kaj -> Latn
- {0xB00A0000u, 41u}, // kam -> Latn
- {0xB80A0000u, 41u}, // kao -> Latn
+ {0x840A0000u, 40u}, // kab -> Latn
+ {0x880A0000u, 40u}, // kac -> Latn
+ {0x8C0A0000u, 40u}, // kad -> Latn
+ {0xA00A0000u, 40u}, // kai -> Latn
+ {0xA40A0000u, 40u}, // kaj -> Latn
+ {0xB00A0000u, 40u}, // kam -> Latn
+ {0xB80A0000u, 40u}, // kao -> Latn
{0x8C2A0000u, 15u}, // kbd -> Cyrl
- {0x984A0000u, 41u}, // kcg -> Latn
- {0xA84A0000u, 41u}, // kck -> Latn
- {0x906A0000u, 41u}, // kde -> Latn
- {0xCC6A0000u, 79u}, // kdt -> Thai
- {0x808A0000u, 41u}, // kea -> Latn
- {0xB48A0000u, 41u}, // ken -> Latn
- {0xB8AA0000u, 41u}, // kfo -> Latn
+ {0xB02A0000u, 40u}, // kbm -> Latn
+ {0xBC2A0000u, 40u}, // kbp -> Latn
+ {0xC02A0000u, 40u}, // kbq -> Latn
+ {0xDC2A0000u, 40u}, // kbx -> Latn
+ {0xE02A0000u, 1u}, // kby -> Arab
+ {0x984A0000u, 40u}, // kcg -> Latn
+ {0xA84A0000u, 40u}, // kck -> Latn
+ {0xAC4A0000u, 40u}, // kcl -> Latn
+ {0xCC4A0000u, 40u}, // kct -> Latn
+ {0x906A0000u, 40u}, // kde -> Latn
+ {0x9C6A0000u, 1u}, // kdh -> Arab
+ {0xAC6A0000u, 40u}, // kdl -> Latn
+ {0xCC6A0000u, 80u}, // kdt -> Thai
+ {0x808A0000u, 40u}, // kea -> Latn
+ {0xB48A0000u, 40u}, // ken -> Latn
+ {0xE48A0000u, 40u}, // kez -> Latn
+ {0xB8AA0000u, 40u}, // kfo -> Latn
{0xC4AA0000u, 16u}, // kfr -> Deva
{0xE0AA0000u, 16u}, // kfy -> Deva
- {0x6B670000u, 41u}, // kg -> Latn
- {0x90CA0000u, 41u}, // kge -> Latn
- {0xBCCA0000u, 41u}, // kgp -> Latn
- {0x80EA0000u, 41u}, // kha -> Latn
+ {0x6B670000u, 40u}, // kg -> Latn
+ {0x90CA0000u, 40u}, // kge -> Latn
+ {0x94CA0000u, 40u}, // kgf -> Latn
+ {0xBCCA0000u, 40u}, // kgp -> Latn
+ {0x80EA0000u, 40u}, // kha -> Latn
{0x84EA0000u, 73u}, // khb -> Talu
{0xB4EA0000u, 16u}, // khn -> Deva
- {0xC0EA0000u, 41u}, // khq -> Latn
- {0xCCEA0000u, 53u}, // kht -> Mymr
+ {0xC0EA0000u, 40u}, // khq -> Latn
+ {0xC8EA0000u, 40u}, // khs -> Latn
+ {0xCCEA0000u, 52u}, // kht -> Mymr
{0xD8EA0000u, 1u}, // khw -> Arab
- {0x6B690000u, 41u}, // ki -> Latn
- {0xD10A0000u, 41u}, // kiu -> Latn
- {0x6B6A0000u, 41u}, // kj -> Latn
- {0x992A0000u, 40u}, // kjg -> Laoo
+ {0xE4EA0000u, 40u}, // khz -> Latn
+ {0x6B690000u, 40u}, // ki -> Latn
+ {0xA50A0000u, 40u}, // kij -> Latn
+ {0xD10A0000u, 40u}, // kiu -> Latn
+ {0xD90A0000u, 40u}, // kiw -> Latn
+ {0x6B6A0000u, 40u}, // kj -> Latn
+ {0x8D2A0000u, 40u}, // kjd -> Latn
+ {0x992A0000u, 39u}, // kjg -> Laoo
+ {0xC92A0000u, 40u}, // kjs -> Latn
+ {0xE12A0000u, 40u}, // kjy -> Latn
{0x6B6B0000u, 15u}, // kk -> Cyrl
{0x6B6B4146u, 1u}, // kk-AF -> Arab
{0x6B6B434Eu, 1u}, // kk-CN -> Arab
{0x6B6B4952u, 1u}, // kk-IR -> Arab
{0x6B6B4D4Eu, 1u}, // kk-MN -> Arab
- {0xA54A0000u, 41u}, // kkj -> Latn
- {0x6B6C0000u, 41u}, // kl -> Latn
- {0xB56A0000u, 41u}, // kln -> Latn
+ {0x894A0000u, 40u}, // kkc -> Latn
+ {0xA54A0000u, 40u}, // kkj -> Latn
+ {0x6B6C0000u, 40u}, // kl -> Latn
+ {0xB56A0000u, 40u}, // kln -> Latn
+ {0xC16A0000u, 40u}, // klq -> Latn
+ {0xCD6A0000u, 40u}, // klt -> Latn
+ {0xDD6A0000u, 40u}, // klx -> Latn
{0x6B6D0000u, 35u}, // km -> Khmr
- {0x858A0000u, 41u}, // kmb -> Latn
+ {0x858A0000u, 40u}, // kmb -> Latn
+ {0x9D8A0000u, 40u}, // kmh -> Latn
+ {0xB98A0000u, 40u}, // kmo -> Latn
+ {0xC98A0000u, 40u}, // kms -> Latn
+ {0xD18A0000u, 40u}, // kmu -> Latn
+ {0xD98A0000u, 40u}, // kmw -> Latn
{0x6B6E0000u, 36u}, // kn -> Knda
+ {0xBDAA0000u, 40u}, // knp -> Latn
{0x6B6F0000u, 37u}, // ko -> Kore
{0xA1CA0000u, 15u}, // koi -> Cyrl
{0xA9CA0000u, 16u}, // kok -> Deva
- {0xC9CA0000u, 41u}, // kos -> Latn
- {0x91EA0000u, 41u}, // kpe -> Latn
+ {0xADCA0000u, 40u}, // kol -> Latn
+ {0xC9CA0000u, 40u}, // kos -> Latn
+ {0xE5CA0000u, 40u}, // koz -> Latn
+ {0x91EA0000u, 40u}, // kpe -> Latn
+ {0x95EA0000u, 40u}, // kpf -> Latn
+ {0xB9EA0000u, 40u}, // kpo -> Latn
+ {0xC5EA0000u, 40u}, // kpr -> Latn
+ {0xDDEA0000u, 40u}, // kpx -> Latn
+ {0x860A0000u, 40u}, // kqb -> Latn
+ {0x960A0000u, 40u}, // kqf -> Latn
+ {0xCA0A0000u, 40u}, // kqs -> Latn
+ {0xE20A0000u, 18u}, // kqy -> Ethi
{0x8A2A0000u, 15u}, // krc -> Cyrl
- {0xA22A0000u, 41u}, // kri -> Latn
- {0xA62A0000u, 41u}, // krj -> Latn
- {0xAE2A0000u, 41u}, // krl -> Latn
+ {0xA22A0000u, 40u}, // kri -> Latn
+ {0xA62A0000u, 40u}, // krj -> Latn
+ {0xAE2A0000u, 40u}, // krl -> Latn
+ {0xCA2A0000u, 40u}, // krs -> Latn
{0xD22A0000u, 16u}, // kru -> Deva
{0x6B730000u, 1u}, // ks -> Arab
- {0x864A0000u, 41u}, // ksb -> Latn
- {0x964A0000u, 41u}, // ksf -> Latn
- {0x9E4A0000u, 41u}, // ksh -> Latn
- {0x6B750000u, 41u}, // ku -> Latn
+ {0x864A0000u, 40u}, // ksb -> Latn
+ {0x8E4A0000u, 40u}, // ksd -> Latn
+ {0x964A0000u, 40u}, // ksf -> Latn
+ {0x9E4A0000u, 40u}, // ksh -> Latn
+ {0xA64A0000u, 40u}, // ksj -> Latn
+ {0xC64A0000u, 40u}, // ksr -> Latn
+ {0x866A0000u, 18u}, // ktb -> Ethi
+ {0xB26A0000u, 40u}, // ktm -> Latn
+ {0xBA6A0000u, 40u}, // kto -> Latn
+ {0x6B750000u, 40u}, // ku -> Latn
{0x6B754952u, 1u}, // ku-IR -> Arab
{0x6B754C42u, 1u}, // ku-LB -> Arab
+ {0x868A0000u, 40u}, // kub -> Latn
+ {0x8E8A0000u, 40u}, // kud -> Latn
+ {0x928A0000u, 40u}, // kue -> Latn
+ {0xA68A0000u, 40u}, // kuj -> Latn
{0xB28A0000u, 15u}, // kum -> Cyrl
+ {0xB68A0000u, 40u}, // kun -> Latn
+ {0xBE8A0000u, 40u}, // kup -> Latn
+ {0xCA8A0000u, 40u}, // kus -> Latn
{0x6B760000u, 15u}, // kv -> Cyrl
- {0xC6AA0000u, 41u}, // kvr -> Latn
+ {0x9AAA0000u, 40u}, // kvg -> Latn
+ {0xC6AA0000u, 40u}, // kvr -> Latn
{0xDEAA0000u, 1u}, // kvx -> Arab
- {0x6B770000u, 41u}, // kw -> Latn
- {0xB2EA0000u, 79u}, // kxm -> Thai
+ {0x6B770000u, 40u}, // kw -> Latn
+ {0xA6CA0000u, 40u}, // kwj -> Latn
+ {0xBACA0000u, 40u}, // kwo -> Latn
+ {0x82EA0000u, 40u}, // kxa -> Latn
+ {0x8AEA0000u, 18u}, // kxc -> Ethi
+ {0xB2EA0000u, 80u}, // kxm -> Thai
{0xBEEA0000u, 1u}, // kxp -> Arab
+ {0xDAEA0000u, 40u}, // kxw -> Latn
+ {0xE6EA0000u, 40u}, // kxz -> Latn
{0x6B790000u, 15u}, // ky -> Cyrl
{0x6B79434Eu, 1u}, // ky-CN -> Arab
- {0x6B795452u, 41u}, // ky-TR -> Latn
- {0x6C610000u, 41u}, // la -> Latn
- {0x840B0000u, 43u}, // lab -> Lina
+ {0x6B795452u, 40u}, // ky-TR -> Latn
+ {0x930A0000u, 40u}, // kye -> Latn
+ {0xDF0A0000u, 40u}, // kyx -> Latn
+ {0xC72A0000u, 40u}, // kzr -> Latn
+ {0x6C610000u, 40u}, // la -> Latn
+ {0x840B0000u, 42u}, // lab -> Lina
{0x8C0B0000u, 27u}, // lad -> Hebr
- {0x980B0000u, 41u}, // lag -> Latn
+ {0x980B0000u, 40u}, // lag -> Latn
{0x9C0B0000u, 1u}, // lah -> Arab
- {0xA40B0000u, 41u}, // laj -> Latn
- {0x6C620000u, 41u}, // lb -> Latn
+ {0xA40B0000u, 40u}, // laj -> Latn
+ {0xC80B0000u, 40u}, // las -> Latn
+ {0x6C620000u, 40u}, // lb -> Latn
{0x902B0000u, 15u}, // lbe -> Cyrl
- {0xD82B0000u, 41u}, // lbw -> Latn
- {0xBC4B0000u, 79u}, // lcp -> Thai
- {0xBC8B0000u, 42u}, // lep -> Lepc
+ {0xD02B0000u, 40u}, // lbu -> Latn
+ {0xD82B0000u, 40u}, // lbw -> Latn
+ {0xB04B0000u, 40u}, // lcm -> Latn
+ {0xBC4B0000u, 80u}, // lcp -> Thai
+ {0x846B0000u, 40u}, // ldb -> Latn
+ {0x8C8B0000u, 40u}, // led -> Latn
+ {0x908B0000u, 40u}, // lee -> Latn
+ {0xB08B0000u, 40u}, // lem -> Latn
+ {0xBC8B0000u, 41u}, // lep -> Lepc
+ {0xC08B0000u, 40u}, // leq -> Latn
+ {0xD08B0000u, 40u}, // leu -> Latn
{0xE48B0000u, 15u}, // lez -> Cyrl
- {0x6C670000u, 41u}, // lg -> Latn
- {0x6C690000u, 41u}, // li -> Latn
+ {0x6C670000u, 40u}, // lg -> Latn
+ {0x98CB0000u, 40u}, // lgg -> Latn
+ {0x6C690000u, 40u}, // li -> Latn
+ {0x810B0000u, 40u}, // lia -> Latn
+ {0x8D0B0000u, 40u}, // lid -> Latn
{0x950B0000u, 16u}, // lif -> Deva
- {0xA50B0000u, 41u}, // lij -> Latn
- {0xC90B0000u, 44u}, // lis -> Lisu
- {0xBD2B0000u, 41u}, // ljp -> Latn
+ {0x990B0000u, 40u}, // lig -> Latn
+ {0x9D0B0000u, 40u}, // lih -> Latn
+ {0xA50B0000u, 40u}, // lij -> Latn
+ {0xC90B0000u, 43u}, // lis -> Lisu
+ {0xBD2B0000u, 40u}, // ljp -> Latn
{0xA14B0000u, 1u}, // lki -> Arab
- {0xCD4B0000u, 41u}, // lkt -> Latn
- {0xB58B0000u, 76u}, // lmn -> Telu
- {0xB98B0000u, 41u}, // lmo -> Latn
- {0x6C6E0000u, 41u}, // ln -> Latn
- {0x6C6F0000u, 40u}, // lo -> Laoo
- {0xADCB0000u, 41u}, // lol -> Latn
- {0xE5CB0000u, 41u}, // loz -> Latn
+ {0xCD4B0000u, 40u}, // lkt -> Latn
+ {0x916B0000u, 40u}, // lle -> Latn
+ {0xB56B0000u, 40u}, // lln -> Latn
+ {0xB58B0000u, 77u}, // lmn -> Telu
+ {0xB98B0000u, 40u}, // lmo -> Latn
+ {0xBD8B0000u, 40u}, // lmp -> Latn
+ {0x6C6E0000u, 40u}, // ln -> Latn
+ {0xC9AB0000u, 40u}, // lns -> Latn
+ {0xD1AB0000u, 40u}, // lnu -> Latn
+ {0x6C6F0000u, 39u}, // lo -> Laoo
+ {0xA5CB0000u, 40u}, // loj -> Latn
+ {0xA9CB0000u, 40u}, // lok -> Latn
+ {0xADCB0000u, 40u}, // lol -> Latn
+ {0xC5CB0000u, 40u}, // lor -> Latn
+ {0xC9CB0000u, 40u}, // los -> Latn
+ {0xE5CB0000u, 40u}, // loz -> Latn
{0x8A2B0000u, 1u}, // lrc -> Arab
- {0x6C740000u, 41u}, // lt -> Latn
- {0x9A6B0000u, 41u}, // ltg -> Latn
- {0x6C750000u, 41u}, // lu -> Latn
- {0x828B0000u, 41u}, // lua -> Latn
- {0xBA8B0000u, 41u}, // luo -> Latn
- {0xE28B0000u, 41u}, // luy -> Latn
+ {0x6C740000u, 40u}, // lt -> Latn
+ {0x9A6B0000u, 40u}, // ltg -> Latn
+ {0x6C750000u, 40u}, // lu -> Latn
+ {0x828B0000u, 40u}, // lua -> Latn
+ {0xBA8B0000u, 40u}, // luo -> Latn
+ {0xE28B0000u, 40u}, // luy -> Latn
{0xE68B0000u, 1u}, // luz -> Arab
- {0x6C760000u, 41u}, // lv -> Latn
- {0xAECB0000u, 79u}, // lwl -> Thai
+ {0x6C760000u, 40u}, // lv -> Latn
+ {0xAECB0000u, 80u}, // lwl -> Thai
{0x9F2B0000u, 24u}, // lzh -> Hans
- {0xE72B0000u, 41u}, // lzz -> Latn
- {0x8C0C0000u, 41u}, // mad -> Latn
- {0x940C0000u, 41u}, // maf -> Latn
+ {0xE72B0000u, 40u}, // lzz -> Latn
+ {0x8C0C0000u, 40u}, // mad -> Latn
+ {0x940C0000u, 40u}, // maf -> Latn
{0x980C0000u, 16u}, // mag -> Deva
{0xA00C0000u, 16u}, // mai -> Deva
- {0xA80C0000u, 41u}, // mak -> Latn
- {0xB40C0000u, 41u}, // man -> Latn
- {0xB40C474Eu, 55u}, // man-GN -> Nkoo
- {0xC80C0000u, 41u}, // mas -> Latn
- {0xE40C0000u, 41u}, // maz -> Latn
+ {0xA80C0000u, 40u}, // mak -> Latn
+ {0xB40C0000u, 40u}, // man -> Latn
+ {0xB40C474Eu, 54u}, // man-GN -> Nkoo
+ {0xC80C0000u, 40u}, // mas -> Latn
+ {0xD80C0000u, 40u}, // maw -> Latn
+ {0xE40C0000u, 40u}, // maz -> Latn
+ {0x9C2C0000u, 40u}, // mbh -> Latn
+ {0xB82C0000u, 40u}, // mbo -> Latn
+ {0xC02C0000u, 40u}, // mbq -> Latn
+ {0xD02C0000u, 40u}, // mbu -> Latn
+ {0xD82C0000u, 40u}, // mbw -> Latn
+ {0xA04C0000u, 40u}, // mci -> Latn
+ {0xBC4C0000u, 40u}, // mcp -> Latn
+ {0xC04C0000u, 40u}, // mcq -> Latn
+ {0xC44C0000u, 40u}, // mcr -> Latn
+ {0xD04C0000u, 40u}, // mcu -> Latn
+ {0x806C0000u, 40u}, // mda -> Latn
+ {0x906C0000u, 1u}, // mde -> Arab
{0x946C0000u, 15u}, // mdf -> Cyrl
- {0x9C6C0000u, 41u}, // mdh -> Latn
- {0xC46C0000u, 41u}, // mdr -> Latn
- {0xB48C0000u, 41u}, // men -> Latn
- {0xC48C0000u, 41u}, // mer -> Latn
+ {0x9C6C0000u, 40u}, // mdh -> Latn
+ {0xA46C0000u, 40u}, // mdj -> Latn
+ {0xC46C0000u, 40u}, // mdr -> Latn
+ {0xDC6C0000u, 18u}, // mdx -> Ethi
+ {0x8C8C0000u, 40u}, // med -> Latn
+ {0x908C0000u, 40u}, // mee -> Latn
+ {0xA88C0000u, 40u}, // mek -> Latn
+ {0xB48C0000u, 40u}, // men -> Latn
+ {0xC48C0000u, 40u}, // mer -> Latn
+ {0xCC8C0000u, 40u}, // met -> Latn
+ {0xD08C0000u, 40u}, // meu -> Latn
{0x80AC0000u, 1u}, // mfa -> Arab
- {0x90AC0000u, 41u}, // mfe -> Latn
- {0x6D670000u, 41u}, // mg -> Latn
- {0x9CCC0000u, 41u}, // mgh -> Latn
- {0xB8CC0000u, 41u}, // mgo -> Latn
+ {0x90AC0000u, 40u}, // mfe -> Latn
+ {0xB4AC0000u, 40u}, // mfn -> Latn
+ {0xB8AC0000u, 40u}, // mfo -> Latn
+ {0xC0AC0000u, 40u}, // mfq -> Latn
+ {0x6D670000u, 40u}, // mg -> Latn
+ {0x9CCC0000u, 40u}, // mgh -> Latn
+ {0xACCC0000u, 40u}, // mgl -> Latn
+ {0xB8CC0000u, 40u}, // mgo -> Latn
{0xBCCC0000u, 16u}, // mgp -> Deva
- {0xE0CC0000u, 41u}, // mgy -> Latn
- {0x6D680000u, 41u}, // mh -> Latn
- {0x6D690000u, 41u}, // mi -> Latn
- {0xB50C0000u, 41u}, // min -> Latn
+ {0xE0CC0000u, 40u}, // mgy -> Latn
+ {0x6D680000u, 40u}, // mh -> Latn
+ {0xA0EC0000u, 40u}, // mhi -> Latn
+ {0xACEC0000u, 40u}, // mhl -> Latn
+ {0x6D690000u, 40u}, // mi -> Latn
+ {0x950C0000u, 40u}, // mif -> Latn
+ {0xB50C0000u, 40u}, // min -> Latn
{0xC90C0000u, 26u}, // mis -> Hatr
+ {0xD90C0000u, 40u}, // miw -> Latn
{0x6D6B0000u, 15u}, // mk -> Cyrl
- {0x6D6C0000u, 50u}, // ml -> Mlym
- {0xC96C0000u, 41u}, // mls -> Latn
+ {0xA14C0000u, 1u}, // mki -> Arab
+ {0xAD4C0000u, 40u}, // mkl -> Latn
+ {0xBD4C0000u, 40u}, // mkp -> Latn
+ {0xD94C0000u, 40u}, // mkw -> Latn
+ {0x6D6C0000u, 49u}, // ml -> Mlym
+ {0x916C0000u, 40u}, // mle -> Latn
+ {0xBD6C0000u, 40u}, // mlp -> Latn
+ {0xC96C0000u, 40u}, // mls -> Latn
+ {0xB98C0000u, 40u}, // mmo -> Latn
+ {0xD18C0000u, 40u}, // mmu -> Latn
+ {0xDD8C0000u, 40u}, // mmx -> Latn
{0x6D6E0000u, 15u}, // mn -> Cyrl
- {0x6D6E434Eu, 51u}, // mn-CN -> Mong
+ {0x6D6E434Eu, 50u}, // mn-CN -> Mong
+ {0x81AC0000u, 40u}, // mna -> Latn
+ {0x95AC0000u, 40u}, // mnf -> Latn
{0xA1AC0000u, 7u}, // mni -> Beng
- {0xD9AC0000u, 53u}, // mnw -> Mymr
- {0x91CC0000u, 41u}, // moe -> Latn
- {0x9DCC0000u, 41u}, // moh -> Latn
- {0xC9CC0000u, 41u}, // mos -> Latn
+ {0xD9AC0000u, 52u}, // mnw -> Mymr
+ {0x81CC0000u, 40u}, // moa -> Latn
+ {0x91CC0000u, 40u}, // moe -> Latn
+ {0x9DCC0000u, 40u}, // moh -> Latn
+ {0xC9CC0000u, 40u}, // mos -> Latn
+ {0xDDCC0000u, 40u}, // mox -> Latn
+ {0xBDEC0000u, 40u}, // mpp -> Latn
+ {0xC9EC0000u, 40u}, // mps -> Latn
+ {0xCDEC0000u, 40u}, // mpt -> Latn
+ {0xDDEC0000u, 40u}, // mpx -> Latn
+ {0xAE0C0000u, 40u}, // mql -> Latn
{0x6D720000u, 16u}, // mr -> Deva
{0x8E2C0000u, 16u}, // mrd -> Deva
{0xA62C0000u, 15u}, // mrj -> Cyrl
- {0xD22C0000u, 52u}, // mru -> Mroo
- {0x6D730000u, 41u}, // ms -> Latn
+ {0xBA2C0000u, 51u}, // mro -> Mroo
+ {0x6D730000u, 40u}, // ms -> Latn
{0x6D734343u, 1u}, // ms-CC -> Arab
{0x6D734944u, 1u}, // ms-ID -> Arab
- {0x6D740000u, 41u}, // mt -> Latn
+ {0x6D740000u, 40u}, // mt -> Latn
+ {0x8A6C0000u, 40u}, // mtc -> Latn
+ {0x966C0000u, 40u}, // mtf -> Latn
+ {0xA26C0000u, 40u}, // mti -> Latn
{0xC66C0000u, 16u}, // mtr -> Deva
- {0x828C0000u, 41u}, // mua -> Latn
- {0xCA8C0000u, 41u}, // mus -> Latn
+ {0x828C0000u, 40u}, // mua -> Latn
+ {0xC68C0000u, 40u}, // mur -> Latn
+ {0xCA8C0000u, 40u}, // mus -> Latn
+ {0x82AC0000u, 40u}, // mva -> Latn
+ {0xB6AC0000u, 40u}, // mvn -> Latn
{0xE2AC0000u, 1u}, // mvy -> Arab
- {0xAACC0000u, 41u}, // mwk -> Latn
+ {0xAACC0000u, 40u}, // mwk -> Latn
{0xC6CC0000u, 16u}, // mwr -> Deva
- {0xD6CC0000u, 41u}, // mwv -> Latn
- {0x8AEC0000u, 41u}, // mxc -> Latn
- {0x6D790000u, 53u}, // my -> Mymr
+ {0xD6CC0000u, 40u}, // mwv -> Latn
+ {0x8AEC0000u, 40u}, // mxc -> Latn
+ {0xB2EC0000u, 40u}, // mxm -> Latn
+ {0x6D790000u, 52u}, // my -> Mymr
+ {0xAB0C0000u, 40u}, // myk -> Latn
+ {0xB30C0000u, 18u}, // mym -> Ethi
{0xD70C0000u, 15u}, // myv -> Cyrl
- {0xDF0C0000u, 41u}, // myx -> Latn
- {0xE70C0000u, 47u}, // myz -> Mand
+ {0xDB0C0000u, 40u}, // myw -> Latn
+ {0xDF0C0000u, 40u}, // myx -> Latn
+ {0xE70C0000u, 46u}, // myz -> Mand
+ {0xAB2C0000u, 40u}, // mzk -> Latn
+ {0xB32C0000u, 40u}, // mzm -> Latn
{0xB72C0000u, 1u}, // mzn -> Arab
- {0x6E610000u, 41u}, // na -> Latn
+ {0xBF2C0000u, 40u}, // mzp -> Latn
+ {0xDB2C0000u, 40u}, // mzw -> Latn
+ {0xE72C0000u, 40u}, // mzz -> Latn
+ {0x6E610000u, 40u}, // na -> Latn
+ {0x880D0000u, 40u}, // nac -> Latn
+ {0x940D0000u, 40u}, // naf -> Latn
+ {0xA80D0000u, 40u}, // nak -> Latn
{0xB40D0000u, 24u}, // nan -> Hans
- {0xBC0D0000u, 41u}, // nap -> Latn
- {0xC00D0000u, 41u}, // naq -> Latn
- {0x6E620000u, 41u}, // nb -> Latn
- {0x9C4D0000u, 41u}, // nch -> Latn
- {0x6E640000u, 41u}, // nd -> Latn
- {0x886D0000u, 41u}, // ndc -> Latn
- {0xC86D0000u, 41u}, // nds -> Latn
+ {0xBC0D0000u, 40u}, // nap -> Latn
+ {0xC00D0000u, 40u}, // naq -> Latn
+ {0xC80D0000u, 40u}, // nas -> Latn
+ {0x6E620000u, 40u}, // nb -> Latn
+ {0x804D0000u, 40u}, // nca -> Latn
+ {0x904D0000u, 40u}, // nce -> Latn
+ {0x944D0000u, 40u}, // ncf -> Latn
+ {0x9C4D0000u, 40u}, // nch -> Latn
+ {0xB84D0000u, 40u}, // nco -> Latn
+ {0xD04D0000u, 40u}, // ncu -> Latn
+ {0x6E640000u, 40u}, // nd -> Latn
+ {0x886D0000u, 40u}, // ndc -> Latn
+ {0xC86D0000u, 40u}, // nds -> Latn
{0x6E650000u, 16u}, // ne -> Deva
+ {0x848D0000u, 40u}, // neb -> Latn
{0xD88D0000u, 16u}, // new -> Deva
- {0x6E670000u, 41u}, // ng -> Latn
- {0xACCD0000u, 41u}, // ngl -> Latn
- {0x90ED0000u, 41u}, // nhe -> Latn
- {0xD8ED0000u, 41u}, // nhw -> Latn
- {0xA50D0000u, 41u}, // nij -> Latn
- {0xD10D0000u, 41u}, // niu -> Latn
- {0xB92D0000u, 41u}, // njo -> Latn
- {0x6E6C0000u, 41u}, // nl -> Latn
- {0x998D0000u, 41u}, // nmg -> Latn
- {0x6E6E0000u, 41u}, // nn -> Latn
- {0x9DAD0000u, 41u}, // nnh -> Latn
- {0x6E6F0000u, 41u}, // no -> Latn
- {0x8DCD0000u, 39u}, // nod -> Lana
+ {0xDC8D0000u, 40u}, // nex -> Latn
+ {0xC4AD0000u, 40u}, // nfr -> Latn
+ {0x6E670000u, 40u}, // ng -> Latn
+ {0x80CD0000u, 40u}, // nga -> Latn
+ {0x84CD0000u, 40u}, // ngb -> Latn
+ {0xACCD0000u, 40u}, // ngl -> Latn
+ {0x84ED0000u, 40u}, // nhb -> Latn
+ {0x90ED0000u, 40u}, // nhe -> Latn
+ {0xD8ED0000u, 40u}, // nhw -> Latn
+ {0x950D0000u, 40u}, // nif -> Latn
+ {0xA10D0000u, 40u}, // nii -> Latn
+ {0xA50D0000u, 40u}, // nij -> Latn
+ {0xB50D0000u, 40u}, // nin -> Latn
+ {0xD10D0000u, 40u}, // niu -> Latn
+ {0xE10D0000u, 40u}, // niy -> Latn
+ {0xE50D0000u, 40u}, // niz -> Latn
+ {0xB92D0000u, 40u}, // njo -> Latn
+ {0x994D0000u, 40u}, // nkg -> Latn
+ {0xB94D0000u, 40u}, // nko -> Latn
+ {0x6E6C0000u, 40u}, // nl -> Latn
+ {0x998D0000u, 40u}, // nmg -> Latn
+ {0xE58D0000u, 40u}, // nmz -> Latn
+ {0x6E6E0000u, 40u}, // nn -> Latn
+ {0x95AD0000u, 40u}, // nnf -> Latn
+ {0x9DAD0000u, 40u}, // nnh -> Latn
+ {0xA9AD0000u, 40u}, // nnk -> Latn
+ {0xB1AD0000u, 40u}, // nnm -> Latn
+ {0x6E6F0000u, 40u}, // no -> Latn
+ {0x8DCD0000u, 38u}, // nod -> Lana
{0x91CD0000u, 16u}, // noe -> Deva
{0xB5CD0000u, 64u}, // non -> Runr
- {0xBA0D0000u, 55u}, // nqo -> Nkoo
- {0x6E720000u, 41u}, // nr -> Latn
+ {0xBDCD0000u, 40u}, // nop -> Latn
+ {0xD1CD0000u, 40u}, // nou -> Latn
+ {0xBA0D0000u, 54u}, // nqo -> Nkoo
+ {0x6E720000u, 40u}, // nr -> Latn
+ {0x862D0000u, 40u}, // nrb -> Latn
{0xAA4D0000u, 9u}, // nsk -> Cans
- {0xBA4D0000u, 41u}, // nso -> Latn
- {0xCA8D0000u, 41u}, // nus -> Latn
- {0x6E760000u, 41u}, // nv -> Latn
- {0xC2ED0000u, 41u}, // nxq -> Latn
- {0x6E790000u, 41u}, // ny -> Latn
- {0xB30D0000u, 41u}, // nym -> Latn
- {0xB70D0000u, 41u}, // nyn -> Latn
- {0xA32D0000u, 41u}, // nzi -> Latn
- {0x6F630000u, 41u}, // oc -> Latn
- {0x6F6D0000u, 41u}, // om -> Latn
- {0x6F720000u, 58u}, // or -> Orya
+ {0xB64D0000u, 40u}, // nsn -> Latn
+ {0xBA4D0000u, 40u}, // nso -> Latn
+ {0xCA4D0000u, 40u}, // nss -> Latn
+ {0xB26D0000u, 40u}, // ntm -> Latn
+ {0xC66D0000u, 40u}, // ntr -> Latn
+ {0xA28D0000u, 40u}, // nui -> Latn
+ {0xBE8D0000u, 40u}, // nup -> Latn
+ {0xCA8D0000u, 40u}, // nus -> Latn
+ {0xD68D0000u, 40u}, // nuv -> Latn
+ {0xDE8D0000u, 40u}, // nux -> Latn
+ {0x6E760000u, 40u}, // nv -> Latn
+ {0x86CD0000u, 40u}, // nwb -> Latn
+ {0xC2ED0000u, 40u}, // nxq -> Latn
+ {0xC6ED0000u, 40u}, // nxr -> Latn
+ {0x6E790000u, 40u}, // ny -> Latn
+ {0xB30D0000u, 40u}, // nym -> Latn
+ {0xB70D0000u, 40u}, // nyn -> Latn
+ {0xA32D0000u, 40u}, // nzi -> Latn
+ {0x6F630000u, 40u}, // oc -> Latn
+ {0x88CE0000u, 40u}, // ogc -> Latn
+ {0xC54E0000u, 40u}, // okr -> Latn
+ {0xD54E0000u, 40u}, // okv -> Latn
+ {0x6F6D0000u, 40u}, // om -> Latn
+ {0x99AE0000u, 40u}, // ong -> Latn
+ {0xB5AE0000u, 40u}, // onn -> Latn
+ {0xC9AE0000u, 40u}, // ons -> Latn
+ {0xB1EE0000u, 40u}, // opm -> Latn
+ {0x6F720000u, 57u}, // or -> Orya
+ {0xBA2E0000u, 40u}, // oro -> Latn
+ {0xD22E0000u, 1u}, // oru -> Arab
{0x6F730000u, 15u}, // os -> Cyrl
- {0xAA6E0000u, 57u}, // otk -> Orkh
+ {0x824E0000u, 58u}, // osa -> Osge
+ {0x826E0000u, 1u}, // ota -> Arab
+ {0xAA6E0000u, 56u}, // otk -> Orkh
+ {0xB32E0000u, 40u}, // ozm -> Latn
{0x70610000u, 23u}, // pa -> Guru
{0x7061504Bu, 1u}, // pa-PK -> Arab
- {0x980F0000u, 41u}, // pag -> Latn
+ {0x980F0000u, 40u}, // pag -> Latn
{0xAC0F0000u, 60u}, // pal -> Phli
- {0xB00F0000u, 41u}, // pam -> Latn
- {0xBC0F0000u, 41u}, // pap -> Latn
- {0xD00F0000u, 41u}, // pau -> Latn
- {0x8C4F0000u, 41u}, // pcd -> Latn
- {0xB04F0000u, 41u}, // pcm -> Latn
- {0x886F0000u, 41u}, // pdc -> Latn
- {0xCC6F0000u, 41u}, // pdt -> Latn
- {0xB88F0000u, 83u}, // peo -> Xpeo
- {0xACAF0000u, 41u}, // pfl -> Latn
+ {0xB00F0000u, 40u}, // pam -> Latn
+ {0xBC0F0000u, 40u}, // pap -> Latn
+ {0xD00F0000u, 40u}, // pau -> Latn
+ {0xA02F0000u, 40u}, // pbi -> Latn
+ {0x8C4F0000u, 40u}, // pcd -> Latn
+ {0xB04F0000u, 40u}, // pcm -> Latn
+ {0x886F0000u, 40u}, // pdc -> Latn
+ {0xCC6F0000u, 40u}, // pdt -> Latn
+ {0x8C8F0000u, 40u}, // ped -> Latn
+ {0xB88F0000u, 84u}, // peo -> Xpeo
+ {0xDC8F0000u, 40u}, // pex -> Latn
+ {0xACAF0000u, 40u}, // pfl -> Latn
+ {0xACEF0000u, 1u}, // phl -> Arab
{0xB4EF0000u, 61u}, // phn -> Phnx
+ {0xAD0F0000u, 40u}, // pil -> Latn
+ {0xBD0F0000u, 40u}, // pip -> Latn
{0x814F0000u, 8u}, // pka -> Brah
- {0xB94F0000u, 41u}, // pko -> Latn
- {0x706C0000u, 41u}, // pl -> Latn
- {0xC98F0000u, 41u}, // pms -> Latn
+ {0xB94F0000u, 40u}, // pko -> Latn
+ {0x706C0000u, 40u}, // pl -> Latn
+ {0x816F0000u, 40u}, // pla -> Latn
+ {0xC98F0000u, 40u}, // pms -> Latn
+ {0x99AF0000u, 40u}, // png -> Latn
+ {0xB5AF0000u, 40u}, // pnn -> Latn
{0xCDAF0000u, 21u}, // pnt -> Grek
- {0xB5CF0000u, 41u}, // pon -> Latn
+ {0xB5CF0000u, 40u}, // pon -> Latn
+ {0xB9EF0000u, 40u}, // ppo -> Latn
{0x822F0000u, 34u}, // pra -> Khar
{0x8E2F0000u, 1u}, // prd -> Arab
- {0x9A2F0000u, 41u}, // prg -> Latn
+ {0x9A2F0000u, 40u}, // prg -> Latn
{0x70730000u, 1u}, // ps -> Arab
- {0x70740000u, 41u}, // pt -> Latn
- {0xD28F0000u, 41u}, // puu -> Latn
- {0x71750000u, 41u}, // qu -> Latn
- {0x8A900000u, 41u}, // quc -> Latn
- {0x9A900000u, 41u}, // qug -> Latn
+ {0xCA4F0000u, 40u}, // pss -> Latn
+ {0x70740000u, 40u}, // pt -> Latn
+ {0xBE6F0000u, 40u}, // ptp -> Latn
+ {0xD28F0000u, 40u}, // puu -> Latn
+ {0x82CF0000u, 40u}, // pwa -> Latn
+ {0x71750000u, 40u}, // qu -> Latn
+ {0x8A900000u, 40u}, // quc -> Latn
+ {0x9A900000u, 40u}, // qug -> Latn
+ {0xA0110000u, 40u}, // rai -> Latn
{0xA4110000u, 16u}, // raj -> Deva
- {0x94510000u, 41u}, // rcf -> Latn
- {0xA4910000u, 41u}, // rej -> Latn
- {0xB4D10000u, 41u}, // rgn -> Latn
- {0x81110000u, 41u}, // ria -> Latn
- {0x95110000u, 77u}, // rif -> Tfng
- {0x95114E4Cu, 41u}, // rif-NL -> Latn
+ {0xB8110000u, 40u}, // rao -> Latn
+ {0x94510000u, 40u}, // rcf -> Latn
+ {0xA4910000u, 40u}, // rej -> Latn
+ {0xAC910000u, 40u}, // rel -> Latn
+ {0xC8910000u, 40u}, // res -> Latn
+ {0xB4D10000u, 40u}, // rgn -> Latn
+ {0x98F10000u, 1u}, // rhg -> Arab
+ {0x81110000u, 40u}, // ria -> Latn
+ {0x95110000u, 78u}, // rif -> Tfng
+ {0x95114E4Cu, 40u}, // rif-NL -> Latn
{0xC9310000u, 16u}, // rjs -> Deva
{0xCD510000u, 7u}, // rkt -> Beng
- {0x726D0000u, 41u}, // rm -> Latn
- {0x95910000u, 41u}, // rmf -> Latn
- {0xB9910000u, 41u}, // rmo -> Latn
+ {0x726D0000u, 40u}, // rm -> Latn
+ {0x95910000u, 40u}, // rmf -> Latn
+ {0xB9910000u, 40u}, // rmo -> Latn
{0xCD910000u, 1u}, // rmt -> Arab
- {0xD1910000u, 41u}, // rmu -> Latn
- {0x726E0000u, 41u}, // rn -> Latn
- {0x99B10000u, 41u}, // rng -> Latn
- {0x726F0000u, 41u}, // ro -> Latn
- {0x85D10000u, 41u}, // rob -> Latn
- {0x95D10000u, 41u}, // rof -> Latn
- {0xB2710000u, 41u}, // rtm -> Latn
+ {0xD1910000u, 40u}, // rmu -> Latn
+ {0x726E0000u, 40u}, // rn -> Latn
+ {0x81B10000u, 40u}, // rna -> Latn
+ {0x99B10000u, 40u}, // rng -> Latn
+ {0x726F0000u, 40u}, // ro -> Latn
+ {0x85D10000u, 40u}, // rob -> Latn
+ {0x95D10000u, 40u}, // rof -> Latn
+ {0xB9D10000u, 40u}, // roo -> Latn
+ {0xBA310000u, 40u}, // rro -> Latn
+ {0xB2710000u, 40u}, // rtm -> Latn
{0x72750000u, 15u}, // ru -> Cyrl
{0x92910000u, 15u}, // rue -> Cyrl
- {0x9A910000u, 41u}, // rug -> Latn
- {0x72770000u, 41u}, // rw -> Latn
- {0xAAD10000u, 41u}, // rwk -> Latn
+ {0x9A910000u, 40u}, // rug -> Latn
+ {0x72770000u, 40u}, // rw -> Latn
+ {0xAAD10000u, 40u}, // rwk -> Latn
+ {0xBAD10000u, 40u}, // rwo -> Latn
{0xD3110000u, 33u}, // ryu -> Kana
{0x73610000u, 16u}, // sa -> Deva
- {0x94120000u, 41u}, // saf -> Latn
+ {0x94120000u, 40u}, // saf -> Latn
{0x9C120000u, 15u}, // sah -> Cyrl
- {0xC0120000u, 41u}, // saq -> Latn
- {0xC8120000u, 41u}, // sas -> Latn
- {0xCC120000u, 41u}, // sat -> Latn
+ {0xC0120000u, 40u}, // saq -> Latn
+ {0xC8120000u, 40u}, // sas -> Latn
+ {0xCC120000u, 40u}, // sat -> Latn
{0xE4120000u, 67u}, // saz -> Saur
- {0xBC320000u, 41u}, // sbp -> Latn
- {0x73630000u, 41u}, // sc -> Latn
+ {0x80320000u, 40u}, // sba -> Latn
+ {0x90320000u, 40u}, // sbe -> Latn
+ {0xBC320000u, 40u}, // sbp -> Latn
+ {0x73630000u, 40u}, // sc -> Latn
{0xA8520000u, 16u}, // sck -> Deva
- {0xB4520000u, 41u}, // scn -> Latn
- {0xB8520000u, 41u}, // sco -> Latn
- {0xC8520000u, 41u}, // scs -> Latn
+ {0xAC520000u, 1u}, // scl -> Arab
+ {0xB4520000u, 40u}, // scn -> Latn
+ {0xB8520000u, 40u}, // sco -> Latn
+ {0xC8520000u, 40u}, // scs -> Latn
{0x73640000u, 1u}, // sd -> Arab
- {0x88720000u, 41u}, // sdc -> Latn
+ {0x88720000u, 40u}, // sdc -> Latn
{0x9C720000u, 1u}, // sdh -> Arab
- {0x73650000u, 41u}, // se -> Latn
- {0x94920000u, 41u}, // sef -> Latn
- {0x9C920000u, 41u}, // seh -> Latn
- {0xA0920000u, 41u}, // sei -> Latn
- {0xC8920000u, 41u}, // ses -> Latn
- {0x73670000u, 41u}, // sg -> Latn
- {0x80D20000u, 56u}, // sga -> Ogam
- {0xC8D20000u, 41u}, // sgs -> Latn
- {0x73680000u, 41u}, // sh -> Latn
- {0xA0F20000u, 77u}, // shi -> Tfng
- {0xB4F20000u, 53u}, // shn -> Mymr
+ {0x73650000u, 40u}, // se -> Latn
+ {0x94920000u, 40u}, // sef -> Latn
+ {0x9C920000u, 40u}, // seh -> Latn
+ {0xA0920000u, 40u}, // sei -> Latn
+ {0xC8920000u, 40u}, // ses -> Latn
+ {0x73670000u, 40u}, // sg -> Latn
+ {0x80D20000u, 55u}, // sga -> Ogam
+ {0xC8D20000u, 40u}, // sgs -> Latn
+ {0xD8D20000u, 18u}, // sgw -> Ethi
+ {0xE4D20000u, 40u}, // sgz -> Latn
+ {0x73680000u, 40u}, // sh -> Latn
+ {0xA0F20000u, 78u}, // shi -> Tfng
+ {0xA8F20000u, 40u}, // shk -> Latn
+ {0xB4F20000u, 52u}, // shn -> Mymr
+ {0xD0F20000u, 1u}, // shu -> Arab
{0x73690000u, 69u}, // si -> Sinh
- {0x8D120000u, 41u}, // sid -> Latn
- {0x736B0000u, 41u}, // sk -> Latn
+ {0x8D120000u, 40u}, // sid -> Latn
+ {0x99120000u, 40u}, // sig -> Latn
+ {0xAD120000u, 40u}, // sil -> Latn
+ {0xB1120000u, 40u}, // sim -> Latn
+ {0xC5320000u, 40u}, // sjr -> Latn
+ {0x736B0000u, 40u}, // sk -> Latn
+ {0x89520000u, 40u}, // skc -> Latn
{0xC5520000u, 1u}, // skr -> Arab
- {0x736C0000u, 41u}, // sl -> Latn
- {0xA1720000u, 41u}, // sli -> Latn
- {0xE1720000u, 41u}, // sly -> Latn
- {0x736D0000u, 41u}, // sm -> Latn
- {0x81920000u, 41u}, // sma -> Latn
- {0xA5920000u, 41u}, // smj -> Latn
- {0xB5920000u, 41u}, // smn -> Latn
+ {0xC9520000u, 40u}, // sks -> Latn
+ {0x736C0000u, 40u}, // sl -> Latn
+ {0x8D720000u, 40u}, // sld -> Latn
+ {0xA1720000u, 40u}, // sli -> Latn
+ {0xAD720000u, 40u}, // sll -> Latn
+ {0xE1720000u, 40u}, // sly -> Latn
+ {0x736D0000u, 40u}, // sm -> Latn
+ {0x81920000u, 40u}, // sma -> Latn
+ {0xA5920000u, 40u}, // smj -> Latn
+ {0xB5920000u, 40u}, // smn -> Latn
{0xBD920000u, 65u}, // smp -> Samr
- {0xC9920000u, 41u}, // sms -> Latn
- {0x736E0000u, 41u}, // sn -> Latn
- {0xA9B20000u, 41u}, // snk -> Latn
- {0x736F0000u, 41u}, // so -> Latn
- {0xD1D20000u, 79u}, // sou -> Thai
- {0x73710000u, 41u}, // sq -> Latn
+ {0xC1920000u, 40u}, // smq -> Latn
+ {0xC9920000u, 40u}, // sms -> Latn
+ {0x736E0000u, 40u}, // sn -> Latn
+ {0x89B20000u, 40u}, // snc -> Latn
+ {0xA9B20000u, 40u}, // snk -> Latn
+ {0xBDB20000u, 40u}, // snp -> Latn
+ {0xDDB20000u, 40u}, // snx -> Latn
+ {0xE1B20000u, 40u}, // sny -> Latn
+ {0x736F0000u, 40u}, // so -> Latn
+ {0xA9D20000u, 40u}, // sok -> Latn
+ {0xC1D20000u, 40u}, // soq -> Latn
+ {0xD1D20000u, 80u}, // sou -> Thai
+ {0xE1D20000u, 40u}, // soy -> Latn
+ {0x8DF20000u, 40u}, // spd -> Latn
+ {0xADF20000u, 40u}, // spl -> Latn
+ {0xC9F20000u, 40u}, // sps -> Latn
+ {0x73710000u, 40u}, // sq -> Latn
{0x73720000u, 15u}, // sr -> Cyrl
- {0x73724D45u, 41u}, // sr-ME -> Latn
- {0x7372524Fu, 41u}, // sr-RO -> Latn
- {0x73725255u, 41u}, // sr-RU -> Latn
- {0x73725452u, 41u}, // sr-TR -> Latn
+ {0x73724D45u, 40u}, // sr-ME -> Latn
+ {0x7372524Fu, 40u}, // sr-RO -> Latn
+ {0x73725255u, 40u}, // sr-RU -> Latn
+ {0x73725452u, 40u}, // sr-TR -> Latn
{0x86320000u, 70u}, // srb -> Sora
- {0xB6320000u, 41u}, // srn -> Latn
- {0xC6320000u, 41u}, // srr -> Latn
+ {0xB6320000u, 40u}, // srn -> Latn
+ {0xC6320000u, 40u}, // srr -> Latn
{0xDE320000u, 16u}, // srx -> Deva
- {0x73730000u, 41u}, // ss -> Latn
- {0xE2520000u, 41u}, // ssy -> Latn
- {0x73740000u, 41u}, // st -> Latn
- {0xC2720000u, 41u}, // stq -> Latn
- {0x73750000u, 41u}, // su -> Latn
- {0xAA920000u, 41u}, // suk -> Latn
- {0xCA920000u, 41u}, // sus -> Latn
- {0x73760000u, 41u}, // sv -> Latn
- {0x73770000u, 41u}, // sw -> Latn
+ {0x73730000u, 40u}, // ss -> Latn
+ {0x8E520000u, 40u}, // ssd -> Latn
+ {0x9A520000u, 40u}, // ssg -> Latn
+ {0xE2520000u, 40u}, // ssy -> Latn
+ {0x73740000u, 40u}, // st -> Latn
+ {0xAA720000u, 40u}, // stk -> Latn
+ {0xC2720000u, 40u}, // stq -> Latn
+ {0x73750000u, 40u}, // su -> Latn
+ {0x82920000u, 40u}, // sua -> Latn
+ {0x92920000u, 40u}, // sue -> Latn
+ {0xAA920000u, 40u}, // suk -> Latn
+ {0xC6920000u, 40u}, // sur -> Latn
+ {0xCA920000u, 40u}, // sus -> Latn
+ {0x73760000u, 40u}, // sv -> Latn
+ {0x73770000u, 40u}, // sw -> Latn
{0x86D20000u, 1u}, // swb -> Arab
- {0x8AD20000u, 41u}, // swc -> Latn
- {0x9AD20000u, 41u}, // swg -> Latn
+ {0x8AD20000u, 40u}, // swc -> Latn
+ {0x9AD20000u, 40u}, // swg -> Latn
+ {0xBED20000u, 40u}, // swp -> Latn
{0xD6D20000u, 16u}, // swv -> Deva
- {0xB6F20000u, 41u}, // sxn -> Latn
+ {0xB6F20000u, 40u}, // sxn -> Latn
+ {0xDAF20000u, 40u}, // sxw -> Latn
{0xAF120000u, 7u}, // syl -> Beng
{0xC7120000u, 71u}, // syr -> Syrc
- {0xAF320000u, 41u}, // szl -> Latn
+ {0xAF320000u, 40u}, // szl -> Latn
{0x74610000u, 74u}, // ta -> Taml
{0xA4130000u, 16u}, // taj -> Deva
- {0xD8330000u, 41u}, // tbw -> Latn
+ {0xAC130000u, 40u}, // tal -> Latn
+ {0xB4130000u, 40u}, // tan -> Latn
+ {0xC0130000u, 40u}, // taq -> Latn
+ {0x88330000u, 40u}, // tbc -> Latn
+ {0x8C330000u, 40u}, // tbd -> Latn
+ {0x94330000u, 40u}, // tbf -> Latn
+ {0x98330000u, 40u}, // tbg -> Latn
+ {0xB8330000u, 40u}, // tbo -> Latn
+ {0xD8330000u, 40u}, // tbw -> Latn
+ {0xE4330000u, 40u}, // tbz -> Latn
+ {0xA0530000u, 40u}, // tci -> Latn
{0xE0530000u, 36u}, // tcy -> Knda
{0x8C730000u, 72u}, // tdd -> Tale
{0x98730000u, 16u}, // tdg -> Deva
{0x9C730000u, 16u}, // tdh -> Deva
- {0x74650000u, 76u}, // te -> Telu
- {0xB0930000u, 41u}, // tem -> Latn
- {0xB8930000u, 41u}, // teo -> Latn
- {0xCC930000u, 41u}, // tet -> Latn
+ {0x74650000u, 77u}, // te -> Telu
+ {0x8C930000u, 40u}, // ted -> Latn
+ {0xB0930000u, 40u}, // tem -> Latn
+ {0xB8930000u, 40u}, // teo -> Latn
+ {0xCC930000u, 40u}, // tet -> Latn
+ {0xA0B30000u, 40u}, // tfi -> Latn
{0x74670000u, 15u}, // tg -> Cyrl
{0x7467504Bu, 1u}, // tg-PK -> Arab
- {0x74680000u, 79u}, // th -> Thai
+ {0x88D30000u, 40u}, // tgc -> Latn
+ {0xB8D30000u, 40u}, // tgo -> Latn
+ {0xD0D30000u, 40u}, // tgu -> Latn
+ {0x74680000u, 80u}, // th -> Thai
{0xACF30000u, 16u}, // thl -> Deva
{0xC0F30000u, 16u}, // thq -> Deva
{0xC4F30000u, 16u}, // thr -> Deva
{0x74690000u, 18u}, // ti -> Ethi
+ {0x95130000u, 40u}, // tif -> Latn
{0x99130000u, 18u}, // tig -> Ethi
- {0xD5130000u, 41u}, // tiv -> Latn
- {0x746B0000u, 41u}, // tk -> Latn
- {0xAD530000u, 41u}, // tkl -> Latn
- {0xC5530000u, 41u}, // tkr -> Latn
+ {0xA9130000u, 40u}, // tik -> Latn
+ {0xB1130000u, 40u}, // tim -> Latn
+ {0xB9130000u, 40u}, // tio -> Latn
+ {0xD5130000u, 40u}, // tiv -> Latn
+ {0x746B0000u, 40u}, // tk -> Latn
+ {0xAD530000u, 40u}, // tkl -> Latn
+ {0xC5530000u, 40u}, // tkr -> Latn
{0xCD530000u, 16u}, // tkt -> Deva
- {0x746C0000u, 41u}, // tl -> Latn
- {0xE1730000u, 41u}, // tly -> Latn
- {0x9D930000u, 41u}, // tmh -> Latn
- {0x746E0000u, 41u}, // tn -> Latn
- {0x746F0000u, 41u}, // to -> Latn
- {0x99D30000u, 41u}, // tog -> Latn
- {0xA1F30000u, 41u}, // tpi -> Latn
- {0x74720000u, 41u}, // tr -> Latn
- {0xD2330000u, 41u}, // tru -> Latn
- {0xD6330000u, 41u}, // trv -> Latn
- {0x74730000u, 41u}, // ts -> Latn
+ {0x746C0000u, 40u}, // tl -> Latn
+ {0x95730000u, 40u}, // tlf -> Latn
+ {0xDD730000u, 40u}, // tlx -> Latn
+ {0xE1730000u, 40u}, // tly -> Latn
+ {0x9D930000u, 40u}, // tmh -> Latn
+ {0xE1930000u, 40u}, // tmy -> Latn
+ {0x746E0000u, 40u}, // tn -> Latn
+ {0x9DB30000u, 40u}, // tnh -> Latn
+ {0x746F0000u, 40u}, // to -> Latn
+ {0x95D30000u, 40u}, // tof -> Latn
+ {0x99D30000u, 40u}, // tog -> Latn
+ {0xC1D30000u, 40u}, // toq -> Latn
+ {0xA1F30000u, 40u}, // tpi -> Latn
+ {0xB1F30000u, 40u}, // tpm -> Latn
+ {0xE5F30000u, 40u}, // tpz -> Latn
+ {0xBA130000u, 40u}, // tqo -> Latn
+ {0x74720000u, 40u}, // tr -> Latn
+ {0xD2330000u, 40u}, // tru -> Latn
+ {0xD6330000u, 40u}, // trv -> Latn
+ {0xDA330000u, 1u}, // trw -> Arab
+ {0x74730000u, 40u}, // ts -> Latn
{0x8E530000u, 21u}, // tsd -> Grek
{0x96530000u, 16u}, // tsf -> Deva
- {0x9A530000u, 41u}, // tsg -> Latn
- {0xA6530000u, 80u}, // tsj -> Tibt
+ {0x9A530000u, 40u}, // tsg -> Latn
+ {0xA6530000u, 81u}, // tsj -> Tibt
+ {0xDA530000u, 40u}, // tsw -> Latn
{0x74740000u, 15u}, // tt -> Cyrl
- {0xA6730000u, 41u}, // ttj -> Latn
- {0xCA730000u, 79u}, // tts -> Thai
- {0xCE730000u, 41u}, // ttt -> Latn
- {0xB2930000u, 41u}, // tum -> Latn
- {0xAEB30000u, 41u}, // tvl -> Latn
- {0xC2D30000u, 41u}, // twq -> Latn
- {0x74790000u, 41u}, // ty -> Latn
+ {0x8E730000u, 40u}, // ttd -> Latn
+ {0x92730000u, 40u}, // tte -> Latn
+ {0xA6730000u, 40u}, // ttj -> Latn
+ {0xC6730000u, 40u}, // ttr -> Latn
+ {0xCA730000u, 80u}, // tts -> Thai
+ {0xCE730000u, 40u}, // ttt -> Latn
+ {0x9E930000u, 40u}, // tuh -> Latn
+ {0xAE930000u, 40u}, // tul -> Latn
+ {0xB2930000u, 40u}, // tum -> Latn
+ {0xC2930000u, 40u}, // tuq -> Latn
+ {0x8EB30000u, 40u}, // tvd -> Latn
+ {0xAEB30000u, 40u}, // tvl -> Latn
+ {0xD2B30000u, 40u}, // tvu -> Latn
+ {0x9ED30000u, 40u}, // twh -> Latn
+ {0xC2D30000u, 40u}, // twq -> Latn
+ {0x9AF30000u, 75u}, // txg -> Tang
+ {0x74790000u, 40u}, // ty -> Latn
+ {0x83130000u, 40u}, // tya -> Latn
{0xD7130000u, 15u}, // tyv -> Cyrl
- {0xB3330000u, 41u}, // tzm -> Latn
+ {0xB3330000u, 40u}, // tzm -> Latn
+ {0xD0340000u, 40u}, // ubu -> Latn
{0xB0740000u, 15u}, // udm -> Cyrl
{0x75670000u, 1u}, // ug -> Arab
{0x75674B5Au, 15u}, // ug-KZ -> Cyrl
{0x75674D4Eu, 15u}, // ug-MN -> Cyrl
- {0x80D40000u, 81u}, // uga -> Ugar
+ {0x80D40000u, 82u}, // uga -> Ugar
{0x756B0000u, 15u}, // uk -> Cyrl
- {0xA1740000u, 41u}, // uli -> Latn
- {0x85940000u, 41u}, // umb -> Latn
+ {0xA1740000u, 40u}, // uli -> Latn
+ {0x85940000u, 40u}, // umb -> Latn
{0xC5B40000u, 7u}, // unr -> Beng
{0xC5B44E50u, 16u}, // unr-NP -> Deva
{0xDDB40000u, 7u}, // unx -> Beng
{0x75720000u, 1u}, // ur -> Arab
- {0x757A0000u, 41u}, // uz -> Latn
+ {0xA2340000u, 40u}, // uri -> Latn
+ {0xCE340000u, 40u}, // urt -> Latn
+ {0xDA340000u, 40u}, // urw -> Latn
+ {0x82540000u, 40u}, // usa -> Latn
+ {0xC6740000u, 40u}, // utr -> Latn
+ {0x9EB40000u, 40u}, // uvh -> Latn
+ {0xAEB40000u, 40u}, // uvl -> Latn
+ {0x757A0000u, 40u}, // uz -> Latn
{0x757A4146u, 1u}, // uz-AF -> Arab
{0x757A434Eu, 15u}, // uz-CN -> Cyrl
- {0xA0150000u, 82u}, // vai -> Vaii
- {0x76650000u, 41u}, // ve -> Latn
- {0x88950000u, 41u}, // vec -> Latn
- {0xBC950000u, 41u}, // vep -> Latn
- {0x76690000u, 41u}, // vi -> Latn
- {0x89150000u, 41u}, // vic -> Latn
- {0xC9750000u, 41u}, // vls -> Latn
- {0x95950000u, 41u}, // vmf -> Latn
- {0xD9950000u, 41u}, // vmw -> Latn
- {0x766F0000u, 41u}, // vo -> Latn
- {0xCDD50000u, 41u}, // vot -> Latn
- {0xBA350000u, 41u}, // vro -> Latn
- {0xB6950000u, 41u}, // vun -> Latn
- {0x77610000u, 41u}, // wa -> Latn
- {0x90160000u, 41u}, // wae -> Latn
+ {0x98150000u, 40u}, // vag -> Latn
+ {0xA0150000u, 83u}, // vai -> Vaii
+ {0xB4150000u, 40u}, // van -> Latn
+ {0x76650000u, 40u}, // ve -> Latn
+ {0x88950000u, 40u}, // vec -> Latn
+ {0xBC950000u, 40u}, // vep -> Latn
+ {0x76690000u, 40u}, // vi -> Latn
+ {0x89150000u, 40u}, // vic -> Latn
+ {0xD5150000u, 40u}, // viv -> Latn
+ {0xC9750000u, 40u}, // vls -> Latn
+ {0x95950000u, 40u}, // vmf -> Latn
+ {0xD9950000u, 40u}, // vmw -> Latn
+ {0x766F0000u, 40u}, // vo -> Latn
+ {0xCDD50000u, 40u}, // vot -> Latn
+ {0xBA350000u, 40u}, // vro -> Latn
+ {0xB6950000u, 40u}, // vun -> Latn
+ {0xCE950000u, 40u}, // vut -> Latn
+ {0x77610000u, 40u}, // wa -> Latn
+ {0x90160000u, 40u}, // wae -> Latn
+ {0xA4160000u, 40u}, // waj -> Latn
{0xAC160000u, 18u}, // wal -> Ethi
- {0xC4160000u, 41u}, // war -> Latn
- {0xBC360000u, 41u}, // wbp -> Latn
- {0xC0360000u, 76u}, // wbq -> Telu
+ {0xB4160000u, 40u}, // wan -> Latn
+ {0xC4160000u, 40u}, // war -> Latn
+ {0xBC360000u, 40u}, // wbp -> Latn
+ {0xC0360000u, 77u}, // wbq -> Telu
{0xC4360000u, 16u}, // wbr -> Deva
- {0xC9760000u, 41u}, // wls -> Latn
+ {0xA0560000u, 40u}, // wci -> Latn
+ {0xC4960000u, 40u}, // wer -> Latn
+ {0xA0D60000u, 40u}, // wgi -> Latn
+ {0x98F60000u, 40u}, // whg -> Latn
+ {0x85160000u, 40u}, // wib -> Latn
+ {0xD1160000u, 40u}, // wiu -> Latn
+ {0xD5160000u, 40u}, // wiv -> Latn
+ {0x81360000u, 40u}, // wja -> Latn
+ {0xA1360000u, 40u}, // wji -> Latn
+ {0xC9760000u, 40u}, // wls -> Latn
+ {0xB9960000u, 40u}, // wmo -> Latn
+ {0x89B60000u, 40u}, // wnc -> Latn
{0xA1B60000u, 1u}, // wni -> Arab
- {0x776F0000u, 41u}, // wo -> Latn
+ {0xD1B60000u, 40u}, // wnu -> Latn
+ {0x776F0000u, 40u}, // wo -> Latn
+ {0x85D60000u, 40u}, // wob -> Latn
+ {0xC9D60000u, 40u}, // wos -> Latn
+ {0xCA360000u, 40u}, // wrs -> Latn
+ {0xAA560000u, 40u}, // wsk -> Latn
{0xB2760000u, 16u}, // wtm -> Deva
{0xD2960000u, 24u}, // wuu -> Hans
- {0xD4170000u, 41u}, // xav -> Latn
+ {0xD6960000u, 40u}, // wuv -> Latn
+ {0x82D60000u, 40u}, // wwa -> Latn
+ {0xD4170000u, 40u}, // xav -> Latn
+ {0xA0370000u, 40u}, // xbi -> Latn
{0xC4570000u, 10u}, // xcr -> Cari
- {0x78680000u, 41u}, // xh -> Latn
- {0x89770000u, 45u}, // xlc -> Lyci
- {0x8D770000u, 46u}, // xld -> Lydi
+ {0xC8970000u, 40u}, // xes -> Latn
+ {0x78680000u, 40u}, // xh -> Latn
+ {0x81770000u, 40u}, // xla -> Latn
+ {0x89770000u, 44u}, // xlc -> Lyci
+ {0x8D770000u, 45u}, // xld -> Lydi
{0x95970000u, 19u}, // xmf -> Geor
- {0xB5970000u, 48u}, // xmn -> Mani
- {0xC5970000u, 49u}, // xmr -> Merc
- {0x81B70000u, 54u}, // xna -> Narb
+ {0xB5970000u, 47u}, // xmn -> Mani
+ {0xC5970000u, 48u}, // xmr -> Merc
+ {0x81B70000u, 53u}, // xna -> Narb
{0xC5B70000u, 16u}, // xnr -> Deva
- {0x99D70000u, 41u}, // xog -> Latn
+ {0x99D70000u, 40u}, // xog -> Latn
+ {0xB5D70000u, 40u}, // xon -> Latn
{0xC5F70000u, 63u}, // xpr -> Prti
+ {0x86370000u, 40u}, // xrb -> Latn
{0x82570000u, 66u}, // xsa -> Sarb
+ {0xA2570000u, 40u}, // xsi -> Latn
+ {0xB2570000u, 40u}, // xsm -> Latn
{0xC6570000u, 16u}, // xsr -> Deva
- {0xB8180000u, 41u}, // yao -> Latn
- {0xBC180000u, 41u}, // yap -> Latn
- {0xD4180000u, 41u}, // yav -> Latn
- {0x84380000u, 41u}, // ybb -> Latn
+ {0x92D70000u, 40u}, // xwe -> Latn
+ {0xB0180000u, 40u}, // yam -> Latn
+ {0xB8180000u, 40u}, // yao -> Latn
+ {0xBC180000u, 40u}, // yap -> Latn
+ {0xC8180000u, 40u}, // yas -> Latn
+ {0xCC180000u, 40u}, // yat -> Latn
+ {0xD4180000u, 40u}, // yav -> Latn
+ {0xE0180000u, 40u}, // yay -> Latn
+ {0xE4180000u, 40u}, // yaz -> Latn
+ {0x80380000u, 40u}, // yba -> Latn
+ {0x84380000u, 40u}, // ybb -> Latn
+ {0xE0380000u, 40u}, // yby -> Latn
+ {0xC4980000u, 40u}, // yer -> Latn
+ {0xC4D80000u, 40u}, // ygr -> Latn
+ {0xD8D80000u, 40u}, // ygw -> Latn
{0x79690000u, 27u}, // yi -> Hebr
- {0x796F0000u, 41u}, // yo -> Latn
- {0xAE380000u, 41u}, // yrl -> Latn
- {0x82980000u, 41u}, // yua -> Latn
- {0x7A610000u, 41u}, // za -> Latn
- {0x98190000u, 41u}, // zag -> Latn
+ {0xB9580000u, 40u}, // yko -> Latn
+ {0x91780000u, 40u}, // yle -> Latn
+ {0x99780000u, 40u}, // ylg -> Latn
+ {0xAD780000u, 40u}, // yll -> Latn
+ {0xAD980000u, 40u}, // yml -> Latn
+ {0x796F0000u, 40u}, // yo -> Latn
+ {0xB5D80000u, 40u}, // yon -> Latn
+ {0x86380000u, 40u}, // yrb -> Latn
+ {0x92380000u, 40u}, // yre -> Latn
+ {0xAE380000u, 40u}, // yrl -> Latn
+ {0xCA580000u, 40u}, // yss -> Latn
+ {0x82980000u, 40u}, // yua -> Latn
+ {0x92980000u, 25u}, // yue -> Hant
+ {0x9298434Eu, 24u}, // yue-CN -> Hans
+ {0xA6980000u, 40u}, // yuj -> Latn
+ {0xCE980000u, 40u}, // yut -> Latn
+ {0xDA980000u, 40u}, // yuw -> Latn
+ {0x7A610000u, 40u}, // za -> Latn
+ {0x98190000u, 40u}, // zag -> Latn
{0xA4790000u, 1u}, // zdj -> Arab
- {0x80990000u, 41u}, // zea -> Latn
- {0x9CD90000u, 77u}, // zgh -> Tfng
+ {0x80990000u, 40u}, // zea -> Latn
+ {0x9CD90000u, 78u}, // zgh -> Tfng
{0x7A680000u, 24u}, // zh -> Hans
{0x7A684155u, 25u}, // zh-AU -> Hant
{0x7A68424Eu, 25u}, // zh-BN -> Hant
@@ -829,9 +1437,12 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
{0x7A685457u, 25u}, // zh-TW -> Hant
{0x7A685553u, 25u}, // zh-US -> Hant
{0x7A68564Eu, 25u}, // zh-VN -> Hant
- {0xA1990000u, 41u}, // zmi -> Latn
- {0x7A750000u, 41u}, // zu -> Latn
- {0x83390000u, 41u}, // zza -> Latn
+ {0x81190000u, 40u}, // zia -> Latn
+ {0xB1790000u, 40u}, // zlm -> Latn
+ {0xA1990000u, 40u}, // zmi -> Latn
+ {0x91B90000u, 40u}, // zne -> Latn
+ {0x7A750000u, 40u}, // zu -> Latn
+ {0x83390000u, 40u}, // zza -> Latn
});
std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
@@ -854,6 +1465,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0x616D455445746869llu, // am_Ethi_ET
0xB9804E474C61746Ellu, // amo_Latn_NG
0xE5C049444C61746Ellu, // aoz_Latn_ID
+ 0x8DE0544741726162llu, // apd_Arab_TG
0x6172454741726162llu, // ar_Arab_EG
0x8A20495241726D69llu, // arc_Armi_IR
0x8A204A4F4E626174llu, // arc_Nbat_JO
@@ -896,7 +1508,6 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0x88C1494E44657661llu, // bgc_Deva_IN
0xB4C1504B41726162llu, // bgn_Arab_PK
0xDCC154524772656Bllu, // bgx_Grek_TR
- 0x6268494E4B746869llu, // bh_Kthi_IN
0x84E1494E44657661llu, // bhb_Deva_IN
0xA0E1494E44657661llu, // bhi_Deva_IN
0xA8E150484C61746Ellu, // bhk_Latn_PH
@@ -980,6 +1591,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0x864344454C61746Ellu, // dsb_Latn_DE
0xB2634D4C4C61746Ellu, // dtm_Latn_ML
0xBE634D594C61746Ellu, // dtp_Latn_MY
+ 0xE2634E5044657661llu, // dty_Deva_NP
0x8283434D4C61746Ellu, // dua_Latn_CM
0x64764D5654686161llu, // dv_Thaa_MV
0xBB03534E4C61746Ellu, // dyo_Latn_SN
@@ -1006,6 +1618,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0xCEE445534C61746Ellu, // ext_Latn_ES
0x6661495241726162llu, // fa_Arab_IR
0xB40547514C61746Ellu, // fan_Latn_GQ
+ 0x6666474E41646C6Dllu, // ff_Adlm_GN
0x6666534E4C61746Ellu, // ff_Latn_SN
0xB0A54D4C4C61746Ellu, // ffm_Latn_ML
0x666946494C61746Ellu, // fi_Latn_FI
@@ -1020,7 +1633,9 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0xBE2546524C61746Ellu, // frp_Latn_FR
0xC62544454C61746Ellu, // frr_Latn_DE
0xCA2544454C61746Ellu, // frs_Latn_DE
+ 0x8685434D41726162llu, // fub_Arab_CM
0x8E8557464C61746Ellu, // fud_Latn_WF
+ 0x9685474E4C61746Ellu, // fuf_Latn_GN
0xC2854E454C61746Ellu, // fuq_Latn_NE
0xC68549544C61746Ellu, // fur_Latn_IT
0xD6854E474C61746Ellu, // fuv_Latn_NG
@@ -1104,7 +1719,6 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0x6A614A504A70616Ellu, // ja_Jpan_JP
0xB0094A4D4C61746Ellu, // jam_Latn_JM
0xB8C9434D4C61746Ellu, // jgo_Latn_CM
- 0x6A69554148656272llu, // ji_Hebr_UA
0x8989545A4C61746Ellu, // jmc_Latn_TZ
0xAD894E5044657661llu, // jml_Deva_NP
0xCE89444B4C61746Ellu, // jut_Latn_DK
@@ -1118,9 +1732,11 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0xB00A4B454C61746Ellu, // kam_Latn_KE
0xB80A4D4C4C61746Ellu, // kao_Latn_ML
0x8C2A52554379726Cllu, // kbd_Cyrl_RU
+ 0xE02A4E4541726162llu, // kby_Arab_NE
0x984A4E474C61746Ellu, // kcg_Latn_NG
0xA84A5A574C61746Ellu, // kck_Latn_ZW
0x906A545A4C61746Ellu, // kde_Latn_TZ
+ 0x9C6A544741726162llu, // kdh_Arab_TG
0xCC6A544854686169llu, // kdt_Thai_TH
0x808A43564C61746Ellu, // kea_Latn_CV
0xB48A434D4C61746Ellu, // ken_Latn_CM
@@ -1251,7 +1867,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0x6D72494E44657661llu, // mr_Deva_IN
0x8E2C4E5044657661llu, // mrd_Deva_NP
0xA62C52554379726Cllu, // mrj_Cyrl_RU
- 0xD22C42444D726F6Fllu, // mru_Mroo_BD
+ 0xBA2C42444D726F6Fllu, // mro_Mroo_BD
0x6D734D594C61746Ellu, // ms_Latn_MY
0x6D744D544C61746Ellu, // mt_Latn_MT
0xC66C494E44657661llu, // mtr_Deva_IN
@@ -1308,6 +1924,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0x6F6D45544C61746Ellu, // om_Latn_ET
0x6F72494E4F727961llu, // or_Orya_IN
0x6F7347454379726Cllu, // os_Cyrl_GE
+ 0x824E55534F736765llu, // osa_Osge_US
0xAA6E4D4E4F726B68llu, // otk_Orkh_MN
0x7061504B41726162llu, // pa_Arab_PK
0x7061494E47757275llu, // pa_Guru_IN
@@ -1479,6 +2096,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0xB2934D574C61746Ellu, // tum_Latn_MW
0xAEB354564C61746Ellu, // tvl_Latn_TV
0xC2D34E454C61746Ellu, // twq_Latn_NE
+ 0x9AF3434E54616E67llu, // txg_Tang_CN
0x747950464C61746Ellu, // ty_Latn_PF
0xD71352554379726Cllu, // tyv_Cyrl_RU
0xB3334D414C61746Ellu, // tzm_Latn_MA
@@ -1540,14 +2158,18 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0x796F4E474C61746Ellu, // yo_Latn_NG
0xAE3842524C61746Ellu, // yrl_Latn_BR
0x82984D584C61746Ellu, // yua_Latn_MX
+ 0x9298434E48616E73llu, // yue_Hans_CN
+ 0x9298484B48616E74llu, // yue_Hant_HK
0x7A61434E4C61746Ellu, // za_Latn_CN
0x981953444C61746Ellu, // zag_Latn_SD
0xA4794B4D41726162llu, // zdj_Arab_KM
0x80994E4C4C61746Ellu, // zea_Latn_NL
0x9CD94D4154666E67llu, // zgh_Tfng_MA
0x7A685457426F706Fllu, // zh_Bopo_TW
+ 0x7A68545748616E62llu, // zh_Hanb_TW
0x7A68434E48616E73llu, // zh_Hans_CN
0x7A68545748616E74llu, // zh_Hant_TW
+ 0xB17954474C61746Ellu, // zlm_Latn_TG
0xA1994D594C61746Ellu, // zmi_Latn_MY
0x7A755A414C61746Ellu, // zu_Latn_ZA
0x833954524C61746Ellu, // zza_Latn_TR
@@ -1662,6 +2284,7 @@ const std::unordered_map<uint32_t, uint32_t> LATN_PARENTS({
{0x656E5A57u, 0x656E8400u}, // en-ZW -> en-001
{0x65734152u, 0x6573A424u}, // es-AR -> es-419
{0x6573424Fu, 0x6573A424u}, // es-BO -> es-419
+ {0x65734252u, 0x6573A424u}, // es-BR -> es-419
{0x6573434Cu, 0x6573A424u}, // es-CL -> es-419
{0x6573434Fu, 0x6573A424u}, // es-CO -> es-419
{0x65734352u, 0x6573A424u}, // es-CR -> es-419
@@ -1681,8 +2304,11 @@ const std::unordered_map<uint32_t, uint32_t> LATN_PARENTS({
{0x65735559u, 0x6573A424u}, // es-UY -> es-419
{0x65735645u, 0x6573A424u}, // es-VE -> es-419
{0x7074414Fu, 0x70745054u}, // pt-AO -> pt-PT
+ {0x70744348u, 0x70745054u}, // pt-CH -> pt-PT
{0x70744356u, 0x70745054u}, // pt-CV -> pt-PT
+ {0x70744751u, 0x70745054u}, // pt-GQ -> pt-PT
{0x70744757u, 0x70745054u}, // pt-GW -> pt-PT
+ {0x70744C55u, 0x70745054u}, // pt-LU -> pt-PT
{0x70744D4Fu, 0x70745054u}, // pt-MO -> pt-PT
{0x70744D5Au, 0x70745054u}, // pt-MZ -> pt-PT
{0x70745354u, 0x70745054u}, // pt-ST -> pt-PT
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index e10db05e8557..a30c8497b0f8 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -140,7 +140,7 @@ static void fill9patchOffsets(Res_png_9patch* patch) {
patch->colorsOffset = patch->yDivsOffset + (patch->numYDivs * sizeof(int32_t));
}
-inline void Res_value::copyFrom_dtoh(const Res_value& src)
+void Res_value::copyFrom_dtoh(const Res_value& src)
{
size = dtohs(src.size);
res0 = src.res0;
@@ -804,8 +804,14 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const
if (off < (mStringPoolSize-1)) {
const uint8_t* strings = (uint8_t*)mStrings;
const uint8_t* str = strings+off;
- *outLen = decodeLength(&str);
- size_t encLen = decodeLength(&str);
+
+ // Decode the UTF-16 length. This is not used if we're not
+ // converting to UTF-16 from UTF-8.
+ decodeLength(&str);
+
+ const size_t encLen = decodeLength(&str);
+ *outLen = encLen;
+
if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
return (const char*)str;
} else {
@@ -2024,7 +2030,6 @@ int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const {
((o.localeVariant[0] != '\0') ? 2 : 0);
return score - oScore;
-
}
bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const {
@@ -2170,6 +2175,23 @@ bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const {
return false;
}
+// Codes for specially handled languages and regions
+static const char kEnglish[2] = {'e', 'n'}; // packed version of "en"
+static const char kUnitedStates[2] = {'U', 'S'}; // packed version of "US"
+static const char kFilipino[2] = {'\xAD', '\x05'}; // packed version of "fil"
+static const char kTagalog[2] = {'t', 'l'}; // packed version of "tl"
+
+// Checks if two language or region codes are identical
+inline bool areIdentical(const char code1[2], const char code2[2]) {
+ return code1[0] == code2[0] && code1[1] == code2[1];
+}
+
+inline bool langsAreEquivalent(const char lang1[2], const char lang2[2]) {
+ return areIdentical(lang1, lang2) ||
+ (areIdentical(lang1, kTagalog) && areIdentical(lang2, kFilipino)) ||
+ (areIdentical(lang1, kFilipino) && areIdentical(lang2, kTagalog));
+}
+
bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
const ResTable_config* requested) const {
if (requested->locale == 0) {
@@ -2179,7 +2201,7 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
}
if (locale == 0 && o.locale == 0) {
- // The locales parts of both resources are empty, so no one is better
+ // The locale part of both resources is empty, so none is better
// than the other.
return false;
}
@@ -2194,10 +2216,11 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
// 2) If the request's script is known, the resource scripts are either
// unknown or match the request.
- if (language[0] != o.language[0]) {
- // The languages of the two resources are not the same. We can only
- // assume that one of the two resources matched the request because one
- // doesn't have a language and the other has a matching language.
+ if (!langsAreEquivalent(language, o.language)) {
+ // The languages of the two resources are not equivalent. If we are
+ // here, we can only assume that the two resources matched the request
+ // because one doesn't have a language and the other has a matching
+ // language.
//
// We consider the one that has the language specified a better match.
//
@@ -2205,15 +2228,15 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
// for US English and similar locales than locales that are a descendant
// of Internatinal English (en-001), since no-language resources are
// where the US English resource have traditionally lived for most apps.
- if (requested->language[0] == 'e' && requested->language[1] == 'n') {
- if (requested->country[0] == 'U' && requested->country[1] == 'S') {
+ if (areIdentical(requested->language, kEnglish)) {
+ if (areIdentical(requested->country, kUnitedStates)) {
// For US English itself, we consider a no-locale resource a
// better match if the other resource has a country other than
// US specified.
if (language[0] != '\0') {
- return country[0] == '\0' || (country[0] == 'U' && country[1] == 'S');
+ return country[0] == '\0' || areIdentical(country, kUnitedStates);
} else {
- return !(o.country[0] == '\0' || (o.country[0] == 'U' && o.country[1] == 'S'));
+ return !(o.country[0] == '\0' || areIdentical(o.country, kUnitedStates));
}
} else if (localeDataIsCloseToUsEnglish(requested->country)) {
if (language[0] != '\0') {
@@ -2226,27 +2249,38 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
return (language[0] != '\0');
}
- // If we are here, both the resources have the same non-empty language as
- // the request.
+ // If we are here, both the resources have an equivalent non-empty language
+ // to the request.
//
- // Because the languages are the same, computeScript() always
- // returns a non-empty script for languages it knows about, and we have passed
- // the script checks in match(), the scripts are either all unknown or are
- // all the same. So we can't gain anything by checking the scripts. We need
- // to check the region and variant.
+ // Because the languages are equivalent, computeScript() always returns a
+ // non-empty script for languages it knows about, and we have passed the
+ // script checks in match(), the scripts are either all unknown or are all
+ // the same. So we can't gain anything by checking the scripts. We need to
+ // check the region and variant.
- // See if any of the regions is better than the other
+ // See if any of the regions is better than the other.
const int region_comparison = localeDataCompareRegions(
country, o.country,
- language, requested->localeScript, requested->country);
+ requested->language, requested->localeScript, requested->country);
if (region_comparison != 0) {
return (region_comparison > 0);
}
// The regions are the same. Try the variant.
- if (requested->localeVariant[0] != '\0'
- && strncmp(localeVariant, requested->localeVariant, sizeof(localeVariant)) == 0) {
- return (strncmp(o.localeVariant, requested->localeVariant, sizeof(localeVariant)) != 0);
+ const bool localeMatches = strncmp(
+ localeVariant, requested->localeVariant, sizeof(localeVariant)) == 0;
+ const bool otherMatches = strncmp(
+ o.localeVariant, requested->localeVariant, sizeof(localeVariant)) == 0;
+ if (localeMatches != otherMatches) {
+ return localeMatches;
+ }
+
+ // Finally, the languages, although equivalent, may still be different
+ // (like for Tagalog and Filipino). Identical is better than just
+ // equivalent.
+ if (areIdentical(language, requested->language)
+ && !areIdentical(o.language, requested->language)) {
+ return true;
}
return false;
@@ -2522,7 +2556,7 @@ bool ResTable_config::match(const ResTable_config& settings) const {
//
// If two configs differ only in their country and variant,
// they can be weeded out in the isMoreSpecificThan test.
- if (language[0] != settings.language[0] || language[1] != settings.language[1]) {
+ if (!langsAreEquivalent(language, settings.language)) {
return false;
}
@@ -2550,9 +2584,7 @@ bool ResTable_config::match(const ResTable_config& settings) const {
}
if (countriesMustMatch) {
- if (country[0] != '\0'
- && (country[0] != settings.country[0]
- || country[1] != settings.country[1])) {
+ if (country[0] != '\0' && !areIdentical(country, settings.country)) {
return false;
}
} else {
@@ -2734,37 +2766,43 @@ void ResTable_config::appendDirLocale(String8& out) const {
}
}
-void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN]) const {
+void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool canonicalize) const {
memset(str, 0, RESTABLE_MAX_LOCALE_LEN);
// This represents the "any" locale value, which has traditionally been
// represented by the empty string.
- if (!language[0] && !country[0]) {
+ if (language[0] == '\0' && country[0] == '\0') {
return;
}
size_t charsWritten = 0;
- if (language[0]) {
- charsWritten += unpackLanguage(str);
+ if (language[0] != '\0') {
+ if (canonicalize && areIdentical(language, kTagalog)) {
+ // Replace Tagalog with Filipino if we are canonicalizing
+ str[0] = 'f'; str[1] = 'i'; str[2] = 'l'; str[3] = '\0'; // 3-letter code for Filipino
+ charsWritten += 3;
+ } else {
+ charsWritten += unpackLanguage(str);
+ }
}
- if (localeScript[0] && !localeScriptWasComputed) {
- if (charsWritten) {
+ if (localeScript[0] != '\0' && !localeScriptWasComputed) {
+ if (charsWritten > 0) {
str[charsWritten++] = '-';
}
memcpy(str + charsWritten, localeScript, sizeof(localeScript));
charsWritten += sizeof(localeScript);
}
- if (country[0]) {
- if (charsWritten) {
+ if (country[0] != '\0') {
+ if (charsWritten > 0) {
str[charsWritten++] = '-';
}
charsWritten += unpackRegion(str + charsWritten);
}
- if (localeVariant[0]) {
- if (charsWritten) {
+ if (localeVariant[0] != '\0') {
+ if (charsWritten > 0) {
str[charsWritten++] = '-';
}
memcpy(str + charsWritten, localeVariant, sizeof(localeVariant));
@@ -2956,6 +2994,9 @@ String8 ResTable_config::toString() const {
case ResTable_config::UI_MODE_TYPE_WATCH:
res.append("watch");
break;
+ case ResTable_config::UI_MODE_TYPE_VR_HEADSET:
+ res.append("vrheadset");
+ break;
default:
res.appendFormat("uiModeType=%d",
dtohs(screenLayout&ResTable_config::MASK_UI_MODE_TYPE));
@@ -5881,12 +5922,13 @@ static bool compareString8AndCString(const String8& str, const char* cStr) {
return strcmp(str.string(), cStr) < 0;
}
-void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales) const {
+void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales,
+ bool mergeEquivalentLangs) const {
char locale[RESTABLE_MAX_LOCALE_LEN];
forEachConfiguration(false, false, includeSystemLocales, [&](const ResTable_config& cfg) {
if (cfg.locale != 0) {
- cfg.getBcp47Locale(locale);
+ cfg.getBcp47Locale(locale, mergeEquivalentLangs /* canonicalize if merging */);
const auto beginIter = locales->begin();
const auto endIter = locales->end();
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
new file mode 100644
index 000000000000..a3d67f04eb90
--- /dev/null
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef APKASSETS_H_
+#define APKASSETS_H_
+
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+#include "ziparchive/zip_archive.h"
+
+#include "androidfw/Asset.h"
+#include "androidfw/LoadedArsc.h"
+
+namespace android {
+
+// Holds an APK.
+class ApkAssets {
+ public:
+ static std::unique_ptr<ApkAssets> Load(const std::string& path);
+
+ std::unique_ptr<Asset> Open(const std::string& path,
+ Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
+
+ inline const std::string& GetPath() const { return path_; }
+
+ inline const LoadedArsc* GetLoadedArsc() const { return loaded_arsc_.get(); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ApkAssets);
+
+ ApkAssets() = default;
+
+ struct ZipArchivePtrCloser {
+ void operator()(::ZipArchiveHandle handle) { ::CloseArchive(handle); }
+ };
+
+ using ZipArchivePtr =
+ std::unique_ptr<typename std::remove_pointer<::ZipArchiveHandle>::type, ZipArchivePtrCloser>;
+ ZipArchivePtr zip_handle_;
+ std::string path_;
+ std::unique_ptr<Asset> resources_asset_;
+ std::unique_ptr<LoadedArsc> loaded_arsc_;
+};
+
+} // namespace android
+
+#endif /* APKASSETS_H_ */
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 2cd8c0bf3c78..461e773e5818 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -26,11 +26,12 @@
#include <utils/Compat.h>
#include <utils/Errors.h>
-#include <utils/FileMap.h>
#include <utils/String8.h>
namespace android {
+class FileMap;
+
/*
* Instances of this class provide read-only operations on a byte stream.
*
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index 594dba5049dd..becd307d114d 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -50,10 +50,7 @@ struct ResTable_config;
* single instance may be shared across multiple threads, and a single
* thread may have more than one instance (the latter is discouraged).
*
- * The purpose of the AssetManager is to create Asset objects. To do
- * this efficiently it may cache information about the locations of
- * files it has seen. This can be controlled with the "cacheMode"
- * argument.
+ * The purpose of the AssetManager is to create Asset objects.
*
* The asset hierarchy may be examined like a filesystem, using
* AssetDir objects to peruse a single directory.
@@ -69,18 +66,16 @@ public:
* OVERLAY_DIR.
*/
static const char* OVERLAY_THEME_DIR_PROPERTY;
+ /**
+ * If OVERLAY_THEME_DIR_PERSIST_PROPERTY, use it to override
+ * OVERLAY_THEME_DIR_PROPERTY.
+ */
+ static const char* OVERLAY_THEME_DIR_PERSIST_PROPERTY;
static const char* TARGET_PACKAGE_NAME;
static const char* TARGET_APK_PATH;
static const char* IDMAP_DIR;
- typedef enum CacheMode {
- CACHE_UNKNOWN = 0,
- CACHE_OFF, // don't try to cache file locations
- CACHE_DEFER, // construct cache as pieces are needed
- //CACHE_SCAN, // scan full(!) asset hierarchy at init() time
- } CacheMode;
-
- AssetManager(CacheMode cacheMode = CACHE_OFF);
+ AssetManager();
virtual ~AssetManager(void);
static int32_t getGlobalCount();
@@ -117,23 +112,16 @@ public:
int32_t nextAssetPath(const int32_t cookie) const;
/*
- * Return an asset path in the manager. 'which' must be between 0 and
- * countAssetPaths().
+ * Return an asset path in the manager. 'cookie' must be a non-negative value
+ * previously returned from addAssetPath() or nextAssetPath().
*/
String8 getAssetPath(const int32_t cookie) const;
/*
- * Set the current locale and vendor. The locale can change during
- * the lifetime of an AssetManager if the user updates the device's
- * language setting. The vendor is less likely to change.
- *
- * Pass in NULL to indicate no preference.
- */
- void setLocale(const char* locale);
- void setVendor(const char* vendor);
-
- /*
- * Choose screen orientation for resources values returned.
+ * Sets various device configuration parameters, like screen orientation, layout,
+ * size, locale, etc.
+ * The optional 'locale' string takes precedence over the locale within 'config'
+ * and must be in bcp47 format.
*/
void setConfiguration(const ResTable_config& config, const char* locale = NULL);
@@ -144,9 +132,6 @@ public:
/*
* Open an asset.
*
- * This will search through locale-specific and vendor-specific
- * directories and packages to find the file.
- *
* The object returned does not depend on the AssetManager. It should
* be freed by calling Asset::close().
*/
@@ -156,9 +141,8 @@ public:
* Open a non-asset file as an asset.
*
* This is for opening files that are included in an asset package
- * but aren't assets. These sit outside the usual "locale/vendor"
- * path hierarchy, and will not be seen by "AssetDir" or included
- * in our filename cache.
+ * but aren't assets. These sit outside the usual "assets/"
+ * path hierarchy, and will not be seen by "AssetDir".
*/
Asset* openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie = NULL);
@@ -171,11 +155,6 @@ public:
/*
* Open a directory within the asset hierarchy.
*
- * The contents of the directory are an amalgam of vendor-specific,
- * locale-specific, and generic assets stored loosely or in asset
- * packages. Depending on the cache setting and previous accesses,
- * this call may incur significant disk overhead.
- *
* To open the top-level directory, pass in "".
*/
AssetDir* openDir(const char* dirName);
@@ -183,11 +162,6 @@ public:
/*
* Open a directory within a particular path of the asset manager.
*
- * The contents of the directory are an amalgam of vendor-specific,
- * locale-specific, and generic assets stored loosely or in asset
- * packages. Depending on the cache setting and previous accesses,
- * this call may incur significant disk overhead.
- *
* To open the top-level directory, pass in "".
*/
AssetDir* openNonAssetDir(const int32_t cookie, const char* dirName);
@@ -206,13 +180,6 @@ public:
const ResTable& getResources(bool required = true) const;
/*
- * Discard cached filename information. This only needs to be called
- * if somebody has updated the set of "loose" files, and we want to
- * discard our cached notion of what's where.
- */
- void purge(void) { purgeFileNameCacheLocked(); }
-
- /*
* Return true if the files this AssetManager references are all
* up-to-date (have not been changed since it was created). If false
* is returned, you will need to create a new AssetManager to get
@@ -244,14 +211,8 @@ private:
bool isSystemAsset;
};
- Asset* openInPathLocked(const char* fileName, AccessMode mode,
- const asset_path& path);
Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode,
const asset_path& path);
- Asset* openInLocaleVendorLocked(const char* fileName, AccessMode mode,
- const asset_path& path, const char* locale, const char* vendor);
- String8 createPathNameLocked(const asset_path& path, const char* locale,
- const char* vendor);
String8 createPathNameLocked(const asset_path& path, const char* rootDir);
String8 createZipSourceNameLocked(const String8& zipFileName,
const String8& dirName, const String8& fileName);
@@ -269,15 +230,6 @@ private:
void mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
const SortedVector<AssetDir::FileInfo>* pContents);
- void loadFileNameCacheLocked(void);
- void fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
- const char* dirName);
- bool fncScanAndMergeDirLocked(
- SortedVector<AssetDir::FileInfo>* pMergedInfo,
- const asset_path& path, const char* locale, const char* vendor,
- const char* dirName);
- void purgeFileNameCacheLocked(void);
-
const ResTable* getResTable(bool required = true) const;
void setLocaleLocked(const char* locale);
void updateResourceParamsLocked() const;
@@ -334,8 +286,8 @@ private:
*/
class ZipSet {
public:
- ZipSet(void);
- ~ZipSet(void);
+ ZipSet() = default;
+ ~ZipSet();
/*
* Return a ZipFileRO structure for a ZipFileRO with the specified
@@ -372,23 +324,9 @@ private:
Vector<asset_path> mAssetPaths;
char* mLocale;
- char* mVendor;
mutable ResTable* mResources;
ResTable_config* mConfig;
-
- /*
- * Cached data for "loose" files. This lets us avoid poking at the
- * filesystem when searching for loose assets. Each entry is the
- * "extended partial" path, e.g. "default/default/foo/bar.txt". The
- * full set of files is present, including ".EXCLUDE" entries.
- *
- * We do not cache directory names. We don't retain the ".gz",
- * because to our clients "foo" and "foo.gz" both look like "foo".
- */
- CacheMode mCacheMode; // is the cache enabled?
- bool mCacheValid; // clear when locale or vendor changes
- SortedVector<AssetDir::FileInfo> mCache;
};
}; // namespace android
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
new file mode 100644
index 000000000000..66d5034463a5
--- /dev/null
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROIDFW_ASSETMANAGER2_H_
+#define ANDROIDFW_ASSETMANAGER2_H_
+
+#include "android-base/macros.h"
+
+#include <limits>
+#include <unordered_map>
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/Asset.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/Util.h"
+
+namespace android {
+
+class Theme;
+
+using ApkAssetsCookie = int32_t;
+
+enum : ApkAssetsCookie {
+ kInvalidCookie = -1,
+};
+
+// Holds a bag that has been merged with its parent, if one exists.
+struct ResolvedBag {
+ // A single key-value entry in a bag.
+ struct Entry {
+ // The key, as described in ResTable_map::name.
+ uint32_t key;
+
+ Res_value value;
+
+ // Which ApkAssets this entry came from.
+ ApkAssetsCookie cookie;
+
+ ResStringPool* key_pool;
+ ResStringPool* type_pool;
+ };
+
+ // Denotes the configuration axis that this bag varies with.
+ // If a configuration changes with respect to one of these axis,
+ // the bag should be reloaded.
+ uint32_t type_spec_flags;
+
+ // The number of entries in this bag. Access them by indexing into `entries`.
+ uint32_t entry_count;
+
+ // The array of entries for this bag. An empty array is a neat trick to force alignment
+ // of the Entry structs that follow this structure and avoids a bunch of casts.
+ Entry entries[0];
+};
+
+// AssetManager2 is the main entry point for accessing assets and resources.
+// AssetManager2 provides caching of resources retrieved via the underlying
+// ApkAssets.
+class AssetManager2 : public ::AAssetManager {
+ public:
+ struct ResourceName {
+ const char* package = nullptr;
+ size_t package_len = 0u;
+
+ const char* type = nullptr;
+ const char16_t* type16 = nullptr;
+ size_t type_len = 0u;
+
+ const char* entry = nullptr;
+ const char16_t* entry16 = nullptr;
+ size_t entry_len = 0u;
+ };
+
+ AssetManager2();
+
+ // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
+ // are not owned by the AssetManager, and must have a longer lifetime.
+ //
+ // Only pass invalidate_caches=false when it is known that the structure
+ // change in ApkAssets is due to a safe addition of resources with completely
+ // new resource IDs.
+ bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true);
+
+ const std::vector<const ApkAssets*> GetApkAssets() const;
+
+ // Returns the string pool for the given asset cookie.
+ // Use the string pool returned here with a valid Res_value object of
+ // type Res_value::TYPE_STRING.
+ const ResStringPool* GetStringPoolForCookie(ApkAssetsCookie cookie) const;
+
+ // Sets/resets the configuration for this AssetManager. This will cause all
+ // caches that are related to the configuration change to be invalidated.
+ void SetConfiguration(const ResTable_config& configuration);
+
+ const ResTable_config& GetConfiguration() const;
+
+ // Searches the set of APKs loaded by this AssetManager and opens the first one found located
+ // in the assets/ directory.
+ // `mode` controls how the file is opened.
+ //
+ // NOTE: The loaded APKs are searched in reverse order.
+ std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode);
+
+ // Opens a file within the assets/ directory of the APK specified by `cookie`.
+ // `mode` controls how the file is opened.
+ std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie,
+ Asset::AccessMode mode);
+
+ // Searches the set of APKs loaded by this AssetManager and opens the first one found.
+ // `mode` controls how the file is opened.
+ // `out_cookie` is populated with the cookie of the APK this file was found in.
+ //
+ // NOTE: The loaded APKs are searched in reverse order.
+ std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode,
+ ApkAssetsCookie* out_cookie = nullptr);
+
+ // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened.
+ // This is typically used to open a specific AndroidManifest.xml, or a binary XML file
+ // referenced by a resource lookup with GetResource().
+ std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
+ Asset::AccessMode mode);
+
+ // Populates the `out_name` parameter with resource name information.
+ // Utf8 strings are preferred, and only if they are unavailable are
+ // the Utf16 variants populated.
+ // Returns false if the resource was not found or the name was missing/corrupt.
+ bool GetResourceName(uint32_t resid, ResourceName* out_name);
+
+ // Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
+ // See ResTable_config for the list of configuration axis.
+ // Returns false if the resource was not found.
+ bool GetResourceFlags(uint32_t resid, uint32_t* out_flags);
+
+ // Retrieves the best matching resource with ID `resid`. The resource value is filled into
+ // `out_value` and the configuration for the selected value is populated in `out_selected_config`.
+ // `out_flags` holds the same flags as retrieved with GetResourceFlags().
+ // If `density_override` is non-zero, the configuration to match against is overridden with that
+ // density.
+ //
+ // Returns a valid cookie if the resource was found. If the resource was not found, or if the
+ // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false,
+ // this function logs if the resource was a map/bag type before returning kInvalidCookie.
+ ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override,
+ Res_value* out_value, ResTable_config* out_selected_config,
+ uint32_t* out_flags);
+
+ // Retrieves the best matching bag/map resource with ID `resid`.
+ // This method will resolve all parent references for this bag and merge keys with the child.
+ // To iterate over the keys, use the following idiom:
+ //
+ // const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id);
+ // if (bag != nullptr) {
+ // for (auto iter = begin(bag); iter != end(bag); ++iter) {
+ // ...
+ // }
+ // }
+ const ResolvedBag* GetBag(uint32_t resid);
+
+ // Creates a new Theme from this AssetManager.
+ std::unique_ptr<Theme> NewTheme();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AssetManager2);
+
+ // Finds the best entry for `resid` amongst all the ApkAssets. The entry can be a simple
+ // Res_value, or a complex map/bag type.
+ //
+ // `density_override` overrides the density of the current configuration when doing a search.
+ //
+ // When `stop_at_first_match` is true, the first match found is selected and the search
+ // terminates. This is useful for methods that just look up the name of a resource and don't
+ // care about the value. In this case, the value of `out_flags` is incomplete and should not
+ // be used.
+ //
+ // `out_flags` stores the resulting bitmask of configuration axis with which the resource
+ // value varies.
+ ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
+ LoadedArsc::Entry* out_entry, ResTable_config* out_selected_config,
+ uint32_t* out_flags);
+
+ // Purge all resources that are cached and vary by the configuration axis denoted by the
+ // bitmask `diff`.
+ void InvalidateCaches(uint32_t diff);
+
+ // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
+ // have a longer lifetime.
+ std::vector<const ApkAssets*> apk_assets_;
+
+ // The current configuration set for this AssetManager. When this changes, cached resources
+ // may need to be purged.
+ ResTable_config configuration_;
+
+ // Cached set of bags. These are cached because they can inherit keys from parent bags,
+ // which involves some calculation.
+ std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
+};
+
+class Theme {
+ friend class AssetManager2;
+
+ public:
+ // Applies the style identified by `resid` to this theme. This can be called
+ // multiple times with different styles. By default, any theme attributes that
+ // are already defined before this call are not overridden. If `force` is set
+ // to true, this behavior is changed and all theme attributes from the style at
+ // `resid` are applied.
+ // Returns false if the style failed to apply.
+ bool ApplyStyle(uint32_t resid, bool force = false);
+
+ // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
+ // Returns false if the AssetManagers of the Themes were not compatible.
+ bool SetTo(const Theme& o);
+
+ void Clear();
+
+ inline const AssetManager2* GetAssetManager() const { return asset_manager_; }
+
+ // Returns a bit mask of configuration changes that will impact this
+ // theme (and thus require completely reloading it).
+ inline uint32_t GetChangingConfigurations() const { return type_spec_flags_; }
+
+ // Retrieve a value in the theme. If the theme defines this value,
+ // returns an asset cookie indicating which ApkAssets it came from
+ // and populates `out_value` with the value. If `out_flags` is non-null,
+ // populates it with a bitmask of the configuration axis the resource
+ // varies with.
+ //
+ // If the attribute is not found, returns kInvalidCookie.
+ //
+ // NOTE: This function does not do reference traversal. If you want
+ // to follow references to other resources to get the "real" value to
+ // use, you need to call ResolveReference() after this function.
+ ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value,
+ uint32_t* out_flags = nullptr) const;
+
+ // This is like AssetManager2::ResolveReference(), but also takes
+ // care of resolving attribute references to the theme.
+ ApkAssetsCookie ResolveAttributeReference(Res_value* in_out_value, ApkAssetsCookie src_cookie,
+ uint32_t* out_last_ref = nullptr,
+ uint32_t* in_out_type_spec_flags = nullptr,
+ ResTable_config* out_selected_config = nullptr) const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Theme);
+
+ // Called by AssetManager2.
+ explicit inline Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) {}
+
+ struct Entry {
+ ApkAssetsCookie cookie;
+ uint32_t type_spec_flags;
+ Res_value value;
+ };
+
+ struct Type {
+ // Use uint32_t for fewer cycles when loading from memory.
+ uint32_t entry_count;
+ uint32_t entry_capacity;
+ Entry entries[0];
+ };
+
+ static constexpr const size_t kPackageCount = std::numeric_limits<uint8_t>::max() + 1;
+ static constexpr const size_t kTypeCount = std::numeric_limits<uint8_t>::max() + 1;
+
+ struct Package {
+ // Each element of Type will be a dynamically sized object
+ // allocated to have the entries stored contiguously with the Type.
+ util::unique_cptr<Type> types[kTypeCount];
+ };
+
+ AssetManager2* asset_manager_;
+ uint32_t type_spec_flags_ = 0u;
+ std::unique_ptr<Package> packages_[kPackageCount];
+};
+
+inline const ResolvedBag::Entry* begin(const ResolvedBag* bag) { return bag->entries; }
+
+inline const ResolvedBag::Entry* end(const ResolvedBag* bag) {
+ return bag->entries + bag->entry_count;
+}
+
+} // namespace android
+
+#endif /* ANDROIDFW_ASSETMANAGER2_H_ */
diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h
index acf70565c4f9..f281921824e7 100644
--- a/libs/androidfw/include/androidfw/AttributeFinder.h
+++ b/libs/androidfw/include/androidfw/AttributeFinder.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-#ifndef H_ATTRIBUTE_FINDER
-#define H_ATTRIBUTE_FINDER
+#ifndef ANDROIDFW_ATTRIBUTE_FINDER_H
+#define ANDROIDFW_ATTRIBUTE_FINDER_H
-#include <stdint.h>
#include <utils/KeyedVector.h>
+#include <stdint.h>
+
namespace android {
-static inline uint32_t getPackage(uint32_t attr) {
- return attr >> 24;
-}
+static inline uint32_t get_package(uint32_t attr) { return attr >> 24; }
/**
* A helper class to search linearly for the requested
@@ -55,152 +54,153 @@ static inline uint32_t getPackage(uint32_t attr) {
*/
template <typename Derived, typename Iterator>
class BackTrackingAttributeFinder {
-public:
- BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
+ public:
+ BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
- Iterator find(uint32_t attr);
+ Iterator Find(uint32_t attr);
-private:
- void jumpToClosestAttribute(uint32_t packageId);
- void markCurrentPackageId(uint32_t packageId);
+ private:
+ void JumpToClosestAttribute(uint32_t package_id);
+ void MarkCurrentPackageId(uint32_t package_id);
- bool mFirstTime;
- Iterator mBegin;
- Iterator mEnd;
- Iterator mCurrent;
- Iterator mLargest;
- uint32_t mLastPackageId;
- uint32_t mCurrentAttr;
+ bool first_time_;
+ Iterator begin_;
+ Iterator end_;
+ Iterator current_;
+ Iterator largest_;
+ uint32_t last_package_id_;
+ uint32_t current_attr_;
- // Package Offsets (best-case, fast look-up).
- Iterator mFrameworkStart;
- Iterator mAppStart;
+ // Package offsets (best-case, fast look-up).
+ Iterator framework_start_;
+ Iterator app_start_;
- // Worst case, we have shared-library resources.
- KeyedVector<uint32_t, Iterator> mPackageOffsets;
+ // Worst case, we have shared-library resources.
+ KeyedVector<uint32_t, Iterator> package_offsets_;
};
-template <typename Derived, typename Iterator> inline
-BackTrackingAttributeFinder<Derived, Iterator>::BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end)
- : mFirstTime(true)
- , mBegin(begin)
- , mEnd(end)
- , mCurrent(begin)
- , mLargest(begin)
- , mLastPackageId(0)
- , mCurrentAttr(0)
- , mFrameworkStart(end)
- , mAppStart(end) {
-}
+template <typename Derived, typename Iterator>
+inline BackTrackingAttributeFinder<Derived, Iterator>::BackTrackingAttributeFinder(
+ const Iterator& begin, const Iterator& end)
+ : first_time_(true),
+ begin_(begin),
+ end_(end),
+ current_(begin),
+ largest_(begin),
+ last_package_id_(0),
+ current_attr_(0),
+ framework_start_(end),
+ app_start_(end) {}
template <typename Derived, typename Iterator>
-void BackTrackingAttributeFinder<Derived, Iterator>::jumpToClosestAttribute(const uint32_t packageId) {
- switch (packageId) {
- case 0x01:
- mCurrent = mFrameworkStart;
- break;
- case 0x7f:
- mCurrent = mAppStart;
- break;
- default: {
- ssize_t idx = mPackageOffsets.indexOfKey(packageId);
- if (idx >= 0) {
- // We have seen this package ID before, so jump to the first
- // attribute with this package ID.
- mCurrent = mPackageOffsets[idx];
- } else {
- mCurrent = mEnd;
- }
- break;
- }
+void BackTrackingAttributeFinder<Derived, Iterator>::JumpToClosestAttribute(
+ const uint32_t package_id) {
+ switch (package_id) {
+ case 0x01u:
+ current_ = framework_start_;
+ break;
+ case 0x7fu:
+ current_ = app_start_;
+ break;
+ default: {
+ ssize_t idx = package_offsets_.indexOfKey(package_id);
+ if (idx >= 0) {
+ // We have seen this package ID before, so jump to the first
+ // attribute with this package ID.
+ current_ = package_offsets_[idx];
+ } else {
+ current_ = end_;
+ }
+ break;
}
+ }
- // We have never seen this package ID yet, so jump to the
- // latest/largest index we have processed so far.
- if (mCurrent == mEnd) {
- mCurrent = mLargest;
- }
+ // We have never seen this package ID yet, so jump to the
+ // latest/largest index we have processed so far.
+ if (current_ == end_) {
+ current_ = largest_;
+ }
- if (mCurrent != mEnd) {
- mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent);
- }
+ if (current_ != end_) {
+ current_attr_ = static_cast<const Derived*>(this)->GetAttribute(current_);
+ }
}
template <typename Derived, typename Iterator>
-void BackTrackingAttributeFinder<Derived, Iterator>::markCurrentPackageId(const uint32_t packageId) {
- switch (packageId) {
- case 0x01:
- mFrameworkStart = mCurrent;
- break;
- case 0x7f:
- mAppStart = mCurrent;
- break;
- default:
- mPackageOffsets.add(packageId, mCurrent);
- break;
- }
+void BackTrackingAttributeFinder<Derived, Iterator>::MarkCurrentPackageId(
+ const uint32_t package_id) {
+ switch (package_id) {
+ case 0x01u:
+ framework_start_ = current_;
+ break;
+ case 0x7fu:
+ app_start_ = current_;
+ break;
+ default:
+ package_offsets_.add(package_id, current_);
+ break;
+ }
}
template <typename Derived, typename Iterator>
-Iterator BackTrackingAttributeFinder<Derived, Iterator>::find(uint32_t attr) {
- if (!(mBegin < mEnd)) {
- return mEnd;
+Iterator BackTrackingAttributeFinder<Derived, Iterator>::Find(uint32_t attr) {
+ if (!(begin_ < end_)) {
+ return end_;
+ }
+
+ if (first_time_) {
+ // One-time initialization. We do this here instead of the constructor
+ // because the derived class we access in getAttribute() may not be
+ // fully constructed.
+ first_time_ = false;
+ current_attr_ = static_cast<const Derived*>(this)->GetAttribute(begin_);
+ last_package_id_ = get_package(current_attr_);
+ MarkCurrentPackageId(last_package_id_);
+ }
+
+ // Looking for the needle (attribute we're looking for)
+ // in the haystack (the attributes we're searching through)
+ const uint32_t needle_package_id = get_package(attr);
+ if (last_package_id_ != needle_package_id) {
+ JumpToClosestAttribute(needle_package_id);
+ last_package_id_ = needle_package_id;
+ }
+
+ // Walk through the xml attributes looking for the requested attribute.
+ while (current_ != end_) {
+ const uint32_t haystack_package_id = get_package(current_attr_);
+ if (needle_package_id == haystack_package_id && attr < current_attr_) {
+ // The attribute we are looking was not found.
+ break;
}
-
- if (mFirstTime) {
- // One-time initialization. We do this here instead of the constructor
- // because the derived class we access in getAttribute() may not be
- // fully constructed.
- mFirstTime = false;
- mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mBegin);
- mLastPackageId = getPackage(mCurrentAttr);
- markCurrentPackageId(mLastPackageId);
+ const uint32_t prev_attr = current_attr_;
+
+ // Move to the next attribute in the XML.
+ ++current_;
+ if (current_ != end_) {
+ current_attr_ = static_cast<const Derived*>(this)->GetAttribute(current_);
+ const uint32_t new_haystack_package_id = get_package(current_attr_);
+ if (haystack_package_id != new_haystack_package_id) {
+ // We've moved to the next group of attributes
+ // with a new package ID, so we should record
+ // the offset of this new package ID.
+ MarkCurrentPackageId(new_haystack_package_id);
+ }
}
- // Looking for the needle (attribute we're looking for)
- // in the haystack (the attributes we're searching through)
- const uint32_t needlePackageId = getPackage(attr);
- if (mLastPackageId != needlePackageId) {
- jumpToClosestAttribute(needlePackageId);
- mLastPackageId = needlePackageId;
+ if (current_ > largest_) {
+ // We've moved past the latest attribute we've seen.
+ largest_ = current_;
}
- // Walk through the xml attributes looking for the requested attribute.
- while (mCurrent != mEnd) {
- const uint32_t haystackPackageId = getPackage(mCurrentAttr);
- if (needlePackageId == haystackPackageId && attr < mCurrentAttr) {
- // The attribute we are looking was not found.
- break;
- }
- const uint32_t prevAttr = mCurrentAttr;
-
- // Move to the next attribute in the XML.
- ++mCurrent;
- if (mCurrent != mEnd) {
- mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent);
- const uint32_t newHaystackPackageId = getPackage(mCurrentAttr);
- if (haystackPackageId != newHaystackPackageId) {
- // We've moved to the next group of attributes
- // with a new package ID, so we should record
- // the offset of this new package ID.
- markCurrentPackageId(newHaystackPackageId);
- }
- }
-
- if (mCurrent > mLargest) {
- // We've moved past the latest attribute we've
- // seen.
- mLargest = mCurrent;
- }
-
- if (attr == prevAttr) {
- // We found the attribute we were looking for.
- return mCurrent - 1;
- }
+ if (attr == prev_attr) {
+ // We found the attribute we were looking for.
+ return current_ - 1;
}
- return mEnd;
+ }
+ return end_;
}
-} // namespace android
+} // namespace android
-#endif // H_ATTRIBUTE_FINDER
+#endif // ANDROIDFW_ATTRIBUTE_FINDER_H
diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h
new file mode 100644
index 000000000000..69b760414846
--- /dev/null
+++ b/libs/androidfw/include/androidfw/AttributeResolution.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H
+#define ANDROIDFW_ATTRIBUTERESOLUTION_H
+
+#include <androidfw/ResourceTypes.h>
+
+namespace android {
+
+// Offsets into the outValues array populated by the methods below. outValues is a uint32_t
+// array, but each logical element takes up 6 uint32_t-sized physical elements.
+enum {
+ STYLE_NUM_ENTRIES = 6,
+ STYLE_TYPE = 0,
+ STYLE_DATA = 1,
+ STYLE_ASSET_COOKIE = 2,
+ STYLE_RESOURCE_ID = 3,
+ STYLE_CHANGING_CONFIGURATIONS = 4,
+ STYLE_DENSITY = 5
+};
+
+// These are all variations of the same method. They each perform the exact same operation,
+// but on various data sources. I *think* they are re-written to avoid an extra branch
+// in the inner loop, but after one branch miss (some pointer != null), the branch predictor should
+// predict the rest of the iterations' branch correctly.
+// TODO(adamlesinski): Run performance tests against these methods and a new, single method
+// that uses all the sources and branches to the right ones within the inner loop.
+
+// `out_values` must NOT be nullptr.
+// `out_indices` may be nullptr.
+bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
+ uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
+
+// `out_values` must NOT be nullptr.
+// `out_indices` is NOT optional and must NOT be nullptr.
+void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length,
+ uint32_t* out_values, uint32_t* out_indices);
+
+// `out_values` must NOT be nullptr.
+// `out_indices` may be nullptr.
+bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
+
+} // namespace android
+
+#endif /* ANDROIDFW_ATTRIBUTERESOLUTION_H */
diff --git a/libs/androidfw/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h
index 87c6b128eca1..d84a207697e9 100644
--- a/libs/androidfw/include/androidfw/ByteBucketArray.h
+++ b/libs/androidfw/include/androidfw/ByteBucketArray.h
@@ -17,9 +17,10 @@
#ifndef __BYTE_BUCKET_ARRAY_H
#define __BYTE_BUCKET_ARRAY_H
-#include <utils/Log.h>
-#include <stdint.h>
-#include <string.h>
+#include <cstdint>
+#include <cstring>
+
+#include "android-base/logging.h"
namespace android {
@@ -27,71 +28,65 @@ namespace android {
* Stores a sparsely populated array. Has a fixed size of 256
* (number of entries that a byte can represent).
*/
-template<typename T>
+template <typename T>
class ByteBucketArray {
-public:
- ByteBucketArray() : mDefault() {
- memset(mBuckets, 0, sizeof(mBuckets));
+ public:
+ ByteBucketArray() : default_() { memset(buckets_, 0, sizeof(buckets_)); }
+
+ ~ByteBucketArray() {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ if (buckets_[i] != NULL) {
+ delete[] buckets_[i];
+ }
}
+ memset(buckets_, 0, sizeof(buckets_));
+ }
- ~ByteBucketArray() {
- for (size_t i = 0; i < NUM_BUCKETS; i++) {
- if (mBuckets[i] != NULL) {
- delete [] mBuckets[i];
- }
- }
- memset(mBuckets, 0, sizeof(mBuckets));
- }
+ inline size_t size() const { return kNumBuckets * kBucketSize; }
- inline size_t size() const {
- return NUM_BUCKETS * BUCKET_SIZE;
- }
+ inline const T& get(size_t index) const { return (*this)[index]; }
- inline const T& get(size_t index) const {
- return (*this)[index];
+ const T& operator[](size_t index) const {
+ if (index >= size()) {
+ return default_;
}
- const T& operator[](size_t index) const {
- if (index >= size()) {
- return mDefault;
- }
-
- uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
- T* bucket = mBuckets[bucketIndex];
- if (bucket == NULL) {
- return mDefault;
- }
- return bucket[0x0f & static_cast<uint8_t>(index)];
+ uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
+ T* bucket = buckets_[bucket_index];
+ if (bucket == NULL) {
+ return default_;
}
+ return bucket[0x0f & static_cast<uint8_t>(index)];
+ }
- T& editItemAt(size_t index) {
- ALOG_ASSERT(index < size(), "ByteBucketArray.getOrCreate(index=%u) with size=%u",
- (uint32_t) index, (uint32_t) size());
+ T& editItemAt(size_t index) {
+ CHECK(index < size()) << "ByteBucketArray.getOrCreate(index=" << index
+ << ") with size=" << size();
- uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
- T* bucket = mBuckets[bucketIndex];
- if (bucket == NULL) {
- bucket = mBuckets[bucketIndex] = new T[BUCKET_SIZE]();
- }
- return bucket[0x0f & static_cast<uint8_t>(index)];
+ uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
+ T* bucket = buckets_[bucket_index];
+ if (bucket == NULL) {
+ bucket = buckets_[bucket_index] = new T[kBucketSize]();
}
+ return bucket[0x0f & static_cast<uint8_t>(index)];
+ }
- bool set(size_t index, const T& value) {
- if (index >= size()) {
- return false;
- }
-
- editItemAt(index) = value;
- return true;
+ bool set(size_t index, const T& value) {
+ if (index >= size()) {
+ return false;
}
-private:
- enum { NUM_BUCKETS = 16, BUCKET_SIZE = 16 };
+ editItemAt(index) = value;
+ return true;
+ }
+
+ private:
+ enum { kNumBuckets = 16, kBucketSize = 16 };
- T* mBuckets[NUM_BUCKETS];
- T mDefault;
+ T* buckets_[kNumBuckets];
+ T default_;
};
-} // namespace android
+} // namespace android
-#endif // __BYTE_BUCKET_ARRAY_H
+#endif // __BYTE_BUCKET_ARRAY_H
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
new file mode 100644
index 000000000000..e2e56c88ee1d
--- /dev/null
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOADEDARSC_H_
+#define LOADEDARSC_H_
+
+#include <memory>
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "androidfw/ResourceTypes.h"
+
+namespace android {
+
+class Chunk;
+class LoadedPackage;
+
+// Read-only view into a resource table. This class validates all data
+// when loading, including offsets and lengths.
+class LoadedArsc {
+ public:
+ // Load the resource table from memory. The data's lifetime must out-live the
+ // object returned from this method.
+ static std::unique_ptr<LoadedArsc> Load(const void* data, size_t len);
+
+ ~LoadedArsc();
+
+ // Returns the string pool where all string resource values
+ // (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
+ inline const ResStringPool* GetStringPool() const { return &global_string_pool_; }
+
+ struct Entry {
+ // A pointer to the resource table entry for this resource.
+ // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
+ // a ResTable_map_entry and processed as a bag/map.
+ const ResTable_entry* entry = nullptr;
+
+ // The string pool reference to the type's name. This uses a different string pool than
+ // the global string pool, but this is hidden from the caller.
+ StringPoolRef type_string_ref;
+
+ // The string pool reference to the entry's name. This uses a different string pool than
+ // the global string pool, but this is hidden from the caller.
+ StringPoolRef entry_string_ref;
+ };
+
+ // Finds the resource with ID `resid` with the best value for configuration `config`.
+ // The parameter `out_entry` will be filled with the resulting resource entry.
+ // The resource entry can be a simple entry (ResTable_entry) or a complex bag
+ // (ResTable_entry_map).
+ bool FindEntry(uint32_t resid, const ResTable_config& config, Entry* out_entry,
+ ResTable_config* selected_config, uint32_t* out_flags) const;
+
+ // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist.
+ const std::string* GetPackageNameForId(uint32_t resid) const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
+
+ LoadedArsc() = default;
+ bool LoadTable(const Chunk& chunk);
+
+ ResStringPool global_string_pool_;
+ std::vector<std::unique_ptr<LoadedPackage>> packages_;
+};
+
+} // namespace android
+
+#endif /* LOADEDARSC_H_ */
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 12a6b0f9a4ec..c118b57510f9 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -22,7 +22,6 @@
#include <androidfw/Asset.h>
#include <androidfw/LocaleData.h>
-#include <utils/ByteOrder.h>
#include <utils/Errors.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -266,7 +265,7 @@ struct Res_value
uint8_t res0;
// Type of the data value.
- enum {
+ enum : uint8_t {
// The 'data' is either 0 or 1, specifying this resource is either
// undefined or empty, respectively.
TYPE_NULL = 0x00,
@@ -1103,6 +1102,7 @@ struct ResTable_config
UI_MODE_TYPE_TELEVISION = ACONFIGURATION_UI_MODE_TYPE_TELEVISION,
UI_MODE_TYPE_APPLIANCE = ACONFIGURATION_UI_MODE_TYPE_APPLIANCE,
UI_MODE_TYPE_WATCH = ACONFIGURATION_UI_MODE_TYPE_WATCH,
+ UI_MODE_TYPE_VR_HEADSET = ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET,
// uiMode bits for the night switch.
MASK_UI_MODE_NIGHT = 0x30,
@@ -1227,7 +1227,10 @@ struct ResTable_config
// |RESTABLE_MAX_LOCALE_LEN| (including a terminating '\0').
//
// Example: en-US, en-Latn-US, en-POSIX.
- void getBcp47Locale(char* out) const;
+ //
+ // If canonicalize is set, Tagalog (tl) locales get converted
+ // to Filipino (fil).
+ void getBcp47Locale(char* out, bool canonicalize=false) const;
// Append to str the resource-qualifer string representation of the
// locale component of this Config. If the locale is only country
@@ -1849,7 +1852,8 @@ public:
void getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap=false,
bool ignoreAndroidPackage=false, bool includeSystemConfigs=true) const;
- void getLocales(Vector<String8>* locales, bool includeSystemLocales=true) const;
+ void getLocales(Vector<String8>* locales, bool includeSystemLocales=true,
+ bool mergeEquivalentLangs=false) const;
// Generate an idmap.
//
diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h
index fd848736d2d6..f1daf3365c28 100644
--- a/libs/androidfw/include/androidfw/TypeWrappers.h
+++ b/libs/androidfw/include/androidfw/TypeWrappers.h
@@ -18,6 +18,7 @@
#define __TYPE_WRAPPERS_H
#include <androidfw/ResourceTypes.h>
+#include <utils/ByteOrder.h>
namespace android {
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
new file mode 100644
index 000000000000..5266d09e84e3
--- /dev/null
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <cstdlib>
+#include <memory>
+
+#include "android-base/macros.h"
+
+namespace android {
+namespace util {
+
+/**
+ * Makes a std::unique_ptr<> with the template parameter inferred by the
+ * compiler.
+ * This will be present in C++14 and can be removed then.
+ */
+template <typename T, class... Args>
+std::unique_ptr<T> make_unique(Args&&... args) {
+ return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
+}
+
+// Based on std::unique_ptr, but uses free() to release malloc'ed memory
+// without incurring the size increase of holding on to a custom deleter.
+template <typename T>
+class unique_cptr {
+ public:
+ using pointer = typename std::add_pointer<T>::type;
+
+ constexpr unique_cptr() : ptr_(nullptr) {}
+ constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
+ explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
+ unique_cptr(unique_cptr&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; }
+
+ ~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); }
+
+ inline unique_cptr& operator=(unique_cptr&& o) {
+ if (&o == this) {
+ return *this;
+ }
+
+ std::free(reinterpret_cast<void*>(ptr_));
+ ptr_ = o.ptr_;
+ o.ptr_ = nullptr;
+ return *this;
+ }
+
+ inline unique_cptr& operator=(std::nullptr_t) {
+ std::free(reinterpret_cast<void*>(ptr_));
+ ptr_ = nullptr;
+ return *this;
+ }
+
+ pointer release() {
+ pointer result = ptr_;
+ ptr_ = nullptr;
+ return result;
+ }
+
+ inline pointer get() const { return ptr_; }
+
+ void reset(pointer ptr = pointer()) {
+ if (ptr == ptr_) {
+ return;
+ }
+
+ pointer old_ptr = ptr_;
+ ptr_ = ptr;
+ std::free(reinterpret_cast<void*>(old_ptr));
+ }
+
+ inline void swap(unique_cptr& o) { std::swap(ptr_, o.ptr_); }
+
+ inline explicit operator bool() const { return ptr_ != nullptr; }
+
+ inline typename std::add_lvalue_reference<T>::type operator*() const { return *ptr_; }
+
+ inline pointer operator->() const { return ptr_; }
+
+ inline bool operator==(const unique_cptr& o) const { return ptr_ == o.ptr_; }
+
+ inline bool operator==(std::nullptr_t) const { return ptr_ == nullptr; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(unique_cptr);
+
+ pointer ptr_;
+};
+
+inline uint8_t get_package_id(uint32_t resid) {
+ return static_cast<uint8_t>((resid >> 24) & 0x000000ffu);
+}
+
+// The type ID is 1-based, so if the returned value is 0 it is invalid.
+inline uint8_t get_type_id(uint32_t resid) {
+ return static_cast<uint8_t>((resid >> 16) & 0x000000ffu);
+}
+
+inline uint16_t get_entry_id(uint32_t resid) { return static_cast<uint16_t>(resid & 0x0000ffffu); }
+
+inline bool is_internal_id(uint32_t resid) {
+ return (resid & 0xffff0000u) != 0 && (resid & 0x00ff0000u) == 0;
+}
+
+inline bool is_valid_resid(uint32_t resid) {
+ return (resid & 0x00ff0000u) != 0 && (resid & 0xff000000u) != 0;
+}
+
+} // namespace util
+} // namespace android
+
+#endif /* UTIL_H_ */
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 1fe1773578fa..6754cd89944a 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -21,26 +21,37 @@
LOCAL_PATH:= $(call my-dir)
testFiles := \
+ ApkAssets_test.cpp \
AppAsLib_test.cpp \
Asset_test.cpp \
+ AssetManager2_test.cpp \
AttributeFinder_test.cpp \
+ AttributeResolution_test.cpp \
ByteBucketArray_test.cpp \
Config_test.cpp \
ConfigLocale_test.cpp \
Idmap_test.cpp \
+ LoadedArsc_test.cpp \
ResTable_test.cpp \
Split_test.cpp \
TestHelpers.cpp \
+ TestMain.cpp \
Theme_test.cpp \
TypeWrappers_test.cpp \
ZipUtils_test.cpp
+benchmarkFiles := \
+ AssetManager2_bench.cpp \
+ BenchMain.cpp \
+ TestHelpers.cpp \
+ Theme_bench.cpp
+
androidfw_test_cflags := \
-Wall \
-Werror \
-Wunused \
-Wunreachable-code \
- -Wno-missing-field-initializers \
+ -Wno-missing-field-initializers
# gtest is broken.
androidfw_test_cflags += -Wno-unnamed-type-template-args
@@ -55,10 +66,13 @@ LOCAL_CFLAGS := $(androidfw_test_cflags)
LOCAL_SRC_FILES := $(testFiles)
LOCAL_STATIC_LIBRARIES := \
libandroidfw \
+ libbase \
libutils \
libcutils \
liblog \
libz \
+ libziparchive
+LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
include $(BUILD_HOST_NATIVE_TEST)
@@ -76,9 +90,32 @@ LOCAL_SRC_FILES := $(testFiles) \
LOCAL_SHARED_LIBRARIES := \
libandroidfw \
+ libbase \
libcutils \
libutils \
libui \
+ libziparchive
+LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
+
+include $(BUILD_NATIVE_TEST)
+
+# ==========================================================
+# Build the device benchmarks: libandroidfw_benchmarks
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libandroidfw_benchmarks
+LOCAL_CFLAGS := $(androidfw_test_cflags)
+LOCAL_SRC_FILES := $(benchmarkFiles)
+LOCAL_STATIC_LIBRARIES := \
+ libgoogle-benchmark
+LOCAL_SHARED_LIBRARIES := \
+ libandroidfw \
+ libbase \
+ libcutils \
+ libutils \
+ libziparchive
+LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
include $(BUILD_NATIVE_TEST)
endif # Not SDK_ONLY
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
new file mode 100644
index 000000000000..3a1fc8fa0d3f
--- /dev/null
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/ApkAssets.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+
+using com::android::basic::R;
+
+namespace android {
+
+TEST(ApkAssetsTest, LoadApk) {
+ std::unique_ptr<ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+ ASSERT_NE(nullptr, loaded_apk);
+
+ std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
+ ASSERT_NE(nullptr, asset);
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/AppAsLib_test.cpp b/libs/androidfw/tests/AppAsLib_test.cpp
index 8489acf6246f..ddaa46d274cd 100644
--- a/libs/androidfw/tests/AppAsLib_test.cpp
+++ b/libs/androidfw/tests/AppAsLib_test.cpp
@@ -14,55 +14,69 @@
* limitations under the License.
*/
-#include <androidfw/ResourceTypes.h>
+#include "androidfw/ResourceTypes.h"
+#include "TestHelpers.h"
#include "data/appaslib/R.h"
-#include <gtest/gtest.h>
+namespace app = com::android::appaslib::app;
+namespace lib = com::android::appaslib::lib;
-using namespace android;
-
-namespace {
-
-#include "data/appaslib/appaslib_arsc.h"
-#include "data/appaslib/appaslib_lib_arsc.h"
+namespace android {
// This tests the app resources loaded as app.
-TEST(AppAsLibTest, loadedAsApp) {
+TEST(AppAsLibTest, LoadedAsApp) {
+ std::string contents;
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk",
+ "resources.arsc", &contents));
+
ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(appaslib_arsc, appaslib_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
Res_value val;
- ssize_t block = table.getResource(appaslib::R::app::integer::number1, &val);
+ ssize_t block = table.getResource(app::R::integer::number1, &val);
ASSERT_GE(block, 0);
ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
- ASSERT_EQ(appaslib::R::app::array::integerArray1, val.data);
+ ASSERT_EQ(app::R::array::integerArray1, val.data);
}
// This tests the app resources loaded as shared-lib.
-TEST(AppAsLibTest, loadedAsSharedLib) {
+TEST(AppAsLibTest, LoadedAsSharedLib) {
+ std::string contents;
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk",
+ "resources.arsc", &contents));
+
ResTable table;
// Load as shared library.
- ASSERT_EQ(NO_ERROR, table.add(appaslib_arsc, appaslib_arsc_len, NULL, 0, -1, false, true));
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size(), NULL, 0, -1,
+ false, true));
Res_value val;
- ssize_t block = table.getResource(appaslib::R::lib::integer::number1, &val);
+ ssize_t block = table.getResource(lib::R::integer::number1, &val);
ASSERT_GE(block, 0);
ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
- ASSERT_EQ(appaslib::R::lib::array::integerArray1, val.data);
+ ASSERT_EQ(lib::R::array::integerArray1, val.data);
}
// This tests the shared-lib loaded with appAsLib as true.
-TEST(AppAsLibTest, loadedSharedLib) {
+TEST(AppAsLibTest, LoadedSharedLib) {
+ std::string contents;
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib_lib.apk",
+ "resources.arsc", &contents));
+
ResTable table;
// Load shared library with appAsLib as true.
- ASSERT_EQ(NO_ERROR, table.add(appaslib_lib_arsc, appaslib_lib_arsc_len, NULL, 0, -1, false, true));
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size(), NULL, 0, -1,
+ false, true));
Res_value val;
- ssize_t block = table.getResource(appaslib::R::lib::integer::number1, &val);
+ ssize_t block = table.getResource(lib::R::integer::number1, &val);
ASSERT_GE(block, 0);
ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
- ASSERT_EQ(appaslib::R::lib::array::integerArray1, val.data);
+ ASSERT_EQ(lib::R::array::integerArray1, val.data);
}
-}
+} // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
new file mode 100644
index 000000000000..9ff947807a1e
--- /dev/null
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "benchmark/benchmark.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+#include "data/styles/R.h"
+
+namespace basic = com::android::basic;
+namespace app = com::android::app;
+
+namespace android {
+
+constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk";
+
+static void BM_AssetManagerLoadAssets(benchmark::State& state) {
+ std::string path = GetTestDataPath() + "/basic/basic.apk";
+ while (state.KeepRunning()) {
+ std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+ AssetManager2 assets;
+ assets.SetApkAssets({apk.get()});
+ }
+}
+BENCHMARK(BM_AssetManagerLoadAssets);
+
+static void BM_AssetManagerLoadAssetsOld(benchmark::State& state) {
+ String8 path((GetTestDataPath() + "/basic/basic.apk").data());
+ while (state.KeepRunning()) {
+ AssetManager assets;
+ assets.addAssetPath(path, nullptr /* cookie */, false /* appAsLib */,
+ false /* isSystemAsset */);
+
+ // Force creation.
+ assets.getResources(true);
+ }
+}
+BENCHMARK(BM_AssetManagerLoadAssetsOld);
+
+static void BM_AssetManagerLoadFrameworkAssets(benchmark::State& state) {
+ std::string path = kFrameworkPath;
+ while (state.KeepRunning()) {
+ std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+ AssetManager2 assets;
+ assets.SetApkAssets({apk.get()});
+ }
+}
+BENCHMARK(BM_AssetManagerLoadFrameworkAssets);
+
+static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) {
+ String8 path(kFrameworkPath);
+ while (state.KeepRunning()) {
+ AssetManager assets;
+ assets.addAssetPath(path, nullptr /* cookie */, false /* appAsLib */,
+ false /* isSystemAsset */);
+
+ // Force creation.
+ assets.getResources(true);
+ }
+}
+BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld);
+
+static void BM_AssetManagerGetResource(benchmark::State& state) {
+ std::unique_ptr<ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+ if (apk == nullptr) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ AssetManager2 assets;
+ assets.SetApkAssets({apk.get()});
+
+ Res_value value;
+ ResTable_config selected_config;
+ uint32_t flags;
+
+ while (state.KeepRunning()) {
+ assets.GetResource(basic::R::integer::number1, false /* may_be_bag */,
+ 0u /* density_override */, &value, &selected_config, &flags);
+ }
+}
+BENCHMARK(BM_AssetManagerGetResource);
+
+static void BM_AssetManagerGetResourceOld(benchmark::State& state) {
+ AssetManager assets;
+ if (!assets.addAssetPath(String8((GetTestDataPath() + "/basic/basic.apk").data()),
+ nullptr /* cookie */, false /* appAsLib */,
+ false /* isSystemAssets */)) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ const ResTable& table = assets.getResources(true);
+
+ Res_value value;
+ ResTable_config selected_config;
+ uint32_t flags;
+
+ while (state.KeepRunning()) {
+ table.getResource(basic::R::integer::number1, &value, false /* may_be_bag */,
+ 0u /* density_override */, &flags, &selected_config);
+ }
+}
+BENCHMARK(BM_AssetManagerGetResourceOld);
+
+constexpr static const uint32_t kStringOkId = 0x0104000au;
+
+static void BM_AssetManagerGetResourceFrameworkLocale(benchmark::State& state) {
+ std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+ if (apk == nullptr) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ AssetManager2 assets;
+ assets.SetApkAssets({apk.get()});
+
+ ResTable_config config;
+ memset(&config, 0, sizeof(config));
+ memcpy(config.language, "fr", 2);
+ assets.SetConfiguration(config);
+
+ Res_value value;
+ ResTable_config selected_config;
+ uint32_t flags;
+
+ while (state.KeepRunning()) {
+ assets.GetResource(kStringOkId, false /* may_be_bag */, 0u /* density_override */, &value,
+ &selected_config, &flags);
+ }
+}
+BENCHMARK(BM_AssetManagerGetResourceFrameworkLocale);
+
+static void BM_AssetManagerGetResourceFrameworkLocaleOld(benchmark::State& state) {
+ AssetManager assets;
+ if (!assets.addAssetPath(String8((GetTestDataPath() + "/basic/basic.apk").data()),
+ nullptr /* cookie */, false /* appAsLib */,
+ false /* isSystemAssets */)) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ ResTable_config config;
+ memset(&config, 0, sizeof(config));
+ memcpy(config.language, "fr", 2);
+ assets.setConfiguration(config, nullptr);
+
+ const ResTable& table = assets.getResources(true);
+
+ Res_value value;
+ ResTable_config selected_config;
+ uint32_t flags;
+
+ while (state.KeepRunning()) {
+ table.getResource(kStringOkId, &value, false /* may_be_bag */, 0u /* density_override */,
+ &flags, &selected_config);
+ }
+}
+BENCHMARK(BM_AssetManagerGetResourceFrameworkLocaleOld);
+
+static void BM_AssetManagerGetBag(benchmark::State& state) {
+ std::unique_ptr<ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+ if (apk == nullptr) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ AssetManager2 assets;
+ assets.SetApkAssets({apk.get()});
+
+ while (state.KeepRunning()) {
+ const ResolvedBag* bag = assets.GetBag(app::R::style::StyleTwo);
+ const auto bag_end = end(bag);
+ for (auto iter = begin(bag); iter != bag_end; ++iter) {
+ uint32_t key = iter->key;
+ Res_value value = iter->value;
+ benchmark::DoNotOptimize(key);
+ benchmark::DoNotOptimize(value);
+ }
+ }
+}
+BENCHMARK(BM_AssetManagerGetBag);
+
+static void BM_AssetManagerGetBagOld(benchmark::State& state) {
+ AssetManager assets;
+ if (!assets.addAssetPath(String8((GetTestDataPath() + "/styles/styles.apk").data()),
+ nullptr /* cookie */, false /* appAsLib */,
+ false /* isSystemAssets */)) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ const ResTable& table = assets.getResources(true);
+
+ while (state.KeepRunning()) {
+ const ResTable::bag_entry* bag_begin;
+ const ssize_t N = table.lockBag(app::R::style::StyleTwo, &bag_begin);
+ const ResTable::bag_entry* const bag_end = bag_begin + N;
+ for (auto iter = bag_begin; iter != bag_end; ++iter) {
+ uint32_t key = iter->map.name.ident;
+ Res_value value = iter->map.value;
+ benchmark::DoNotOptimize(key);
+ benchmark::DoNotOptimize(value);
+ }
+ table.unlockBag(bag_begin);
+ }
+}
+BENCHMARK(BM_AssetManagerGetBagOld);
+
+} // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
new file mode 100644
index 000000000000..39c5381feb04
--- /dev/null
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/AssetManager2.h"
+#include "androidfw/AssetManager.h"
+
+#include "android-base/logging.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+#include "data/styles/R.h"
+
+namespace basic = com::android::basic;
+namespace app = com::android::app;
+
+namespace android {
+
+class AssetManager2Test : public ::testing::Test {
+ public:
+ void SetUp() override {
+ basic_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+ ASSERT_NE(nullptr, basic_assets_);
+
+ basic_de_fr_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic_de_fr.apk");
+ ASSERT_NE(nullptr, basic_de_fr_assets_);
+
+ style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+ ASSERT_NE(nullptr, style_assets_);
+ }
+
+ protected:
+ std::unique_ptr<ApkAssets> basic_assets_;
+ std::unique_ptr<ApkAssets> basic_de_fr_assets_;
+ std::unique_ptr<ApkAssets> style_assets_;
+};
+
+TEST_F(AssetManager2Test, FindsResourcesFromSingleApkAssets) {
+ ResTable_config desired_config;
+ memset(&desired_config, 0, sizeof(desired_config));
+ desired_config.language[0] = 'd';
+ desired_config.language[1] = 'e';
+
+ AssetManager2 assetmanager;
+ assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetApkAssets({basic_assets_.get()});
+
+ Res_value value;
+ ResTable_config selected_config;
+ uint32_t flags;
+
+ ApkAssetsCookie cookie =
+ assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+ 0 /*density_override*/, &value, &selected_config, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+
+ // Came from our ApkAssets.
+ EXPECT_EQ(0, cookie);
+
+ // It is the default config.
+ EXPECT_EQ(0, selected_config.language[0]);
+ EXPECT_EQ(0, selected_config.language[1]);
+
+ // It is a string.
+ EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+}
+
+TEST_F(AssetManager2Test, FindsResourcesFromMultipleApkAssets) {
+ ResTable_config desired_config;
+ memset(&desired_config, 0, sizeof(desired_config));
+ desired_config.language[0] = 'd';
+ desired_config.language[1] = 'e';
+
+ AssetManager2 assetmanager;
+ assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
+
+ Res_value value;
+ ResTable_config selected_config;
+ uint32_t flags;
+
+ ApkAssetsCookie cookie =
+ assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
+ 0 /*density_override*/, &value, &selected_config, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+
+ // Came from our de_fr ApkAssets.
+ EXPECT_EQ(1, cookie);
+
+ // The configuration is german.
+ EXPECT_EQ('d', selected_config.language[0]);
+ EXPECT_EQ('e', selected_config.language[1]);
+
+ // It is a string.
+ EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+}
+
+TEST_F(AssetManager2Test, FindsBagResourcesFromSingleApkAssets) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({basic_assets_.get()});
+
+ const ResolvedBag* bag = assetmanager.GetBag(basic::R::array::integerArray1);
+ ASSERT_NE(nullptr, bag);
+ ASSERT_EQ(3u, bag->entry_count);
+
+ EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[0].value.dataType);
+ EXPECT_EQ(1u, bag->entries[0].value.data);
+ EXPECT_EQ(0, bag->entries[0].cookie);
+
+ EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[1].value.dataType);
+ EXPECT_EQ(2u, bag->entries[1].value.data);
+ EXPECT_EQ(0, bag->entries[1].cookie);
+
+ EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[2].value.dataType);
+ EXPECT_EQ(3u, bag->entries[2].value.data);
+ EXPECT_EQ(0, bag->entries[2].cookie);
+}
+
+TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({style_assets_.get()});
+
+ const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleOne);
+ ASSERT_NE(nullptr, bag_one);
+ ASSERT_EQ(2u, bag_one->entry_count);
+
+ EXPECT_EQ(app::R::attr::attr_one, bag_one->entries[0].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[0].value.dataType);
+ EXPECT_EQ(1u, bag_one->entries[0].value.data);
+ EXPECT_EQ(0, bag_one->entries[0].cookie);
+
+ EXPECT_EQ(app::R::attr::attr_two, bag_one->entries[1].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[1].value.dataType);
+ EXPECT_EQ(2u, bag_one->entries[1].value.data);
+ EXPECT_EQ(0, bag_one->entries[1].cookie);
+
+ const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
+ ASSERT_NE(nullptr, bag_two);
+ ASSERT_EQ(5u, bag_two->entry_count);
+
+ // attr_one is inherited from StyleOne.
+ EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[0].value.dataType);
+ EXPECT_EQ(1u, bag_two->entries[0].value.data);
+ EXPECT_EQ(0, bag_two->entries[0].cookie);
+
+ // attr_two should be overridden from StyleOne by StyleTwo.
+ EXPECT_EQ(app::R::attr::attr_two, bag_two->entries[1].key);
+ EXPECT_EQ(Res_value::TYPE_STRING, bag_two->entries[1].value.dataType);
+ EXPECT_EQ(0, bag_two->entries[1].cookie);
+ EXPECT_EQ(std::string("string"), GetStringFromPool(assetmanager.GetStringPoolForCookie(0),
+ bag_two->entries[1].value.data));
+
+ // The rest are new attributes.
+
+ EXPECT_EQ(app::R::attr::attr_three, bag_two->entries[2].key);
+ EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, bag_two->entries[2].value.dataType);
+ EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[2].value.data);
+ EXPECT_EQ(0, bag_two->entries[2].cookie);
+
+ EXPECT_EQ(app::R::attr::attr_five, bag_two->entries[3].key);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, bag_two->entries[3].value.dataType);
+ EXPECT_EQ(app::R::string::string_one, bag_two->entries[3].value.data);
+ EXPECT_EQ(0, bag_two->entries[3].cookie);
+
+ EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[4].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType);
+ EXPECT_EQ(3u, bag_two->entries[4].value.data);
+ EXPECT_EQ(0, bag_two->entries[4].cookie);
+}
+
+TEST_F(AssetManager2Test, FindsBagResourcesFromMultipleApkAssets) {}
+
+TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {}
+
+TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {}
+
+} // namespace android
diff --git a/libs/androidfw/tests/Asset_test.cpp b/libs/androidfw/tests/Asset_test.cpp
index 45c8cef92918..5018218066cb 100644
--- a/libs/androidfw/tests/Asset_test.cpp
+++ b/libs/androidfw/tests/Asset_test.cpp
@@ -14,24 +14,26 @@
* limitations under the License.
*/
-#include <androidfw/Asset.h>
+#include "androidfw/Asset.h"
-#include <gtest/gtest.h>
+#include "gtest/gtest.h"
-using namespace android;
+namespace android {
TEST(AssetTest, FileAssetRegistersItself) {
- const int32_t count = Asset::getGlobalCount();
- Asset* asset = new _FileAsset();
- EXPECT_EQ(count + 1, Asset::getGlobalCount());
- delete asset;
- EXPECT_EQ(count, Asset::getGlobalCount());
+ const int32_t count = Asset::getGlobalCount();
+ Asset* asset = new _FileAsset();
+ EXPECT_EQ(count + 1, Asset::getGlobalCount());
+ delete asset;
+ EXPECT_EQ(count, Asset::getGlobalCount());
}
TEST(AssetTest, CompressedAssetRegistersItself) {
- const int32_t count = Asset::getGlobalCount();
- Asset* asset = new _CompressedAsset();
- EXPECT_EQ(count + 1, Asset::getGlobalCount());
- delete asset;
- EXPECT_EQ(count, Asset::getGlobalCount());
+ const int32_t count = Asset::getGlobalCount();
+ Asset* asset = new _CompressedAsset();
+ EXPECT_EQ(count + 1, Asset::getGlobalCount());
+ delete asset;
+ EXPECT_EQ(count, Asset::getGlobalCount());
}
+
+} // nameapce android
diff --git a/libs/androidfw/tests/AttributeFinder_test.cpp b/libs/androidfw/tests/AttributeFinder_test.cpp
index 5054624579ea..7264b812737c 100644
--- a/libs/androidfw/tests/AttributeFinder_test.cpp
+++ b/libs/androidfw/tests/AttributeFinder_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,115 +14,112 @@
* limitations under the License.
*/
-#include <androidfw/AttributeFinder.h>
+#include "androidfw/AttributeFinder.h"
-#include <gtest/gtest.h>
+#include "android-base/macros.h"
+#include "gtest/gtest.h"
-using android::BackTrackingAttributeFinder;
+namespace android {
-class MockAttributeFinder : public BackTrackingAttributeFinder<MockAttributeFinder, int> {
-public:
- MockAttributeFinder(const uint32_t* attrs, int len)
- : BackTrackingAttributeFinder(0, len) {
- mAttrs = new uint32_t[len];
- memcpy(mAttrs, attrs, sizeof(*attrs) * len);
- }
+class MockAttributeFinder
+ : public BackTrackingAttributeFinder<MockAttributeFinder, int> {
+ public:
+ MockAttributeFinder(const uint32_t* attrs, int len)
+ : BackTrackingAttributeFinder(0, len) {
+ attrs_ = new uint32_t[len];
+ memcpy(attrs_, attrs, sizeof(*attrs) * len);
+ }
- ~MockAttributeFinder() {
- delete mAttrs;
- }
+ ~MockAttributeFinder() { delete attrs_; }
- inline uint32_t getAttribute(const int index) const {
- return mAttrs[index];
- }
+ inline uint32_t GetAttribute(const int index) const { return attrs_[index]; }
-private:
- uint32_t* mAttrs;
+ private:
+ uint32_t* attrs_;
};
-static const uint32_t sortedAttributes[] = {
- 0x01010000, 0x01010001, 0x01010002, 0x01010004,
- 0x02010001, 0x02010010, 0x7f010001
-};
+static const uint32_t kSortedAttributes[] = {0x01010000, 0x01010001, 0x01010002,
+ 0x01010004, 0x02010001, 0x02010010,
+ 0x7f010001};
-static const uint32_t packageUnsortedAttributes[] = {
- 0x02010001, 0x02010010, 0x01010000, 0x01010001,
- 0x01010002, 0x01010004, 0x7f010001
-};
+static const uint32_t kPackageUnsortedAttributes[] = {
+ 0x02010001, 0x02010010, 0x01010000, 0x01010001,
+ 0x01010002, 0x01010004, 0x7f010001};
-static const uint32_t singlePackageAttributes[] = {
- 0x7f010007, 0x7f01000a, 0x7f01000d, 0x00000000
-};
+static const uint32_t kSinglePackageAttributes[] = {0x7f010007, 0x7f01000a,
+ 0x7f01000d, 0x00000000};
TEST(AttributeFinderTest, IteratesSequentially) {
- const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
- MockAttributeFinder finder(sortedAttributes, end);
-
- EXPECT_EQ(0, finder.find(0x01010000));
- EXPECT_EQ(1, finder.find(0x01010001));
- EXPECT_EQ(2, finder.find(0x01010002));
- EXPECT_EQ(3, finder.find(0x01010004));
- EXPECT_EQ(4, finder.find(0x02010001));
- EXPECT_EQ(5, finder.find(0x02010010));
- EXPECT_EQ(6, finder.find(0x7f010001));
- EXPECT_EQ(end, finder.find(0x7f010002));
+ const int end = arraysize(kSortedAttributes);
+ MockAttributeFinder finder(kSortedAttributes, end);
+
+ EXPECT_EQ(0, finder.Find(0x01010000));
+ EXPECT_EQ(1, finder.Find(0x01010001));
+ EXPECT_EQ(2, finder.Find(0x01010002));
+ EXPECT_EQ(3, finder.Find(0x01010004));
+ EXPECT_EQ(4, finder.Find(0x02010001));
+ EXPECT_EQ(5, finder.Find(0x02010010));
+ EXPECT_EQ(6, finder.Find(0x7f010001));
+ EXPECT_EQ(end, finder.Find(0x7f010002));
}
TEST(AttributeFinderTest, PackagesAreOutOfOrder) {
- const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
- MockAttributeFinder finder(sortedAttributes, end);
-
- EXPECT_EQ(6, finder.find(0x7f010001));
- EXPECT_EQ(end, finder.find(0x7f010002));
- EXPECT_EQ(4, finder.find(0x02010001));
- EXPECT_EQ(5, finder.find(0x02010010));
- EXPECT_EQ(0, finder.find(0x01010000));
- EXPECT_EQ(1, finder.find(0x01010001));
- EXPECT_EQ(2, finder.find(0x01010002));
- EXPECT_EQ(3, finder.find(0x01010004));
+ const int end = arraysize(kSortedAttributes);
+ MockAttributeFinder finder(kSortedAttributes, end);
+
+ EXPECT_EQ(6, finder.Find(0x7f010001));
+ EXPECT_EQ(end, finder.Find(0x7f010002));
+ EXPECT_EQ(4, finder.Find(0x02010001));
+ EXPECT_EQ(5, finder.Find(0x02010010));
+ EXPECT_EQ(0, finder.Find(0x01010000));
+ EXPECT_EQ(1, finder.Find(0x01010001));
+ EXPECT_EQ(2, finder.Find(0x01010002));
+ EXPECT_EQ(3, finder.Find(0x01010004));
}
TEST(AttributeFinderTest, SomeAttributesAreNotFound) {
- const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
- MockAttributeFinder finder(sortedAttributes, end);
-
- EXPECT_EQ(0, finder.find(0x01010000));
- EXPECT_EQ(1, finder.find(0x01010001));
- EXPECT_EQ(2, finder.find(0x01010002));
- EXPECT_EQ(end, finder.find(0x01010003));
- EXPECT_EQ(3, finder.find(0x01010004));
- EXPECT_EQ(end, finder.find(0x01010005));
- EXPECT_EQ(end, finder.find(0x01010006));
- EXPECT_EQ(4, finder.find(0x02010001));
- EXPECT_EQ(end, finder.find(0x02010002));
+ const int end = arraysize(kSortedAttributes);
+ MockAttributeFinder finder(kSortedAttributes, end);
+
+ EXPECT_EQ(0, finder.Find(0x01010000));
+ EXPECT_EQ(1, finder.Find(0x01010001));
+ EXPECT_EQ(2, finder.Find(0x01010002));
+ EXPECT_EQ(end, finder.Find(0x01010003));
+ EXPECT_EQ(3, finder.Find(0x01010004));
+ EXPECT_EQ(end, finder.Find(0x01010005));
+ EXPECT_EQ(end, finder.Find(0x01010006));
+ EXPECT_EQ(4, finder.Find(0x02010001));
+ EXPECT_EQ(end, finder.Find(0x02010002));
}
TEST(AttributeFinderTest, FindAttributesInPackageUnsortedAttributeList) {
- const int end = sizeof(packageUnsortedAttributes) / sizeof(*packageUnsortedAttributes);
- MockAttributeFinder finder(packageUnsortedAttributes, end);
-
- EXPECT_EQ(2, finder.find(0x01010000));
- EXPECT_EQ(3, finder.find(0x01010001));
- EXPECT_EQ(4, finder.find(0x01010002));
- EXPECT_EQ(end, finder.find(0x01010003));
- EXPECT_EQ(5, finder.find(0x01010004));
- EXPECT_EQ(end, finder.find(0x01010005));
- EXPECT_EQ(end, finder.find(0x01010006));
- EXPECT_EQ(0, finder.find(0x02010001));
- EXPECT_EQ(end, finder.find(0x02010002));
- EXPECT_EQ(1, finder.find(0x02010010));
- EXPECT_EQ(6, finder.find(0x7f010001));
+ const int end = arraysize(kPackageUnsortedAttributes);
+ MockAttributeFinder finder(kPackageUnsortedAttributes, end);
+
+ EXPECT_EQ(2, finder.Find(0x01010000));
+ EXPECT_EQ(3, finder.Find(0x01010001));
+ EXPECT_EQ(4, finder.Find(0x01010002));
+ EXPECT_EQ(end, finder.Find(0x01010003));
+ EXPECT_EQ(5, finder.Find(0x01010004));
+ EXPECT_EQ(end, finder.Find(0x01010005));
+ EXPECT_EQ(end, finder.Find(0x01010006));
+ EXPECT_EQ(0, finder.Find(0x02010001));
+ EXPECT_EQ(end, finder.Find(0x02010002));
+ EXPECT_EQ(1, finder.Find(0x02010010));
+ EXPECT_EQ(6, finder.Find(0x7f010001));
}
TEST(AttributeFinderTest, FindAttributesInSinglePackageAttributeList) {
- const int end = sizeof(singlePackageAttributes) / sizeof(*singlePackageAttributes);
- MockAttributeFinder finder(singlePackageAttributes, end);
-
- EXPECT_EQ(end, finder.find(0x010100f4));
- EXPECT_EQ(end, finder.find(0x010100f5));
- EXPECT_EQ(end, finder.find(0x010100f6));
- EXPECT_EQ(end, finder.find(0x010100f7));
- EXPECT_EQ(end, finder.find(0x010100f8));
- EXPECT_EQ(end, finder.find(0x010100fa));
- EXPECT_EQ(0, finder.find(0x7f010007));
+ const int end = arraysize(kSinglePackageAttributes);
+ MockAttributeFinder finder(kSinglePackageAttributes, end);
+
+ EXPECT_EQ(end, finder.Find(0x010100f4));
+ EXPECT_EQ(end, finder.Find(0x010100f5));
+ EXPECT_EQ(end, finder.Find(0x010100f6));
+ EXPECT_EQ(end, finder.Find(0x010100f7));
+ EXPECT_EQ(end, finder.Find(0x010100f8));
+ EXPECT_EQ(end, finder.Find(0x010100fa));
+ EXPECT_EQ(0, finder.Find(0x7f010007));
}
+
+} // namespace android
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
new file mode 100644
index 000000000000..1ff2ed40cfc4
--- /dev/null
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/AttributeResolution.h"
+
+#include <array>
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+
+#include "TestHelpers.h"
+#include "data/styles/R.h"
+
+using com::android::app::R;
+
+namespace android {
+
+class AttributeResolutionTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(
+ GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents));
+ ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(),
+ 1 /*cookie*/, true /*copyData*/));
+ }
+
+ protected:
+ ResTable table_;
+};
+
+class AttributeResolutionXmlTest : public AttributeResolutionTest {
+ public:
+ virtual void SetUp() override {
+ AttributeResolutionTest::SetUp();
+
+ std::string contents;
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk",
+ "res/layout/layout.xml", &contents));
+
+ ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(),
+ true /*copyData*/));
+
+ // Skip to the first tag.
+ while (xml_parser_.next() != ResXMLParser::START_TAG) {
+ }
+ }
+
+ protected:
+ ResXMLTree xml_parser_;
+};
+
+TEST_F(AttributeResolutionTest, Theme) {
+ ResTable::Theme theme(table_);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
+
+ std::array<uint32_t, 4> attrs{
+ {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four}};
+ std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+
+ ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/,
+ nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(),
+ attrs.size(), values.data(), nullptr /*out_indices*/));
+
+ const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
+
+ const uint32_t* values_cursor = values.data();
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(1u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(3u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(Res_value::DATA_NULL_UNDEFINED, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+}
+
+TEST_F(AttributeResolutionXmlTest, XmlParser) {
+ std::array<uint32_t, 4> attrs{
+ {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four}};
+ std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+
+ ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(),
+ nullptr /*out_indices*/));
+
+ uint32_t* values_cursor = values.data();
+ EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(10u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(R::attr::attr_indirect, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+}
+
+TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
+ ResTable::Theme theme(table_);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
+
+ std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+ R::attr::attr_four, R::attr::attr_five}};
+ std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
+ std::array<uint32_t, attrs.size()> indices;
+
+ ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(),
+ attrs.size(), values.data(), indices.data());
+
+ const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
+
+ uint32_t* values_cursor = values.data();
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(1u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(10u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(3u, values_cursor[STYLE_DATA]);
+ EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+ values_cursor += STYLE_NUM_ENTRIES;
+ EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]);
+ EXPECT_EQ(R::string::string_one, values_cursor[STYLE_RESOURCE_ID]);
+ EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+ EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+ EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+}
+
+} // namespace android
+
diff --git a/libs/androidfw/tests/BenchMain.cpp b/libs/androidfw/tests/BenchMain.cpp
new file mode 100644
index 000000000000..105c5f9551b7
--- /dev/null
+++ b/libs/androidfw/tests/BenchMain.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include "benchmark/benchmark.h"
+
+#include "TestHelpers.h"
+
+int main(int argc, char** argv) {
+ ::benchmark::Initialize(&argc, argv);
+ ::android::InitializeTest(&argc, argv);
+
+ std::cerr << "using --testdata=" << ::android::GetTestDataPath() << "\n";
+
+ size_t result = ::benchmark::RunSpecifiedBenchmarks();
+ return result == 0 ? 1 : 0;
+}
diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp
index 376e79c6e7cb..5d464c7dc0f7 100644
--- a/libs/androidfw/tests/ByteBucketArray_test.cpp
+++ b/libs/androidfw/tests/ByteBucketArray_test.cpp
@@ -14,28 +14,42 @@
* limitations under the License.
*/
-#include <androidfw/ByteBucketArray.h>
+#include "androidfw/ByteBucketArray.h"
-#include <gtest/gtest.h>
+#include "gtest/gtest.h"
-using android::ByteBucketArray;
+namespace android {
TEST(ByteBucketArrayTest, TestSparseInsertion) {
- ByteBucketArray<int> bba;
- ASSERT_TRUE(bba.set(0, 1));
- ASSERT_TRUE(bba.set(10, 2));
- ASSERT_TRUE(bba.set(26, 3));
- ASSERT_TRUE(bba.set(129, 4));
- ASSERT_TRUE(bba.set(234, 5));
+ ByteBucketArray<int> bba;
+ ASSERT_TRUE(bba.set(0, 1));
+ ASSERT_TRUE(bba.set(10, 2));
+ ASSERT_TRUE(bba.set(26, 3));
+ ASSERT_TRUE(bba.set(129, 4));
+ ASSERT_TRUE(bba.set(234, 5));
- for (size_t i = 0; i < bba.size(); i++) {
- switch (i) {
- case 0: EXPECT_EQ(1, bba[i]); break;
- case 10: EXPECT_EQ(2, bba[i]); break;
- case 26: EXPECT_EQ(3, bba[i]); break;
- case 129: EXPECT_EQ(4, bba[i]); break;
- case 234: EXPECT_EQ(5, bba[i]); break;
- default: EXPECT_EQ(0, bba[i]); break;
- }
+ for (size_t i = 0; i < bba.size(); i++) {
+ switch (i) {
+ case 0:
+ EXPECT_EQ(1, bba[i]);
+ break;
+ case 10:
+ EXPECT_EQ(2, bba[i]);
+ break;
+ case 26:
+ EXPECT_EQ(3, bba[i]);
+ break;
+ case 129:
+ EXPECT_EQ(4, bba[i]);
+ break;
+ case 234:
+ EXPECT_EQ(5, bba[i]);
+ break;
+ default:
+ EXPECT_EQ(0, bba[i]);
+ break;
}
+ }
}
+
+} // namespace android
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 10f4d46058ec..86a627e1485d 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -279,6 +279,23 @@ TEST(ConfigLocaleTest, getBcp47Locale_script) {
EXPECT_EQ(0, strcmp("en", out));
}
+TEST(ConfigLocaleTest, getBcp47Locale_canonicalize) {
+ ResTable_config config;
+ char out[RESTABLE_MAX_LOCALE_LEN];
+
+ fillIn("tl", NULL, NULL, NULL, &config);
+ config.getBcp47Locale(out);
+ EXPECT_EQ(0, strcmp("tl", out));
+ config.getBcp47Locale(out, true /* canonicalize */);
+ EXPECT_EQ(0, strcmp("fil", out));
+
+ fillIn("tl", "PH", NULL, NULL, &config);
+ config.getBcp47Locale(out);
+ EXPECT_EQ(0, strcmp("tl-PH", out));
+ config.getBcp47Locale(out, true /* canonicalize */);
+ EXPECT_EQ(0, strcmp("fil-PH", out));
+}
+
TEST(ConfigLocaleTest, match) {
ResTable_config supported, requested;
@@ -292,6 +309,11 @@ TEST(ConfigLocaleTest, match) {
// Different languages don't match.
EXPECT_FALSE(supported.match(requested));
+ fillIn("tl", "PH", NULL, NULL, &supported);
+ fillIn("fil", "PH", NULL, NULL, &requested);
+ // Equivalent languages match.
+ EXPECT_TRUE(supported.match(requested));
+
fillIn("qaa", "FR", NULL, NULL, &supported);
fillIn("qaa", "CA", NULL, NULL, &requested);
// If we can't infer the scripts, different regions don't match.
@@ -406,6 +428,12 @@ TEST(ConfigLocaleTest, isLocaleBetterThan_basics) {
EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
fillIn("de", "DE", NULL, NULL, &request);
+ fillIn("de", "DE", NULL, NULL, &config1);
+ fillIn("de", "DE", NULL, "1901", &config2);
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("de", "DE", NULL, NULL, &request);
fillIn("de", "DE", NULL, "1901", &config1);
fillIn("de", "DE", NULL, "1996", &config2);
EXPECT_FALSE(config1.isLocaleBetterThan(config2, &request));
@@ -422,6 +450,24 @@ TEST(ConfigLocaleTest, isLocaleBetterThan_basics) {
fillIn("de", "DE", NULL, NULL, &config2);
EXPECT_FALSE(config1.isLocaleBetterThan(config2, &request));
EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("fil", "PH", NULL, NULL, &request);
+ fillIn("tl", "PH", NULL, NULL, &config1);
+ fillIn("fil", "US", NULL, NULL, &config2);
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("fil", "PH", NULL, "fonipa", &request);
+ fillIn("tl", "PH", NULL, "fonipa", &config1);
+ fillIn("fil", "PH", NULL, NULL, &config2);
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("fil", "PH", NULL, NULL, &request);
+ fillIn("fil", "PH", NULL, NULL, &config1);
+ fillIn("tl", "PH", NULL, NULL, &config2);
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
}
TEST(ConfigLocaleTest, isLocaleBetterThan_regionComparison) {
diff --git a/libs/androidfw/tests/Config_test.cpp b/libs/androidfw/tests/Config_test.cpp
index 738947a46200..778c7bfb2053 100644
--- a/libs/androidfw/tests/Config_test.cpp
+++ b/libs/androidfw/tests/Config_test.cpp
@@ -14,168 +14,172 @@
* limitations under the License.
*/
-#include <androidfw/ResourceTypes.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
+#include "androidfw/ResourceTypes.h"
+
+#include "utils/Log.h"
+#include "utils/String8.h"
+#include "utils/Vector.h"
#include "TestHelpers.h"
-#include <gtest/gtest.h>
+#include "gtest/gtest.h"
namespace android {
static ResTable_config selectBest(const ResTable_config& target,
- const Vector<ResTable_config>& configs) {
- ResTable_config bestConfig;
- memset(&bestConfig, 0, sizeof(bestConfig));
- const size_t configCount = configs.size();
- for (size_t i = 0; i < configCount; i++) {
- const ResTable_config& thisConfig = configs[i];
- if (!thisConfig.match(target)) {
- continue;
- }
-
- if (thisConfig.isBetterThan(bestConfig, &target)) {
- bestConfig = thisConfig;
- }
+ const Vector<ResTable_config>& configs) {
+ ResTable_config bestConfig;
+ memset(&bestConfig, 0, sizeof(bestConfig));
+ const size_t configCount = configs.size();
+ for (size_t i = 0; i < configCount; i++) {
+ const ResTable_config& thisConfig = configs[i];
+ if (!thisConfig.match(target)) {
+ continue;
+ }
+
+ if (thisConfig.isBetterThan(bestConfig, &target)) {
+ bestConfig = thisConfig;
}
- return bestConfig;
+ }
+ return bestConfig;
}
static ResTable_config buildDensityConfig(int density) {
- ResTable_config config;
- memset(&config, 0, sizeof(config));
- config.density = uint16_t(density);
- config.sdkVersion = 4;
- return config;
+ ResTable_config config;
+ memset(&config, 0, sizeof(config));
+ config.density = uint16_t(density);
+ config.sdkVersion = 4;
+ return config;
}
TEST(ConfigTest, shouldSelectBestDensity) {
- ResTable_config deviceConfig;
- memset(&deviceConfig, 0, sizeof(deviceConfig));
- deviceConfig.density = ResTable_config::DENSITY_XHIGH;
- deviceConfig.sdkVersion = 21;
+ ResTable_config deviceConfig;
+ memset(&deviceConfig, 0, sizeof(deviceConfig));
+ deviceConfig.density = ResTable_config::DENSITY_XHIGH;
+ deviceConfig.sdkVersion = 21;
- Vector<ResTable_config> configs;
+ Vector<ResTable_config> configs;
- ResTable_config expectedBest = buildDensityConfig(ResTable_config::DENSITY_HIGH);
- configs.add(expectedBest);
- ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+ ResTable_config expectedBest =
+ buildDensityConfig(ResTable_config::DENSITY_HIGH);
+ configs.add(expectedBest);
+ ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
- expectedBest = buildDensityConfig(ResTable_config::DENSITY_XXHIGH);
- configs.add(expectedBest);
- ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+ expectedBest = buildDensityConfig(ResTable_config::DENSITY_XXHIGH);
+ configs.add(expectedBest);
+ ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
- expectedBest = buildDensityConfig(int(ResTable_config::DENSITY_XXHIGH) - 20);
- configs.add(expectedBest);
- ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+ expectedBest = buildDensityConfig(int(ResTable_config::DENSITY_XXHIGH) - 20);
+ configs.add(expectedBest);
+ ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
- configs.add(buildDensityConfig(int(ResTable_config::DENSITY_HIGH) + 20));
- ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+ configs.add(buildDensityConfig(int(ResTable_config::DENSITY_HIGH) + 20));
+ ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
- expectedBest = buildDensityConfig(ResTable_config::DENSITY_XHIGH);
- configs.add(expectedBest);
- ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+ expectedBest = buildDensityConfig(ResTable_config::DENSITY_XHIGH);
+ configs.add(expectedBest);
+ ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
- expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY);
- expectedBest.sdkVersion = 21;
- configs.add(expectedBest);
- ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+ expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY);
+ expectedBest.sdkVersion = 21;
+ configs.add(expectedBest);
+ ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
}
TEST(ConfigTest, shouldSelectBestDensityWhenNoneSpecified) {
- ResTable_config deviceConfig;
- memset(&deviceConfig, 0, sizeof(deviceConfig));
- deviceConfig.sdkVersion = 21;
+ ResTable_config deviceConfig;
+ memset(&deviceConfig, 0, sizeof(deviceConfig));
+ deviceConfig.sdkVersion = 21;
- Vector<ResTable_config> configs;
- configs.add(buildDensityConfig(ResTable_config::DENSITY_HIGH));
+ Vector<ResTable_config> configs;
+ configs.add(buildDensityConfig(ResTable_config::DENSITY_HIGH));
- ResTable_config expectedBest = buildDensityConfig(ResTable_config::DENSITY_MEDIUM);
- configs.add(expectedBest);
- ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+ ResTable_config expectedBest =
+ buildDensityConfig(ResTable_config::DENSITY_MEDIUM);
+ configs.add(expectedBest);
+ ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
- expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY);
- configs.add(expectedBest);
- ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+ expectedBest = buildDensityConfig(ResTable_config::DENSITY_ANY);
+ configs.add(expectedBest);
+ ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
}
TEST(ConfigTest, shouldMatchRoundQualifier) {
- ResTable_config deviceConfig;
- memset(&deviceConfig, 0, sizeof(deviceConfig));
+ ResTable_config deviceConfig;
+ memset(&deviceConfig, 0, sizeof(deviceConfig));
- ResTable_config roundConfig;
- memset(&roundConfig, 0, sizeof(roundConfig));
- roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
+ ResTable_config roundConfig;
+ memset(&roundConfig, 0, sizeof(roundConfig));
+ roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
- EXPECT_FALSE(roundConfig.match(deviceConfig));
+ EXPECT_FALSE(roundConfig.match(deviceConfig));
- deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
+ deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
- EXPECT_TRUE(roundConfig.match(deviceConfig));
+ EXPECT_TRUE(roundConfig.match(deviceConfig));
- deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_NO;
+ deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_NO;
- EXPECT_FALSE(roundConfig.match(deviceConfig));
+ EXPECT_FALSE(roundConfig.match(deviceConfig));
- ResTable_config notRoundConfig;
- memset(&notRoundConfig, 0, sizeof(notRoundConfig));
- notRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_NO;
+ ResTable_config notRoundConfig;
+ memset(&notRoundConfig, 0, sizeof(notRoundConfig));
+ notRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_NO;
- EXPECT_TRUE(notRoundConfig.match(deviceConfig));
+ EXPECT_TRUE(notRoundConfig.match(deviceConfig));
}
TEST(ConfigTest, RoundQualifierShouldHaveStableSortOrder) {
- ResTable_config defaultConfig;
- memset(&defaultConfig, 0, sizeof(defaultConfig));
+ ResTable_config defaultConfig;
+ memset(&defaultConfig, 0, sizeof(defaultConfig));
- ResTable_config longConfig = defaultConfig;
- longConfig.screenLayout = ResTable_config::SCREENLONG_YES;
+ ResTable_config longConfig = defaultConfig;
+ longConfig.screenLayout = ResTable_config::SCREENLONG_YES;
- ResTable_config longRoundConfig = longConfig;
- longRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
+ ResTable_config longRoundConfig = longConfig;
+ longRoundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
- ResTable_config longRoundPortConfig = longConfig;
- longRoundPortConfig.orientation = ResTable_config::ORIENTATION_PORT;
+ ResTable_config longRoundPortConfig = longConfig;
+ longRoundPortConfig.orientation = ResTable_config::ORIENTATION_PORT;
- EXPECT_TRUE(longConfig.compare(longRoundConfig) < 0);
- EXPECT_TRUE(longConfig.compareLogical(longRoundConfig) < 0);
- EXPECT_TRUE(longRoundConfig.compare(longConfig) > 0);
- EXPECT_TRUE(longRoundConfig.compareLogical(longConfig) > 0);
+ EXPECT_TRUE(longConfig.compare(longRoundConfig) < 0);
+ EXPECT_TRUE(longConfig.compareLogical(longRoundConfig) < 0);
+ EXPECT_TRUE(longRoundConfig.compare(longConfig) > 0);
+ EXPECT_TRUE(longRoundConfig.compareLogical(longConfig) > 0);
- EXPECT_TRUE(longRoundConfig.compare(longRoundPortConfig) < 0);
- EXPECT_TRUE(longRoundConfig.compareLogical(longRoundPortConfig) < 0);
- EXPECT_TRUE(longRoundPortConfig.compare(longRoundConfig) > 0);
- EXPECT_TRUE(longRoundPortConfig.compareLogical(longRoundConfig) > 0);
+ EXPECT_TRUE(longRoundConfig.compare(longRoundPortConfig) < 0);
+ EXPECT_TRUE(longRoundConfig.compareLogical(longRoundPortConfig) < 0);
+ EXPECT_TRUE(longRoundPortConfig.compare(longRoundConfig) > 0);
+ EXPECT_TRUE(longRoundPortConfig.compareLogical(longRoundConfig) > 0);
}
TEST(ConfigTest, ScreenShapeHasCorrectDiff) {
- ResTable_config defaultConfig;
- memset(&defaultConfig, 0, sizeof(defaultConfig));
+ ResTable_config defaultConfig;
+ memset(&defaultConfig, 0, sizeof(defaultConfig));
- ResTable_config roundConfig = defaultConfig;
- roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
+ ResTable_config roundConfig = defaultConfig;
+ roundConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
- EXPECT_EQ(defaultConfig.diff(roundConfig), ResTable_config::CONFIG_SCREEN_ROUND);
+ EXPECT_EQ(defaultConfig.diff(roundConfig),
+ ResTable_config::CONFIG_SCREEN_ROUND);
}
TEST(ConfigTest, RoundIsMoreSpecific) {
- ResTable_config deviceConfig;
- memset(&deviceConfig, 0, sizeof(deviceConfig));
- deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
- deviceConfig.screenLayout = ResTable_config::SCREENLONG_YES;
+ ResTable_config deviceConfig;
+ memset(&deviceConfig, 0, sizeof(deviceConfig));
+ deviceConfig.screenLayout2 = ResTable_config::SCREENROUND_YES;
+ deviceConfig.screenLayout = ResTable_config::SCREENLONG_YES;
- ResTable_config targetConfigA;
- memset(&targetConfigA, 0, sizeof(targetConfigA));
+ ResTable_config targetConfigA;
+ memset(&targetConfigA, 0, sizeof(targetConfigA));
- ResTable_config targetConfigB = targetConfigA;
- targetConfigB.screenLayout = ResTable_config::SCREENLONG_YES;
+ ResTable_config targetConfigB = targetConfigA;
+ targetConfigB.screenLayout = ResTable_config::SCREENLONG_YES;
- ResTable_config targetConfigC = targetConfigB;
- targetConfigC.screenLayout2 = ResTable_config::SCREENROUND_YES;
+ ResTable_config targetConfigC = targetConfigB;
+ targetConfigC.screenLayout2 = ResTable_config::SCREENROUND_YES;
- EXPECT_TRUE(targetConfigB.isBetterThan(targetConfigA, &deviceConfig));
- EXPECT_TRUE(targetConfigC.isBetterThan(targetConfigB, &deviceConfig));
+ EXPECT_TRUE(targetConfigB.isBetterThan(targetConfigA, &deviceConfig));
+ EXPECT_TRUE(targetConfigC.isBetterThan(targetConfigB, &deviceConfig));
}
} // namespace android.
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index f50c17890b1e..0928b1b976c3 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -14,102 +14,102 @@
* limitations under the License.
*/
-#include <androidfw/ResourceTypes.h>
+#include "androidfw/ResourceTypes.h"
+
+#include "utils/String16.h"
+#include "utils/String8.h"
-#include <utils/String8.h>
-#include <utils/String16.h>
#include "TestHelpers.h"
#include "data/basic/R.h"
-#include <gtest/gtest.h>
-
-using namespace android;
-
-namespace {
-
-/**
- * Include a binary resource table.
- *
- * Package: com.android.test.basic
- */
-#include "data/basic/basic_arsc.h"
-
-/**
- * Include a binary resource table.
- * This table is an overlay.
- *
- * Package: com.android.test.basic
- */
-#include "data/overlay/overlay_arsc.h"
+using com::android::basic::R;
-enum { MAY_NOT_BE_BAG = false };
+namespace android {
class IdmapTest : public ::testing::Test {
-protected:
- virtual void SetUp() {
- ASSERT_EQ(NO_ERROR, mTargetTable.add(basic_arsc, basic_arsc_len));
- ASSERT_EQ(NO_ERROR, mOverlayTable.add(overlay_arsc, overlay_arsc_len));
- char targetName[256] = "com.android.test.basic";
- ASSERT_EQ(NO_ERROR, mTargetTable.createIdmap(mOverlayTable, 0, 0,
- targetName, targetName, &mData, &mDataSize));
- }
-
- virtual void TearDown() {
- free(mData);
- }
-
- ResTable mTargetTable;
- ResTable mOverlayTable;
- void* mData;
- size_t mDataSize;
+ protected:
+ void SetUp() override {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &contents));
+ ASSERT_EQ(NO_ERROR,
+ target_table_.add(contents.data(), contents.size(), 0, true));
+
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk",
+ "resources.arsc", &overlay_data_));
+ ResTable overlay_table;
+ ASSERT_EQ(NO_ERROR,
+ overlay_table.add(overlay_data_.data(), overlay_data_.size()));
+
+ char target_name[256] = "com.android.basic";
+ ASSERT_EQ(NO_ERROR,
+ target_table_.createIdmap(overlay_table, 0, 0, target_name,
+ target_name, &data_, &data_size_));
+ }
+
+ void TearDown() override { ::free(data_); }
+
+ ResTable target_table_;
+ std::string overlay_data_;
+ void* data_ = nullptr;
+ size_t data_size_ = 0;
};
TEST_F(IdmapTest, canLoadIdmap) {
- ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+ ASSERT_EQ(NO_ERROR,
+ target_table_.add(overlay_data_.data(), overlay_data_.size(), data_,
+ data_size_));
}
TEST_F(IdmapTest, overlayOverridesResourceValue) {
- Res_value val;
- ssize_t block = mTargetTable.getResource(base::R::string::test2, &val, false);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
- const ResStringPool* pool = mTargetTable.getTableStringBlock(block);
- ASSERT_TRUE(pool != NULL);
- ASSERT_LT(val.data, pool->size());
-
- size_t strLen;
- const char16_t* targetStr16 = pool->stringAt(val.data, &strLen);
- ASSERT_TRUE(targetStr16 != NULL);
- ASSERT_EQ(String16("test2"), String16(targetStr16, strLen));
-
- ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
-
- ssize_t newBlock = mTargetTable.getResource(base::R::string::test2, &val, false);
- ASSERT_GE(newBlock, 0);
- ASSERT_NE(block, newBlock);
- ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
- pool = mTargetTable.getTableStringBlock(newBlock);
- ASSERT_TRUE(pool != NULL);
- ASSERT_LT(val.data, pool->size());
-
- targetStr16 = pool->stringAt(val.data, &strLen);
- ASSERT_TRUE(targetStr16 != NULL);
- ASSERT_EQ(String16("test2-overlay"), String16(targetStr16, strLen));
+ Res_value val;
+ ssize_t block = target_table_.getResource(R::string::test2, &val, false);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+ const ResStringPool* pool = target_table_.getTableStringBlock(block);
+ ASSERT_TRUE(pool != NULL);
+ ASSERT_LT(val.data, pool->size());
+
+ size_t strLen;
+ const char16_t* targetStr16 = pool->stringAt(val.data, &strLen);
+ ASSERT_TRUE(targetStr16 != NULL);
+ ASSERT_EQ(String16("test2"), String16(targetStr16, strLen));
+
+ ASSERT_EQ(NO_ERROR,
+ target_table_.add(overlay_data_.data(), overlay_data_.size(), data_,
+ data_size_));
+
+ ssize_t newBlock = target_table_.getResource(R::string::test2, &val, false);
+ ASSERT_GE(newBlock, 0);
+ ASSERT_NE(block, newBlock);
+ ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+ pool = target_table_.getTableStringBlock(newBlock);
+ ASSERT_TRUE(pool != NULL);
+ ASSERT_LT(val.data, pool->size());
+
+ targetStr16 = pool->stringAt(val.data, &strLen);
+ ASSERT_TRUE(targetStr16 != NULL);
+ ASSERT_EQ(String16("test2-overlay"), String16(targetStr16, strLen));
}
TEST_F(IdmapTest, overlaidResourceHasSameName) {
- ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
-
- ResTable::resource_name resName;
- ASSERT_TRUE(mTargetTable.getResourceName(base::R::array::integerArray1, false, &resName));
-
- ASSERT_TRUE(resName.package != NULL);
- ASSERT_TRUE(resName.type != NULL);
- ASSERT_TRUE(resName.name != NULL);
-
- EXPECT_EQ(String16("com.android.test.basic"), String16(resName.package, resName.packageLen));
- EXPECT_EQ(String16("array"), String16(resName.type, resName.typeLen));
- EXPECT_EQ(String16("integerArray1"), String16(resName.name, resName.nameLen));
+ ASSERT_EQ(NO_ERROR,
+ target_table_.add(overlay_data_.data(), overlay_data_.size(), data_,
+ data_size_));
+
+ ResTable::resource_name resName;
+ ASSERT_TRUE(
+ target_table_.getResourceName(R::array::integerArray1, false, &resName));
+
+ ASSERT_TRUE(resName.package != NULL);
+ ASSERT_TRUE(resName.type != NULL);
+ ASSERT_TRUE(resName.name != NULL);
+
+ EXPECT_EQ(String16("com.android.basic"),
+ String16(resName.package, resName.packageLen));
+ EXPECT_EQ(String16("array"), String16(resName.type, resName.typeLen));
+ EXPECT_EQ(String16("integerArray1"), String16(resName.name, resName.nameLen));
}
-} // namespace
+} // namespace
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
new file mode 100644
index 000000000000..47b3894f0398
--- /dev/null
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/LoadedArsc.h"
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+
+#include "TestHelpers.h"
+#include "data/basic/R.h"
+#include "data/styles/R.h"
+
+namespace app = com::android::app;
+namespace basic = com::android::basic;
+
+namespace android {
+
+TEST(LoadedArscTest, LoadSinglePackageArsc) {
+ base::ScopedLogSeverity _log(base::LogSeverity::DEBUG);
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc",
+ &contents));
+
+ std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+ ASSERT_NE(nullptr, loaded_arsc);
+
+ ResTable_config config;
+ memset(&config, 0, sizeof(config));
+ config.sdkVersion = 24;
+
+ LoadedArsc::Entry entry;
+ ResTable_config selected_config;
+ uint32_t flags;
+
+ ASSERT_TRUE(
+ loaded_arsc->FindEntry(app::R::string::string_one, config, &entry, &selected_config, &flags));
+ ASSERT_NE(nullptr, entry.entry);
+}
+
+TEST(LoadedArscTest, FindDefaultEntry) {
+ base::ScopedLogSeverity _log(base::LogSeverity::DEBUG);
+ std::string contents;
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
+
+ std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+ ASSERT_NE(nullptr, loaded_arsc);
+
+ ResTable_config desired_config;
+ memset(&desired_config, 0, sizeof(desired_config));
+ desired_config.language[0] = 'd';
+ desired_config.language[1] = 'e';
+
+ LoadedArsc::Entry entry;
+ ResTable_config selected_config;
+ uint32_t flags;
+
+ ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry,
+ &selected_config, &flags));
+ ASSERT_NE(nullptr, entry.entry);
+}
+
+// structs with size fields (like Res_value, ResTable_entry) should be
+// backwards and forwards compatible (aka checking the size field against
+// sizeof(Res_value) might not be backwards compatible.
+TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+
+} // namespace android
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 9e53dd279195..b151f3f96496 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -14,352 +14,386 @@
* limitations under the License.
*/
-#include <androidfw/ResourceTypes.h>
+#include "androidfw/ResourceTypes.h"
#include <codecvt>
#include <locale>
#include <string>
-#include <utils/String8.h>
-#include <utils/String16.h>
+#include "utils/String16.h"
+#include "utils/String8.h"
+
#include "TestHelpers.h"
#include "data/basic/R.h"
#include "data/lib/R.h"
-#include <gtest/gtest.h>
+namespace basic = com::android::basic;
+namespace lib = com::android::lib;
-using namespace android;
+namespace android {
-namespace {
+TEST(ResTableTest, ShouldLoadSuccessfully) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &contents));
-/**
- * Include a binary resource table.
- *
- * Package: com.android.test.basic
- */
-#include "data/basic/basic_arsc.h"
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
+}
-/**
- * Include a binary library resource table.
- *
- * Package: com.android.test.basic
- */
-#include "data/lib/lib_arsc.h"
+TEST(ResTableTest, SimpleTypeIsRetrievedCorrectly) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &contents));
-/**
- * Include a system resource table.
- *
- * Package: android
- */
-#include "data/system/system_arsc.h"
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
-TEST(ResTableTest, shouldLoadSuccessfully) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+ EXPECT_TRUE(IsStringEqual(table, basic::R::string::test1, "test1"));
}
-TEST(ResTableTest, simpleTypeIsRetrievedCorrectly) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
-
- EXPECT_TRUE(IsStringEqual(table, base::R::string::test1, "test1"));
+TEST(ResTableTest, ResourceNameIsResolved) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &contents));
+
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
+
+ String16 defPackage("com.android.basic");
+ String16 testName("@string/test1");
+ uint32_t resID =
+ table.identifierForName(testName.string(), testName.size(), 0, 0,
+ defPackage.string(), defPackage.size());
+ ASSERT_NE(uint32_t(0x00000000), resID);
+ ASSERT_EQ(basic::R::string::test1, resID);
}
-TEST(ResTableTest, resourceNameIsResolved) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
-
- String16 defPackage("com.android.test.basic");
- String16 testName("@string/test1");
- uint32_t resID = table.identifierForName(testName.string(), testName.size(),
- 0, 0,
- defPackage.string(), defPackage.size());
- ASSERT_NE(uint32_t(0x00000000), resID);
- ASSERT_EQ(base::R::string::test1, resID);
-}
+TEST(ResTableTest, NoParentThemeIsAppliedCorrectly) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &contents));
-TEST(ResTableTest, noParentThemeIsAppliedCorrectly) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
- ResTable::Theme theme(table);
- ASSERT_EQ(NO_ERROR, theme.applyStyle(base::R::style::Theme1));
+ ResTable::Theme theme(table);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(basic::R::style::Theme1));
- Res_value val;
- uint32_t specFlags = 0;
- ssize_t index = theme.getAttribute(base::R::attr::attr1, &val, &specFlags);
- ASSERT_GE(index, 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- ASSERT_EQ(uint32_t(100), val.data);
+ Res_value val;
+ uint32_t specFlags = 0;
+ ssize_t index = theme.getAttribute(basic::R::attr::attr1, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(100), val.data);
- index = theme.getAttribute(base::R::attr::attr2, &val, &specFlags);
- ASSERT_GE(index, 0);
- ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
- ASSERT_EQ(base::R::integer::number1, val.data);
+ index = theme.getAttribute(basic::R::attr::attr2, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(basic::R::integer::number1, val.data);
}
-TEST(ResTableTest, parentThemeIsAppliedCorrectly) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+TEST(ResTableTest, ParentThemeIsAppliedCorrectly) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &contents));
- ResTable::Theme theme(table);
- ASSERT_EQ(NO_ERROR, theme.applyStyle(base::R::style::Theme2));
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
- Res_value val;
- uint32_t specFlags = 0;
- ssize_t index = theme.getAttribute(base::R::attr::attr1, &val, &specFlags);
- ASSERT_GE(index, 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- ASSERT_EQ(uint32_t(300), val.data);
+ ResTable::Theme theme(table);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(basic::R::style::Theme2));
- index = theme.getAttribute(base::R::attr::attr2, &val, &specFlags);
- ASSERT_GE(index, 0);
- ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
- ASSERT_EQ(base::R::integer::number1, val.data);
-}
+ Res_value val;
+ uint32_t specFlags = 0;
+ ssize_t index = theme.getAttribute(basic::R::attr::attr1, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(300), val.data);
-TEST(ResTableTest, libraryThemeIsAppliedCorrectly) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(lib_arsc, lib_arsc_len));
-
- ResTable::Theme theme(table);
- ASSERT_EQ(NO_ERROR, theme.applyStyle(lib::R::style::Theme));
-
- Res_value val;
- uint32_t specFlags = 0;
- ssize_t index = theme.getAttribute(lib::R::attr::attr1, &val, &specFlags);
- ASSERT_GE(index, 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- ASSERT_EQ(uint32_t(700), val.data);
-
- index = theme.getAttribute(lib::R::attr::attr2, &val, &specFlags);
- ASSERT_GE(index, 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- ASSERT_EQ(uint32_t(700), val.data);
+ index = theme.getAttribute(basic::R::attr::attr2, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(basic::R::integer::number1, val.data);
}
-TEST(ResTableTest, referenceToBagIsNotResolved) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
-
- Res_value val;
- ssize_t block = table.getResource(base::R::integer::number2, &val, MAY_NOT_BE_BAG);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
- ASSERT_EQ(base::R::array::integerArray1, val.data);
-
- ssize_t newBlock = table.resolveReference(&val, block);
- EXPECT_EQ(block, newBlock);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
- EXPECT_EQ(base::R::array::integerArray1, val.data);
-}
+TEST(ResTableTest, LibraryThemeIsAppliedCorrectly) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib/lib.apk",
+ "resources.arsc", &contents));
-TEST(ResTableTest, resourcesStillAccessibleAfterParameterChange) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
- Res_value val;
- ssize_t block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ResTable::Theme theme(table);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(lib::R::style::Theme));
- const ResTable::bag_entry* entry;
- ssize_t count = table.lockBag(base::R::array::integerArray1, &entry);
- ASSERT_GE(count, 0);
- table.unlockBag(entry);
+ Res_value val;
+ uint32_t specFlags = 0;
+ ssize_t index = theme.getAttribute(lib::R::attr::attr1, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(700), val.data);
- ResTable_config param;
- memset(&param, 0, sizeof(param));
- param.density = 320;
- table.setParameters(&param);
+ index = theme.getAttribute(lib::R::attr::attr2, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(700), val.data);
+}
- block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+TEST(ResTableTest, ReferenceToBagIsNotResolved) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &contents));
+
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
+
+ Res_value val;
+ ssize_t block =
+ table.getResource(basic::R::integer::number2, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(basic::R::array::integerArray1, val.data);
+
+ ssize_t newBlock = table.resolveReference(&val, block);
+ EXPECT_EQ(block, newBlock);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ EXPECT_EQ(basic::R::array::integerArray1, val.data);
+}
- count = table.lockBag(base::R::array::integerArray1, &entry);
- ASSERT_GE(count, 0);
- table.unlockBag(entry);
+TEST(ResTableTest, ResourcesStillAccessibleAfterParameterChange) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &contents));
+
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
+
+ Res_value val;
+ ssize_t block =
+ table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+
+ const ResTable::bag_entry* entry;
+ ssize_t count = table.lockBag(basic::R::array::integerArray1, &entry);
+ ASSERT_GE(count, 0);
+ table.unlockBag(entry);
+
+ ResTable_config param;
+ memset(&param, 0, sizeof(param));
+ param.density = 320;
+ table.setParameters(&param);
+
+ block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+
+ count = table.lockBag(basic::R::array::integerArray1, &entry);
+ ASSERT_GE(count, 0);
+ table.unlockBag(entry);
}
-TEST(ResTableTest, resourceIsOverridenWithBetterConfig) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
-
- Res_value val;
- ssize_t block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- ASSERT_EQ(uint32_t(200), val.data);
-
- ResTable_config param;
- memset(&param, 0, sizeof(param));
- param.language[0] = 's';
- param.language[1] = 'v';
- param.country[0] = 'S';
- param.country[1] = 'E';
- table.setParameters(&param);
-
- block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- ASSERT_EQ(uint32_t(400), val.data);
+TEST(ResTableTest, ResourceIsOverridenWithBetterConfig) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &contents));
+
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
+
+ Res_value val;
+ ssize_t block =
+ table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(200), val.data);
+
+ ResTable_config param;
+ memset(&param, 0, sizeof(param));
+ param.language[0] = 's';
+ param.language[1] = 'v';
+ param.country[0] = 'S';
+ param.country[1] = 'E';
+ table.setParameters(&param);
+
+ block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(400), val.data);
}
TEST(ResTableTest, emptyTableHasSensibleDefaults) {
- const int32_t assetCookie = 1;
+ const int32_t assetCookie = 1;
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.addEmpty(assetCookie));
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.addEmpty(assetCookie));
- // Adding an empty table gives us one table!
- ASSERT_EQ(uint32_t(1), table.getTableCount());
+ // Adding an empty table gives us one table!
+ ASSERT_EQ(uint32_t(1), table.getTableCount());
- // Adding an empty table doesn't mean we get packages.
- ASSERT_EQ(uint32_t(0), table.getBasePackageCount());
+ // Adding an empty table doesn't mean we get packages.
+ ASSERT_EQ(uint32_t(0), table.getBasePackageCount());
- Res_value val;
- ASSERT_LT(table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG), 0);
+ Res_value val;
+ ASSERT_LT(table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG),
+ 0);
}
void testU16StringToInt(const char16_t* str, uint32_t expectedValue,
bool expectSuccess, bool expectHex) {
- size_t len = std::char_traits<char16_t>::length(str);
+ size_t len = std::char_traits<char16_t>::length(str);
- // Gtest can't print UTF-16 strings, so we have to convert to UTF-8 :(
- std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
- std::string s = convert.to_bytes(std::u16string(str, len));
+ // Gtest can't print UTF-16 strings, so we have to convert to UTF-8 :(
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
+ std::string s = convert.to_bytes(std::u16string(str, len));
- Res_value out = {};
- ASSERT_EQ(expectSuccess, U16StringToInt(str, len, &out))
- << "Failed with " << s;
+ Res_value out = {};
+ ASSERT_EQ(expectSuccess, U16StringToInt(str, len, &out)) << "Failed with "
+ << s;
- if (!expectSuccess) {
- ASSERT_EQ(out.TYPE_NULL, out.dataType) << "Failed with " << s;
- return;
- }
+ if (!expectSuccess) {
+ ASSERT_EQ(out.TYPE_NULL, out.dataType) << "Failed with " << s;
+ return;
+ }
- if (expectHex) {
- ASSERT_EQ(out.TYPE_INT_HEX, out.dataType) << "Failed with " << s;
- } else {
- ASSERT_EQ(out.TYPE_INT_DEC, out.dataType) << "Failed with " << s;
- }
+ if (expectHex) {
+ ASSERT_EQ(out.TYPE_INT_HEX, out.dataType) << "Failed with " << s;
+ } else {
+ ASSERT_EQ(out.TYPE_INT_DEC, out.dataType) << "Failed with " << s;
+ }
- ASSERT_EQ(expectedValue, out.data) << "Failed with " << s;
+ ASSERT_EQ(expectedValue, out.data) << "Failed with " << s;
}
TEST(ResTableTest, U16StringToInt) {
- testU16StringToInt(u"", 0U, false, false);
- testU16StringToInt(u" ", 0U, false, false);
- testU16StringToInt(u"\t\n", 0U, false, false);
-
- testU16StringToInt(u"abcd", 0U, false, false);
- testU16StringToInt(u"10abcd", 0U, false, false);
- testU16StringToInt(u"42 42", 0U, false, false);
- testU16StringToInt(u"- 42", 0U, false, false);
- testU16StringToInt(u"-", 0U, false, false);
-
- testU16StringToInt(u"0x", 0U, false, true);
- testU16StringToInt(u"0xnope", 0U, false, true);
- testU16StringToInt(u"0X42", 0U, false, true);
- testU16StringToInt(u"0x42 0x42", 0U, false, true);
- testU16StringToInt(u"-0x0", 0U, false, true);
- testU16StringToInt(u"-0x42", 0U, false, true);
- testU16StringToInt(u"- 0x42", 0U, false, true);
-
- // Note that u" 42" would pass. This preserves the old behavior, but it may
- // not be desired.
- testU16StringToInt(u"42 ", 0U, false, false);
- testU16StringToInt(u"0x42 ", 0U, false, true);
-
- // Decimal cases.
- testU16StringToInt(u"0", 0U, true, false);
- testU16StringToInt(u"-0", 0U, true, false);
- testU16StringToInt(u"42", 42U, true, false);
- testU16StringToInt(u" 42", 42U, true, false);
- testU16StringToInt(u"-42", static_cast<uint32_t>(-42), true, false);
- testU16StringToInt(u" -42", static_cast<uint32_t>(-42), true, false);
- testU16StringToInt(u"042", 42U, true, false);
- testU16StringToInt(u"-042", static_cast<uint32_t>(-42), true, false);
-
- // Hex cases.
- testU16StringToInt(u"0x0", 0x0, true, true);
- testU16StringToInt(u"0x42", 0x42, true, true);
- testU16StringToInt(u" 0x42", 0x42, true, true);
-
- // Just before overflow cases:
- testU16StringToInt(u"2147483647", INT_MAX, true, false);
- testU16StringToInt(u"-2147483648", static_cast<uint32_t>(INT_MIN), true,
- false);
- testU16StringToInt(u"0xffffffff", UINT_MAX, true, true);
-
- // Overflow cases:
- testU16StringToInt(u"2147483648", 0U, false, false);
- testU16StringToInt(u"-2147483649", 0U, false, false);
- testU16StringToInt(u"0x1ffffffff", 0U, false, true);
+ testU16StringToInt(u"", 0U, false, false);
+ testU16StringToInt(u" ", 0U, false, false);
+ testU16StringToInt(u"\t\n", 0U, false, false);
+
+ testU16StringToInt(u"abcd", 0U, false, false);
+ testU16StringToInt(u"10abcd", 0U, false, false);
+ testU16StringToInt(u"42 42", 0U, false, false);
+ testU16StringToInt(u"- 42", 0U, false, false);
+ testU16StringToInt(u"-", 0U, false, false);
+
+ testU16StringToInt(u"0x", 0U, false, true);
+ testU16StringToInt(u"0xnope", 0U, false, true);
+ testU16StringToInt(u"0X42", 0U, false, true);
+ testU16StringToInt(u"0x42 0x42", 0U, false, true);
+ testU16StringToInt(u"-0x0", 0U, false, true);
+ testU16StringToInt(u"-0x42", 0U, false, true);
+ testU16StringToInt(u"- 0x42", 0U, false, true);
+
+ // Note that u" 42" would pass. This preserves the old behavior, but it may
+ // not be desired.
+ testU16StringToInt(u"42 ", 0U, false, false);
+ testU16StringToInt(u"0x42 ", 0U, false, true);
+
+ // Decimal cases.
+ testU16StringToInt(u"0", 0U, true, false);
+ testU16StringToInt(u"-0", 0U, true, false);
+ testU16StringToInt(u"42", 42U, true, false);
+ testU16StringToInt(u" 42", 42U, true, false);
+ testU16StringToInt(u"-42", static_cast<uint32_t>(-42), true, false);
+ testU16StringToInt(u" -42", static_cast<uint32_t>(-42), true, false);
+ testU16StringToInt(u"042", 42U, true, false);
+ testU16StringToInt(u"-042", static_cast<uint32_t>(-42), true, false);
+
+ // Hex cases.
+ testU16StringToInt(u"0x0", 0x0, true, true);
+ testU16StringToInt(u"0x42", 0x42, true, true);
+ testU16StringToInt(u" 0x42", 0x42, true, true);
+
+ // Just before overflow cases:
+ testU16StringToInt(u"2147483647", INT_MAX, true, false);
+ testU16StringToInt(u"-2147483648", static_cast<uint32_t>(INT_MIN), true,
+ false);
+ testU16StringToInt(u"0xffffffff", UINT_MAX, true, true);
+
+ // Overflow cases:
+ testU16StringToInt(u"2147483648", 0U, false, false);
+ testU16StringToInt(u"-2147483649", 0U, false, false);
+ testU16StringToInt(u"0x1ffffffff", 0U, false, true);
}
TEST(ResTableTest, ShareButDontModifyResTable) {
- ResTable sharedTable;
- ASSERT_EQ(NO_ERROR, sharedTable.add(basic_arsc, basic_arsc_len));
-
- ResTable_config param;
- memset(&param, 0, sizeof(param));
- param.language[0] = 'v';
- param.language[1] = 's';
- sharedTable.setParameters(&param);
-
- // Check that we get the default value for @integer:number1
- Res_value val;
- ssize_t block = sharedTable.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- ASSERT_EQ(uint32_t(600), val.data);
-
- // Create a new table that shares the entries of the shared table.
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(&sharedTable, false));
-
- // Set a new configuration on the new table.
- memset(&param, 0, sizeof(param));
- param.language[0] = 's';
- param.language[1] = 'v';
- param.country[0] = 'S';
- param.country[1] = 'E';
- table.setParameters(&param);
-
- // Check that we get a new value in the new table.
- block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- ASSERT_EQ(uint32_t(400), val.data);
-
- // Check that we still get the old value in the shared table.
- block = sharedTable.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- ASSERT_EQ(uint32_t(600), val.data);
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &contents));
+
+ ResTable sharedTable;
+ ASSERT_EQ(NO_ERROR, sharedTable.add(contents.data(), contents.size()));
+
+ ResTable_config param;
+ memset(&param, 0, sizeof(param));
+ param.language[0] = 'v';
+ param.language[1] = 's';
+ sharedTable.setParameters(&param);
+
+ // Check that we get the default value for @integer:number1
+ Res_value val;
+ ssize_t block =
+ sharedTable.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(600), val.data);
+
+ // Create a new table that shares the entries of the shared table.
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(&sharedTable, false));
+
+ // Set a new configuration on the new table.
+ memset(&param, 0, sizeof(param));
+ param.language[0] = 's';
+ param.language[1] = 'v';
+ param.country[0] = 'S';
+ param.country[1] = 'E';
+ table.setParameters(&param);
+
+ // Check that we get a new value in the new table.
+ block = table.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(400), val.data);
+
+ // Check that we still get the old value in the shared table.
+ block =
+ sharedTable.getResource(basic::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(600), val.data);
}
TEST(ResTableTest, GetConfigurationsReturnsUniqueList) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len));
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &contents));
+
+ std::string system_contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/system/system.apk",
+ "resources.arsc", &system_contents));
+
+ ResTable table;
+ ASSERT_EQ(NO_ERROR,
+ table.add(system_contents.data(), system_contents.size()));
+ ASSERT_EQ(NO_ERROR, table.add(contents.data(), contents.size()));
- ResTable_config configSv;
- memset(&configSv, 0, sizeof(configSv));
- configSv.language[0] = 's';
- configSv.language[1] = 'v';
+ ResTable_config configSv;
+ memset(&configSv, 0, sizeof(configSv));
+ configSv.language[0] = 's';
+ configSv.language[1] = 'v';
- Vector<ResTable_config> configs;
- table.getConfigurations(&configs);
+ Vector<ResTable_config> configs;
+ table.getConfigurations(&configs);
- EXPECT_EQ(1, std::count(configs.begin(), configs.end(), configSv));
+ EXPECT_EQ(1, std::count(configs.begin(), configs.end(), configSv));
- Vector<String8> locales;
- table.getLocales(&locales);
+ Vector<String8> locales;
+ table.getLocales(&locales);
- EXPECT_EQ(1, std::count(locales.begin(), locales.end(), String8("sv")));
+ EXPECT_EQ(1, std::count(locales.begin(), locales.end(), String8("sv")));
}
-} // namespace
+} // namespace android
diff --git a/libs/androidfw/tests/Split_test.cpp b/libs/androidfw/tests/Split_test.cpp
index b69d68572e6a..2c242dbd3e28 100644
--- a/libs/androidfw/tests/Split_test.cpp
+++ b/libs/androidfw/tests/Split_test.cpp
@@ -14,233 +14,255 @@
* limitations under the License.
*/
-#include <androidfw/ResourceTypes.h>
+#include "androidfw/ResourceTypes.h"
+
+#include "utils/String16.h"
+#include "utils/String8.h"
-#include <utils/String8.h>
-#include <utils/String16.h>
#include "TestHelpers.h"
#include "data/basic/R.h"
-#include <gtest/gtest.h>
-
-using namespace android;
-
-namespace {
-
-/**
- * Include a binary resource table. This table
- * is a base table for an APK split.
- *
- * Package: com.android.test.basic
- */
-#include "data/basic/basic_arsc.h"
-
-/**
- * Include a binary resource table. This table
- * is a configuration split table for an APK split.
- *
- * Package: com.android.test.basic
- */
-#include "data/basic/split_de_fr_arsc.h"
-#include "data/basic/split_hdpi_v4_arsc.h"
-#include "data/basic/split_xhdpi_v4_arsc.h"
-#include "data/basic/split_xxhdpi_v4_arsc.h"
-
-/**
- * Include a binary resource table. This table
- * is a feature split table for an APK split.
- *
- * Package: com.android.test.basic
- */
-#include "data/feature/feature_arsc.h"
+using com::android::basic::R;
-enum { MAY_NOT_BE_BAG = false };
+namespace android {
-void makeConfigFrench(ResTable_config* config) {
- memset(config, 0, sizeof(*config));
- config->language[0] = 'f';
- config->language[1] = 'r';
+static void makeConfigFrench(ResTable_config* config) {
+ memset(config, 0, sizeof(*config));
+ config->language[0] = 'f';
+ config->language[1] = 'r';
}
-TEST(SplitTest, TestLoadBase) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+class SplitTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk",
+ "resources.arsc", &basic_contents_));
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/basic/basic_de_fr.apk",
+ "resources.arsc", &basic_de_fr_contents_));
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/basic/basic_hdpi-v4.apk",
+ "resources.arsc", &basic_hdpi_contents_));
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/basic/basic_xhdpi-v4.apk",
+ "resources.arsc", &basic_xhdpi_contents_));
+ ASSERT_TRUE(ReadFileFromZipToString(
+ GetTestDataPath() + "/basic/basic_xxhdpi-v4.apk", "resources.arsc",
+ &basic_xxhdpi_contents_));
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk",
+ "resources.arsc", &feature_contents_));
+ }
+
+ protected:
+ std::string basic_contents_;
+ std::string basic_de_fr_contents_;
+ std::string basic_hdpi_contents_;
+ std::string basic_xhdpi_contents_;
+ std::string basic_xxhdpi_contents_;
+ std::string feature_contents_;
+};
+
+TEST_F(SplitTest, TestLoadBase) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR,
+ table.add(basic_contents_.data(), basic_contents_.size()));
}
-TEST(SplitTest, TestGetResourceFromBase) {
- ResTable_config frenchConfig;
- makeConfigFrench(&frenchConfig);
+TEST_F(SplitTest, TestGetResourceFromBase) {
+ ResTable_config frenchConfig;
+ makeConfigFrench(&frenchConfig);
- ResTable table;
- table.setParameters(&frenchConfig);
+ ResTable table;
+ table.setParameters(&frenchConfig);
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+ ASSERT_EQ(NO_ERROR,
+ table.add(basic_contents_.data(), basic_contents_.size()));
- ResTable_config expectedConfig;
- memset(&expectedConfig, 0, sizeof(expectedConfig));
+ ResTable_config expectedConfig;
+ memset(&expectedConfig, 0, sizeof(expectedConfig));
- Res_value val;
- ResTable_config config;
- ssize_t block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
+ Res_value val;
+ ResTable_config config;
+ ssize_t block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0,
+ NULL, &config);
- // The returned block should tell us which string pool to get the value, if it is a string.
- EXPECT_GE(block, 0);
+ // The returned block should tell us which string pool to get the value, if it
+ // is a string.
+ EXPECT_GE(block, 0);
- // We expect the default resource to be selected since it is the only resource configuration.
- EXPECT_EQ(0, expectedConfig.compare(config));
+ // We expect the default resource to be selected since it is the only resource
+ // configuration.
+ EXPECT_EQ(0, expectedConfig.compare(config));
- EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+ EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
}
-TEST(SplitTest, TestGetResourceFromSplit) {
- ResTable_config expectedConfig;
- makeConfigFrench(&expectedConfig);
+TEST_F(SplitTest, TestGetResourceFromSplit) {
+ ResTable_config expectedConfig;
+ makeConfigFrench(&expectedConfig);
- ResTable table;
- table.setParameters(&expectedConfig);
+ ResTable table;
+ table.setParameters(&expectedConfig);
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
- ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+ ASSERT_EQ(NO_ERROR,
+ table.add(basic_contents_.data(), basic_contents_.size()));
+ ASSERT_EQ(NO_ERROR, table.add(basic_de_fr_contents_.data(),
+ basic_de_fr_contents_.size()));
- Res_value val;
- ResTable_config config;
- ssize_t block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
+ Res_value val;
+ ResTable_config config;
+ ssize_t block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0,
+ NULL, &config);
- EXPECT_GE(block, 0);
+ EXPECT_GE(block, 0);
- EXPECT_EQ(0, expectedConfig.compare(config));
+ EXPECT_EQ(0, expectedConfig.compare(config));
- EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+ EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
}
-TEST(SplitTest, ResourcesFromBaseAndSplitHaveSameNames) {
- ResTable_config expectedConfig;
- makeConfigFrench(&expectedConfig);
+TEST_F(SplitTest, ResourcesFromBaseAndSplitHaveSameNames) {
+ ResTable_config expectedConfig;
+ makeConfigFrench(&expectedConfig);
- ResTable table;
- table.setParameters(&expectedConfig);
+ ResTable table;
+ table.setParameters(&expectedConfig);
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+ ASSERT_EQ(NO_ERROR,
+ table.add(basic_contents_.data(), basic_contents_.size()));
- ResTable::resource_name baseName;
- EXPECT_TRUE(table.getResourceName(base::R::string::test1, false, &baseName));
+ ResTable::resource_name baseName;
+ EXPECT_TRUE(table.getResourceName(R::string::test1, false, &baseName));
- ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(basic_de_fr_contents_.data(),
+ basic_de_fr_contents_.size()));
- ResTable::resource_name frName;
- EXPECT_TRUE(table.getResourceName(base::R::string::test1, false, &frName));
+ ResTable::resource_name frName;
+ EXPECT_TRUE(table.getResourceName(R::string::test1, false, &frName));
- EXPECT_EQ(
- String16(baseName.package, baseName.packageLen),
+ EXPECT_EQ(String16(baseName.package, baseName.packageLen),
String16(frName.package, frName.packageLen));
- EXPECT_EQ(
- String16(baseName.type, baseName.typeLen),
+ EXPECT_EQ(String16(baseName.type, baseName.typeLen),
String16(frName.type, frName.typeLen));
- EXPECT_EQ(
- String16(baseName.name, baseName.nameLen),
+ EXPECT_EQ(String16(baseName.name, baseName.nameLen),
String16(frName.name, frName.nameLen));
}
-TEST(SplitTest, TypeEntrySpecFlagsAreUpdated) {
- ResTable_config defaultConfig;
- memset(&defaultConfig, 0, sizeof(defaultConfig));
+TEST_F(SplitTest, TypeEntrySpecFlagsAreUpdated) {
+ ResTable_config defaultConfig;
+ memset(&defaultConfig, 0, sizeof(defaultConfig));
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+ ResTable table;
+ ASSERT_EQ(NO_ERROR,
+ table.add(basic_contents_.data(), basic_contents_.size()));
- Res_value val;
- uint32_t specFlags = 0;
- ssize_t block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, &specFlags, NULL);
- EXPECT_GE(block, 0);
+ Res_value val;
+ uint32_t specFlags = 0;
+ ssize_t block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0,
+ &specFlags, NULL);
+ EXPECT_GE(block, 0);
- EXPECT_EQ(static_cast<uint32_t>(0), specFlags);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), specFlags);
- ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(basic_de_fr_contents_.data(),
+ basic_de_fr_contents_.size()));
- uint32_t frSpecFlags = 0;
- block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG, 0, &frSpecFlags, NULL);
- EXPECT_GE(block, 0);
+ uint32_t frSpecFlags = 0;
+ block = table.getResource(R::string::test1, &val, MAY_NOT_BE_BAG, 0,
+ &frSpecFlags, NULL);
+ ASSERT_GE(block, 0);
- EXPECT_EQ(ResTable_config::CONFIG_LOCALE, frSpecFlags);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_config::CONFIG_LOCALE | ResTable_typeSpec::SPEC_PUBLIC),
+ frSpecFlags);
}
-TEST(SplitTest, SelectBestDensity) {
- ResTable_config baseConfig;
- memset(&baseConfig, 0, sizeof(baseConfig));
- baseConfig.density = ResTable_config::DENSITY_XHIGH;
- baseConfig.sdkVersion = 21;
+TEST_F(SplitTest, SelectBestDensity) {
+ ResTable_config baseConfig;
+ memset(&baseConfig, 0, sizeof(baseConfig));
+ baseConfig.density = ResTable_config::DENSITY_XHIGH;
+ baseConfig.sdkVersion = 21;
- ResTable table;
- table.setParameters(&baseConfig);
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
- ASSERT_EQ(NO_ERROR, table.add(split_hdpi_v4_arsc, split_hdpi_v4_arsc_len));
+ ResTable table;
+ table.setParameters(&baseConfig);
+ ASSERT_EQ(NO_ERROR,
+ table.add(basic_contents_.data(), basic_contents_.size()));
+ ASSERT_EQ(NO_ERROR, table.add(basic_hdpi_contents_.data(),
+ basic_hdpi_contents_.size()));
- EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "hdpi"));
+ EXPECT_TRUE(IsStringEqual(table, R::string::density, "hdpi"));
- ASSERT_EQ(NO_ERROR, table.add(split_xhdpi_v4_arsc, split_xhdpi_v4_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(basic_xhdpi_contents_.data(),
+ basic_xhdpi_contents_.size()));
- EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "xhdpi"));
+ EXPECT_TRUE(IsStringEqual(table, R::string::density, "xhdpi"));
- ASSERT_EQ(NO_ERROR, table.add(split_xxhdpi_v4_arsc, split_xxhdpi_v4_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(basic_xxhdpi_contents_.data(),
+ basic_xxhdpi_contents_.size()));
- EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "xhdpi"));
+ EXPECT_TRUE(IsStringEqual(table, R::string::density, "xhdpi"));
- baseConfig.density = ResTable_config::DENSITY_XXHIGH;
- table.setParameters(&baseConfig);
+ baseConfig.density = ResTable_config::DENSITY_XXHIGH;
+ table.setParameters(&baseConfig);
- EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "xxhdpi"));
+ EXPECT_TRUE(IsStringEqual(table, R::string::density, "xxhdpi"));
}
-TEST(SplitFeatureTest, TestNewResourceIsAccessible) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+TEST_F(SplitTest, TestNewResourceIsAccessible) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR,
+ table.add(basic_contents_.data(), basic_contents_.size()));
- Res_value val;
- ssize_t block = table.getResource(base::R::string::test3, &val, MAY_NOT_BE_BAG);
- EXPECT_LT(block, 0);
+ Res_value val;
+ ssize_t block = table.getResource(R::string::test3, &val, MAY_NOT_BE_BAG);
+ EXPECT_LT(block, 0);
- ASSERT_EQ(NO_ERROR, table.add(feature_arsc, feature_arsc_len));
+ ASSERT_EQ(NO_ERROR,
+ table.add(feature_contents_.data(), feature_contents_.size()));
- block = table.getResource(base::R::string::test3, &val, MAY_NOT_BE_BAG);
- EXPECT_GE(block, 0);
+ block = table.getResource(R::string::test3, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
- EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+ EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
}
-TEST(SplitFeatureTest, TestNewResourceNameHasCorrectName) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+TEST_F(SplitTest, TestNewResourceNameHasCorrectName) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR,
+ table.add(basic_contents_.data(), basic_contents_.size()));
- ResTable::resource_name name;
- EXPECT_FALSE(table.getResourceName(base::R::string::test3, false, &name));
+ ResTable::resource_name name;
+ EXPECT_FALSE(table.getResourceName(R::string::test3, false, &name));
- ASSERT_EQ(NO_ERROR, table.add(feature_arsc, feature_arsc_len));
+ ASSERT_EQ(NO_ERROR,
+ table.add(feature_contents_.data(), feature_contents_.size()));
- ASSERT_TRUE(table.getResourceName(base::R::string::test3, false, &name));
+ ASSERT_TRUE(table.getResourceName(R::string::test3, false, &name));
- EXPECT_EQ(String16("com.android.test.basic"),
+ EXPECT_EQ(String16("com.android.basic"),
String16(name.package, name.packageLen));
- EXPECT_EQ(String16("string"),
- String16(name.type, name.typeLen));
+ EXPECT_EQ(String16("string"), String16(name.type, name.typeLen));
- EXPECT_EQ(String16("test3"),
- String16(name.name, name.nameLen));
+ EXPECT_EQ(String16("test3"), String16(name.name, name.nameLen));
}
-TEST(SplitFeatureTest, TestNewResourceIsAccessibleByName) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
- ASSERT_EQ(NO_ERROR, table.add(feature_arsc, feature_arsc_len));
-
- const String16 name("test3");
- const String16 type("string");
- const String16 package("com.android.test.basic");
- ASSERT_EQ(base::R::string::test3, table.identifierForName(name.string(), name.size(),
- type.string(), type.size(),
- package.string(), package.size()));
+TEST_F(SplitTest, TestNewResourceIsAccessibleByName) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR,
+ table.add(basic_contents_.data(), basic_contents_.size()));
+ ASSERT_EQ(NO_ERROR,
+ table.add(feature_contents_.data(), feature_contents_.size()));
+
+ const String16 name("test3");
+ const String16 type("string");
+ const String16 package("com.android.basic");
+ ASSERT_EQ(
+ R::string::test3,
+ table.identifierForName(name.string(), name.size(), type.string(),
+ type.size(), package.string(), package.size()));
}
-} // namespace
+} // namespace
diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp
index 41a19a7f2b24..1e763a5e53a8 100644
--- a/libs/androidfw/tests/TestHelpers.cpp
+++ b/libs/androidfw/tests/TestHelpers.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,33 +16,119 @@
#include "TestHelpers.h"
-#include <androidfw/ResourceTypes.h>
-#include <utils/String8.h>
-#include <gtest/gtest.h>
+#include <libgen.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include "android-base/file.h"
+#include "android-base/logging.h"
+#include "android-base/strings.h"
+#include "ziparchive/zip_archive.h"
namespace android {
-::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resourceId, const char* expectedStr) {
- Res_value val;
- ssize_t block = table.getResource(resourceId, &val, MAY_NOT_BE_BAG);
- if (block < 0) {
- return ::testing::AssertionFailure() << "could not find resource";
- }
+static std::string sTestDataPath;
- if (val.dataType != Res_value::TYPE_STRING) {
- return ::testing::AssertionFailure() << "resource is not a string";
- }
+// Extract the directory of the current executable path.
+static std::string GetExecutableDir() {
+ const std::string path = base::GetExecutablePath();
+ std::unique_ptr<char, decltype(&std::free)> mutable_path = {strdup(path.c_str()), std::free};
+ std::string executable_dir = dirname(mutable_path.get());
+ return executable_dir;
+}
- const ResStringPool* pool = table.getTableStringBlock(block);
- if (pool == NULL) {
- return ::testing::AssertionFailure() << "table has no string pool for block " << block;
- }
+void InitializeTest(int* argc, char** argv) {
+ // Set the default test data path to be the executable path directory.
+ SetTestDataPath(GetExecutableDir());
- const String8 actual = pool->string8ObjectAt(val.data);
- if (String8(expectedStr) != actual) {
- return ::testing::AssertionFailure() << actual.string();
+ for (int i = 1; i < *argc; i++) {
+ const std::string arg = argv[i];
+ if (base::StartsWith(arg, "--testdata=")) {
+ SetTestDataPath(arg.substr(strlen("--testdata=")));
+ for (int j = i; j != *argc; j++) {
+ argv[j] = argv[j + 1];
+ }
+ --(*argc);
+ --i;
+ } else if (arg == "-h" || arg == "--help") {
+ std::cerr << "\nAdditional options specific to this test:\n"
+ " --testdata=[PATH]\n"
+ " Specify the location of test data used within the tests.\n";
+ exit(1);
}
- return ::testing::AssertionSuccess() << actual.string();
+ }
+}
+
+void SetTestDataPath(const std::string& path) { sTestDataPath = path; }
+
+const std::string& GetTestDataPath() {
+ CHECK(!sTestDataPath.empty()) << "no test data path set.";
+ return sTestDataPath;
+}
+
+::testing::AssertionResult ReadFileFromZipToString(const std::string& zip_path,
+ const std::string& file,
+ std::string* out_contents) {
+ out_contents->clear();
+ ::ZipArchiveHandle handle;
+ int32_t result = OpenArchive(zip_path.c_str(), &handle);
+ if (result != 0) {
+ return ::testing::AssertionFailure() << "Failed to open zip '" << zip_path
+ << "': " << ::ErrorCodeString(result);
+ }
+
+ ::ZipString name(file.c_str());
+ ::ZipEntry entry;
+ result = ::FindEntry(handle, name, &entry);
+ if (result != 0) {
+ ::CloseArchive(handle);
+ return ::testing::AssertionFailure() << "Could not find file '" << file << "' in zip '"
+ << zip_path << "' : " << ::ErrorCodeString(result);
+ }
+
+ out_contents->resize(entry.uncompressed_length);
+ result = ::ExtractToMemory(
+ handle, &entry, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(out_contents->data())),
+ out_contents->size());
+ if (result != 0) {
+ ::CloseArchive(handle);
+ return ::testing::AssertionFailure() << "Failed to extract file '" << file << "' from zip '"
+ << zip_path << "': " << ::ErrorCodeString(result);
+ }
+
+ ::CloseArchive(handle);
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
+ const char* expected_str) {
+ Res_value val;
+ ssize_t block = table.getResource(resource_id, &val, MAY_NOT_BE_BAG);
+ if (block < 0) {
+ return ::testing::AssertionFailure() << "could not find resource";
+ }
+
+ if (val.dataType != Res_value::TYPE_STRING) {
+ return ::testing::AssertionFailure() << "resource is not a string";
+ }
+
+ const ResStringPool* pool = table.getTableStringBlock(block);
+ if (pool == NULL) {
+ return ::testing::AssertionFailure() << "table has no string pool for block " << block;
+ }
+
+ const String8 actual_str = pool->string8ObjectAt(val.data);
+ if (String8(expected_str) != actual_str) {
+ return ::testing::AssertionFailure() << actual_str.string();
+ }
+ return ::testing::AssertionSuccess() << actual_str.string();
+}
+
+std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) {
+ String8 str = pool->string8ObjectAt(idx);
+ return std::string(str.string(), str.length());
}
-} // namespace android
+} // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index ff9be164dbb0..a11ea8416c7d 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -1,35 +1,65 @@
-#ifndef __TEST_HELPERS_H
-#define __TEST_HELPERS_H
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEST_HELPERS_H_
+#define TEST_HELPERS_H_
#include <ostream>
+#include <string>
-#include <androidfw/ResourceTypes.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-#include <gtest/gtest.h>
+#include "androidfw/ResourceTypes.h"
+#include "gtest/gtest.h"
+#include "utils/String16.h"
+#include "utils/String8.h"
static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) {
- return out << str.string();
+ return out << str.string();
}
static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) {
- return out << android::String8(str).string();
+ return out << android::String8(str).string();
}
namespace android {
+void InitializeTest(int* argc, char** argv);
+
enum { MAY_NOT_BE_BAG = false };
-static inline bool operator==(const android::ResTable_config& a, const android::ResTable_config& b) {
- return a.compare(b) == 0;
+void SetTestDataPath(const std::string& path);
+
+const std::string& GetTestDataPath();
+
+::testing::AssertionResult ReadFileFromZipToString(const std::string& zip_path,
+ const std::string& file,
+ std::string* out_contents);
+
+static inline bool operator==(const ResTable_config& a, const ResTable_config& b) {
+ return a.compare(b) == 0;
}
-static inline ::std::ostream& operator<<(::std::ostream& out, const android::ResTable_config& c) {
- return out << c.toString().string();
+static inline ::std::ostream& operator<<(::std::ostream& out, const ResTable_config& c) {
+ return out << c.toString().string();
}
-::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resourceId, const char* expectedStr);
+::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
+ const char* expected_str);
+
+std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx);
-} // namespace android
+} // namespace android
-#endif // __TEST_HELPERS_H
+#endif // TEST_HELPERS_H_
diff --git a/libs/androidfw/tests/TestMain.cpp b/libs/androidfw/tests/TestMain.cpp
new file mode 100644
index 000000000000..d1c0f6057dd1
--- /dev/null
+++ b/libs/androidfw/tests/TestMain.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include "TestHelpers.h"
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::android::InitializeTest(&argc, argv);
+
+ std::cerr << "using --testdata=" << ::android::GetTestDataPath() << "\n";
+
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp
new file mode 100644
index 000000000000..c471be6cd09d
--- /dev/null
+++ b/libs/androidfw/tests/Theme_bench.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "benchmark/benchmark.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
+
+namespace android {
+
+constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk";
+constexpr const static uint32_t kStyleId = 0x01030237u; // android:style/Theme.Material.Light
+constexpr const static uint32_t kAttrId = 0x01010030u; // android:attr/colorForeground
+
+static void BM_ThemeApplyStyleFramework(benchmark::State& state) {
+ std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+ if (apk == nullptr) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ AssetManager2 assets;
+ assets.SetApkAssets({apk.get()});
+
+ while (state.KeepRunning()) {
+ auto theme = assets.NewTheme();
+ theme->ApplyStyle(kStyleId, false /* force */);
+ }
+}
+BENCHMARK(BM_ThemeApplyStyleFramework);
+
+static void BM_ThemeApplyStyleFrameworkOld(benchmark::State& state) {
+ AssetManager assets;
+ if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /* cookie */, false /* appAsLib */,
+ true /* isSystemAsset */)) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ const ResTable& res_table = assets.getResources(true);
+
+ while (state.KeepRunning()) {
+ std::unique_ptr<ResTable::Theme> theme{new ResTable::Theme(res_table)};
+ theme->applyStyle(kStyleId, false /* force */);
+ }
+}
+BENCHMARK(BM_ThemeApplyStyleFrameworkOld);
+
+static void BM_ThemeGetAttribute(benchmark::State& state) {
+ std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+
+ AssetManager2 assets;
+ assets.SetApkAssets({apk.get()});
+
+ auto theme = assets.NewTheme();
+ theme->ApplyStyle(kStyleId, false /* force */);
+
+ Res_value value;
+ uint32_t flags;
+
+ while (state.KeepRunning()) {
+ theme->GetAttribute(kAttrId, &value, &flags);
+ }
+}
+BENCHMARK(BM_ThemeGetAttribute);
+
+static void BM_ThemeGetAttributeOld(benchmark::State& state) {
+ AssetManager assets;
+ assets.addAssetPath(String8(kFrameworkPath), nullptr /* cookie */, false /* appAsLib */,
+ true /* isSystemAsset */);
+ const ResTable& res_table = assets.getResources(true);
+ std::unique_ptr<ResTable::Theme> theme{new ResTable::Theme(res_table)};
+ theme->applyStyle(kStyleId, false /* force */);
+
+ Res_value value;
+ uint32_t flags;
+
+ while (state.KeepRunning()) {
+ theme->getAttribute(kAttrId, &value, &flags);
+ }
+}
+BENCHMARK(BM_ThemeGetAttributeOld);
+
+} // namespace android
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 4d07130acb39..c0011b6d6e89 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,55 +14,221 @@
* limitations under the License.
*/
-#include <androidfw/ResourceTypes.h>
+#include "androidfw/AssetManager2.h"
+
+#include "android-base/logging.h"
-#include <utils/String8.h>
-#include <utils/String16.h>
#include "TestHelpers.h"
-#include "data/system/R.h"
-#include "data/app/R.h"
+#include "data/styles/R.h"
-#include <gtest/gtest.h>
+namespace app = com::android::app;
-using namespace android;
+namespace android {
-namespace {
+class ThemeTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+ ASSERT_NE(nullptr, style_assets_);
+ }
-#include "data/system/system_arsc.h"
-#include "data/app/app_arsc.h"
+ protected:
+ std::unique_ptr<ApkAssets> style_assets_;
+};
-enum { MAY_NOT_BE_BAG = false };
+TEST_F(ThemeTest, EmptyTheme) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({style_assets_.get()});
-/**
- * TODO(adamlesinski): Enable when fixed.
- */
-TEST(ThemeTest, DISABLED_shouldCopyThemeFromDifferentResTable) {
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(system_arsc, system_arsc_len));
- ASSERT_EQ(NO_ERROR, table.add(app_arsc, app_arsc_len));
-
- ResTable::Theme theme1(table);
- ASSERT_EQ(NO_ERROR, theme1.applyStyle(app::R::style::Theme_One));
- Res_value val;
- ASSERT_GE(theme1.getAttribute(android::R::attr::background, &val), 0);
- ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
- ASSERT_EQ(uint32_t(0xffff0000), val.data);
- ASSERT_GE(theme1.getAttribute(app::R::attr::number, &val), 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- ASSERT_EQ(uint32_t(1), val.data);
-
- ResTable table2;
- ASSERT_EQ(NO_ERROR, table2.add(system_arsc, system_arsc_len));
- ASSERT_EQ(NO_ERROR, table2.add(app_arsc, app_arsc_len));
-
- ResTable::Theme theme2(table2);
- ASSERT_EQ(NO_ERROR, theme2.setTo(theme1));
- ASSERT_GE(theme2.getAttribute(android::R::attr::background, &val), 0);
- ASSERT_EQ(Res_value::TYPE_INT_COLOR_RGB8, val.dataType);
- ASSERT_EQ(uint32_t(0xffff0000), val.data);
- ASSERT_GE(theme2.getAttribute(app::R::attr::number, &val), 0);
- ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
- ASSERT_EQ(uint32_t(1), val.data);
+ std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+ EXPECT_EQ(0u, theme->GetChangingConfigurations());
+ EXPECT_EQ(&assetmanager, theme->GetAssetManager());
+
+ Res_value value;
+ uint32_t flags;
+ EXPECT_EQ(kInvalidCookie, theme->GetAttribute(app::R::attr::attr_one, &value, &flags));
+}
+
+TEST_F(ThemeTest, SingleThemeNoParent) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({style_assets_.get()});
+
+ std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne));
+
+ Res_value value;
+ uint32_t flags;
+ ApkAssetsCookie cookie;
+
+ cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+ EXPECT_EQ(1u, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+ cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+ EXPECT_EQ(2u, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, SingleThemeWithParent) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({style_assets_.get()});
+
+ std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
+
+ Res_value value;
+ uint32_t flags;
+ ApkAssetsCookie cookie;
+
+ cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+ EXPECT_EQ(1u, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+ cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+ EXPECT_EQ(0, cookie);
+ EXPECT_EQ(std::string("string"),
+ GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value.data));
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+ // This attribute should point to an attr_indirect, so the result should be 3.
+ cookie = theme->GetAttribute(app::R::attr::attr_three, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+ EXPECT_EQ(3u, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({style_assets_.get()});
+
+ std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree));
+
+ Res_value value;
+ uint32_t flags;
+ ApkAssetsCookie cookie;
+
+ // attr_one is still here from the base.
+ cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+ EXPECT_EQ(1u, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+ // check for the new attr_six
+ cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+ EXPECT_EQ(6u, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+ // check for the old attr_five (force=true was not used).
+ cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+ EXPECT_EQ(app::R::string::string_one, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
}
+TEST_F(ThemeTest, MultipleThemesOverlaidForced) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({style_assets_.get()});
+
+ std::unique_ptr<Theme> theme = assetmanager.NewTheme();
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */));
+
+ Res_value value;
+ uint32_t flags;
+ ApkAssetsCookie cookie;
+
+ // attr_one is still here from the base.
+ cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+ EXPECT_EQ(1u, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+ // check for the new attr_six
+ cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+ EXPECT_EQ(6u, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+ // check for the new attr_five (force=true was used).
+ cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+ EXPECT_EQ(5u, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
}
+
+TEST_F(ThemeTest, CopyThemeSameAssetManager) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({style_assets_.get()});
+
+ std::unique_ptr<Theme> theme_one = assetmanager.NewTheme();
+ ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
+
+ Res_value value;
+ uint32_t flags;
+ ApkAssetsCookie cookie;
+
+ // attr_one is still here from the base.
+ cookie = theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+ EXPECT_EQ(1u, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+
+ // attr_six is not here.
+ EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags));
+
+ std::unique_ptr<Theme> theme_two = assetmanager.NewTheme();
+ ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree));
+
+ // Copy the theme to theme_one.
+ ASSERT_TRUE(theme_one->SetTo(*theme_two));
+
+ // Clear theme_two to make sure we test that there WAS a copy.
+ theme_two->Clear();
+
+ // attr_one is now not here.
+ EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+
+ // attr_six is now here because it was copied.
+ cookie = theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
+ EXPECT_EQ(6u, value.data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+}
+
+TEST_F(ThemeTest, FailToCopyThemeWithDifferentAssetManager) {
+ AssetManager2 assetmanager_one;
+ assetmanager_one.SetApkAssets({style_assets_.get()});
+
+ AssetManager2 assetmanager_two;
+ assetmanager_two.SetApkAssets({style_assets_.get()});
+
+ auto theme_one = assetmanager_one.NewTheme();
+ ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
+
+ auto theme_two = assetmanager_two.NewTheme();
+ ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleTwo));
+
+ EXPECT_FALSE(theme_one->SetTo(*theme_two));
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/data/.gitignore b/libs/androidfw/tests/data/.gitignore
deleted file mode 100644
index c05cfb043024..000000000000
--- a/libs/androidfw/tests/data/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.apk
-*.arsc
diff --git a/libs/androidfw/tests/data/app/R.h b/libs/androidfw/tests/data/app/R.h
index 23e68e3e80dc..5be2eee11bfa 100644
--- a/libs/androidfw/tests/data/app/R.h
+++ b/libs/androidfw/tests/data/app/R.h
@@ -14,25 +14,31 @@
* limitations under the License.
*/
-#ifndef __APP_R_H
-#define __APP_R_H
+#ifndef TEST_DATA_APP_R_H_
+#define TEST_DATA_APP_R_H_
+#include <cstdint>
+
+namespace com {
+namespace android {
namespace app {
-namespace R {
-namespace attr {
- enum {
- number = 0x7f010000, // default
+struct R {
+ struct attr {
+ enum : uint32_t {
+ number = 0x7f010000, // default
};
-}
+ };
-namespace style {
- enum {
- Theme_One = 0x7f020000, // default
+ struct style {
+ enum : uint32_t {
+ Theme_One = 0x7f020000, // default
};
-}
+ };
+};
-} // namespace R
-} // namespace app
+} // namespace app
+} // namespace android
+} // namespace com
-#endif // __APP_R_H
+#endif // TEST_DATA_APP_R_H_
diff --git a/libs/androidfw/tests/data/app/app.apk b/libs/androidfw/tests/data/app/app.apk
new file mode 100644
index 000000000000..ccb08242a656
--- /dev/null
+++ b/libs/androidfw/tests/data/app/app.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/app/app_arsc.h b/libs/androidfw/tests/data/app/app_arsc.h
deleted file mode 100644
index d5d9a3b8be5b..000000000000
--- a/libs/androidfw/tests/data/app/app_arsc.h
+++ /dev/null
@@ -1,62 +0,0 @@
-unsigned char app_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x9c, 0x02, 0x00, 0x00,
- 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
- 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
- 0x64, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
- 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
- 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
- 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6e, 0x00,
- 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
- 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
- 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
- 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00
-};
-unsigned int app_arsc_len = 708;
diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build
index 62257bc26d4b..d418158c547b 100755
--- a/libs/androidfw/tests/data/app/build
+++ b/libs/androidfw/tests/data/app/build
@@ -15,7 +15,6 @@
# limitations under the License.
#
-aapt package -v -I ../system/bundle.apk -M AndroidManifest.xml -S res -F bundle.apk -f && \
-unzip bundle.apk resources.arsc && \
-mv resources.arsc app.arsc && \
-xxd -i app.arsc > app_arsc.h
+set -e
+
+aapt package -I ../system/system.apk -M AndroidManifest.xml -S res -F app.apk -f
diff --git a/libs/androidfw/tests/data/appaslib/R.h b/libs/androidfw/tests/data/appaslib/R.h
index 3af921a7ba65..5a2132733795 100644
--- a/libs/androidfw/tests/data/appaslib/R.h
+++ b/libs/androidfw/tests/data/appaslib/R.h
@@ -14,39 +14,53 @@
* limitations under the License.
*/
-#ifndef __APPASLIB_R_H
-#define __APPASLIB_R_H
+#ifndef DATA_APPASLIB_R_H_
+#define DATA_APPASLIB_R_H_
+#include <cstdint>
+
+namespace com {
+namespace android {
namespace appaslib {
-namespace R {
+
namespace lib {
-namespace integer {
- enum {
- number1 = 0x02020000, // default
+
+struct R {
+ struct integer {
+ enum : uint32_t {
+ number1 = 0x02020000, // default
};
-}
+ };
-namespace array {
- enum {
- integerArray1 = 0x02030000, // default
+ struct array {
+ enum : uint32_t {
+ integerArray1 = 0x02030000, // default
};
-}
-} // namespace lib
+ };
+};
+
+} // namespace lib
namespace app {
-namespace integer {
- enum {
- number1 = 0x7f020000, // default
+
+struct R {
+ struct integer {
+ enum : uint32_t {
+ number1 = 0x7f020000, // default
};
-}
+ };
-namespace array {
- enum {
- integerArray1 = 0x7f030000, // default
+ struct array {
+ enum : uint32_t {
+ integerArray1 = 0x7f030000, // default
};
-}
-} // namespace app
-} // namespace R
-} // namespace appaslib
+ };
+};
+
+} // namespace app
+
+} // namespace appaslib
+} // namespace android
+} // namespace com
-#endif // __APPASLIB_R_H
+#endif // DATA_APPASLIB_R_H_
diff --git a/libs/androidfw/tests/data/appaslib/appaslib.apk b/libs/androidfw/tests/data/appaslib/appaslib.apk
new file mode 100644
index 000000000000..6ebd8237a2d8
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/appaslib.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/appaslib/appaslib_arsc.h b/libs/androidfw/tests/data/appaslib/appaslib_arsc.h
deleted file mode 100644
index be176ab5e63f..000000000000
--- a/libs/androidfw/tests/data/appaslib/appaslib_arsc.h
+++ /dev/null
@@ -1,68 +0,0 @@
-unsigned char appaslib_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x04, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xdc, 0x02, 0x00, 0x00,
- 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
- 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
- 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
- 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
- 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
- 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00,
- 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00,
- 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0d, 0x00,
- 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00,
- 0x72, 0x00, 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
- 0x31, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, 0x5c, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x7f,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00,
- 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x4c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
- 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
- 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
- 0x03, 0x00, 0x00, 0x00
-};
-unsigned int appaslib_arsc_len = 772;
diff --git a/libs/androidfw/tests/data/appaslib/appaslib_lib.apk b/libs/androidfw/tests/data/appaslib/appaslib_lib.apk
new file mode 100644
index 000000000000..ee1521cb4fdd
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/appaslib_lib.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h b/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h
deleted file mode 100644
index 099285a17aad..000000000000
--- a/libs/androidfw/tests/data/appaslib/appaslib_lib_arsc.h
+++ /dev/null
@@ -1,68 +0,0 @@
-unsigned char appaslib_lib_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x04, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xdc, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
- 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
- 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
- 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
- 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
- 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00,
- 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00,
- 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0d, 0x00,
- 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00,
- 0x72, 0x00, 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
- 0x31, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00, 0x5c, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x48, 0x00,
- 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x4c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
- 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
- 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
- 0x03, 0x00, 0x00, 0x00
-};
-unsigned int appaslib_lib_arsc_len = 772;
diff --git a/libs/androidfw/tests/data/appaslib/build b/libs/androidfw/tests/data/appaslib/build
index e4bd88b4032c..baaf70075feb 100755
--- a/libs/androidfw/tests/data/appaslib/build
+++ b/libs/androidfw/tests/data/appaslib/build
@@ -15,14 +15,9 @@
# limitations under the License.
#
-PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar
+set -e
-aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F bundle.apk -f && \
-unzip bundle.apk resources.arsc && \
-mv resources.arsc appaslib.arsc && \
-xxd -i appaslib.arsc > appaslib_arsc.h && \
-aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F bundle.apk -f --shared-lib && \
-unzip bundle.apk resources.arsc && \
-mv resources.arsc appaslib_lib.arsc && \
-xxd -i appaslib_lib.arsc > appaslib_lib_arsc.h \
+PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar
+aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F appaslib.apk -f
+aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES -F appaslib_lib.apk -f --shared-lib
diff --git a/libs/androidfw/tests/data/basic/AndroidManifest.xml b/libs/androidfw/tests/data/basic/AndroidManifest.xml
index a56ac18e900b..b117882802d7 100644
--- a/libs/androidfw/tests/data/basic/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/basic/AndroidManifest.xml
@@ -15,7 +15,6 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.basic">
- <application>
- </application>
+ package="com.android.basic">
+ <application />
</manifest>
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index 6694dd0c36e1..9352b5c6629e 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,59 +14,67 @@
* limitations under the License.
*/
-#ifndef __BASE_R_H
-#define __BASE_R_H
+#ifndef TESTS_DATA_BASIC_R_H_
+#define TESTS_DATA_BASIC_R_H_
-namespace base {
-namespace R {
+#include <cstdint>
-namespace attr {
- enum {
- attr1 = 0x7f010000, // default
- attr2 = 0x7f010001, // default
+namespace com {
+namespace android {
+namespace basic {
+
+struct R {
+ struct attr {
+ enum : uint32_t {
+ attr1 = 0x7f010000,
+ attr2 = 0x7f010001,
};
-}
+ };
-namespace layout {
- enum {
- main = 0x7f020000, // default, fr-sw600dp-v13
+ struct layout {
+ enum : uint32_t {
+ main = 0x7f020000,
};
-}
+ };
-namespace string {
- enum {
- test1 = 0x7f030000, // default
- test2 = 0x7f030001, // default
- density = 0x7f030002, // default
+ struct string {
+ enum : uint32_t {
+ test1 = 0x7f030000,
+ test2 = 0x7f030001,
+ density = 0x7f030002,
- test3 = 0x7f080000, // default (in feature)
- test4 = 0x7f080001, // default (in feature)
+ // From feature
+ test3 = 0x7f080000,
+ test4 = 0x7f080001,
};
-}
+ };
-namespace integer {
- enum {
- number1 = 0x7f040000, // default, sv, vs
- number2 = 0x7f040001, // default
+ struct integer {
+ enum : uint32_t {
+ number1 = 0x7f040000,
+ number2 = 0x7f040001,
- test3 = 0x7f090000, // default (in feature)
+ // From feature
+ number3 = 0x7f090000,
};
-}
+ };
-namespace style {
- enum {
- Theme1 = 0x7f050000, // default
- Theme2 = 0x7f050001, // default
+ struct style {
+ enum : uint32_t {
+ Theme1 = 0x7f050000,
+ Theme2 = 0x7f050001,
};
-}
+ };
-namespace array {
- enum {
- integerArray1 = 0x7f060000, // default
+ struct array {
+ enum : uint32_t {
+ integerArray1 = 0x7f060000,
};
-}
+ };
+};
-} // namespace R
-} // namespace base
+} // namespace basic
+} // namespace android
+} // namespace com
-#endif // __BASE_R_H
+#endif /* TESTS_DATA_BASIC_R_H_ */
diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk
new file mode 100644
index 000000000000..2c9771b18934
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/basic.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_arsc.h b/libs/androidfw/tests/data/basic/basic_arsc.h
deleted file mode 100644
index e497401bdddb..000000000000
--- a/libs/androidfw/tests/data/basic/basic_arsc.h
+++ /dev/null
@@ -1,175 +0,0 @@
-unsigned char basic_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
- 0x72, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x13, 0x00, 0x72, 0x00,
- 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
- 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00,
- 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00,
- 0x00, 0x00, 0x22, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00,
- 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00,
- 0x2d, 0x00, 0x66, 0x00, 0x72, 0x00, 0x2d, 0x00, 0x73, 0x00, 0x77, 0x00,
- 0x36, 0x00, 0x30, 0x00, 0x30, 0x00, 0x64, 0x00, 0x70, 0x00, 0x2d, 0x00,
- 0x76, 0x00, 0x31, 0x00, 0x33, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00,
- 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00,
- 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
- 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01,
- 0x44, 0x07, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
- 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
- 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00,
- 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00,
- 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
- 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
- 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00,
- 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00,
- 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
- 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
- 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00,
- 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00,
- 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
- 0xec, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x28, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
- 0x56, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
- 0x88, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
- 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00,
- 0x74, 0x00, 0x72, 0x00, 0x32, 0x00, 0x00, 0x00, 0x04, 0x00, 0x6d, 0x00,
- 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00,
- 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00,
- 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00,
- 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00,
- 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x32, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
- 0x31, 0x00, 0x00, 0x00, 0x06, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00,
- 0x6d, 0x00, 0x65, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00,
- 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
- 0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x8c, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
- 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x04, 0x24, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
- 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
- 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02,
- 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x4c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x4c, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
- 0xc8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x7f, 0x01, 0x02, 0x4c, 0x00,
- 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x76, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x58, 0x02, 0x00, 0x00,
- 0x01, 0x02, 0x4c, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
- 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
- 0x90, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x98, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
- 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f,
- 0x08, 0x00, 0x00, 0x10, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7f,
- 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x7f, 0x10, 0x00, 0x01, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x7f, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x2c, 0x01, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
- 0x84, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
- 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02,
- 0x08, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02,
- 0x08, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00
-};
-unsigned int basic_arsc_len = 2060;
diff --git a/libs/androidfw/tests/data/basic/basic_de_fr.apk b/libs/androidfw/tests/data/basic/basic_de_fr.apk
new file mode 100644
index 000000000000..04814440e0f8
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/basic_de_fr.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk
new file mode 100644
index 000000000000..a8d06e7f3c19
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk
new file mode 100644
index 000000000000..d1dfb143f91b
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk
new file mode 100644
index 000000000000..dca6f2fbc0ca
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build
index fd289fad62b1..af0fd8787f48 100755
--- a/libs/androidfw/tests/data/basic/build
+++ b/libs/androidfw/tests/data/basic/build
@@ -15,25 +15,8 @@
# limitations under the License.
#
-PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar
+set -e
-aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --split hdpi --split xhdpi --split xxhdpi --split fr,de -F bundle.apk -f && \
-unzip bundle.apk resources.arsc && \
-mv resources.arsc basic.arsc && \
-xxd -i basic.arsc > basic_arsc.h && \
-\
-unzip bundle_de_fr.apk resources.arsc && \
-mv resources.arsc split_de_fr.arsc && \
-xxd -i split_de_fr.arsc > split_de_fr_arsc.h && \
-\
-unzip bundle_hdpi-v4.apk resources.arsc && \
-mv resources.arsc split_hdpi_v4.arsc && \
-xxd -i split_hdpi_v4.arsc > split_hdpi_v4_arsc.h && \
-\
-unzip bundle_xhdpi-v4.apk resources.arsc && \
-mv resources.arsc split_xhdpi_v4.arsc && \
-xxd -i split_xhdpi_v4.arsc > split_xhdpi_v4_arsc.h && \
-\
-unzip bundle_xxhdpi-v4.apk resources.arsc && \
-mv resources.arsc split_xxhdpi_v4.arsc && \
-xxd -i split_xxhdpi_v4.arsc > split_xxhdpi_v4_arsc.h \
+PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar
+
+aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --split hdpi --split xhdpi --split xxhdpi --split fr,de -F basic.apk -f
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index a010cca182ee..11f6b8adbdba 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -15,24 +15,40 @@
-->
<resources>
+ <public type="attr" name="attr1" id="0x7f010000" />
<attr name="attr1" format="reference|integer" />
+
+ <public type="attr" name="attr2" id="0x7f010001" />
<attr name="attr2" format="reference|integer" />
+ <public type="layout" name="main" id="0x7f020000" />
+
+ <public type="string" name="test1" id="0x7f030000" />
<string name="test1">test1</string>
+
+ <public type="string" name="test2" id="0x7f030001" />
<string name="test2">test2</string>
+ <public type="string" name="density" id="0x7f030002" />
+
+ <public type="integer" name="number1" id="0x7f040000" />
<integer name="number1">200</integer>
+
+ <public type="integer" name="number2" id="0x7f040001" />
<integer name="number2">@array/integerArray1</integer>
+ <public type="style" name="Theme1" id="0x7f050000" />
<style name="Theme1">
- <item name="com.android.test.basic:attr1">100</item>
- <item name="com.android.test.basic:attr2">@integer/number1</item>
+ <item name="com.android.basic:attr1">100</item>
+ <item name="com.android.basic:attr2">@integer/number1</item>
</style>
- <style name="Theme2" parent="@com.android.test.basic:style/Theme1">
- <item name="com.android.test.basic:attr1">300</item>
+ <public type="style" name="Theme2" id="0x7f050001" />
+ <style name="Theme2" parent="@com.android.basic:style/Theme1">
+ <item name="com.android.basic:attr1">300</item>
</style>
+ <public type="array" name="integerArray1" id="0x7f060000" />
<integer-array name="integerArray1">
<item>1</item>
<item>2</item>
diff --git a/libs/androidfw/tests/data/basic/split_de_fr_arsc.h b/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
deleted file mode 100644
index a2aa598e5c90..000000000000
--- a/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
+++ /dev/null
@@ -1,88 +0,0 @@
-unsigned char split_de_fr_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0xf4, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
- 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x76, 0x00,
- 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x75, 0x00, 0x63, 0x00, 0x68, 0x00,
- 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x09, 0x00, 0x76, 0x00, 0x65, 0x00,
- 0x72, 0x00, 0x73, 0x00, 0x75, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00,
- 0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00,
- 0x61, 0x00, 0x69, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00,
- 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x61, 0x00, 0x69, 0x00, 0x20, 0x00,
- 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x6c, 0x03, 0x00, 0x00,
- 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
- 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
- 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x90, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
- 0x3e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
- 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00,
- 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00,
- 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00,
- 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00,
- 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
- 0x74, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00,
- 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
- 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
- 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00
-};
-unsigned int split_de_fr_arsc_len = 1012;
diff --git a/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h
deleted file mode 100644
index 0cc39154a5ec..000000000000
--- a/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h
+++ /dev/null
@@ -1,69 +0,0 @@
-unsigned char split_hdpi_v4_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x68, 0x00,
- 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01,
- 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
- 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
- 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00,
- 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00,
- 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
- 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00,
- 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00,
- 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
- 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
- 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00,
- 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00,
- 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
- 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00,
- 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00
-};
-unsigned int split_hdpi_v4_arsc_len = 784;
diff --git a/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h
deleted file mode 100644
index d44ba9630aba..000000000000
--- a/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h
+++ /dev/null
@@ -1,69 +0,0 @@
-unsigned char split_xhdpi_v4_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x14, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x78, 0x00,
- 0x68, 0x00, 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x20, 0x01, 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
- 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00,
- 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00,
- 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00,
- 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0xb0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
- 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00,
- 0x4c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
- 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
- 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00,
- 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00,
- 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00,
- 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x64, 0x00,
- 0x65, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
- 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-unsigned int split_xhdpi_v4_arsc_len = 788;
diff --git a/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h
deleted file mode 100644
index 2f3f682fb6e4..000000000000
--- a/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h
+++ /dev/null
@@ -1,69 +0,0 @@
-unsigned char split_xxhdpi_v4_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x14, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x78, 0x00,
- 0x78, 0x00, 0x68, 0x00, 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x20, 0x01, 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
- 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00,
- 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00,
- 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00,
- 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0xb0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
- 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00,
- 0x4c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
- 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
- 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00,
- 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00,
- 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00,
- 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x64, 0x00,
- 0x65, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
- 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-unsigned int split_xxhdpi_v4_arsc_len = 788;
diff --git a/libs/androidfw/tests/data/feature/AndroidManifest.xml b/libs/androidfw/tests/data/feature/AndroidManifest.xml
index c2343b75e16e..c972372508be 100644
--- a/libs/androidfw/tests/data/feature/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/feature/AndroidManifest.xml
@@ -15,5 +15,5 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.basic">
+ package="com.android.basic">
</manifest>
diff --git a/libs/androidfw/tests/data/feature/build b/libs/androidfw/tests/data/feature/build
index 0f3307f518ec..6ed3e416fb10 100755
--- a/libs/androidfw/tests/data/feature/build
+++ b/libs/androidfw/tests/data/feature/build
@@ -15,7 +15,8 @@
# limitations under the License.
#
-aapt package -M AndroidManifest.xml -S res --feature-of ../basic/bundle.apk -F bundle.apk -f && \
-unzip bundle.apk resources.arsc && \
-mv resources.arsc feature.arsc && \
-xxd -i feature.arsc > feature_arsc.h
+set -e
+
+PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar
+
+aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --feature-of ../basic/basic.apk -F feature.apk -f
diff --git a/libs/androidfw/tests/data/feature/feature.apk b/libs/androidfw/tests/data/feature/feature.apk
new file mode 100644
index 000000000000..04940fb9bce2
--- /dev/null
+++ b/libs/androidfw/tests/data/feature/feature.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/feature/feature_arsc.h b/libs/androidfw/tests/data/feature/feature_arsc.h
deleted file mode 100644
index cd299102e8f6..000000000000
--- a/libs/androidfw/tests/data/feature/feature_arsc.h
+++ /dev/null
@@ -1,73 +0,0 @@
-unsigned char feature_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x44, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x33, 0x00,
- 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x34, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf8, 0x02, 0x00, 0x00,
- 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
- 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
- 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
- 0x09, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
- 0x1e, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x3c, 0x00,
- 0x65, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3e, 0x00,
- 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00,
- 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00,
- 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00,
- 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x58, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
- 0x1c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
- 0x74, 0x00, 0x33, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00,
- 0x73, 0x00, 0x74, 0x00, 0x34, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00,
- 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x33, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
- 0x6c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x10, 0xc8, 0x00, 0x00, 0x00
-};
-unsigned int feature_arsc_len = 836;
diff --git a/libs/androidfw/tests/data/feature/res/values/values.xml b/libs/androidfw/tests/data/feature/res/values/values.xml
index 343fd6c8389f..59f7d93ee389 100644
--- a/libs/androidfw/tests/data/feature/res/values/values.xml
+++ b/libs/androidfw/tests/data/feature/res/values/values.xml
@@ -15,8 +15,13 @@
-->
<resources>
+ <!-- Features are offset, so 7f020000 will become 7f080000 at runtime. -->
+ <public type="string" name="test3" id="0x7f020000" />
<string name="test3">test3</string>
+
+ <public type="string" name="test4" id="0x7f020001" />
<string name="test4">test4</string>
+ <public type="integer" name="number3" id="0x7f030000" />
<integer name="number3">200</integer>
</resources>
diff --git a/libs/androidfw/tests/data/lib/AndroidManifest.xml b/libs/androidfw/tests/data/lib/AndroidManifest.xml
index a56ac18e900b..02f5d3efabea 100644
--- a/libs/androidfw/tests/data/lib/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/lib/AndroidManifest.xml
@@ -15,7 +15,6 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.basic">
- <application>
- </application>
+ package="com.android.lib">
+ <application />
</manifest>
diff --git a/libs/androidfw/tests/data/lib/R.h b/libs/androidfw/tests/data/lib/R.h
index 6013973072cc..bb22d22f90e1 100644
--- a/libs/androidfw/tests/data/lib/R.h
+++ b/libs/androidfw/tests/data/lib/R.h
@@ -14,26 +14,32 @@
* limitations under the License.
*/
-#ifndef __LIB_R_H
-#define __LIB_R_H
+#ifndef TEST_DATA_LIB_R_H_
+#define TEST_DATA_LIB_R_H_
+#include <cstdint>
+
+namespace com {
+namespace android {
namespace lib {
-namespace R {
-namespace attr {
- enum {
- attr1 = 0x02010000, // default
- attr2 = 0x02010001, // default
+struct R {
+ struct attr {
+ enum : uint32_t {
+ attr1 = 0x02010000, // default
+ attr2 = 0x02010001, // default
};
-}
+ };
-namespace style {
- enum {
- Theme = 0x02020000, // default
+ struct style {
+ enum : uint32_t {
+ Theme = 0x02020000, // default
};
-}
+ };
+};
-} // namespace R
-} // namespace lib
+} // namespace lib
+} // namespace android
+} // namespace com
-#endif // __LIB_R_H
+#endif // TEST_DATA_R_H_
diff --git a/libs/androidfw/tests/data/lib/build b/libs/androidfw/tests/data/lib/build
index 41029031028c..5c3d02c850bf 100755
--- a/libs/androidfw/tests/data/lib/build
+++ b/libs/androidfw/tests/data/lib/build
@@ -15,7 +15,6 @@
# limitations under the License.
#
-aapt package -M AndroidManifest.xml -S res -F bundle.apk -f --shared-lib && \
-unzip bundle.apk resources.arsc && \
-mv resources.arsc lib.arsc && \
-xxd -i lib.arsc > lib_arsc.h
+set -e
+
+aapt package -M AndroidManifest.xml -S res -F lib.apk -f --shared-lib
diff --git a/libs/androidfw/tests/data/lib/lib.apk b/libs/androidfw/tests/data/lib/lib.apk
new file mode 100644
index 000000000000..44c27c79ae7c
--- /dev/null
+++ b/libs/androidfw/tests/data/lib/lib.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/lib/lib_arsc.h b/libs/androidfw/tests/data/lib/lib_arsc.h
deleted file mode 100644
index 62bed655f914..000000000000
--- a/libs/androidfw/tests/data/lib/lib_arsc.h
+++ /dev/null
@@ -1,68 +0,0 @@
-unsigned char lib_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xe4, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
- 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
- 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
- 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00,
- 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
- 0x54, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x31, 0x00,
- 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00,
- 0x32, 0x00, 0x00, 0x00, 0x05, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00,
- 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
- 0x8c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x4c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x10, 0xbc, 0x02, 0x00, 0x00,
- 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00
-};
-unsigned int lib_arsc_len = 780;
diff --git a/libs/androidfw/tests/data/lib/res/values/values.xml b/libs/androidfw/tests/data/lib/res/values/values.xml
index ec8117a78c56..51e3a407c538 100644
--- a/libs/androidfw/tests/data/lib/res/values/values.xml
+++ b/libs/androidfw/tests/data/lib/res/values/values.xml
@@ -19,7 +19,7 @@
<attr name="attr2" format="integer" />
<style name="Theme">
- <item name="com.android.test.basic:attr1">700</item>
- <item name="com.android.test.basic:attr2">?com.android.test.basic:attr1</item>
+ <item name="com.android.lib:attr1">700</item>
+ <item name="com.android.lib:attr2">?com.android.lib:attr1</item>
</style>
</resources>
diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build
index f73767749274..112f373ead30 100755
--- a/libs/androidfw/tests/data/overlay/build
+++ b/libs/androidfw/tests/data/overlay/build
@@ -15,7 +15,6 @@
# limitations under the License.
#
-aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \
-unzip bundle.apk resources.arsc && \
-mv resources.arsc overlay.arsc && \
-xxd -i overlay.arsc > overlay_arsc.h
+set -e
+
+aapt package -M AndroidManifest.xml -S res -F overlay.apk -f
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
new file mode 100644
index 000000000000..e0e054343601
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/overlay_arsc.h b/libs/androidfw/tests/data/overlay/overlay_arsc.h
deleted file mode 100644
index 5bd98b28409d..000000000000
--- a/libs/androidfw/tests/data/overlay/overlay_arsc.h
+++ /dev/null
@@ -1,69 +0,0 @@
-unsigned char overlay_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x74, 0x00,
- 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x6f, 0x00,
- 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xc4, 0x02, 0x00, 0x00,
- 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
- 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
- 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
- 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00,
- 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x50, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
- 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00,
- 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00,
- 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
- 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
- 0x0b, 0x00, 0x00, 0x00
-};
-unsigned int overlay_arsc_len = 784;
diff --git a/libs/androidfw/tests/data/styles/AndroidManifest.xml b/libs/androidfw/tests/data/styles/AndroidManifest.xml
new file mode 100644
index 000000000000..521131659cfe
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.app">
+</manifest>
diff --git a/libs/androidfw/tests/data/styles/R.h b/libs/androidfw/tests/data/styles/R.h
new file mode 100644
index 000000000000..68527c744b37
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/R.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEST_DATA_STYLES_R_H_
+#define TEST_DATA_STYLES_R_H_
+
+#include <cstdint>
+
+namespace com {
+namespace android {
+namespace app {
+
+struct R {
+ struct attr {
+ enum : uint32_t {
+ attr_one = 0x7f010000u,
+ attr_two = 0x7f010001u,
+ attr_three = 0x7f010002u,
+ attr_four = 0x7f010003u,
+ attr_five = 0x7f010004u,
+ attr_indirect = 0x7f010005u,
+ attr_six = 0x7f010006u,
+ };
+ };
+
+ struct string {
+ enum : uint32_t {
+ string_one = 0x7f030000u,
+ };
+ };
+
+ struct style {
+ enum : uint32_t {
+ StyleOne = 0x7f020000u,
+ StyleTwo = 0x7f020001u,
+ StyleThree = 0x7f020002u,
+ };
+ };
+};
+
+} // namespace app
+} // namespace android
+} // namespace com
+
+#endif // TEST_DATA_STYLES_R_H_
diff --git a/libs/androidfw/tests/data/styles/build b/libs/androidfw/tests/data/styles/build
new file mode 100755
index 000000000000..81f78b1b7b7f
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/build
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+set -e
+
+aapt package -F styles.apk -M AndroidManifest.xml -S res -f
diff --git a/libs/androidfw/tests/data/styles/res/layout/layout.xml b/libs/androidfw/tests/data/styles/res/layout/layout.xml
new file mode 100644
index 000000000000..f3aa0f83d9a3
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/res/layout/layout.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<View xmlns:app="http://schemas.android.com/apk/res-auto"
+ app:attr_four="?attr/attr_indirect"
+ app:attr_three="10" />
+
diff --git a/libs/androidfw/tests/data/styles/res/values/styles.xml b/libs/androidfw/tests/data/styles/res/values/styles.xml
new file mode 100644
index 000000000000..da592f806d21
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/res/values/styles.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <public type="attr" name="attr_one" id="0x7f010000" />
+ <attr name="attr_one" />
+
+ <public type="attr" name="attr_two" id="0x7f010001" />
+ <attr name="attr_two" />
+
+ <public type="attr" name="attr_three" id="0x7f010002" />
+ <attr name="attr_three" />
+
+ <public type="attr" name="attr_four" id="0x7f010003" />
+ <attr name="attr_four" />
+
+ <public type="attr" name="attr_five" id="0x7f010004" />
+ <attr name="attr_five" />
+
+ <public type="attr" name="attr_indirect" id="0x7f010005" />
+ <attr name="attr_indirect" />
+
+ <public type="string" name="string_one" id="0x7f030000" />
+ <string name="string_one">Hi</string>
+
+ <public type="style" name="StyleOne" id="0x7f020000" />
+ <style name="StyleOne">
+ <item name="attr_one">1</item>
+ <item name="attr_two">2</item>
+ </style>
+
+ <public type="style" name="StyleTwo" id="0x7f020001" />
+ <style name="StyleTwo" parent="@style/StyleOne">
+ <item name="attr_indirect">3</item>
+ <item name="attr_two">"string"</item>
+ <item name="attr_three">?attr/attr_indirect</item>
+ <item name="attr_five">@string/string_one</item>
+ </style>
+
+ <public type="attr" name="attr_six" id="0x7f010006" />
+ <attr name="attr_six" />
+
+ <public type="style" name="StyleThree" id="0x7f020002" />
+ <style name="StyleThree">
+ <item name="attr_six">6</item>
+ <item name="attr_five">5</item>
+ </style>
+
+</resources>
diff --git a/libs/androidfw/tests/data/styles/styles.apk b/libs/androidfw/tests/data/styles/styles.apk
new file mode 100644
index 000000000000..d4ccb8391202
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/styles.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
index 6a31fb8ff088..becb38830fb3 100644
--- a/libs/androidfw/tests/data/system/R.h
+++ b/libs/androidfw/tests/data/system/R.h
@@ -14,32 +14,34 @@
* limitations under the License.
*/
-#ifndef __ANDROID_R_H
-#define __ANDROID_R_H
+#ifndef TEST_DATA_SYSTEM_R_H_
+#define TEST_DATA_SYSTEM_R_H_
+
+#include <cstdint>
namespace android {
-namespace R {
-namespace attr {
- enum {
- background = 0x01010000, // default
- foreground = 0x01010001, // default
+struct R {
+ struct attr {
+ enum : uint32_t {
+ background = 0x01010000, // default
+ foreground = 0x01010001, // default
};
-}
+ };
-namespace style {
- enum {
- Theme_One = 0x01020000, // default
+ struct style {
+ enum : uint32_t {
+ Theme_One = 0x01020000, // default
};
-}
+ };
-namespace integer {
- enum {
- number = 0x01030000, // sv
+ struct integer {
+ enum : uint32_t {
+ number = 0x01030000, // sv
};
-}
+ };
+};
-} // namespace R
-} // namespace android
+} // namespace android
-#endif // __ANDROID_R_H
+#endif // TEST_DATA_SYSTEM_R_H_
diff --git a/libs/androidfw/tests/data/system/build b/libs/androidfw/tests/data/system/build
index 1a70e84114b0..bfbdf4ca770b 100755
--- a/libs/androidfw/tests/data/system/build
+++ b/libs/androidfw/tests/data/system/build
@@ -15,7 +15,6 @@
# limitations under the License.
#
-aapt package -x -M AndroidManifest.xml -S res -F bundle.apk -f && \
-unzip bundle.apk resources.arsc && \
-mv resources.arsc system.arsc && \
-xxd -i system.arsc > system_arsc.h
+set -e
+
+aapt package -x -M AndroidManifest.xml -S res -F system.apk -f
diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk
new file mode 100644
index 000000000000..1299016a0f83
--- /dev/null
+++ b/libs/androidfw/tests/data/system/system.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/system/system_arsc.h b/libs/androidfw/tests/data/system/system_arsc.h
deleted file mode 100644
index b0dab6b357e9..000000000000
--- a/libs/androidfw/tests/data/system/system_arsc.h
+++ /dev/null
@@ -1,88 +0,0 @@
-unsigned char system_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0xf8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xd0, 0x03, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
- 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x78, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0c, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00,
- 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00,
- 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x5e, 0x00,
- 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x2d, 0x00, 0x70, 0x00,
- 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x84, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00,
- 0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x67, 0x00,
- 0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
- 0x0a, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x67, 0x00,
- 0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x00, 0x00,
- 0x09, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
- 0x2e, 0x00, 0x4f, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x00, 0x00, 0x06, 0x00,
- 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
- 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x8c, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
- 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x11, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00,
- 0x78, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
- 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x01, 0x01,
- 0x08, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xff, 0x02, 0x02, 0x10, 0x00,
- 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x40, 0x01, 0x02, 0x4c, 0x00, 0x60, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
- 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
- 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-unsigned int system_arsc_len = 1016;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index a7cbf5e562d1..8f7787bd5d15 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,7 +2,6 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-HWUI_NEW_OPS := true
BUGREPORT_FONT_CACHE_USAGE := false
# Enables fine-grained GLES error checking
@@ -11,6 +10,7 @@ BUGREPORT_FONT_CACHE_USAGE := false
HWUI_ENABLE_OPENGL_VALIDATION := false
hwui_src_files := \
+ hwui/Bitmap.cpp \
font/CacheTexture.cpp \
font/Font.cpp \
hwui/Canvas.cpp \
@@ -18,6 +18,17 @@ hwui_src_files := \
hwui/MinikinUtils.cpp \
hwui/PaintImpl.cpp \
hwui/Typeface.cpp \
+ pipeline/skia/GLFunctorDrawable.cpp \
+ pipeline/skia/LayerDrawable.cpp \
+ pipeline/skia/RenderNodeDrawable.cpp \
+ pipeline/skia/ReorderBarrierDrawables.cpp \
+ pipeline/skia/SkiaDisplayList.cpp \
+ pipeline/skia/SkiaOpenGLPipeline.cpp \
+ pipeline/skia/SkiaOpenGLReadback.cpp \
+ pipeline/skia/SkiaPipeline.cpp \
+ pipeline/skia/SkiaProfileRenderer.cpp \
+ pipeline/skia/SkiaRecordingCanvas.cpp \
+ pipeline/skia/SkiaVulkanPipeline.cpp \
renderstate/Blend.cpp \
renderstate/MeshState.cpp \
renderstate/OffscreenBufferPool.cpp \
@@ -27,17 +38,19 @@ hwui_src_files := \
renderstate/Stencil.cpp \
renderstate/TextureState.cpp \
renderthread/CanvasContext.cpp \
+ renderthread/OpenGLPipeline.cpp \
renderthread/DrawFrameTask.cpp \
renderthread/EglManager.cpp \
+ renderthread/VulkanManager.cpp \
renderthread/RenderProxy.cpp \
renderthread/RenderTask.cpp \
renderthread/RenderThread.cpp \
renderthread/TimeLord.cpp \
+ renderthread/Frame.cpp \
thread/TaskManager.cpp \
utils/Blur.cpp \
utils/GLUtils.cpp \
utils/LinearAllocator.cpp \
- utils/NinePatchImpl.cpp \
utils/StringUtils.cpp \
utils/TestWindowContext.cpp \
utils/VectorDrawableUtils.cpp \
@@ -45,23 +58,24 @@ hwui_src_files := \
AnimationContext.cpp \
Animator.cpp \
AnimatorManager.cpp \
- AssetAtlas.cpp \
+ BakedOpDispatcher.cpp \
+ BakedOpRenderer.cpp \
+ BakedOpState.cpp \
Caches.cpp \
CanvasState.cpp \
ClipArea.cpp \
DamageAccumulator.cpp \
- DeferredDisplayList.cpp \
DeferredLayerUpdater.cpp \
DeviceInfo.cpp \
DisplayList.cpp \
- DisplayListCanvas.cpp \
- Dither.cpp \
Extensions.cpp \
FboCache.cpp \
FontRenderer.cpp \
+ FrameBuilder.cpp \
FrameInfo.cpp \
FrameInfoVisualizer.cpp \
GammaFontRenderer.cpp \
+ GlLayer.cpp \
GlopBuilder.cpp \
GpuMemoryTracker.cpp \
GradientCache.cpp \
@@ -69,23 +83,24 @@ hwui_src_files := \
Interpolator.cpp \
JankTracker.cpp \
Layer.cpp \
- LayerCache.cpp \
- LayerRenderer.cpp \
+ LayerBuilder.cpp \
LayerUpdateQueue.cpp \
Matrix.cpp \
- OpenGLRenderer.cpp \
+ OpDumper.cpp \
+ OpenGLReadback.cpp \
Patch.cpp \
PatchCache.cpp \
PathCache.cpp \
- PathTessellator.cpp \
PathParser.cpp \
+ PathTessellator.cpp \
PixelBuffer.cpp \
+ ProfileRenderer.cpp \
Program.cpp \
ProgramCache.cpp \
Properties.cpp \
- PropertyValuesHolder.cpp \
PropertyValuesAnimatorSet.cpp \
- Readback.cpp \
+ PropertyValuesHolder.cpp \
+ RecordingCanvas.cpp \
RenderBufferCache.cpp \
RenderNode.cpp \
RenderProperties.cpp \
@@ -101,14 +116,25 @@ hwui_src_files := \
Texture.cpp \
TextureCache.cpp \
VectorDrawable.cpp \
+ VkLayer.cpp \
protos/hwui.proto
hwui_test_common_src_files := \
$(call all-cpp-files-under, tests/common/scenes) \
+ tests/common/LeakChecker.cpp \
+ tests/common/TestListViewSceneBase.cpp \
tests/common/TestContext.cpp \
tests/common/TestScene.cpp \
tests/common/TestUtils.cpp
+hwui_debug_common_src_files := \
+ debug/wrap_gles.cpp \
+ debug/DefaultGlesDriver.cpp \
+ debug/GlesErrorCheckWrapper.cpp \
+ debug/GlesDriver.cpp \
+ debug/FatalBaseDriver.cpp \
+ debug/NullGlesDriver.cpp
+
hwui_cflags := \
-DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES \
-DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" \
@@ -118,23 +144,20 @@ ifeq ($(TARGET_USES_HWC2),true)
hwui_cflags += -DUSE_HWC2
endif
+# TODO: Linear blending should be enabled by default, but we are
+# TODO: making it an opt-in while it's a work in progress
+# TODO: The final test should be:
+# TODO: ifneq ($(TARGET_ENABLE_LINEAR_BLENDING),false)
+ifeq ($(TARGET_ENABLE_LINEAR_BLENDING),true)
+ hwui_cflags += -DANDROID_ENABLE_LINEAR_BLENDING
+endif
+
# GCC false-positives on this warning, and since we -Werror that's
# a problem
hwui_cflags += -Wno-free-nonheap-object
-ifeq (true, $(HWUI_NEW_OPS))
- hwui_src_files += \
- BakedOpDispatcher.cpp \
- BakedOpRenderer.cpp \
- BakedOpState.cpp \
- FrameBuilder.cpp \
- LayerBuilder.cpp \
- OpDumper.cpp \
- RecordingCanvas.cpp
-
- hwui_cflags += -DHWUI_NEW_OPS
-
-endif
+# clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629
+hwui_cflags += -Wno-missing-braces
ifeq (true, $(BUGREPORT_FONT_CACHE_USAGE))
hwui_src_files += \
@@ -142,7 +165,6 @@ ifeq (true, $(BUGREPORT_FONT_CACHE_USAGE))
hwui_cflags += -DBUGREPORT_FONT_CACHE_USAGE
endif
-
ifndef HWUI_COMPILE_SYMBOLS
hwui_cflags += -fvisibility=hidden
endif
@@ -161,6 +183,9 @@ endef
hwui_c_includes += \
external/skia/include/private \
external/skia/src/core \
+ external/skia/src/effects \
+ external/skia/src/image \
+ external/skia/src/utils \
external/harfbuzz_ng/src \
external/freetype/include
@@ -172,14 +197,6 @@ ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
frameworks/rs
endif
-ifeq (true, $(HWUI_ENABLE_OPENGL_VALIDATION))
- hwui_cflags += -include debug/wrap_gles.h
- hwui_src_files += debug/wrap_gles.cpp
- hwui_c_includes += frameworks/native/opengl/libs/GLES2
- hwui_cflags += -DDEBUG_OPENGL=3
-endif
-
-
# ------------------------
# static library
# ------------------------
@@ -190,6 +207,13 @@ LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE := libhwui_static
LOCAL_CFLAGS := $(hwui_cflags)
LOCAL_SRC_FILES := $(hwui_src_files)
+
+ifeq (true, $(HWUI_ENABLE_OPENGL_VALIDATION))
+ LOCAL_CFLAGS += -include debug/wrap_gles.h
+ LOCAL_CFLAGS += -DDEBUG_OPENGL=3
+ LOCAL_SRC_FILES += $(hwui_debug_common_src_files)
+endif
+
LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(LOCAL_PATH) \
@@ -205,14 +229,15 @@ include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
-LOCAL_MODULE := libhwui_static_null_gpu
+LOCAL_MODULE := libhwui_static_debug
LOCAL_CFLAGS := \
$(hwui_cflags) \
+ -include debug/wrap_gles.h \
-DHWUI_NULL_GPU
LOCAL_SRC_FILES := \
$(hwui_src_files) \
- debug/nullegl.cpp \
- debug/nullgles.cpp
+ $(hwui_debug_common_src_files) \
+ debug/nullegl.cpp
LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(LOCAL_PATH) \
@@ -243,49 +268,55 @@ include $(CLEAR_VARS)
LOCAL_MODULE := hwui_unit_tests
LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu
+LOCAL_STATIC_LIBRARIES := libgmock libhwui_static_debug
LOCAL_SHARED_LIBRARIES := libmemunreachable
LOCAL_CFLAGS := \
$(hwui_cflags) \
+ -include debug/wrap_gles.h \
-DHWUI_NULL_GPU
LOCAL_C_INCLUDES := $(hwui_c_includes)
LOCAL_SRC_FILES += \
$(hwui_test_common_src_files) \
tests/unit/main.cpp \
+ tests/unit/BakedOpDispatcherTests.cpp \
+ tests/unit/BakedOpRendererTests.cpp \
+ tests/unit/BakedOpStateTests.cpp \
+ tests/unit/BitmapTests.cpp \
+ tests/unit/CanvasContextTests.cpp \
tests/unit/CanvasStateTests.cpp \
tests/unit/ClipAreaTests.cpp \
tests/unit/DamageAccumulatorTests.cpp \
+ tests/unit/DeferredLayerUpdaterTests.cpp \
tests/unit/DeviceInfoTests.cpp \
tests/unit/FatVectorTests.cpp \
tests/unit/FontRendererTests.cpp \
+ tests/unit/FrameBuilderTests.cpp \
tests/unit/GlopBuilderTests.cpp \
tests/unit/GpuMemoryTrackerTests.cpp \
tests/unit/GradientCacheTests.cpp \
tests/unit/LayerUpdateQueueTests.cpp \
+ tests/unit/LeakCheckTests.cpp \
tests/unit/LinearAllocatorTests.cpp \
tests/unit/MatrixTests.cpp \
+ tests/unit/MeshStateTests.cpp \
tests/unit/OffscreenBufferPoolTests.cpp \
+ tests/unit/OpDumperTests.cpp \
+ tests/unit/PathInterpolatorTests.cpp \
+ tests/unit/RenderNodeDrawableTests.cpp \
+ tests/unit/RecordingCanvasTests.cpp \
tests/unit/RenderNodeTests.cpp \
tests/unit/RenderPropertiesTests.cpp \
tests/unit/SkiaBehaviorTests.cpp \
+ tests/unit/SkiaDisplayListTests.cpp \
+ tests/unit/SkiaPipelineTests.cpp \
+ tests/unit/SkiaRenderPropertiesTests.cpp \
+ tests/unit/SkiaCanvasTests.cpp \
tests/unit/SnapshotTests.cpp \
tests/unit/StringUtilsTests.cpp \
tests/unit/TestUtilsTests.cpp \
tests/unit/TextDropShadowCacheTests.cpp \
- tests/unit/VectorDrawableTests.cpp
-
-ifeq (true, $(HWUI_NEW_OPS))
- LOCAL_SRC_FILES += \
- tests/unit/BakedOpDispatcherTests.cpp \
- tests/unit/BakedOpRendererTests.cpp \
- tests/unit/BakedOpStateTests.cpp \
- tests/unit/FrameBuilderTests.cpp \
- tests/unit/LeakCheckTests.cpp \
- tests/unit/OpDumperTests.cpp \
- tests/unit/RecordingCanvasTests.cpp \
- tests/unit/SkiaCanvasTests.cpp
-endif
+ tests/unit/VectorDrawableTests.cpp \
include $(LOCAL_PATH)/hwui_static_deps.mk
include $(BUILD_NATIVE_TEST)
@@ -297,17 +328,15 @@ include $(BUILD_NATIVE_TEST)
include $(CLEAR_VARS)
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
-LOCAL_MODULE:= hwuitest
+LOCAL_MODULE:= hwuimacro
LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := hwuitest
-LOCAL_MODULE_STEM_64 := hwuitest64
LOCAL_CFLAGS := $(hwui_cflags)
LOCAL_C_INCLUDES := $(hwui_c_includes)
-# set to libhwui_static_null_gpu to skip actual GL commands
+# set to libhwui_static_debug to skip actual GL commands
LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
+LOCAL_SHARED_LIBRARIES := libmemunreachable
LOCAL_SRC_FILES += \
$(hwui_test_common_src_files) \
@@ -315,43 +344,37 @@ LOCAL_SRC_FILES += \
tests/macrobench/main.cpp
include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_EXECUTABLE)
+include $(BUILD_NATIVE_BENCHMARK)
# ------------------------
# Micro-bench app
# ---------------------
include $(CLEAR_VARS)
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
LOCAL_MODULE:= hwuimicro
LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := hwuimicro
-LOCAL_MODULE_STEM_64 := hwuimicro64
LOCAL_CFLAGS := \
$(hwui_cflags) \
+ -include debug/wrap_gles.h \
-DHWUI_NULL_GPU
LOCAL_C_INCLUDES := $(hwui_c_includes)
-LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static_null_gpu
-LOCAL_STATIC_LIBRARIES := libgoogle-benchmark
+LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static_debug
+LOCAL_SHARED_LIBRARIES := libmemunreachable
LOCAL_SRC_FILES += \
$(hwui_test_common_src_files) \
tests/microbench/main.cpp \
tests/microbench/DisplayListCanvasBench.cpp \
tests/microbench/FontBench.cpp \
+ tests/microbench/FrameBuilderBench.cpp \
tests/microbench/LinearAllocatorBench.cpp \
tests/microbench/PathParserBench.cpp \
+ tests/microbench/RenderNodeBench.cpp \
tests/microbench/ShadowBench.cpp \
tests/microbench/TaskManagerBench.cpp
-ifeq (true, $(HWUI_NEW_OPS))
- LOCAL_SRC_FILES += \
- tests/microbench/FrameBuilderBench.cpp
-endif
include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_EXECUTABLE)
+include $(BUILD_NATIVE_BENCHMARK)
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 74aa3033ee12..b6fbf891f84d 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -317,7 +317,7 @@ struct RenderPropertyAnimator::PropertyAccessors {
const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::PROPERTY_ACCESSOR_LUT[] = {
{RenderNode::TRANSLATION_X, &RenderProperties::getTranslationX, &RenderProperties::setTranslationX },
{RenderNode::TRANSLATION_Y, &RenderProperties::getTranslationY, &RenderProperties::setTranslationY },
- {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ },
+ {RenderNode::TRANSLATION_Z, &RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ },
{RenderNode::SCALE_X, &RenderProperties::getScaleX, &RenderProperties::setScaleX },
{RenderNode::SCALE_Y, &RenderProperties::getScaleY, &RenderProperties::setScaleY },
{RenderNode::ROTATION, &RenderProperties::getRotation, &RenderProperties::setRotation },
diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
deleted file mode 100644
index e2e7037202b8..000000000000
--- a/libs/hwui/AssetAtlas.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "AssetAtlas.h"
-#include "Caches.h"
-#include "Image.h"
-
-#include <GLES2/gl2ext.h>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Lifecycle
-///////////////////////////////////////////////////////////////////////////////
-
-void AssetAtlas::init(const sp<GraphicBuffer>& buffer, int64_t* map, int count) {
- if (mImage) {
- return;
- }
-
- ATRACE_NAME("AssetAtlas::init");
-
- mImage = new Image(buffer);
- if (mImage->getTexture()) {
- if (!mTexture) {
- Caches& caches = Caches::getInstance();
- mTexture = new Texture(caches);
- mTexture->wrap(mImage->getTexture(),
- buffer->getWidth(), buffer->getHeight(), GL_RGBA);
- createEntries(caches, map, count);
- }
- } else {
- ALOGW("Could not create atlas image");
- terminate();
- }
-}
-
-void AssetAtlas::terminate() {
- delete mImage;
- mImage = nullptr;
- delete mTexture;
- mTexture = nullptr;
- mEntries.clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Entries
-///////////////////////////////////////////////////////////////////////////////
-
-AssetAtlas::Entry* AssetAtlas::getEntry(const SkPixelRef* pixelRef) const {
- auto result = mEntries.find(pixelRef);
- return result != mEntries.end() ? result->second.get() : nullptr;
-}
-
-Texture* AssetAtlas::getEntryTexture(const SkPixelRef* pixelRef) const {
- auto result = mEntries.find(pixelRef);
- return result != mEntries.end() ? result->second->texture : nullptr;
-}
-
-/**
- * Delegates changes to wrapping and filtering to the base atlas texture
- * instead of applying the changes to the virtual textures.
- */
-struct DelegateTexture: public Texture {
- DelegateTexture(Caches& caches, Texture* delegate)
- : Texture(caches), mDelegate(delegate) { }
-
- virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
- bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override {
- mDelegate->setWrapST(wrapS, wrapT, bindTexture, force, renderTarget);
- }
-
- virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
- bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override {
- mDelegate->setFilterMinMag(min, mag, bindTexture, force, renderTarget);
- }
-
-private:
- Texture* const mDelegate;
-}; // struct DelegateTexture
-
-void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) {
- const float width = float(mTexture->width());
- const float height = float(mTexture->height());
-
- for (int i = 0; i < count; ) {
- SkPixelRef* pixelRef = reinterpret_cast<SkPixelRef*>(map[i++]);
- // NOTE: We're converting from 64 bit signed values to 32 bit
- // signed values. This is guaranteed to be safe because the "x"
- // and "y" coordinate values are guaranteed to be representable
- // with 32 bits. The array is 64 bits wide so that it can carry
- // pointers on 64 bit architectures.
- const int x = static_cast<int>(map[i++]);
- const int y = static_cast<int>(map[i++]);
-
- // Bitmaps should never be null, we're just extra paranoid
- if (!pixelRef) continue;
-
- const UvMapper mapper(
- x / width, (x + pixelRef->info().width()) / width,
- y / height, (y + pixelRef->info().height()) / height);
-
- Texture* texture = new DelegateTexture(caches, mTexture);
- texture->blend = !SkAlphaTypeIsOpaque(pixelRef->info().alphaType());
- texture->wrap(mTexture->id(), pixelRef->info().width(),
- pixelRef->info().height(), mTexture->format());
-
- std::unique_ptr<Entry> entry(new Entry(pixelRef, texture, mapper, *this));
- texture->uvMapper = &entry->uvMapper;
-
- mEntries.emplace(entry->pixelRef, std::move(entry));
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h
deleted file mode 100644
index b32e51851b94..000000000000
--- a/libs/hwui/AssetAtlas.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_ASSET_ATLAS_H
-#define ANDROID_HWUI_ASSET_ATLAS_H
-
-#include "Texture.h"
-#include "UvMapper.h"
-
-#include <cutils/compiler.h>
-#include <GLES2/gl2.h>
-#include <ui/GraphicBuffer.h>
-#include <SkBitmap.h>
-
-#include <memory>
-#include <unordered_map>
-
-namespace android {
-namespace uirenderer {
-
-class Caches;
-class Image;
-
-/**
- * An asset atlas holds a collection of framework bitmaps in a single OpenGL
- * texture. Each bitmap is associated with a location, defined in pixels,
- * inside the atlas. The atlas is generated by the framework and bound as
- * an external texture using the EGLImageKHR extension.
- */
-class AssetAtlas {
-public:
- /**
- * Entry representing the texture and uvMapper of a PixelRef in the
- * atlas
- */
- class Entry {
- public:
- /*
- * A "virtual texture" object that represents the texture
- * this entry belongs to. This texture should never be
- * modified.
- */
- Texture* texture;
-
- /**
- * Maps texture coordinates in the [0..1] range into the
- * correct range to sample this entry from the atlas.
- */
- const UvMapper uvMapper;
-
- /**
- * Unique identifier used to merge bitmaps and 9-patches stored
- * in the atlas.
- */
- const void* getMergeId() const {
- return texture->blend ? &atlas.mBlendKey : &atlas.mOpaqueKey;
- }
-
- ~Entry() {
- delete texture;
- }
-
- private:
- /**
- * The pixel ref that generated this atlas entry.
- */
- SkPixelRef* pixelRef;
-
- /**
- * Atlas this entry belongs to.
- */
- const AssetAtlas& atlas;
-
- Entry(SkPixelRef* pixelRef, Texture* texture, const UvMapper& mapper,
- const AssetAtlas& atlas)
- : texture(texture)
- , uvMapper(mapper)
- , pixelRef(pixelRef)
- , atlas(atlas) {
- }
-
- friend class AssetAtlas;
- };
-
- AssetAtlas(): mTexture(nullptr), mImage(nullptr),
- mBlendKey(true), mOpaqueKey(false) { }
- ~AssetAtlas() { terminate(); }
-
- /**
- * Initializes the atlas with the specified buffer and
- * map. The buffer is a gralloc'd texture that will be
- * used as an EGLImage. The map is a list of SkBitmap*
- * and their (x, y) positions
- *
- * This method returns immediately if the atlas is already
- * initialized. To re-initialize the atlas, you must
- * first call terminate().
- */
- ANDROID_API void init(const sp<GraphicBuffer>& buffer, int64_t* map, int count);
-
- /**
- * Destroys the atlas texture. This object can be
- * re-initialized after calling this method.
- *
- * After calling this method, the width, height
- * and texture are set to 0.
- */
- void terminate();
-
- /**
- * Returns the width of this atlas in pixels.
- * Can return 0 if the atlas is not initialized.
- */
- uint32_t getWidth() const {
- return mTexture ? mTexture->width() : 0;
- }
-
- /**
- * Returns the height of this atlas in pixels.
- * Can return 0 if the atlas is not initialized.
- */
- uint32_t getHeight() const {
- return mTexture ? mTexture->height() : 0;
- }
-
- /**
- * Returns the OpenGL name of the texture backing this atlas.
- * Can return 0 if the atlas is not initialized.
- */
- GLuint getTexture() const {
- return mTexture ? mTexture->id() : 0;
- }
-
- /**
- * Returns the entry in the atlas associated with the specified
- * pixelRef. If the pixelRef is not in the atlas, return NULL.
- */
- Entry* getEntry(const SkPixelRef* pixelRef) const;
-
- /**
- * Returns the texture for the atlas entry associated with the
- * specified pixelRef. If the pixelRef is not in the atlas, return NULL.
- */
- Texture* getEntryTexture(const SkPixelRef* pixelRef) const;
-
-private:
- void createEntries(Caches& caches, int64_t* map, int count);
-
- Texture* mTexture;
- Image* mImage;
-
- const bool mBlendKey;
- const bool mOpaqueKey;
-
- std::unordered_map<const SkPixelRef*, std::unique_ptr<Entry>> mEntries;
-}; // class AssetAtlas
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_ASSET_ATLAS_H
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 8b3f1722dddf..6079d5def1f8 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -35,29 +35,24 @@
namespace android {
namespace uirenderer {
-static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds, const Rect& texCoord) {
- vertices[0] = { bounds.left, bounds.top, texCoord.left, texCoord.top };
- vertices[1] = { bounds.right, bounds.top, texCoord.right, texCoord.top };
- vertices[2] = { bounds.left, bounds.bottom, texCoord.left, texCoord.bottom };
- vertices[3] = { bounds.right, bounds.bottom, texCoord.right, texCoord.bottom };
+static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds) {
+ vertices[0] = { bounds.left, bounds.top, 0, 0 };
+ vertices[1] = { bounds.right, bounds.top, 1, 0 };
+ vertices[2] = { bounds.left, bounds.bottom, 0, 1 };
+ vertices[3] = { bounds.right, bounds.bottom, 1, 1 };
}
void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
const MergedBakedOpList& opList) {
const BakedOpState& firstState = *(opList.states[0]);
- const SkBitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap;
+ Bitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap;
- AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(bitmap->pixelRef());
- Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(bitmap);
+ Texture* texture = renderer.caches().textureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
TextureVertex vertices[opList.count * 4];
- Rect texCoords(0, 0, 1, 1);
- if (entry) {
- entry->uvMapper.map(texCoords);
- }
for (size_t i = 0; i < opList.count; i++) {
const BakedOpState& state = *(opList.states[i]);
TextureVertex* rectVerts = &vertices[i * 4];
@@ -69,7 +64,7 @@ void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
// pure translate, so snap (same behavior as onBitmapOp)
opBounds.snapToPixelBoundaries();
}
- storeTexturedRect(rectVerts, opBounds, texCoords);
+ storeTexturedRect(rectVerts, opBounds);
renderer.dirtyRenderTarget(opBounds);
}
@@ -92,8 +87,6 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
const MergedBakedOpList& opList) {
const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op));
const BakedOpState& firstState = *(opList.states[0]);
- AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(
- firstOp.bitmap->pixelRef());
// Batches will usually contain a small number of items so it's
// worth performing a first iteration to count the exact number
@@ -105,7 +98,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
// TODO: cache mesh lookups
const Patch* opMesh = renderer.caches().patchCache.get(
- entry, op.bitmap->width(), op.bitmap->height(),
+ op.bitmap->width(), op.bitmap->height(),
op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
totalVertices += opMesh->verticesCount;
}
@@ -126,7 +119,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
// TODO: cache mesh lookups
const Patch* opMesh = renderer.caches().patchCache.get(
- entry, op.bitmap->width(), op.bitmap->height(),
+ op.bitmap->width(), op.bitmap->height(),
op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
@@ -172,7 +165,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
}
- Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap);
+ Texture* texture = renderer.caches().textureCache.get(firstOp.bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -299,7 +292,7 @@ static void renderText(BakedOpRenderer& renderer, const TextOp& op, const BakedO
Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
- SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
+ SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint);
TextDrawFunctor functor(&renderer, &state, renderClip,
x, y, pureTranslate, alpha, mode, op.paint);
@@ -442,7 +435,12 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op
}
void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) {
- const static UvMapper defaultUvMapper;
+ Texture* texture = renderer.caches().textureCache.get(op.bitmap);
+ if (!texture) {
+ return;
+ }
+ const AutoTexture autoCleanup(texture);
+
const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;
std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
@@ -457,9 +455,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe
colors = tempColors.get();
}
- Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef());
- const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper);
-
for (int32_t y = 0; y < op.meshHeight; y++) {
for (int32_t x = 0; x < op.meshWidth; x++) {
uint32_t i = (y * (op.meshWidth + 1) + x) * 2;
@@ -469,8 +464,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe
float v1 = float(y) / op.meshHeight;
float v2 = float(y + 1) / op.meshHeight;
- mapper.map(u1, v1, u2, v2);
-
int ax = i + (op.meshWidth + 1) * 2;
int ay = ax + 1;
int bx = i;
@@ -491,14 +484,6 @@ void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMe
}
}
- if (!texture) {
- texture = renderer.caches().textureCache.get(op.bitmap);
- if (!texture) {
- return;
- }
- }
- const AutoTexture autoCleanup(texture);
-
/*
* TODO: handle alpha_8 textures correctly by applying paint color, but *not*
* shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
@@ -543,7 +528,7 @@ void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRe
void BakedOpDispatcher::onColorOp(BakedOpRenderer& renderer, const ColorOp& op, const BakedOpState& state) {
SkPaint paint;
paint.setColor(op.color);
- paint.setXfermodeMode(op.mode);
+ paint.setBlendMode(op.mode);
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
@@ -599,12 +584,11 @@ void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op,
}
// TODO: avoid redoing the below work each frame:
- AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef());
const Patch* mesh = renderer.caches().patchCache.get(
- entry, op.bitmap->width(), op.bitmap->height(),
+ op.bitmap->width(), op.bitmap->height(),
op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
- Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
+ Texture* texture = renderer.caches().textureCache.get(op.bitmap);
if (CC_LIKELY(texture)) {
const AutoTexture autoCleanup(texture);
Glop glop;
@@ -760,7 +744,7 @@ void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPa
Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
- SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint);
+ SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint);
TextDrawFunctor functor(&renderer, &state, renderTargetClip,
0.0f, 0.0f, false, alpha, mode, op.paint);
@@ -792,11 +776,11 @@ void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const Textur
}
void renderRectForLayer(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state,
- int color, SkXfermode::Mode mode, SkColorFilter* colorFilter) {
+ int color, SkBlendMode mode, SkColorFilter* colorFilter) {
SkPaint paint;
paint.setColor(color);
- paint.setXfermodeMode(mode);
- paint.setColorFilter(colorFilter);
+ paint.setBlendMode(mode);
+ paint.setColorFilter(sk_ref_sp(colorFilter));
RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint);
BakedOpDispatcher::onRectOp(renderer, rectOp, state);
}
@@ -824,11 +808,11 @@ void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op,
if (CC_UNLIKELY(Properties::debugLayersUpdates)) {
// render debug layer highlight
renderRectForLayer(renderer, op, state,
- 0x7f00ff00, SkXfermode::Mode::kSrcOver_Mode, nullptr);
+ 0x7f00ff00, SkBlendMode::kSrcOver, nullptr);
} else if (CC_UNLIKELY(Properties::debugOverdraw)) {
// render transparent to increment overdraw for repaint area
renderRectForLayer(renderer, op, state,
- SK_ColorTRANSPARENT, SkXfermode::Mode::kSrcOver_Mode, nullptr);
+ SK_ColorTRANSPARENT, SkBlendMode::kSrcOver, nullptr);
}
}
}
@@ -845,14 +829,14 @@ void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyF
if (op.paint && op.paint->getAlpha() < 255) {
SkPaint layerPaint;
layerPaint.setAlpha(op.paint->getAlpha());
- layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
- layerPaint.setColorFilter(op.paint->getColorFilter());
+ layerPaint.setBlendMode(SkBlendMode::kDstIn);
+ layerPaint.setColorFilter(sk_ref_sp(op.paint->getColorFilter()));
RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr, &layerPaint);
BakedOpDispatcher::onRectOp(renderer, rectOp, state);
}
OffscreenBuffer& layer = **(op.layerHandle);
- auto mode = PaintUtils::getXfermodeDirect(op.paint);
+ auto mode = PaintUtils::getBlendModeDirect(op.paint);
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
.setRoundRectClipState(state.roundRectClipState)
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 6db345ac0006..e8972aab9f8f 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -181,12 +181,8 @@ void BakedOpRenderer::clearColorBuffer(const Rect& rect) {
if (!mRenderTarget.frameBufferId) mHasDrawn = true;
}
-Texture* BakedOpRenderer::getTexture(const SkBitmap* bitmap) {
- Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap->pixelRef());
- if (!texture) {
- return mCaches.textureCache.get(bitmap);
- }
- return texture;
+Texture* BakedOpRenderer::getTexture(Bitmap* bitmap) {
+ return mCaches.textureCache.get(bitmap);
}
void BakedOpRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 62bc564a4a2a..4d76a3df7a62 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -74,7 +74,7 @@ public:
void endLayer();
WARN_UNUSED_RESULT OffscreenBuffer* copyToLayer(const Rect& area);
- Texture* getTexture(const SkBitmap* bitmap);
+ Texture* getTexture(Bitmap* bitmap);
const LightInfo& getLightInfo() const { return mLightInfo; }
void renderGlop(const BakedOpState& state, const Glop& glop) {
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index a8ced9b2597b..a0366dee3218 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -17,7 +17,7 @@
#include "Caches.h"
#include "GammaFontRenderer.h"
-#include "LayerRenderer.h"
+#include "GlLayer.h"
#include "Properties.h"
#include "renderstate/RenderState.h"
#include "ShadowTessellator.h"
@@ -53,7 +53,6 @@ Caches::Caches(RenderState& renderState)
: gradientCache(mExtensions)
, patchCache(renderState)
, programCache(mExtensions)
- , dither(*this)
, mRenderState(&renderState)
, mInitialized(false) {
INIT_LOGD("Creating OpenGL renderer caches");
@@ -71,8 +70,6 @@ bool Caches::init() {
mRegionMesh = nullptr;
mProgram = nullptr;
- patchCache.init();
-
mInitialized = true;
mPixelBufferState = new PixelBufferState();
@@ -168,17 +165,17 @@ void Caches::dumpMemoryUsage(String8 &log) {
log.appendFormat("Current memory usage / total memory usage (bytes):\n");
log.appendFormat(" TextureCache %8d / %8d\n",
textureCache.getSize(), textureCache.getMaxSize());
- log.appendFormat(" LayerCache %8d / %8d (numLayers = %zu)\n",
- layerCache.getSize(), layerCache.getMaxSize(), layerCache.getCount());
if (mRenderState) {
int memused = 0;
for (std::set<Layer*>::iterator it = mRenderState->mActiveLayers.begin();
it != mRenderState->mActiveLayers.end(); it++) {
const Layer* layer = *it;
- log.appendFormat(" Layer size %dx%d; isTextureLayer()=%d; texid=%u fbo=%u; refs=%d\n",
+ LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL);
+ const GlLayer* glLayer = static_cast<const GlLayer*>(layer);
+ log.appendFormat(" GlLayer size %dx%d; texid=%u refs=%d\n",
layer->getWidth(), layer->getHeight(),
- layer->isTextureLayer(), layer->getTextureId(),
- layer->getFbo(), layer->getStrongCount());
+ glLayer->getTextureId(),
+ layer->getStrongCount());
memused += layer->getWidth() * layer->getHeight() * 4;
}
log.appendFormat(" Layers total %8d (numLayers = %zu)\n",
@@ -242,7 +239,6 @@ void Caches::flush(FlushMode mode) {
gradientCache.clear();
fontRenderer.clear();
fboCache.clear();
- dither.clear();
// fall through
case FlushMode::Moderate:
fontRenderer.flush();
@@ -251,7 +247,6 @@ void Caches::flush(FlushMode mode) {
tessellationCache.clear();
// fall through
case FlushMode::Layers:
- layerCache.clear();
renderBufferCache.clear();
break;
}
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 317c122c62b8..7c2e78c7d3b8 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -14,16 +14,12 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_CACHES_H
-#define ANDROID_HWUI_CACHES_H
+#pragma once
-#include "AssetAtlas.h"
-#include "Dither.h"
#include "Extensions.h"
#include "FboCache.h"
#include "GammaFontRenderer.h"
#include "GradientCache.h"
-#include "LayerCache.h"
#include "PatchCache.h"
#include "ProgramCache.h"
#include "PathCache.h"
@@ -132,6 +128,15 @@ public:
TextureVertex* getRegionMesh();
/**
+ * Returns the GL RGBA internal format to use for the current device
+ * If the device supports linear blending and needSRGB is true,
+ * this function returns GL_SRGB8_ALPHA8, otherwise it returns GL_RGBA
+ */
+ constexpr GLint rgbaInternalFormat(bool needSRGB = true) const {
+ return extensions().hasSRGB() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA;
+ }
+
+ /**
* Displays the memory usage of each cache and the total sum.
*/
void dumpMemoryUsage();
@@ -146,7 +151,6 @@ private:
Extensions mExtensions;
public:
TextureCache textureCache;
- LayerCache layerCache;
RenderBufferCache renderBufferCache;
GradientCache gradientCache;
PatchCache patchCache;
@@ -160,8 +164,6 @@ public:
TaskManager tasks;
- Dither dither;
-
bool gpuPixelBuffersEnabled;
// Debug methods
@@ -172,7 +174,7 @@ public:
void setProgram(const ProgramDescription& description);
void setProgram(Program* program);
- Extensions& extensions() { return mExtensions; }
+ const Extensions& extensions() const { return mExtensions; }
Program& program() { return *mProgram; }
PixelBufferState& pixelBufferState() { return *mPixelBufferState; }
TextureState& textureState() { return *mTextureState; }
@@ -205,5 +207,3 @@ private:
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_CACHES_H
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index e2149d1e4a69..9c068b080d45 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -23,8 +23,7 @@ namespace uirenderer {
CanvasState::CanvasState(CanvasStateClient& renderer)
- : mDirtyClip(false)
- , mWidth(-1)
+ : mWidth(-1)
, mHeight(-1)
, mSaveCount(1)
, mCanvas(renderer)
@@ -203,21 +202,13 @@ void CanvasState::concatMatrix(const Matrix4& matrix) {
// Clip
///////////////////////////////////////////////////////////////////////////////
-bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+bool CanvasState::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
mSnapshot->clip(Rect(left, top, right, bottom), op);
- mDirtyClip = true;
return !mSnapshot->clipIsEmpty();
}
-bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) {
+bool CanvasState::clipPath(const SkPath* path, SkClipOp op) {
mSnapshot->clipPath(*path, op);
- mDirtyClip = true;
- return !mSnapshot->clipIsEmpty();
-}
-
-bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) {
- mSnapshot->clipRegionTransformed(*region, op);
- mDirtyClip = true;
return !mSnapshot->clipIsEmpty();
}
@@ -229,22 +220,13 @@ void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline*
bool outlineIsRounded = MathUtils::isPositive(radius);
if (!outlineIsRounded || currentTransform()->isSimple()) {
// TODO: consider storing this rect separately, so that this can't be replaced with clip ops
- clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkRegion::kIntersect_Op);
+ clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkClipOp::kIntersect);
}
if (outlineIsRounded) {
setClippingRoundRect(allocator, bounds, radius, false);
}
}
-void CanvasState::setClippingRoundRect(LinearAllocator& allocator,
- const Rect& rect, float radius, bool highPriority) {
- mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority);
-}
-
-void CanvasState::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
- mSnapshot->setProjectionPathMask(allocator, path);
-}
-
///////////////////////////////////////////////////////////////////////////////
// Quick Rejection
///////////////////////////////////////////////////////////////////////////////
@@ -263,7 +245,7 @@ bool CanvasState::calculateQuickRejectForScissor(float left, float top,
float right, float bottom,
bool* clipRequired, bool* roundRectClipRequired,
bool snapOut) const {
- if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
+ if (bottom <= top || right <= left) {
return true;
}
@@ -291,7 +273,7 @@ bool CanvasState::calculateQuickRejectForScissor(float left, float top,
bool CanvasState::quickRejectConservative(float left, float top,
float right, float bottom) const {
- if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
+ if (bottom <= top || right <= left) {
return true;
}
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index 22feef523f49..b1fe09eb1aec 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_CANVAS_STATE_H
-#define ANDROID_HWUI_CANVAS_STATE_H
+#pragma once
#include "Snapshot.h"
+#include <SkClipOp.h>
#include <SkMatrix.h>
#include <SkPath.h>
#include <SkRegion.h>
@@ -62,11 +62,11 @@ public:
* Renderer interface. Drawing and recording classes that include a CanvasState will have
* different use cases:
*
- * Drawing code maintaining canvas state (i.e. OpenGLRenderer) can query attributes (such as
+ * Drawing code maintaining canvas state (e.g. FrameBuilder) can query attributes (such as
* transform) or hook into changes (e.g. save/restore) with minimal surface area for manipulating
* the stack itself.
*
- * Recording code maintaining canvas state (i.e. DisplayListCanvas) can both record and pass
+ * Recording code maintaining canvas state (e.g. RecordingCanvas) can both record and pass
* through state operations to CanvasState, so that not only will querying operations work
* (getClip/Matrix), but so that quickRejection can also be used.
*/
@@ -122,9 +122,8 @@ public:
bool quickRejectConservative(float left, float top, float right, float bottom) const;
- bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
- bool clipPath(const SkPath* path, SkRegion::Op op);
- bool clipRegion(const SkRegion* region, SkRegion::Op op);
+ bool clipRect(float left, float top, float right, float bottom, SkClipOp op);
+ bool clipPath(const SkPath* path, SkClipOp op);
/**
* Sets a "clipping outline", which is independent from the regular clip.
@@ -134,8 +133,12 @@ public:
*/
void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
void setClippingRoundRect(LinearAllocator& allocator,
- const Rect& rect, float radius, bool highPriority = true);
- void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
+ const Rect& rect, float radius, bool highPriority = true) {
+ mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority);
+ }
+ void setProjectionPathMask(const SkPath* path) {
+ mSnapshot->setProjectionPathMask(path);
+ }
/**
* Returns true if drawing in the rectangle (left, top, right, bottom)
@@ -145,19 +148,12 @@ public:
bool calculateQuickRejectForScissor(float left, float top, float right, float bottom,
bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const;
- void setDirtyClip(bool opaque) { mDirtyClip = opaque; }
- bool getDirtyClip() const { return mDirtyClip; }
-
void scaleAlpha(float alpha) { mSnapshot->alpha *= alpha; }
- void setEmpty(bool value) { mSnapshot->empty = value; }
- void setInvisible(bool value) { mSnapshot->invisible = value; }
inline const mat4* currentTransform() const { return currentSnapshot()->transform; }
inline const Rect& currentRenderTargetClip() const { return currentSnapshot()->getRenderTargetClip(); }
- inline Region* currentRegion() const { return currentSnapshot()->region; }
inline int currentFlags() const { return currentSnapshot()->flags; }
const Vector3& currentLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); }
- inline bool currentlyIgnored() const { return currentSnapshot()->isIgnored(); }
int getViewportWidth() const { return currentSnapshot()->getViewportWidth(); }
int getViewportHeight() const { return currentSnapshot()->getViewportHeight(); }
int getWidth() const { return mWidth; }
@@ -173,10 +169,6 @@ private:
void freeSnapshot(Snapshot* snapshot);
void freeAllSnapshots();
- /// indicates that the clip has been changed since the last time it was consumed
- // TODO: delete when switching to HWUI_NEW_OPS
- bool mDirtyClip;
-
/// Dimensions of the drawing surface
int mWidth, mHeight;
@@ -201,5 +193,3 @@ private:
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_CANVAS_STATE_H
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 2e561601d452..cf5751606d12 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -146,7 +146,6 @@ public:
void setClip(float left, float top, float right, float bottom);
void clipRectWithTransform(const Rect& r, const mat4* transform,
SkRegion::Op op);
- void clipRegion(const SkRegion& region, SkRegion::Op op);
void clipPathWithTransform(const SkPath& path, const mat4* transform,
SkRegion::Op op);
@@ -195,6 +194,7 @@ private:
void regionModeClipRectWithTransform(const Rect& r, const mat4* transform,
SkRegion::Op op);
+ void clipRegion(const SkRegion& region, SkRegion::Op op);
void ensureClipRegion();
void onClipRegionUpdated();
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 748edef730b7..e29699d0faf4 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -98,6 +98,9 @@
// Turn on to enable debugging shadow
#define DEBUG_SHADOW 0
+// Turn on to enable debugging vector drawable
+#define DEBUG_VECTOR_DRAWABLE 0
+
#if DEBUG_INIT
#define INIT_LOGD(...) ALOGD(__VA_ARGS__)
#else
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
deleted file mode 100644
index 689179dd8fb4..000000000000
--- a/libs/hwui/DeferredDisplayList.cpp
+++ /dev/null
@@ -1,686 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/Trace.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-
-#include "Caches.h"
-#include "Debug.h"
-#include "DeferredDisplayList.h"
-#include "DisplayListOp.h"
-#include "OpenGLRenderer.h"
-#include "Properties.h"
-#include "utils/MathUtils.h"
-
-#if DEBUG_DEFER
- #define DEFER_LOGD(...) ALOGD(__VA_ARGS__)
-#else
- #define DEFER_LOGD(...)
-#endif
-
-namespace android {
-namespace uirenderer {
-
-// Depth of the save stack at the beginning of batch playback at flush time
-#define FLUSH_SAVE_STACK_DEPTH 2
-
-#define DEBUG_COLOR_BARRIER 0x1f000000
-#define DEBUG_COLOR_MERGEDBATCH 0x5f7f7fff
-#define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f
-
-static bool avoidOverdraw() {
- // Don't avoid overdraw when visualizing it, since that makes it harder to
- // debug where it's coming from, and when the problem occurs.
- return !Properties::debugOverdraw;
-};
-
-/////////////////////////////////////////////////////////////////////////////////
-// Operation Batches
-/////////////////////////////////////////////////////////////////////////////////
-
-class Batch {
-public:
- virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0;
- virtual ~Batch() {}
- virtual bool purelyDrawBatch() { return false; }
- virtual bool coversBounds(const Rect& bounds) { return false; }
-};
-
-class DrawBatch : public Batch {
-public:
- explicit DrawBatch(const DeferInfo& deferInfo) : mAllOpsOpaque(true),
- mBatchId(deferInfo.batchId), mMergeId(deferInfo.mergeId) {
- mOps.clear();
- }
-
- virtual ~DrawBatch() { mOps.clear(); }
-
- virtual void add(DrawOp* op, const DeferredDisplayState* state, bool opaqueOverBounds) {
- // NOTE: ignore empty bounds special case, since we don't merge across those ops
- mBounds.unionWith(state->mBounds);
- mAllOpsOpaque &= opaqueOverBounds;
- mOps.push_back(OpStatePair(op, state));
- }
-
- bool intersects(const Rect& rect) {
- if (!rect.intersects(mBounds)) return false;
-
- for (unsigned int i = 0; i < mOps.size(); i++) {
- if (rect.intersects(mOps[i].state->mBounds)) {
-#if DEBUG_DEFER
- DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i].op,
- mOps[i].state->mBounds.left, mOps[i].state->mBounds.top,
- mOps[i].state->mBounds.right, mOps[i].state->mBounds.bottom);
- mOps[i].op->output(2);
-#endif
- return true;
- }
- }
- return false;
- }
-
- virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
- DEFER_LOGD("%d replaying DrawBatch %p, with %d ops (batch id %x, merge id %p)",
- index, this, mOps.size(), getBatchId(), getMergeId());
-
- for (unsigned int i = 0; i < mOps.size(); i++) {
- DrawOp* op = mOps[i].op;
- const DeferredDisplayState* state = mOps[i].state;
- renderer.restoreDisplayState(*state);
-
-#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
- renderer.eventMark(op->name());
-#endif
- op->applyDraw(renderer, dirty);
-
-#if DEBUG_MERGE_BEHAVIOR
- const Rect& bounds = state->mBounds;
- int batchColor = 0x1f000000;
- if (getBatchId() & 0x1) batchColor |= 0x0000ff;
- if (getBatchId() & 0x2) batchColor |= 0x00ff00;
- if (getBatchId() & 0x4) batchColor |= 0xff0000;
- renderer.drawScreenSpaceColorRect(bounds.left, bounds.top, bounds.right, bounds.bottom,
- batchColor);
-#endif
- }
- }
-
- virtual bool purelyDrawBatch() override { return true; }
-
- virtual bool coversBounds(const Rect& bounds) override {
- if (CC_LIKELY(!mAllOpsOpaque || !mBounds.contains(bounds) || count() == 1)) return false;
-
- Region uncovered(android::Rect(bounds.left, bounds.top, bounds.right, bounds.bottom));
- for (unsigned int i = 0; i < mOps.size(); i++) {
- const Rect &r = mOps[i].state->mBounds;
- uncovered.subtractSelf(android::Rect(r.left, r.top, r.right, r.bottom));
- }
- return uncovered.isEmpty();
- }
-
- inline int getBatchId() const { return mBatchId; }
- inline mergeid_t getMergeId() const { return mMergeId; }
- inline int count() const { return mOps.size(); }
-
-protected:
- std::vector<OpStatePair> mOps;
- Rect mBounds; // union of bounds of contained ops
-private:
- bool mAllOpsOpaque;
- int mBatchId;
- mergeid_t mMergeId;
-};
-
-class MergingDrawBatch : public DrawBatch {
-public:
- MergingDrawBatch(DeferInfo& deferInfo, int width, int height) :
- DrawBatch(deferInfo), mClipRect(width, height),
- mClipSideFlags(kClipSide_None) {}
-
- /*
- * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
- * and clip side flags. Positive bounds delta means new bounds fit in old.
- */
- static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
- float boundsDelta) {
- bool currentClipExists = currentFlags & side;
- bool newClipExists = newFlags & side;
-
- // if current is clipped, we must be able to fit new bounds in current
- if (boundsDelta > 0 && currentClipExists) return false;
-
- // if new is clipped, we must be able to fit current bounds in new
- if (boundsDelta < 0 && newClipExists) return false;
-
- return true;
- }
-
- /*
- * Checks if a (mergeable) op can be merged into this batch
- *
- * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
- * important to consider all paint attributes used in the draw calls in deciding both a) if an
- * op tries to merge at all, and b) if the op can merge with another set of ops
- *
- * False positives can lead to information from the paints of subsequent merged operations being
- * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
- */
- bool canMergeWith(const DrawOp* op, const DeferredDisplayState* state) {
- bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text ||
- getBatchId() == DeferredDisplayList::kOpBatch_ColorText;
-
- // Overlapping other operations is only allowed for text without shadow. For other ops,
- // multiDraw isn't guaranteed to overdraw correctly
- if (!isTextBatch || op->hasTextShadow()) {
- if (intersects(state->mBounds)) return false;
- }
- const DeferredDisplayState* lhs = state;
- const DeferredDisplayState* rhs = mOps[0].state;
-
- if (!MathUtils::areEqual(lhs->mAlpha, rhs->mAlpha)) return false;
-
- // Identical round rect clip state means both ops will clip in the same way, or not at all.
- // As the state objects are const, we can compare their pointers to determine mergeability
- if (lhs->mRoundRectClipState != rhs->mRoundRectClipState) return false;
- if (lhs->mProjectionPathMask != rhs->mProjectionPathMask) return false;
-
- /* Clipping compatibility check
- *
- * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
- * clip for that side.
- */
- const int currentFlags = mClipSideFlags;
- const int newFlags = state->mClipSideFlags;
- if (currentFlags != kClipSide_None || newFlags != kClipSide_None) {
- const Rect& opBounds = state->mBounds;
- float boundsDelta = mBounds.left - opBounds.left;
- if (!checkSide(currentFlags, newFlags, kClipSide_Left, boundsDelta)) return false;
- boundsDelta = mBounds.top - opBounds.top;
- if (!checkSide(currentFlags, newFlags, kClipSide_Top, boundsDelta)) return false;
-
- // right and bottom delta calculation reversed to account for direction
- boundsDelta = opBounds.right - mBounds.right;
- if (!checkSide(currentFlags, newFlags, kClipSide_Right, boundsDelta)) return false;
- boundsDelta = opBounds.bottom - mBounds.bottom;
- if (!checkSide(currentFlags, newFlags, kClipSide_Bottom, boundsDelta)) return false;
- }
-
- // if paints are equal, then modifiers + paint attribs don't need to be compared
- if (op->mPaint == mOps[0].op->mPaint) return true;
-
- if (PaintUtils::getAlphaDirect(op->mPaint)
- != PaintUtils::getAlphaDirect(mOps[0].op->mPaint)) {
- return false;
- }
-
- if (op->mPaint && mOps[0].op->mPaint &&
- op->mPaint->getColorFilter() != mOps[0].op->mPaint->getColorFilter()) {
- return false;
- }
-
- if (op->mPaint && mOps[0].op->mPaint &&
- op->mPaint->getShader() != mOps[0].op->mPaint->getShader()) {
- return false;
- }
-
- return true;
- }
-
- virtual void add(DrawOp* op, const DeferredDisplayState* state,
- bool opaqueOverBounds) override {
- DrawBatch::add(op, state, opaqueOverBounds);
-
- const int newClipSideFlags = state->mClipSideFlags;
- mClipSideFlags |= newClipSideFlags;
- if (newClipSideFlags & kClipSide_Left) mClipRect.left = state->mClip.left;
- if (newClipSideFlags & kClipSide_Top) mClipRect.top = state->mClip.top;
- if (newClipSideFlags & kClipSide_Right) mClipRect.right = state->mClip.right;
- if (newClipSideFlags & kClipSide_Bottom) mClipRect.bottom = state->mClip.bottom;
- }
-
- virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
- DEFER_LOGD("%d replaying MergingDrawBatch %p, with %d ops,"
- " clip flags %x (batch id %x, merge id %p)",
- index, this, mOps.size(), mClipSideFlags, getBatchId(), getMergeId());
- if (mOps.size() == 1) {
- DrawBatch::replay(renderer, dirty, -1);
- return;
- }
-
- // clipping in the merged case is done ahead of time since all ops share the clip (if any)
- renderer.setupMergedMultiDraw(mClipSideFlags ? &mClipRect : nullptr);
-
- DrawOp* op = mOps[0].op;
-#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
- renderer.eventMark("multiDraw");
- renderer.eventMark(op->name());
-#endif
- op->multiDraw(renderer, dirty, mOps, mBounds);
-
-#if DEBUG_MERGE_BEHAVIOR
- renderer.drawScreenSpaceColorRect(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom,
- DEBUG_COLOR_MERGEDBATCH);
-#endif
- }
-
-private:
- /*
- * Contains the effective clip rect shared by all merged ops. Initialized to the layer viewport,
- * it will shrink if an op must be clipped on a certain side. The clipped sides are reflected in
- * mClipSideFlags.
- */
- Rect mClipRect;
- int mClipSideFlags;
-};
-
-class StateOpBatch : public Batch {
-public:
- // creates a single operation batch
- StateOpBatch(const StateOp* op, const DeferredDisplayState* state) : mOp(op), mState(state) {}
-
- virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
- DEFER_LOGD("replaying state op batch %p", this);
- renderer.restoreDisplayState(*mState);
-
- // use invalid save count because it won't be used at flush time - RestoreToCountOp is the
- // only one to use it, and we don't use that class at flush time, instead calling
- // renderer.restoreToCount directly
- int saveCount = -1;
- mOp->applyState(renderer, saveCount);
- }
-
-private:
- const StateOp* mOp;
- const DeferredDisplayState* mState;
-};
-
-class RestoreToCountBatch : public Batch {
-public:
- RestoreToCountBatch(const StateOp* op, const DeferredDisplayState* state, int restoreCount) :
- mState(state), mRestoreCount(restoreCount) {}
-
- virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) override {
- DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount);
-
- renderer.restoreDisplayState(*mState);
- renderer.restoreToCount(mRestoreCount);
- }
-
-private:
- // we use the state storage for the RestoreToCountOp, but don't replay the op itself
- const DeferredDisplayState* mState;
-
- /*
- * The count used here represents the flush() time saveCount. This is as opposed to the
- * DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and
- * (saveCount + mCount) respectively). Since the count is different from the original
- * RestoreToCountOp, we don't store a pointer to the op, as elsewhere.
- */
- const int mRestoreCount;
-};
-
-#if DEBUG_MERGE_BEHAVIOR
-class BarrierDebugBatch : public Batch {
- virtual void replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
- renderer.drawScreenSpaceColorRect(0, 0, 10000, 10000, DEBUG_COLOR_BARRIER);
- }
-};
-#endif
-
-/////////////////////////////////////////////////////////////////////////////////
-// DeferredDisplayList
-/////////////////////////////////////////////////////////////////////////////////
-
-void DeferredDisplayList::resetBatchingState() {
- for (int i = 0; i < kOpBatch_Count; i++) {
- mBatchLookup[i] = nullptr;
- mMergingBatches[i].clear();
- }
-#if DEBUG_MERGE_BEHAVIOR
- if (mBatches.size() != 0) {
- mBatches.add(new BarrierDebugBatch());
- }
-#endif
- mEarliestBatchIndex = mBatches.size();
-}
-
-void DeferredDisplayList::clear() {
- resetBatchingState();
- mComplexClipStackStart = -1;
-
- for (unsigned int i = 0; i < mBatches.size(); i++) {
- delete mBatches[i];
- }
- mBatches.clear();
- mSaveStack.clear();
- mEarliestBatchIndex = 0;
- mEarliestUnclearedIndex = 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////
-// Operation adding
-/////////////////////////////////////////////////////////////////////////////////
-
-int DeferredDisplayList::getStateOpDeferFlags() const {
- // For both clipOp and save(Layer)Op, we don't want to save drawing info, and only want to save
- // the clip if we aren't recording a complex clip (and can thus trust it to be a rect)
- return recordingComplexClip() ? 0 : kStateDeferFlag_Clip;
-}
-
-int DeferredDisplayList::getDrawOpDeferFlags() const {
- return kStateDeferFlag_Draw | getStateOpDeferFlags();
-}
-
-/**
- * When an clipping operation occurs that could cause a complex clip, record the operation and all
- * subsequent clipOps, save/restores (if the clip flag is set). During a flush, instead of loading
- * the clip from deferred state, we play back all of the relevant state operations that generated
- * the complex clip.
- *
- * Note that we don't need to record the associated restore operation, since operations at defer
- * time record whether they should store the renderer's current clip
- */
-void DeferredDisplayList::addClip(OpenGLRenderer& renderer, ClipOp* op) {
- if (recordingComplexClip() || op->canCauseComplexClip() || !renderer.hasRectToRectTransform()) {
- DEFER_LOGD("%p Received complex clip operation %p", this, op);
-
- // NOTE: defer clip op before setting mComplexClipStackStart so previous clip is recorded
- storeStateOpBarrier(renderer, op);
-
- if (!recordingComplexClip()) {
- mComplexClipStackStart = renderer.getSaveCount() - 1;
- DEFER_LOGD(" Starting complex clip region, start is %d", mComplexClipStackStart);
- }
- }
-}
-
-/**
- * For now, we record save layer operations as barriers in the batch list, preventing drawing
- * operations from reordering around the saveLayer and it's associated restore()
- *
- * In the future, we should send saveLayer commands (if they can be played out of order) and their
- * contained drawing operations to a seperate list of batches, so that they may draw at the
- * beginning of the frame. This would avoid targetting and removing an FBO in the middle of a frame.
- *
- * saveLayer operations should be pulled to the beginning of the frame if the canvas doesn't have a
- * complex clip, and if the flags (SaveFlags::Clip & SaveFlags::ClipToLayer) are set.
- */
-void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer,
- SaveLayerOp* op, int newSaveCount) {
- DEFER_LOGD("%p adding saveLayerOp %p, flags %x, new count %d",
- this, op, op->getFlags(), newSaveCount);
-
- storeStateOpBarrier(renderer, op);
- mSaveStack.push_back(newSaveCount);
-}
-
-/**
- * Takes save op and it's return value - the new save count - and stores it into the stream as a
- * barrier if it's needed to properly modify a complex clip
- */
-void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount) {
- int saveFlags = op->getFlags();
- DEFER_LOGD("%p adding saveOp %p, flags %x, new count %d", this, op, saveFlags, newSaveCount);
-
- if (recordingComplexClip() && (saveFlags & SaveFlags::Clip)) {
- // store and replay the save operation, as it may be needed to correctly playback the clip
- DEFER_LOGD(" adding save barrier with new save count %d", newSaveCount);
- storeStateOpBarrier(renderer, op);
- mSaveStack.push_back(newSaveCount);
- }
-}
-
-/**
- * saveLayer() commands must be associated with a restoreToCount batch that will clean up and draw
- * the layer in the deferred list
- *
- * other save() commands which occur as children of a snapshot with complex clip will be deferred,
- * and must be restored
- *
- * Either will act as a barrier to draw operation reordering, as we want to play back layer
- * save/restore and complex canvas modifications (including save/restore) in order.
- */
-void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* op,
- int newSaveCount) {
- DEFER_LOGD("%p addRestoreToCount %d", this, newSaveCount);
-
- if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) {
- mComplexClipStackStart = -1;
- resetBatchingState();
- }
-
- if (mSaveStack.empty() || newSaveCount > mSaveStack.back()) {
- return;
- }
-
- while (!mSaveStack.empty() && mSaveStack.back() >= newSaveCount) mSaveStack.pop_back();
-
- storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + FLUSH_SAVE_STACK_DEPTH);
-}
-
-void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
- /* 1: op calculates local bounds */
- DeferredDisplayState* const state = createState();
- if (op->getLocalBounds(state->mBounds)) {
- if (state->mBounds.isEmpty()) {
- // valid empty bounds, don't bother deferring
- tryRecycleState(state);
- return;
- }
- } else {
- state->mBounds.setEmpty();
- }
-
- /* 2: renderer calculates global bounds + stores state */
- if (renderer.storeDisplayState(*state, getDrawOpDeferFlags())) {
- tryRecycleState(state);
- return; // quick rejected
- }
-
- /* 3: ask op for defer info, given renderer state */
- DeferInfo deferInfo;
- op->onDefer(renderer, deferInfo, *state);
-
- // complex clip has a complex set of expectations on the renderer state - for now, avoid taking
- // the merge path in those cases
- deferInfo.mergeable &= !recordingComplexClip();
- deferInfo.opaqueOverBounds &= !recordingComplexClip()
- && mSaveStack.empty()
- && !state->mRoundRectClipState;
-
- if (CC_LIKELY(avoidOverdraw()) && mBatches.size() &&
- state->mClipSideFlags != kClipSide_ConservativeFull &&
- deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) {
- // avoid overdraw by resetting drawing state + discarding drawing ops
- discardDrawingBatches(mBatches.size() - 1);
- resetBatchingState();
- }
-
- if (CC_UNLIKELY(Properties::drawReorderDisabled)) {
- // TODO: elegant way to reuse batches?
- DrawBatch* b = new DrawBatch(deferInfo);
- b->add(op, state, deferInfo.opaqueOverBounds);
- mBatches.push_back(b);
- return;
- }
-
- // find the latest batch of the new op's type, and try to merge the new op into it
- DrawBatch* targetBatch = nullptr;
-
- // insertion point of a new batch, will hopefully be immediately after similar batch
- // (eventually, should be similar shader)
- int insertBatchIndex = mBatches.size();
- if (!mBatches.empty()) {
- if (state->mBounds.isEmpty()) {
- // don't know the bounds for op, so create new batch and start from scratch on next op
- DrawBatch* b = new DrawBatch(deferInfo);
- b->add(op, state, deferInfo.opaqueOverBounds);
- mBatches.push_back(b);
- resetBatchingState();
-#if DEBUG_DEFER
- DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches");
- op->output(2);
-#endif
- return;
- }
-
- if (deferInfo.mergeable) {
- // Try to merge with any existing batch with same mergeId.
- std::unordered_map<mergeid_t, DrawBatch*>& mergingBatch
- = mMergingBatches[deferInfo.batchId];
- auto getResult = mergingBatch.find(deferInfo.mergeId);
- if (getResult != mergingBatch.end()) {
- targetBatch = getResult->second;
- if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
- targetBatch = nullptr;
- }
- }
- } else {
- // join with similar, non-merging batch
- targetBatch = (DrawBatch*)mBatchLookup[deferInfo.batchId];
- }
-
- if (targetBatch || deferInfo.mergeable) {
- // iterate back toward target to see if anything drawn since should overlap the new op
- // if no target, merging ops still interate to find similar batch to insert after
- for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
- DrawBatch* overBatch = (DrawBatch*)mBatches[i];
-
- if (overBatch == targetBatch) break;
-
- // TODO: also consider shader shared between batch types
- if (deferInfo.batchId == overBatch->getBatchId()) {
- insertBatchIndex = i + 1;
- if (!targetBatch) break; // found insert position, quit
- }
-
- if (overBatch->intersects(state->mBounds)) {
- // NOTE: it may be possible to optimize for special cases where two operations
- // of the same batch/paint could swap order, such as with a non-mergeable
- // (clipped) and a mergeable text operation
- targetBatch = nullptr;
-#if DEBUG_DEFER
- DEFER_LOGD("op couldn't join batch %p, was intersected by batch %d",
- targetBatch, i);
- op->output(2);
-#endif
- break;
- }
- }
- }
- }
-
- if (!targetBatch) {
- if (deferInfo.mergeable) {
- targetBatch = new MergingDrawBatch(deferInfo,
- renderer.getViewportWidth(), renderer.getViewportHeight());
- mMergingBatches[deferInfo.batchId].insert(
- std::make_pair(deferInfo.mergeId, targetBatch));
- } else {
- targetBatch = new DrawBatch(deferInfo);
- mBatchLookup[deferInfo.batchId] = targetBatch;
- }
-
- DEFER_LOGD("creating %singBatch %p, bid %x, at %d",
- deferInfo.mergeable ? "Merg" : "Draw",
- targetBatch, deferInfo.batchId, insertBatchIndex);
- mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
- }
-
- targetBatch->add(op, state, deferInfo.opaqueOverBounds);
-}
-
-void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) {
- DEFER_LOGD("%p adding state op barrier at pos %d", this, mBatches.size());
-
- DeferredDisplayState* state = createState();
- renderer.storeDisplayState(*state, getStateOpDeferFlags());
- mBatches.push_back(new StateOpBatch(op, state));
- resetBatchingState();
-}
-
-void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op,
- int newSaveCount) {
- DEFER_LOGD("%p adding restore to count %d barrier, pos %d",
- this, newSaveCount, mBatches.size());
-
- // store displayState for the restore operation, as it may be associated with a saveLayer that
- // doesn't have SaveFlags::Clip set
- DeferredDisplayState* state = createState();
- renderer.storeDisplayState(*state, getStateOpDeferFlags());
- mBatches.push_back(new RestoreToCountBatch(op, state, newSaveCount));
- resetBatchingState();
-}
-
-/////////////////////////////////////////////////////////////////////////////////
-// Replay / flush
-/////////////////////////////////////////////////////////////////////////////////
-
-static void replayBatchList(const std::vector<Batch*>& batchList,
- OpenGLRenderer& renderer, Rect& dirty) {
-
- for (unsigned int i = 0; i < batchList.size(); i++) {
- if (batchList[i]) {
- batchList[i]->replay(renderer, dirty, i);
- }
- }
- DEFER_LOGD("--flushed, drew %d batches", batchList.size());
-}
-
-void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
- ATRACE_NAME("flush drawing commands");
- Caches::getInstance().fontRenderer.endPrecaching();
-
- if (isEmpty()) return; // nothing to flush
- renderer.restoreToCount(1);
-
- DEFER_LOGD("--flushing");
- renderer.eventMark("Flush");
-
- // save and restore so that reordering doesn't affect final state
- renderer.save(SaveFlags::MatrixClip);
-
- if (CC_LIKELY(avoidOverdraw())) {
- for (unsigned int i = 1; i < mBatches.size(); i++) {
- if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
- discardDrawingBatches(i - 1);
- }
- }
- }
- // NOTE: depth of the save stack at this point, before playback, should be reflected in
- // FLUSH_SAVE_STACK_DEPTH, so that save/restores match up correctly
- replayBatchList(mBatches, renderer, dirty);
-
- renderer.restoreToCount(1);
-
- DEFER_LOGD("--flush complete, returning %x", status);
- clear();
-}
-
-void DeferredDisplayList::discardDrawingBatches(const unsigned int maxIndex) {
- for (unsigned int i = mEarliestUnclearedIndex; i <= maxIndex; i++) {
- // leave deferred state ops alone for simplicity (empty save restore pairs may now exist)
- if (mBatches[i] && mBatches[i]->purelyDrawBatch()) {
- delete mBatches[i];
- mBatches[i] = nullptr;
- }
- }
- mEarliestUnclearedIndex = maxIndex + 1;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
deleted file mode 100644
index 15703ea3feef..000000000000
--- a/libs/hwui/DeferredDisplayList.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
-#define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
-
-#include <unordered_map>
-
-#include <utils/Errors.h>
-#include <utils/LinearAllocator.h>
-
-#include "Matrix.h"
-#include "OpenGLRenderer.h"
-#include "Rect.h"
-
-#include <vector>
-
-class SkBitmap;
-
-namespace android {
-namespace uirenderer {
-
-class ClipOp;
-class DrawOp;
-class SaveOp;
-class SaveLayerOp;
-class StateOp;
-
-class DeferredDisplayState;
-
-class Batch;
-class DrawBatch;
-class MergingDrawBatch;
-
-typedef const void* mergeid_t;
-
-class DeferredDisplayState {
-public:
- // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped
- Rect mBounds;
-
- // the below are set and used by the OpenGLRenderer at record and deferred playback
- bool mClipValid;
- Rect mClip;
- int mClipSideFlags; // specifies which sides of the bounds are clipped, unclipped if cleared
- mat4 mMatrix;
- float mAlpha;
- const RoundRectClipState* mRoundRectClipState;
- const ProjectionPathMask* mProjectionPathMask;
-};
-
-class OpStatePair {
-public:
- OpStatePair()
- : op(nullptr), state(nullptr) {}
- OpStatePair(DrawOp* newOp, const DeferredDisplayState* newState)
- : op(newOp), state(newState) {}
- OpStatePair(const OpStatePair& other)
- : op(other.op), state(other.state) {}
- DrawOp* op;
- const DeferredDisplayState* state;
-};
-
-class DeferredDisplayList {
- friend struct DeferStateStruct; // used to give access to allocator
-public:
- explicit DeferredDisplayList(const Rect& bounds)
- : mBounds(bounds) {
- clear();
- }
- ~DeferredDisplayList() { clear(); }
-
- enum OpBatchId {
- kOpBatch_None = 0, // Don't batch
- kOpBatch_Bitmap,
- kOpBatch_Patch,
- kOpBatch_AlphaVertices,
- kOpBatch_Vertices,
- kOpBatch_AlphaMaskTexture,
- kOpBatch_Text,
- kOpBatch_ColorText,
-
- kOpBatch_Count, // Add other batch ids before this
- };
-
- bool isEmpty() { return mBatches.empty(); }
-
- /**
- * Plays back all of the draw ops recorded into batches to the renderer.
- * Adjusts the state of the renderer as necessary, and restores it when complete
- */
- void flush(OpenGLRenderer& renderer, Rect& dirty);
-
- void addClip(OpenGLRenderer& renderer, ClipOp* op);
- void addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount);
- void addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount);
- void addRestoreToCount(OpenGLRenderer& renderer, StateOp* op, int newSaveCount);
-
- /**
- * Add a draw op into the DeferredDisplayList, reordering as needed (for performance) if
- * disallowReorder is false, respecting draw order when overlaps occur.
- */
- void addDrawOp(OpenGLRenderer& renderer, DrawOp* op);
-
-private:
- DeferredDisplayList(const DeferredDisplayList& other); // disallow copy
-
- DeferredDisplayState* createState() {
- return mAllocator.create_trivial<DeferredDisplayState>();
- }
-
- void tryRecycleState(DeferredDisplayState* state) {
- mAllocator.rewindIfLastAlloc(state);
- }
-
- /**
- * Resets the batching back-pointers, creating a barrier in the operation stream so that no ops
- * added in the future will be inserted into a batch that already exist.
- */
- void resetBatchingState();
-
- void clear();
-
- void storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op);
- void storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op, int newSaveCount);
-
- bool recordingComplexClip() const { return mComplexClipStackStart >= 0; }
-
- int getStateOpDeferFlags() const;
- int getDrawOpDeferFlags() const;
-
- void discardDrawingBatches(const unsigned int maxIndex);
-
- // layer space bounds of rendering
- Rect mBounds;
-
- /**
- * At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so
- * that when an associated restoreToCount is deferred, it can be recorded as a
- * RestoreToCountBatch
- */
- std::vector<int> mSaveStack;
- int mComplexClipStackStart;
-
- std::vector<Batch*> mBatches;
-
- // Maps batch ids to the most recent *non-merging* batch of that id
- Batch* mBatchLookup[kOpBatch_Count];
-
- // Points to the index after the most recent barrier
- int mEarliestBatchIndex;
-
- // Points to the first index that may contain a pure drawing batch
- int mEarliestUnclearedIndex;
-
- /**
- * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
- * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
- * collide, which avoids the need to resolve mergeid collisions.
- */
- std::unordered_map<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
-
- LinearAllocator mAllocator;
-};
-
-/**
- * Struct containing information that instructs the defer
- */
-struct DeferInfo {
-public:
- DeferInfo() :
- batchId(DeferredDisplayList::kOpBatch_None),
- mergeId((mergeid_t) -1),
- mergeable(false),
- opaqueOverBounds(false) {
- };
-
- int batchId;
- mergeid_t mergeId;
- bool mergeable;
- bool opaqueOverBounds; // opaque over bounds in DeferredDisplayState - can skip ops below
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index f833a5405a5c..0ae50e96fc39 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -15,11 +15,11 @@
*/
#include "DeferredLayerUpdater.h"
-#include "OpenGLRenderer.h"
-
-#include "LayerRenderer.h"
+#include "GlLayer.h"
+#include "VkLayer.h"
#include "renderthread/EglManager.h"
#include "renderthread/RenderTask.h"
+#include "utils/PaintUtils.h"
namespace android {
namespace uirenderer {
@@ -29,10 +29,9 @@ DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer)
, mTransform(nullptr)
, mNeedsGLContextAttach(false)
, mUpdateTexImage(false)
- , mLayer(layer)
- , mCaches(Caches::getInstance()) {
- mWidth = mLayer->layer.getWidth();
- mHeight = mLayer->layer.getHeight();
+ , mLayer(layer) {
+ mWidth = mLayer->getWidth();
+ mHeight = mLayer->getHeight();
mBlend = mLayer->isBlend();
mColorFilter = SkSafeRef(mLayer->getColorFilter());
mAlpha = mLayer->getAlpha();
@@ -48,24 +47,33 @@ DeferredLayerUpdater::~DeferredLayerUpdater() {
void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
mAlpha = PaintUtils::getAlphaDirect(paint);
- mMode = PaintUtils::getXfermodeDirect(paint);
+ mMode = PaintUtils::getBlendModeDirect(paint);
SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
SkRefCnt_SafeAssign(mColorFilter, colorFilter);
}
void DeferredLayerUpdater::apply() {
- // These properties are applied the same to both layer types
mLayer->setColorFilter(mColorFilter);
mLayer->setAlpha(mAlpha, mMode);
if (mSurfaceTexture.get()) {
- if (mNeedsGLContextAttach) {
- mNeedsGLContextAttach = false;
- mSurfaceTexture->attachToContext(mLayer->getTextureId());
- }
- if (mUpdateTexImage) {
- mUpdateTexImage = false;
- doUpdateTexImage();
+ if (mLayer->getApi() == Layer::Api::Vulkan) {
+ if (mUpdateTexImage) {
+ mUpdateTexImage = false;
+ doUpdateVkTexImage();
+ }
+ } else {
+ LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL,
+ "apply surfaceTexture with non GL backend %x, GL %x, VK %x",
+ mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan);
+ if (mNeedsGLContextAttach) {
+ mNeedsGLContextAttach = false;
+ mSurfaceTexture->attachToContext(static_cast<GlLayer*>(mLayer)->getTextureId());
+ }
+ if (mUpdateTexImage) {
+ mUpdateTexImage = false;
+ doUpdateTexImage();
+ }
}
if (mTransform) {
mLayer->getTransform().load(*mTransform);
@@ -75,6 +83,9 @@ void DeferredLayerUpdater::apply() {
}
void DeferredLayerUpdater::doUpdateTexImage() {
+ LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL,
+ "doUpdateTexImage non GL backend %x, GL %x, VK %x",
+ mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan);
if (mSurfaceTexture->updateTexImage() == NO_ERROR) {
float transform[16];
@@ -110,20 +121,51 @@ void DeferredLayerUpdater::doUpdateTexImage() {
LOG_ALWAYS_FATAL_IF(renderTarget != GL_TEXTURE_2D && renderTarget != GL_TEXTURE_EXTERNAL_OES,
"doUpdateTexImage target %x, 2d %x, EXT %x",
renderTarget, GL_TEXTURE_2D, GL_TEXTURE_EXTERNAL_OES);
- LayerRenderer::updateTextureLayer(mLayer, mWidth, mHeight,
- !mBlend, forceFilter, renderTarget, transform);
+ updateLayer(forceFilter, renderTarget, transform);
+ }
+}
+
+void DeferredLayerUpdater::doUpdateVkTexImage() {
+ LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::Vulkan,
+ "updateLayer non Vulkan backend %x, GL %x, VK %x",
+ mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan);
+
+ static const mat4 identityMatrix;
+ updateLayer(false, GL_NONE, identityMatrix.data);
+
+ VkLayer* vkLayer = static_cast<VkLayer*>(mLayer);
+ vkLayer->updateTexture();
+}
+
+void DeferredLayerUpdater::updateLayer(bool forceFilter, GLenum renderTarget,
+ const float* textureTransform) {
+ mLayer->setBlend(mBlend);
+ mLayer->setForceFilter(forceFilter);
+ mLayer->setSize(mWidth, mHeight);
+ mLayer->getTexTransform().load(textureTransform);
+
+ if (mLayer->getApi() == Layer::Api::OpenGL) {
+ GlLayer* glLayer = static_cast<GlLayer*>(mLayer);
+ if (renderTarget != glLayer->getRenderTarget()) {
+ glLayer->setRenderTarget(renderTarget);
+ glLayer->bindTexture();
+ glLayer->setFilter(GL_NEAREST, false, true);
+ glLayer->setWrap(GL_CLAMP_TO_EDGE, false, true);
+ }
}
}
void DeferredLayerUpdater::detachSurfaceTexture() {
if (mSurfaceTexture.get()) {
- status_t err = mSurfaceTexture->detachFromContext();
- if (err != 0) {
- // TODO: Elevate to fatal exception
- ALOGE("Failed to detach SurfaceTexture from context %d", err);
+ if (mLayer->getApi() == Layer::Api::OpenGL) {
+ status_t err = mSurfaceTexture->detachFromContext();
+ if (err != 0) {
+ // TODO: Elevate to fatal exception
+ ALOGE("Failed to detach SurfaceTexture from context %d", err);
+ }
+ static_cast<GlLayer*>(mLayer)->clearTexture();
}
mSurfaceTexture = nullptr;
- mLayer->clearTexture();
}
}
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 40058223fb48..3814be2cbec4 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef DEFERREDLAYERUPDATE_H_
-#define DEFERREDLAYERUPDATE_H_
+
+#pragma once
#include <cutils/compiler.h>
#include <gui/GLConsumer.h>
@@ -22,6 +22,9 @@
#include <SkMatrix.h>
#include <utils/StrongPointer.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
#include "Layer.h"
#include "Rect.h"
#include "renderthread/RenderThread.h"
@@ -92,6 +95,8 @@ public:
void detachSurfaceTexture();
+ void updateLayer(bool forceFilter, GLenum renderTarget, const float* textureTransform);
+
private:
// Generic properties
int mWidth;
@@ -99,20 +104,17 @@ private:
bool mBlend;
SkColorFilter* mColorFilter;
int mAlpha;
- SkXfermode::Mode mMode;
-
+ SkBlendMode mMode;
sp<GLConsumer> mSurfaceTexture;
SkMatrix* mTransform;
bool mNeedsGLContextAttach;
bool mUpdateTexImage;
Layer* mLayer;
- Caches& mCaches;
void doUpdateTexImage();
+ void doUpdateVkTexImage();
};
} /* namespace uirenderer */
} /* namespace android */
-
-#endif /* DEFERREDLAYERUPDATE_H_ */
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index ff3ee2229b02..d180ba51b304 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -43,6 +43,13 @@ void DeviceInfo::initialize() {
});
}
+void DeviceInfo::initialize(int maxTextureSize) {
+ std::call_once(sInitializedFlag, [maxTextureSize]() {
+ sDeviceInfo = new DeviceInfo();
+ sDeviceInfo->mMaxTextureSize = maxTextureSize;
+ });
+}
+
void DeviceInfo::load() {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
}
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index f576a4f48021..aff84b02d85a 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -16,7 +16,6 @@
#ifndef DEVICEINFO_H
#define DEVICEINFO_H
-#include "Extensions.h"
#include "utils/Macros.h"
namespace android {
@@ -33,8 +32,7 @@ public:
// only call this after GL has been initialized, or at any point if compiled
// with HWUI_NULL_GPU
static void initialize();
-
- const Extensions& extensions() const { return mExtensions; }
+ static void initialize(int maxTextureSize);
int maxTextureSize() const { return mMaxTextureSize; }
@@ -44,7 +42,6 @@ private:
void load();
- Extensions mExtensions;
int mMaxTextureSize;
};
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 28be05c52cfc..5e4a7f7a8d2f 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -19,15 +19,13 @@
#include <utils/Trace.h>
+#include "DamageAccumulator.h"
#include "Debug.h"
#include "DisplayList.h"
-#include "RenderNode.h"
-
-#if HWUI_NEW_OPS
#include "RecordedOp.h"
-#else
-#include "DisplayListOp.h"
-#endif
+#include "RenderNode.h"
+#include "VectorDrawable.h"
+#include "renderthread/CanvasContext.h"
namespace android {
namespace uirenderer {
@@ -45,8 +43,7 @@ DisplayList::DisplayList()
, regions(stdAllocator)
, referenceHolders(stdAllocator)
, functors(stdAllocator)
- , vectorDrawables(stdAllocator)
- , hasDrawOps(false) {
+ , vectorDrawables(stdAllocator) {
}
DisplayList::~DisplayList() {
@@ -92,5 +89,43 @@ size_t DisplayList::addChild(NodeOpType* op) {
return index;
}
+void DisplayList::syncContents() {
+ for (auto& iter : functors) {
+ (*iter.functor)(DrawGlInfo::kModeSync, nullptr);
+ }
+ for (auto& vectorDrawable : vectorDrawables) {
+ vectorDrawable->syncProperties();
+ }
+}
+
+void DisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) {
+ for (auto&& child : children) {
+ updateFn(child->renderNode);
+ }
+}
+
+bool DisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
+ std::function<void(RenderNode*, TreeInfo&, bool)> childFn) {
+ info.prepareTextures = info.canvasContext.pinImages(bitmapResources);
+
+ for (auto&& op : children) {
+ RenderNode* childNode = op->renderNode;
+ info.damageAccumulator->pushTransform(&op->localMatrix);
+ bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip;
+ childFn(childNode, info, childFunctorsNeedLayer);
+ info.damageAccumulator->popTransform();
+ }
+
+ bool isDirty = false;
+ for (auto& vectorDrawable : vectorDrawables) {
+ // If any vector drawable in the display list needs update, damage the node.
+ if (vectorDrawable->isDirty()) {
+ isDirty = true;
+ }
+ vectorDrawable->setPropertyChangeWillBeConsumed(true);
+ }
+ return isDirty;
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index ccf71c6d360b..cab092ffc34c 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_DISPLAY_LIST_H
-#define ANDROID_HWUI_DISPLAY_LIST_H
+#pragma once
#include <SkCamera.h>
+#include <SkDrawable.h>
#include <SkMatrix.h>
#include <private/hwui/DrawGlInfo.h>
@@ -34,10 +34,11 @@
#include "Debug.h"
#include "CanvasProperty.h"
-#include "DeferredDisplayList.h"
#include "GlFunctorLifecycleListener.h"
#include "Matrix.h"
#include "RenderProperties.h"
+#include "TreeInfo.h"
+#include "hwui/Bitmap.h"
#include <vector>
@@ -49,72 +50,20 @@ class SkRegion;
namespace android {
namespace uirenderer {
-class DeferredDisplayList;
-class DisplayListOp;
-class DisplayListCanvas;
-class OpenGLRenderer;
class Rect;
class Layer;
-#if HWUI_NEW_OPS
struct RecordedOp;
struct RenderNodeOp;
typedef RecordedOp BaseOpType;
typedef RenderNodeOp NodeOpType;
-#else
-class DrawRenderNodeOp;
-
-typedef DisplayListOp BaseOpType;
-typedef DrawRenderNodeOp NodeOpType;
-#endif
namespace VectorDrawable {
class Tree;
};
typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
-/**
- * Holds data used in the playback a tree of DisplayLists.
- */
-struct PlaybackStateStruct {
-protected:
- PlaybackStateStruct(OpenGLRenderer& renderer, int replayFlags, LinearAllocator* allocator)
- : mRenderer(renderer)
- , mReplayFlags(replayFlags)
- , mAllocator(allocator) {}
-
-public:
- OpenGLRenderer& mRenderer;
- const int mReplayFlags;
-
- // Allocator with the lifetime of a single frame. replay uses an Allocator owned by the struct,
- // while defer shares the DeferredDisplayList's Allocator
- // TODO: move this allocator to be owned by object with clear frame lifecycle
- LinearAllocator * const mAllocator;
-
- SkPath* allocPathForFrame() {
- return mRenderer.allocPathForFrame();
- }
-};
-
-struct DeferStateStruct : public PlaybackStateStruct {
- DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags)
- : PlaybackStateStruct(renderer, replayFlags, &(deferredList.mAllocator)),
- mDeferredList(deferredList) {}
-
- DeferredDisplayList& mDeferredList;
-};
-
-struct ReplayStateStruct : public PlaybackStateStruct {
- ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags)
- : PlaybackStateStruct(renderer, replayFlags, &mReplayAllocator),
- mDirty(dirty) {}
-
- Rect& mDirty;
- LinearAllocator mReplayAllocator;
-};
-
struct FunctorContainer {
Functor* functor;
GlFunctorLifecycleListener* listener;
@@ -124,7 +73,6 @@ struct FunctorContainer {
* Data structure that holds the list of commands used in display list stream
*/
class DisplayList {
- friend class DisplayListCanvas;
friend class RecordingCanvas;
public:
struct Chunk {
@@ -138,13 +86,13 @@ public:
// whether children with non-zero Z in the chunk should be reordered
bool reorderChildren;
-#if HWUI_NEW_OPS
+
+ // clip at the beginning of a reorder section, applied to reordered children
const ClipBase* reorderClip;
-#endif
};
DisplayList();
- ~DisplayList();
+ virtual ~DisplayList();
// index of DisplayListOp restore, after which projected descendants should be drawn
int projectionReceiveIndex;
@@ -154,9 +102,7 @@ public:
const LsaVector<NodeOpType*>& getChildren() const { return children; }
- const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
- const LsaVector<FunctorContainer>& getFunctors() const { return functors; }
- const LsaVector<VectorDrawableRoot*>& getVectorDrawables() { return vectorDrawables; }
+ const LsaVector<sk_sp<Bitmap>>& getBitmapResources() const { return bitmapResources; }
size_t addChild(NodeOpType* childOp);
@@ -168,19 +114,26 @@ public:
size_t getUsedSize() {
return allocator.usedSize();
}
- bool isEmpty() {
-#if HWUI_NEW_OPS
- return ops.empty();
-#else
- return !hasDrawOps;
-#endif
+
+ virtual bool isEmpty() const { return ops.empty(); }
+ virtual bool hasFunctor() const { return !functors.empty(); }
+ virtual bool hasVectorDrawables() const { return !vectorDrawables.empty(); }
+ virtual bool isSkiaDL() const { return false; }
+ virtual bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) {
+ return false;
}
-private:
+ virtual void syncContents();
+ virtual void updateChildren(std::function<void(RenderNode*)> updateFn);
+ virtual bool prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
+ std::function<void(RenderNode*, TreeInfo&, bool)> childFn);
+
+protected:
// allocator into which all ops and LsaVector arrays allocated
LinearAllocator allocator;
LinearStdAllocator<void*> stdAllocator;
+private:
LsaVector<Chunk> chunks;
LsaVector<BaseOpType*> ops;
@@ -188,7 +141,7 @@ private:
LsaVector<NodeOpType*> children;
// Resources - Skia objects + 9 patches referred to by this DisplayList
- LsaVector<const SkBitmap*> bitmapResources;
+ LsaVector<sk_sp<Bitmap>> bitmapResources;
LsaVector<const SkPath*> pathResources;
LsaVector<const Res_png_9patch*> patchResources;
LsaVector<std::unique_ptr<const SkPaint>> paints;
@@ -203,12 +156,8 @@ private:
// gets special treatment exclusive for webview.
LsaVector<VectorDrawableRoot*> vectorDrawables;
- bool hasDrawOps; // only used if !HWUI_NEW_OPS
-
void cleanupResources();
};
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_OPENGL_RENDERER_H
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
deleted file mode 100644
index bec662959f91..000000000000
--- a/libs/hwui/DisplayListCanvas.cpp
+++ /dev/null
@@ -1,597 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "DisplayListCanvas.h"
-
-#include "DeferredDisplayList.h"
-#include "DeferredLayerUpdater.h"
-#include "DisplayListOp.h"
-#include "ResourceCache.h"
-#include "RenderNode.h"
-#include "VectorDrawable.h"
-#include "utils/PaintUtils.h"
-
-#include <SkCamera.h>
-#include <SkCanvas.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-namespace android {
-namespace uirenderer {
-
-DisplayListCanvas::DisplayListCanvas(int width, int height)
- : mState(*this)
- , mResourceCache(ResourceCache::getInstance())
- , mDisplayList(nullptr)
- , mTranslateX(0.0f)
- , mTranslateY(0.0f)
- , mHasDeferredTranslate(false)
- , mDeferredBarrierType(kBarrier_None)
- , mHighContrastText(false)
- , mRestoreSaveCount(-1) {
- resetRecording(width, height);
-}
-
-DisplayListCanvas::~DisplayListCanvas() {
- LOG_ALWAYS_FATAL_IF(mDisplayList,
- "Destroyed a DisplayListCanvas during a record!");
-}
-
-void DisplayListCanvas::resetRecording(int width, int height) {
- LOG_ALWAYS_FATAL_IF(mDisplayList,
- "prepareDirty called a second time during a recording!");
- mDisplayList = new DisplayList();
-
- mState.initializeSaveStack(width, height,
- 0, 0, width, height, Vector3());
-
- mDeferredBarrierType = kBarrier_InOrder;
- mState.setDirtyClip(false);
- mRestoreSaveCount = -1;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Operations
-///////////////////////////////////////////////////////////////////////////////
-
-DisplayList* DisplayListCanvas::finishRecording() {
- flushRestoreToCount();
- flushTranslate();
-
- mPaintMap.clear();
- mRegionMap.clear();
- mPathMap.clear();
- DisplayList* displayList = mDisplayList;
- mDisplayList = nullptr;
- mSkiaCanvasProxy.reset(nullptr);
- return displayList;
-}
-
-void DisplayListCanvas::callDrawGLFunction(Functor* functor,
- GlFunctorLifecycleListener* listener) {
- addDrawOp(new (alloc()) DrawFunctorOp(functor));
- mDisplayList->functors.push_back({functor, listener});
- mDisplayList->ref(listener);
-}
-
-SkCanvas* DisplayListCanvas::asSkCanvas() {
- LOG_ALWAYS_FATAL_IF(!mDisplayList,
- "attempting to get an SkCanvas when we are not recording!");
- if (!mSkiaCanvasProxy) {
- mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this));
- }
-
- // SkCanvas instances default to identity transform, but should inherit
- // the state of this Canvas; if this code was in the SkiaCanvasProxy
- // constructor, we couldn't cache mSkiaCanvasProxy.
- SkMatrix parentTransform;
- getMatrix(&parentTransform);
- mSkiaCanvasProxy.get()->setMatrix(parentTransform);
-
- return mSkiaCanvasProxy.get();
-}
-
-int DisplayListCanvas::save(SaveFlags::Flags flags) {
- addStateOp(new (alloc()) SaveOp((int) flags));
- return mState.save((int) flags);
-}
-
-void DisplayListCanvas::restore() {
- if (mRestoreSaveCount < 0) {
- restoreToCount(getSaveCount() - 1);
- return;
- }
-
- mRestoreSaveCount--;
- flushTranslate();
- mState.restore();
-}
-
-void DisplayListCanvas::restoreToCount(int saveCount) {
- mRestoreSaveCount = saveCount;
- flushTranslate();
- mState.restoreToCount(saveCount);
-}
-
-int DisplayListCanvas::saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, SaveFlags::Flags flags) {
- // force matrix/clip isolation for layer
- flags |= SaveFlags::MatrixClip;
-
- paint = refPaint(paint);
- addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, paint, (int) flags));
- return mState.save((int) flags);
-}
-
-void DisplayListCanvas::translate(float dx, float dy) {
- if (dx == 0.0f && dy == 0.0f) return;
-
- mHasDeferredTranslate = true;
- mTranslateX += dx;
- mTranslateY += dy;
- flushRestoreToCount();
- mState.translate(dx, dy, 0.0f);
-}
-
-void DisplayListCanvas::rotate(float degrees) {
- if (degrees == 0.0f) return;
-
- addStateOp(new (alloc()) RotateOp(degrees));
- mState.rotate(degrees);
-}
-
-void DisplayListCanvas::scale(float sx, float sy) {
- if (sx == 1.0f && sy == 1.0f) return;
-
- addStateOp(new (alloc()) ScaleOp(sx, sy));
- mState.scale(sx, sy);
-}
-
-void DisplayListCanvas::skew(float sx, float sy) {
- addStateOp(new (alloc()) SkewOp(sx, sy));
- mState.skew(sx, sy);
-}
-
-void DisplayListCanvas::setMatrix(const SkMatrix& matrix) {
- addStateOp(new (alloc()) SetMatrixOp(matrix));
- mState.setMatrix(matrix);
-}
-
-void DisplayListCanvas::concat(const SkMatrix& matrix) {
- addStateOp(new (alloc()) ConcatMatrixOp(matrix));
- mState.concatMatrix(matrix);
-}
-
-bool DisplayListCanvas::getClipBounds(SkRect* outRect) const {
- Rect bounds = mState.getLocalClipBounds();
- *outRect = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom);
- return !(outRect->isEmpty());
-}
-
-bool DisplayListCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
- return mState.quickRejectConservative(left, top, right, bottom);
-}
-
-bool DisplayListCanvas::quickRejectPath(const SkPath& path) const {
- SkRect bounds = path.getBounds();
- return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
-}
-
-
-bool DisplayListCanvas::clipRect(float left, float top, float right, float bottom,
- SkRegion::Op op) {
- addStateOp(new (alloc()) ClipRectOp(left, top, right, bottom, op));
- return mState.clipRect(left, top, right, bottom, op);
-}
-
-bool DisplayListCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
- path = refPath(path);
- addStateOp(new (alloc()) ClipPathOp(path, op));
- return mState.clipPath(path, op);
-}
-
-bool DisplayListCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
- region = refRegion(region);
- addStateOp(new (alloc()) ClipRegionOp(region, op));
- return mState.clipRegion(region, op);
-}
-
-void DisplayListCanvas::drawRenderNode(RenderNode* renderNode) {
- LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode");
- DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(
- renderNode,
- *mState.currentTransform(),
- mState.clipIsSimple());
- addRenderNodeOp(op);
-}
-
-void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle) {
- // We ref the DeferredLayerUpdater due to its thread-safe ref-counting
- // semantics.
- mDisplayList->ref(layerHandle);
- addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer()));
-}
-
-void DisplayListCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
- bitmap = refBitmap(*bitmap);
- paint = refPaint(paint);
-
- addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint));
-}
-
-void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top,
- const SkPaint* paint) {
- save(SaveFlags::Matrix);
- translate(left, top);
- drawBitmap(&bitmap, paint);
- restore();
-}
-
-void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
- const SkPaint* paint) {
- if (matrix.isIdentity()) {
- drawBitmap(&bitmap, paint);
- } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))
- && MathUtils::isPositive(matrix.getScaleX())
- && MathUtils::isPositive(matrix.getScaleY())) {
- // SkMatrix::isScaleTranslate() not available in L
- SkRect src;
- SkRect dst;
- bitmap.getBounds(&src);
- matrix.mapRect(&dst, src);
- drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
- dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
- } else {
- save(SaveFlags::Matrix);
- concat(matrix);
- drawBitmap(&bitmap, paint);
- restore();
- }
-}
-
-void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
- float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, const SkPaint* paint) {
- if (srcLeft == 0 && srcTop == 0
- && srcRight == bitmap.width()
- && srcBottom == bitmap.height()
- && (srcBottom - srcTop == dstBottom - dstTop)
- && (srcRight - srcLeft == dstRight - dstLeft)) {
- // transform simple rect to rect drawing case into position bitmap ops, since they merge
- save(SaveFlags::Matrix);
- translate(dstLeft, dstTop);
- drawBitmap(&bitmap, paint);
- restore();
- } else {
- paint = refPaint(paint);
-
- if (paint && paint->getShader()) {
- float scaleX = (dstRight - dstLeft) / (srcRight - srcLeft);
- float scaleY = (dstBottom - dstTop) / (srcBottom - srcTop);
- if (!MathUtils::areEqual(scaleX, 1.0f) || !MathUtils::areEqual(scaleY, 1.0f)) {
- // Apply the scale transform on the canvas, so that the shader
- // effectively calculates positions relative to src rect space
-
- save(SaveFlags::Matrix);
- translate(dstLeft, dstTop);
- scale(scaleX, scaleY);
-
- dstLeft = 0.0f;
- dstTop = 0.0f;
- dstRight = srcRight - srcLeft;
- dstBottom = srcBottom - srcTop;
-
- addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap),
- srcLeft, srcTop, srcRight, srcBottom,
- dstLeft, dstTop, dstRight, dstBottom, paint));
- restore();
- return;
- }
- }
-
- addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap),
- srcLeft, srcTop, srcRight, srcBottom,
- dstLeft, dstTop, dstRight, dstBottom, paint));
- }
-}
-
-void DisplayListCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors, const SkPaint* paint) {
- int vertexCount = (meshWidth + 1) * (meshHeight + 1);
- vertices = refBuffer<float>(vertices, vertexCount * 2); // 2 floats per vertex
- paint = refPaint(paint);
- colors = refBuffer<int>(colors, vertexCount); // 1 color per vertex
-
- addDrawOp(new (alloc()) DrawBitmapMeshOp(refBitmap(bitmap), meshWidth, meshHeight,
- vertices, colors, paint));
-}
-
-void DisplayListCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& patch,
- float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
- const SkBitmap* bitmapPtr = refBitmap(bitmap);
- const Res_png_9patch* patchPtr = refPatch(&patch);
- paint = refPaint(paint);
-
- addDrawOp(new (alloc()) DrawPatchOp(bitmapPtr, patchPtr,
- dstLeft, dstTop, dstRight, dstBottom, paint));
-}
-
-void DisplayListCanvas::drawColor(int color, SkXfermode::Mode mode) {
- addDrawOp(new (alloc()) DrawColorOp(color, mode));
-}
-
-void DisplayListCanvas::drawPaint(const SkPaint& paint) {
- SkRect bounds;
- if (getClipBounds(&bounds)) {
- drawRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, paint);
- }
-}
-
-
-void DisplayListCanvas::drawRect(float left, float top, float right, float bottom,
- const SkPaint& paint) {
- addDrawOp(new (alloc()) DrawRectOp(left, top, right, bottom, refPaint(&paint)));
-}
-
-void DisplayListCanvas::drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, const SkPaint& paint) {
- addDrawOp(new (alloc()) DrawRoundRectOp(left, top, right, bottom, rx, ry, refPaint(&paint)));
-}
-
-void DisplayListCanvas::drawRoundRect(
- CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
- CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
- CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
- CanvasPropertyPaint* paint) {
- mDisplayList->ref(left);
- mDisplayList->ref(top);
- mDisplayList->ref(right);
- mDisplayList->ref(bottom);
- mDisplayList->ref(rx);
- mDisplayList->ref(ry);
- mDisplayList->ref(paint);
- refBitmapsInShader(paint->value.getShader());
- addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value,
- &right->value, &bottom->value, &rx->value, &ry->value, &paint->value));
-}
-
-void DisplayListCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
- addDrawOp(new (alloc()) DrawCircleOp(x, y, radius, refPaint(&paint)));
-}
-
-void DisplayListCanvas::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
- CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
- mDisplayList->ref(x);
- mDisplayList->ref(y);
- mDisplayList->ref(radius);
- mDisplayList->ref(paint);
- refBitmapsInShader(paint->value.getShader());
- addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value,
- &radius->value, &paint->value));
-}
-
-void DisplayListCanvas::drawOval(float left, float top, float right, float bottom,
- const SkPaint& paint) {
- addDrawOp(new (alloc()) DrawOvalOp(left, top, right, bottom, refPaint(&paint)));
-}
-
-void DisplayListCanvas::drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
- if (fabs(sweepAngle) >= 360.0f) {
- drawOval(left, top, right, bottom, paint);
- } else {
- addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom,
- startAngle, sweepAngle, useCenter, refPaint(&paint)));
- }
-}
-
-void DisplayListCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
- addDrawOp(new (alloc()) DrawPathOp(refPath(&path), refPaint(&paint)));
-}
-
-void DisplayListCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
- points = refBuffer<float>(points, count);
-
- addDrawOp(new (alloc()) DrawLinesOp(points, count, refPaint(&paint)));
-}
-
-void DisplayListCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
- points = refBuffer<float>(points, count);
-
- addDrawOp(new (alloc()) DrawPointsOp(points, count, refPaint(&paint)));
-}
-
-void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
- mDisplayList->ref(tree);
- mDisplayList->vectorDrawables.push_back(tree);
- addDrawOp(new (alloc()) DrawVectorDrawableOp(tree, tree->stagingProperties()->getBounds()));
-}
-
-void DisplayListCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int count,
- const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) {
- if (!glyphs || count <= 0) return;
-
- int bytesCount = 2 * count;
- DrawOp* op = new (alloc()) DrawTextOnPathOp(refBuffer<glyph_t>(glyphs, count),
- bytesCount, count, refPath(&path),
- hOffset, vOffset, refPaint(&paint));
- addDrawOp(op);
-}
-
-void DisplayListCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions,
- int count, const SkPaint& paint, float x, float y,
- float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
- float totalAdvance) {
-
- if (!glyphs || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
-
- int bytesCount = count * 2;
- positions = refBuffer<float>(positions, count * 2);
- Rect bounds(boundsLeft, boundsTop, boundsRight, boundsBottom);
-
- DrawOp* op = new (alloc()) DrawTextOp(refBuffer<glyph_t>(glyphs, count), bytesCount, count,
- x, y, positions, refPaint(&paint), totalAdvance, bounds);
- addDrawOp(op);
- drawTextDecorations(x, y, totalAdvance, paint);
-}
-
-void DisplayListCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
- if (paint.getStyle() != SkPaint::kFill_Style ||
- (paint.isAntiAlias() && !mState.currentTransform()->isSimple())) {
- SkRegion::Iterator it(region);
- while (!it.done()) {
- const SkIRect& r = it.rect();
- drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
- it.next();
- }
- } else {
- int count = 0;
- Vector<float> rects;
- SkRegion::Iterator it(region);
- while (!it.done()) {
- const SkIRect& r = it.rect();
- rects.push(r.fLeft);
- rects.push(r.fTop);
- rects.push(r.fRight);
- rects.push(r.fBottom);
- count += 4;
- it.next();
- }
- drawRects(rects.array(), count, &paint);
- }
-}
-
-void DisplayListCanvas::drawRects(const float* rects, int count, const SkPaint* paint) {
- if (count <= 0) return;
-
- rects = refBuffer<float>(rects, count);
- paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawRectsOp(rects, count, paint));
-}
-
-void DisplayListCanvas::setDrawFilter(SkDrawFilter* filter) {
- mDrawFilter.reset(SkSafeRef(filter));
-}
-
-void DisplayListCanvas::insertReorderBarrier(bool enableReorder) {
- flushRestoreToCount();
- flushTranslate();
- mDeferredBarrierType = enableReorder ? kBarrier_OutOfOrder : kBarrier_InOrder;
-}
-
-void DisplayListCanvas::flushRestoreToCount() {
- if (mRestoreSaveCount >= 0) {
- addOpAndUpdateChunk(new (alloc()) RestoreToCountOp(mRestoreSaveCount));
- mRestoreSaveCount = -1;
- }
-}
-
-void DisplayListCanvas::flushTranslate() {
- if (mHasDeferredTranslate) {
- if (mTranslateX != 0.0f || mTranslateY != 0.0f) {
- addOpAndUpdateChunk(new (alloc()) TranslateOp(mTranslateX, mTranslateY));
- mTranslateX = mTranslateY = 0.0f;
- }
- mHasDeferredTranslate = false;
- }
-}
-
-size_t DisplayListCanvas::addOpAndUpdateChunk(DisplayListOp* op) {
- int insertIndex = mDisplayList->ops.size();
-#if HWUI_NEW_OPS
- LOG_ALWAYS_FATAL("unsupported");
-#else
- mDisplayList->ops.push_back(op);
-#endif
- if (mDeferredBarrierType != kBarrier_None) {
- // op is first in new chunk
- mDisplayList->chunks.emplace_back();
- DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
- newChunk.beginOpIndex = insertIndex;
- newChunk.endOpIndex = insertIndex + 1;
- newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
-
- int nextChildIndex = mDisplayList->children.size();
- newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
- mDeferredBarrierType = kBarrier_None;
- } else {
- // standard case - append to existing chunk
- mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
- }
- return insertIndex;
-}
-
-size_t DisplayListCanvas::flushAndAddOp(DisplayListOp* op) {
- flushRestoreToCount();
- flushTranslate();
- return addOpAndUpdateChunk(op);
-}
-
-size_t DisplayListCanvas::addStateOp(StateOp* op) {
- return flushAndAddOp(op);
-}
-
-size_t DisplayListCanvas::addDrawOp(DrawOp* op) {
- Rect localBounds;
- if (op->getLocalBounds(localBounds)) {
- bool rejected = quickRejectRect(localBounds.left, localBounds.top,
- localBounds.right, localBounds.bottom);
- op->setQuickRejected(rejected);
- }
-
- mDisplayList->hasDrawOps = true;
- return flushAndAddOp(op);
-}
-
-size_t DisplayListCanvas::addRenderNodeOp(DrawRenderNodeOp* op) {
- int opIndex = addDrawOp(op);
-#if !HWUI_NEW_OPS
- int childIndex = mDisplayList->addChild(op);
-
- // update the chunk's child indices
- DisplayList::Chunk& chunk = mDisplayList->chunks.back();
- chunk.endChildIndex = childIndex + 1;
-
- if (op->renderNode->stagingProperties().isProjectionReceiver()) {
- // use staging property, since recording on UI thread
- mDisplayList->projectionReceiveIndex = opIndex;
- }
-#endif
- return opIndex;
-}
-
-void DisplayListCanvas::refBitmapsInShader(const SkShader* shader) {
- if (!shader) return;
-
- // If this paint has an SkShader that has an SkBitmap add
- // it to the bitmap pile
- SkBitmap bitmap;
- SkShader::TileMode xy[2];
- if (shader->isABitmap(&bitmap, nullptr, xy)) {
- refBitmap(bitmap);
- return;
- }
- SkShader::ComposeRec rec;
- if (shader->asACompose(&rec)) {
- refBitmapsInShader(rec.fShaderA);
- refBitmapsInShader(rec.fShaderB);
- return;
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
deleted file mode 100644
index 664f79e283b6..000000000000
--- a/libs/hwui/DisplayListCanvas.h
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
-#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
-
-#include "CanvasState.h"
-#include "DisplayList.h"
-#include "RenderNode.h"
-#include "ResourceCache.h"
-#include "SkiaCanvasProxy.h"
-#include "hwui/Canvas.h"
-#include "utils/Macros.h"
-
-#include <SkDrawFilter.h>
-#include <SkMatrix.h>
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <SkRegion.h>
-#include <SkTLazy.h>
-#include <cutils/compiler.h>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_DISPLAY_LIST
- #define DISPLAY_LIST_LOGD(...) ALOGD(__VA_ARGS__)
-#else
- #define DISPLAY_LIST_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Display list
-///////////////////////////////////////////////////////////////////////////////
-
-class DeferredDisplayList;
-class DeferredLayerUpdater;
-class DisplayListOp;
-class DrawOp;
-class DrawRenderNodeOp;
-class RenderNode;
-class StateOp;
-
-/**
- * Records drawing commands in a display list for later playback into an OpenGLRenderer.
- */
-class ANDROID_API DisplayListCanvas: public Canvas, public CanvasStateClient {
-public:
- DisplayListCanvas(int width, int height);
- virtual ~DisplayListCanvas();
-
- virtual void resetRecording(int width, int height) override;
- virtual WARN_UNUSED_RESULT DisplayList* finishRecording() override;
-
-// ----------------------------------------------------------------------------
-// HWUI Canvas state operations
-// ----------------------------------------------------------------------------
-
- virtual void insertReorderBarrier(bool enableReorder) override;
-
-// ----------------------------------------------------------------------------
-// HWUI Canvas draw operations
-// ----------------------------------------------------------------------------
-
- // Shapes
- virtual void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
- CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
- CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
- CanvasPropertyPaint* paint) override;
- virtual void drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
- CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) override;
-
-// ----------------------------------------------------------------------------
-// HWUI Canvas draw operations - special
-// ----------------------------------------------------------------------------
- virtual void drawLayer(DeferredLayerUpdater* layerHandle) override;
- virtual void drawRenderNode(RenderNode* renderNode) override;
- virtual void callDrawGLFunction(Functor* functor,
- GlFunctorLifecycleListener* listener) override;
-
-// ----------------------------------------------------------------------------
-// CanvasStateClient interface
-// ----------------------------------------------------------------------------
- virtual void onViewportInitialized() override { }
- virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override { }
- virtual GLuint getTargetFbo() const override { return -1; }
-
-// ----------------------------------------------------------------------------
-// android/graphics/Canvas interface
-// ----------------------------------------------------------------------------
- virtual SkCanvas* asSkCanvas() override;
-
- virtual void setBitmap(const SkBitmap& bitmap) override {
- LOG_ALWAYS_FATAL("DisplayListCanvas is not backed by a bitmap.");
- }
-
- virtual bool isOpaque() override { return false; }
- virtual int width() override { return mState.getWidth(); }
- virtual int height() override { return mState.getHeight(); }
-
- virtual void setHighContrastText(bool highContrastText) override {
- mHighContrastText = highContrastText;
- }
- virtual bool isHighContrastText() override { return mHighContrastText; }
-
-// ----------------------------------------------------------------------------
-// android/graphics/Canvas state operations
-// ----------------------------------------------------------------------------
- // Save (layer)
- virtual int getSaveCount() const override { return mState.getSaveCount(); }
- virtual int save(SaveFlags::Flags flags) override;
- virtual void restore() override;
- virtual void restoreToCount(int saveCount) override;
-
- virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
- SaveFlags::Flags flags) override;
- virtual int saveLayerAlpha(float left, float top, float right, float bottom,
- int alpha, SaveFlags::Flags flags) override {
- SkPaint paint;
- paint.setAlpha(alpha);
- return saveLayer(left, top, right, bottom, &paint, flags);
- }
-
- // Matrix
- virtual void getMatrix(SkMatrix* outMatrix) const override { mState.getMatrix(outMatrix); }
- virtual void setMatrix(const SkMatrix& matrix) override;
-
- virtual void concat(const SkMatrix& matrix) override;
- virtual void rotate(float degrees) override;
- virtual void scale(float sx, float sy) override;
- virtual void skew(float sx, float sy) override;
- virtual void translate(float dx, float dy) override;
-
- // Clip
- virtual bool getClipBounds(SkRect* outRect) const override;
- virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
- virtual bool quickRejectPath(const SkPath& path) const override;
-
- virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) override;
- virtual bool clipPath(const SkPath* path, SkRegion::Op op) override;
- virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override;
-
- // Misc
- virtual SkDrawFilter* getDrawFilter() override { return mDrawFilter.get(); }
- virtual void setDrawFilter(SkDrawFilter* filter) override;
-
-// ----------------------------------------------------------------------------
-// android/graphics/Canvas draw operations
-// ----------------------------------------------------------------------------
- virtual void drawColor(int color, SkXfermode::Mode mode) override;
- virtual void drawPaint(const SkPaint& paint) override;
-
- // Geometry
- virtual void drawPoint(float x, float y, const SkPaint& paint) override {
- float points[2] = { x, y };
- drawPoints(points, 2, paint);
- }
- virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
- virtual void drawLine(float startX, float startY, float stopX, float stopY,
- const SkPaint& paint) override {
- float points[4] = { startX, startY, stopX, stopY };
- drawLines(points, 4, paint);
- }
- virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
- virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
- virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
- virtual void drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, const SkPaint& paint) override;
- virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
- virtual void drawOval(float left, float top, float right, float bottom, const SkPaint& paint) override;
- virtual void drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) override;
- virtual void drawPath(const SkPath& path, const SkPaint& paint) override;
- virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
- const float* verts, const float* tex, const int* colors,
- const uint16_t* indices, int indexCount, const SkPaint& paint) override
- { /* DisplayListCanvas does not support drawVertices(); ignore */ }
-
- // Bitmap-based
- virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
- const SkPaint* paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
- float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, const SkPaint* paint) override;
- virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors, const SkPaint* paint) override;
- virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
- float dstLeft, float dstTop, float dstRight, float dstBottom,
- const SkPaint* paint) override;
-
- virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
-
- // Text
- virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int count,
- const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
- float boundsRight, float boundsBottom, float totalAdvance) override;
- virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) override;
- virtual bool drawTextAbsolutePos() const override { return false; }
-
-private:
-
- CanvasState mState;
- std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy;
-
- enum DeferredBarrierType {
- kBarrier_None,
- kBarrier_InOrder,
- kBarrier_OutOfOrder,
- };
-
- void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
- void drawRects(const float* rects, int count, const SkPaint* paint);
-
- void flushRestoreToCount();
- void flushTranslate();
- void flushReorderBarrier();
-
- LinearAllocator& alloc() { return mDisplayList->allocator; }
-
- // Each method returns final index of op
- size_t addOpAndUpdateChunk(DisplayListOp* op);
- // flushes any deferred operations, and appends the op
- size_t flushAndAddOp(DisplayListOp* op);
-
- size_t addStateOp(StateOp* op);
- size_t addDrawOp(DrawOp* op);
- size_t addRenderNodeOp(DrawRenderNodeOp* op);
-
- void refBitmapsInShader(const SkShader* shader);
-
- template<class T>
- inline const T* refBuffer(const T* srcBuffer, int32_t count) {
- if (!srcBuffer) return nullptr;
-
- T* dstBuffer = (T*) mDisplayList->allocator.alloc<T>(count * sizeof(T));
- memcpy(dstBuffer, srcBuffer, count * sizeof(T));
- return dstBuffer;
- }
-
- inline const SkPath* refPath(const SkPath* path) {
- if (!path) return nullptr;
-
- // The points/verbs within the path are refcounted so this copy operation
- // is inexpensive and maintains the generationID of the original path.
- const SkPath* cachedPath = new SkPath(*path);
- mDisplayList->pathResources.push_back(cachedPath);
- return cachedPath;
- }
-
- inline const SkPaint* refPaint(const SkPaint* paint) {
- if (!paint) return nullptr;
-
- // If there is a draw filter apply it here and store the modified paint
- // so that we don't need to modify the paint every time we access it.
- SkTLazy<SkPaint> filteredPaint;
- if (mDrawFilter.get()) {
- filteredPaint.set(*paint);
- mDrawFilter->filter(filteredPaint.get(), SkDrawFilter::kPaint_Type);
- paint = filteredPaint.get();
- }
-
- // compute the hash key for the paint and check the cache.
- const uint32_t key = paint->getHash();
- const SkPaint* cachedPaint = mPaintMap.valueFor(key);
- // In the unlikely event that 2 unique paints have the same hash we do a
- // object equality check to ensure we don't erroneously dedup them.
- if (cachedPaint == nullptr || *cachedPaint != *paint) {
- cachedPaint = new SkPaint(*paint);
- std::unique_ptr<const SkPaint> copy(cachedPaint);
- mDisplayList->paints.push_back(std::move(copy));
-
- // replaceValueFor() performs an add if the entry doesn't exist
- mPaintMap.replaceValueFor(key, cachedPaint);
- refBitmapsInShader(cachedPaint->getShader());
- }
-
- return cachedPaint;
- }
-
- inline const SkRegion* refRegion(const SkRegion* region) {
- if (!region) {
- return region;
- }
-
- const SkRegion* cachedRegion = mRegionMap.valueFor(region);
- // TODO: Add generation ID to SkRegion
- if (cachedRegion == nullptr) {
- std::unique_ptr<const SkRegion> copy(new SkRegion(*region));
- cachedRegion = copy.get();
- mDisplayList->regions.push_back(std::move(copy));
-
- // replaceValueFor() performs an add if the entry doesn't exist
- mRegionMap.replaceValueFor(region, cachedRegion);
- }
-
- return cachedRegion;
- }
-
- inline const SkBitmap* refBitmap(const SkBitmap& bitmap) {
- // Note that this assumes the bitmap is immutable. There are cases this won't handle
- // correctly, such as creating the bitmap from scratch, drawing with it, changing its
- // contents, and drawing again. The only fix would be to always copy it the first time,
- // which doesn't seem worth the extra cycles for this unlikely case.
- SkBitmap* localBitmap = alloc().create<SkBitmap>(bitmap);
- mDisplayList->bitmapResources.push_back(localBitmap);
- return localBitmap;
- }
-
- inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
- mDisplayList->patchResources.push_back(patch);
- mResourceCache.incrementRefcount(patch);
- return patch;
- }
-
- DefaultKeyedVector<uint32_t, const SkPaint*> mPaintMap;
- DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
- DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
-
- ResourceCache& mResourceCache;
- DisplayList* mDisplayList;
-
- float mTranslateX;
- float mTranslateY;
- bool mHasDeferredTranslate;
- DeferredBarrierType mDeferredBarrierType;
- bool mHighContrastText;
-
- int mRestoreSaveCount;
-
- SkAutoTUnref<SkDrawFilter> mDrawFilter;
-
- friend class RenderNode;
-
-}; // class DisplayListCanvas
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
deleted file mode 100644
index 51b987c7ed85..000000000000
--- a/libs/hwui/DisplayListOp.h
+++ /dev/null
@@ -1,1555 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_DISPLAY_OPERATION_H
-#define ANDROID_HWUI_DISPLAY_OPERATION_H
-
-#include "OpenGLRenderer.h"
-#include "AssetAtlas.h"
-#include "DeferredDisplayList.h"
-#include "DisplayListCanvas.h"
-#include "GammaFontRenderer.h"
-#include "Patch.h"
-#include "RenderNode.h"
-#include "renderstate/RenderState.h"
-#include "UvMapper.h"
-#include "utils/LinearAllocator.h"
-#include "utils/PaintUtils.h"
-#include "VectorDrawable.h"
-
-#include <algorithm>
-
-#include <SkColor.h>
-#include <SkPath.h>
-#include <SkPathOps.h>
-#include <SkXfermode.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-// Use OP_LOG for logging with arglist, OP_LOGS if just printing char*
-#define OP_LOGS(s) OP_LOG("%s", (s))
-#define OP_LOG(s, ...) ALOGD( "%*s" s, level * 2, "", __VA_ARGS__ )
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Structure for storing canvas operations when they are recorded into a DisplayList, so that they
- * may be replayed to an OpenGLRenderer.
- *
- * To avoid individual memory allocations, DisplayListOps may only be allocated into a
- * LinearAllocator's managed memory buffers. Each pointer held by a DisplayListOp is either a
- * pointer into memory also allocated in the LinearAllocator (mostly for text and float buffers) or
- * references a externally refcounted object (Sk... and Skia... objects). ~DisplayListOp() is
- * never called as LinearAllocators are simply discarded, so no memory management should be done in
- * this class.
- */
-class DisplayListOp {
-public:
- // These objects should always be allocated with a LinearAllocator, and never destroyed/deleted.
- // standard new() intentionally not implemented, and delete/deconstructor should never be used.
- virtual ~DisplayListOp() { LOG_ALWAYS_FATAL("Destructor not supported"); }
- static void operator delete(void* ptr) { LOG_ALWAYS_FATAL("delete not supported"); }
- static void* operator new(size_t size) = delete; /** PURPOSELY OMITTED **/
- static void* operator new(size_t size, LinearAllocator& allocator) {
- // FIXME: Quick hack to keep old pipeline working, delete this when
- // we no longer need to support HWUI_NEWOPS := false
- return allocator.alloc<char>(size);
- }
-
- enum OpLogFlag {
- kOpLogFlag_Recurse = 0x1,
- kOpLogFlag_JSON = 0x2 // TODO: add?
- };
-
- virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) = 0;
-
- virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
- bool useQuickReject) = 0;
-
- virtual void output(int level, uint32_t logFlags = 0) const = 0;
-
- // NOTE: it would be nice to declare constants and overriding the implementation in each op to
- // point at the constants, but that seems to require a .cpp file
- virtual const char* name() = 0;
-};
-
-class StateOp : public DisplayListOp {
-public:
- virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) override {
- // default behavior only affects immediate, deferrable state, issue directly to renderer
- applyState(deferStruct.mRenderer, saveCount);
- }
-
- /**
- * State operations are applied directly to the renderer, but can cause the deferred drawing op
- * list to flush
- */
- virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
- bool useQuickReject) override {
- applyState(replayStruct.mRenderer, saveCount);
- }
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const = 0;
-};
-
-class DrawOp : public DisplayListOp {
-friend class MergingDrawBatch;
-public:
- explicit DrawOp(const SkPaint* paint)
- : mPaint(paint), mQuickRejected(false) {}
-
- virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) override {
- if (mQuickRejected && CC_LIKELY(useQuickReject)) {
- return;
- }
-
- deferStruct.mDeferredList.addDrawOp(deferStruct.mRenderer, this);
- }
-
- virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
- bool useQuickReject) override {
- if (mQuickRejected && CC_LIKELY(useQuickReject)) {
- return;
- }
-
- applyDraw(replayStruct.mRenderer, replayStruct.mDirty);
- }
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) = 0;
-
- /**
- * Draw multiple instances of an operation, must be overidden for operations that merge
- *
- * Currently guarantees certain similarities between ops (see MergingDrawBatch::canMergeWith),
- * and pure translation transformations. Other guarantees of similarity should be enforced by
- * reducing which operations are tagged as mergeable.
- */
- virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const std::vector<OpStatePair>& ops, const Rect& bounds) {
- for (unsigned int i = 0; i < ops.size(); i++) {
- renderer.restoreDisplayState(*(ops[i].state), true);
- ops[i].op->applyDraw(renderer, dirty);
- }
- }
-
- /**
- * When this method is invoked the state field is initialized to have the
- * final rendering state. We can thus use it to process data as it will be
- * used at draw time.
- *
- * Additionally, this method allows subclasses to provide defer-time preferences for batching
- * and merging.
- *
- * if a subclass can set deferInfo.mergeable to true, it should implement multiDraw()
- */
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) {}
-
- /**
- * Query the conservative, local bounds (unmapped) bounds of the op.
- *
- * returns true if bounds exist
- */
- virtual bool getLocalBounds(Rect& localBounds) {
- return false;
- }
-
- // TODO: better refine localbounds usage
- void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
- bool getQuickRejected() { return mQuickRejected; }
-
- virtual bool hasTextShadow() const {
- return false;
- }
-
- inline float strokeWidthOutset() {
- // since anything AA stroke with less than 1.0 pixel width is drawn with an alpha-reduced
- // 1.0 stroke, treat 1.0 as minimum.
-
- // TODO: it would be nice if this could take scale into account, but scale isn't stable
- // since higher levels of the view hierarchy can change scale out from underneath it.
- return std::max(mPaint->getStrokeWidth(), 1.0f) * 0.5f;
- }
-
-protected:
- // Helper method for determining op opaqueness. Assumes op fills its bounds in local
- // coordinates, and that paint's alpha is used
- inline bool isOpaqueOverBounds(const DeferredDisplayState& state) {
- // ensure that local bounds cover mapped bounds
- if (!state.mMatrix.isSimple()) return false;
-
- if (state.mRoundRectClipState) return false;
-
- // check state/paint for transparency
- if (mPaint) {
- if (mPaint->getAlpha() != 0xFF) {
- return false;
- }
- if (mPaint->getShader() && !mPaint->getShader()->isOpaque()) {
- return false;
- }
- if (PaintUtils::isBlendedColorFilter(mPaint->getColorFilter())) {
- return false;
- }
- }
-
- if (state.mAlpha != 1.0f) return false;
-
- SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
- return (mode == SkXfermode::kSrcOver_Mode ||
- mode == SkXfermode::kSrc_Mode);
-
- }
-
- const SkPaint* mPaint;
- bool mQuickRejected;
-};
-
-class DrawBoundedOp : public DrawOp {
-public:
- DrawBoundedOp(float left, float top, float right, float bottom, const SkPaint* paint)
- : DrawOp(paint), mLocalBounds(left, top, right, bottom) {}
-
- DrawBoundedOp(const Rect& localBounds, const SkPaint* paint)
- : DrawOp(paint), mLocalBounds(localBounds) {}
-
- // Calculates bounds as smallest rect encompassing all points
- // NOTE: requires at least 1 vertex, and doesn't account for stroke size (should be handled in
- // subclass' constructor)
- DrawBoundedOp(const float* points, int count, const SkPaint* paint)
- : DrawOp(paint), mLocalBounds(points[0], points[1], points[0], points[1]) {
- for (int i = 2; i < count; i += 2) {
- mLocalBounds.left = std::min(mLocalBounds.left, points[i]);
- mLocalBounds.right = std::max(mLocalBounds.right, points[i]);
- mLocalBounds.top = std::min(mLocalBounds.top, points[i + 1]);
- mLocalBounds.bottom = std::max(mLocalBounds.bottom, points[i + 1]);
- }
- }
-
- // default empty constructor for bounds, to be overridden in child constructor body
- explicit DrawBoundedOp(const SkPaint* paint): DrawOp(paint) { }
-
- virtual bool getLocalBounds(Rect& localBounds) override {
- localBounds.set(mLocalBounds);
- PaintUtils::TextShadow textShadow;
- if (PaintUtils::getTextShadow(mPaint, &textShadow)) {
- Rect shadow(mLocalBounds);
- shadow.translate(textShadow.dx, textShadow.dx);
- shadow.outset(textShadow.radius);
- localBounds.unionWith(shadow);
- }
- return true;
- }
-
-protected:
- Rect mLocalBounds; // displayed area in LOCAL coord. doesn't incorporate stroke, so check paint
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// STATE OPERATIONS - these may affect the state of the canvas/renderer, but do
-// not directly draw or alter output
-///////////////////////////////////////////////////////////////////////////////
-
-class SaveOp : public StateOp {
-public:
- explicit SaveOp(int flags)
- : mFlags(flags) {}
-
- virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) override {
- int newSaveCount = deferStruct.mRenderer.save(mFlags);
- deferStruct.mDeferredList.addSave(deferStruct.mRenderer, this, newSaveCount);
- }
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.save(mFlags);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Save flags %x", mFlags);
- }
-
- virtual const char* name() override { return "Save"; }
-
- int getFlags() const { return mFlags; }
-private:
- int mFlags;
-};
-
-class RestoreToCountOp : public StateOp {
-public:
- explicit RestoreToCountOp(int count)
- : mCount(count) {}
-
- virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) override {
- deferStruct.mDeferredList.addRestoreToCount(deferStruct.mRenderer,
- this, saveCount + mCount);
- deferStruct.mRenderer.restoreToCount(saveCount + mCount);
- }
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.restoreToCount(saveCount + mCount);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Restore to count %d", mCount);
- }
-
- virtual const char* name() override { return "RestoreToCount"; }
-
-private:
- int mCount;
-};
-
-class SaveLayerOp : public StateOp {
-public:
- SaveLayerOp(float left, float top, float right, float bottom, int alpha, int flags)
- : mArea(left, top, right, bottom)
- , mPaint(&mCachedPaint)
- , mFlags(flags)
- , mConvexMask(nullptr) {
- mCachedPaint.setAlpha(alpha);
- }
-
- SaveLayerOp(float left, float top, float right, float bottom, const SkPaint* paint, int flags)
- : mArea(left, top, right, bottom)
- , mPaint(paint)
- , mFlags(flags)
- , mConvexMask(nullptr)
- {}
-
- virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) override {
- // NOTE: don't bother with actual saveLayer, instead issuing it at flush time
- int newSaveCount = deferStruct.mRenderer.getSaveCount();
- deferStruct.mDeferredList.addSaveLayer(deferStruct.mRenderer, this, newSaveCount);
-
- // NOTE: don't issue full saveLayer, since that has side effects/is costly. instead just
- // setup the snapshot for deferral, and re-issue the op at flush time
- deferStruct.mRenderer.saveLayerDeferred(mArea.left, mArea.top, mArea.right, mArea.bottom,
- mPaint, mFlags);
- }
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom,
- mPaint, mFlags, mConvexMask);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("SaveLayer%s of area " RECT_STRING,
- (isSaveLayerAlpha() ? "Alpha" : ""),RECT_ARGS(mArea));
- }
-
- virtual const char* name() override {
- return isSaveLayerAlpha() ? "SaveLayerAlpha" : "SaveLayer";
- }
-
- int getFlags() { return mFlags; }
-
- // Called to make SaveLayerOp clip to the provided mask when drawing back/restored
- void setMask(const SkPath* convexMask) {
- mConvexMask = convexMask;
- }
-
-private:
- bool isSaveLayerAlpha() const {
- SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
- int alpha = PaintUtils::getAlphaDirect(mPaint);
- return alpha < 255 && mode == SkXfermode::kSrcOver_Mode;
- }
-
- Rect mArea;
- const SkPaint* mPaint;
- SkPaint mCachedPaint;
- int mFlags;
-
- // Convex path, points at data in RenderNode, valid for the duration of the frame only
- // Only used for masking the SaveLayer which wraps projected RenderNodes
- const SkPath* mConvexMask;
-};
-
-class TranslateOp : public StateOp {
-public:
- TranslateOp(float dx, float dy)
- : mDx(dx), mDy(dy) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.translate(mDx, mDy);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Translate by %f %f", mDx, mDy);
- }
-
- virtual const char* name() override { return "Translate"; }
-
-private:
- float mDx;
- float mDy;
-};
-
-class RotateOp : public StateOp {
-public:
- explicit RotateOp(float degrees)
- : mDegrees(degrees) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.rotate(mDegrees);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Rotate by %f degrees", mDegrees);
- }
-
- virtual const char* name() override { return "Rotate"; }
-
-private:
- float mDegrees;
-};
-
-class ScaleOp : public StateOp {
-public:
- ScaleOp(float sx, float sy)
- : mSx(sx), mSy(sy) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.scale(mSx, mSy);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Scale by %f %f", mSx, mSy);
- }
-
- virtual const char* name() override { return "Scale"; }
-
-private:
- float mSx;
- float mSy;
-};
-
-class SkewOp : public StateOp {
-public:
- SkewOp(float sx, float sy)
- : mSx(sx), mSy(sy) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.skew(mSx, mSy);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Skew by %f %f", mSx, mSy);
- }
-
- virtual const char* name() override { return "Skew"; }
-
-private:
- float mSx;
- float mSy;
-};
-
-class SetMatrixOp : public StateOp {
-public:
- explicit SetMatrixOp(const SkMatrix& matrix)
- : mMatrix(matrix) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- // Setting a matrix on a Canvas isn't equivalent to setting a total matrix on the scene.
- // Set a canvas-relative matrix on the renderer instead.
- renderer.setLocalMatrix(mMatrix);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- if (mMatrix.isIdentity()) {
- OP_LOGS("SetMatrix (reset)");
- } else {
- OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
- }
- }
-
- virtual const char* name() override { return "SetMatrix"; }
-
-private:
- const SkMatrix mMatrix;
-};
-
-class ConcatMatrixOp : public StateOp {
-public:
- explicit ConcatMatrixOp(const SkMatrix& matrix)
- : mMatrix(matrix) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.concatMatrix(mMatrix);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
- }
-
- virtual const char* name() override { return "ConcatMatrix"; }
-
-private:
- const SkMatrix mMatrix;
-};
-
-class ClipOp : public StateOp {
-public:
- explicit ClipOp(SkRegion::Op op) : mOp(op) {}
-
- virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) override {
- // NOTE: must defer op BEFORE applying state, since it may read clip
- deferStruct.mDeferredList.addClip(deferStruct.mRenderer, this);
-
- // TODO: Can we avoid applying complex clips at defer time?
- applyState(deferStruct.mRenderer, saveCount);
- }
-
- bool canCauseComplexClip() {
- return ((mOp != SkRegion::kIntersect_Op) && (mOp != SkRegion::kReplace_Op)) || !isRect();
- }
-
-protected:
- virtual bool isRect() { return false; }
-
- SkRegion::Op mOp;
-};
-
-class ClipRectOp : public ClipOp {
-public:
- ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op)
- : ClipOp(op), mArea(left, top, right, bottom) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.clipRect(mArea.left, mArea.top, mArea.right, mArea.bottom, mOp);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("ClipRect " RECT_STRING, RECT_ARGS(mArea));
- }
-
- virtual const char* name() override { return "ClipRect"; }
-
-protected:
- virtual bool isRect() override { return true; }
-
-private:
- Rect mArea;
-};
-
-class ClipPathOp : public ClipOp {
-public:
- ClipPathOp(const SkPath* path, SkRegion::Op op)
- : ClipOp(op), mPath(path) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.clipPath(mPath, mOp);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- SkRect bounds = mPath->getBounds();
- OP_LOG("ClipPath bounds " RECT_STRING,
- bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
- }
-
- virtual const char* name() override { return "ClipPath"; }
-
-private:
- const SkPath* mPath;
-};
-
-class ClipRegionOp : public ClipOp {
-public:
- ClipRegionOp(const SkRegion* region, SkRegion::Op op)
- : ClipOp(op), mRegion(region) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.clipRegion(mRegion, mOp);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- SkIRect bounds = mRegion->getBounds();
- OP_LOG("ClipRegion bounds %d %d %d %d",
- bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
- }
-
- virtual const char* name() override { return "ClipRegion"; }
-
-private:
- const SkRegion* mRegion;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// DRAW OPERATIONS - these are operations that can draw to the canvas's device
-///////////////////////////////////////////////////////////////////////////////
-
-class DrawBitmapOp : public DrawBoundedOp {
-public:
- DrawBitmapOp(const SkBitmap* bitmap, const SkPaint* paint)
- : DrawBoundedOp(0, 0, bitmap->width(), bitmap->height(), paint)
- , mBitmap(bitmap)
- , mEntryValid(false), mEntry(nullptr) {
- }
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawBitmap(mBitmap, mPaint);
- }
-
- AssetAtlas::Entry* getAtlasEntry(OpenGLRenderer& renderer) {
- if (!mEntryValid) {
- mEntryValid = true;
- mEntry = renderer.renderState().assetAtlas().getEntry(mBitmap->pixelRef());
- }
- return mEntry;
- }
-
-#define SET_TEXTURE(ptr, posRect, offsetRect, texCoordsRect, xDim, yDim) \
- TextureVertex::set((ptr)++, (posRect).xDim - (offsetRect).left, (posRect).yDim - (offsetRect).top, \
- (texCoordsRect).xDim, (texCoordsRect).yDim)
-
- /**
- * This multi-draw operation builds a mesh on the stack by generating a quad
- * for each bitmap in the batch. This method is also responsible for dirtying
- * the current layer, if any.
- */
- virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const std::vector<OpStatePair>& ops, const Rect& bounds) override {
- const DeferredDisplayState& firstState = *(ops[0].state);
- renderer.restoreDisplayState(firstState, true); // restore all but the clip
-
- TextureVertex vertices[6 * ops.size()];
- TextureVertex* vertex = &vertices[0];
-
- const bool hasLayer = renderer.hasLayer();
- bool pureTranslate = true;
-
- // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op,
- // and allowing them to be merged in getBatchId()
- for (unsigned int i = 0; i < ops.size(); i++) {
- const DeferredDisplayState& state = *(ops[i].state);
- const Rect& opBounds = state.mBounds;
- // When we reach multiDraw(), the matrix can be either
- // pureTranslate or simple (translate and/or scale).
- // If the matrix is not pureTranslate, then we have a scale
- pureTranslate &= state.mMatrix.isPureTranslate();
-
- Rect texCoords(0, 0, 1, 1);
- ((DrawBitmapOp*) ops[i].op)->uvMap(renderer, texCoords);
-
- SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, top);
- SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
- SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
-
- SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
- SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
- SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom);
-
- if (hasLayer) {
- renderer.dirtyLayer(opBounds.left, opBounds.top, opBounds.right, opBounds.bottom);
- }
- }
-
- renderer.drawBitmaps(mBitmap, mEntry, ops.size(), &vertices[0],
- pureTranslate, bounds, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw bitmap %p of size %dx%d%s",
- mBitmap, mBitmap->width(), mBitmap->height(),
- mEntry ? " using AssetAtlas" : "");
- }
-
- virtual const char* name() override { return "DrawBitmap"; }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
- deferInfo.mergeId = getAtlasEntry(renderer) ?
- (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap;
-
- // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation
- // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
- // MergingDrawBatch::canMergeWith()
- // TODO: support clipped bitmaps by handling them in SET_TEXTURE
- deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() &&
- !state.mClipSideFlags &&
- PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
- (mBitmap->colorType() != kAlpha_8_SkColorType);
- }
-
- void uvMap(OpenGLRenderer& renderer, Rect& texCoords) {
- if (getAtlasEntry(renderer)) {
- mEntry->uvMapper.map(texCoords);
- }
- }
-
- const SkBitmap* bitmap() { return mBitmap; }
-protected:
- const SkBitmap* mBitmap;
- bool mEntryValid;
- AssetAtlas::Entry* mEntry;
-};
-
-class DrawBitmapRectOp : public DrawBoundedOp {
-public:
- DrawBitmapRectOp(const SkBitmap* bitmap,
- float srcLeft, float srcTop, float srcRight, float srcBottom,
- float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint)
- : DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint),
- mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawBitmap(mBitmap, mSrc, mLocalBounds, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw bitmap %p src=" RECT_STRING ", dst=" RECT_STRING,
- mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
- }
-
- virtual const char* name() override { return "DrawBitmapRect"; }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
- }
-
-private:
- const SkBitmap* mBitmap;
- Rect mSrc;
-};
-
-class DrawBitmapMeshOp : public DrawBoundedOp {
-public:
- DrawBitmapMeshOp(const SkBitmap* bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors, const SkPaint* paint)
- : DrawBoundedOp(vertices, 2 * (meshWidth + 1) * (meshHeight + 1), paint),
- mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
- mVertices(vertices), mColors(colors) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawBitmapMesh(mBitmap, mMeshWidth, mMeshHeight,
- mVertices, mColors, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw bitmap %p mesh %d x %d", mBitmap, mMeshWidth, mMeshHeight);
- }
-
- virtual const char* name() override { return "DrawBitmapMesh"; }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
- }
-
-private:
- const SkBitmap* mBitmap;
- int mMeshWidth;
- int mMeshHeight;
- const float* mVertices;
- const int* mColors;
-};
-
-class DrawPatchOp : public DrawBoundedOp {
-public:
- DrawPatchOp(const SkBitmap* bitmap, const Res_png_9patch* patch,
- float left, float top, float right, float bottom, const SkPaint* paint)
- : DrawBoundedOp(left, top, right, bottom, paint),
- mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(nullptr),
- mEntryValid(false), mEntry(nullptr) {
- };
-
- AssetAtlas::Entry* getAtlasEntry(OpenGLRenderer& renderer) {
- if (!mEntryValid) {
- mEntryValid = true;
- mEntry = renderer.renderState().assetAtlas().getEntry(mBitmap->pixelRef());
- }
- return mEntry;
- }
-
- const Patch* getMesh(OpenGLRenderer& renderer) {
- if (!mMesh || renderer.getCaches().patchCache.getGenerationId() != mGenerationId) {
- PatchCache& cache = renderer.getCaches().patchCache;
- mMesh = cache.get(getAtlasEntry(renderer), mBitmap->width(), mBitmap->height(),
- mLocalBounds.getWidth(), mLocalBounds.getHeight(), mPatch);
- mGenerationId = cache.getGenerationId();
- }
- return mMesh;
- }
-
- /**
- * This multi-draw operation builds an indexed mesh on the stack by copying
- * and transforming the vertices of each 9-patch in the batch. This method
- * is also responsible for dirtying the current layer, if any.
- */
- virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const std::vector<OpStatePair>& ops, const Rect& bounds) override {
- const DeferredDisplayState& firstState = *(ops[0].state);
- renderer.restoreDisplayState(firstState, true); // restore all but the clip
-
- // Batches will usually contain a small number of items so it's
- // worth performing a first iteration to count the exact number
- // of vertices we need in the new mesh
- uint32_t totalVertices = 0;
- for (unsigned int i = 0; i < ops.size(); i++) {
- totalVertices += ((DrawPatchOp*) ops[i].op)->getMesh(renderer)->verticesCount;
- }
-
- const bool hasLayer = renderer.hasLayer();
-
- uint32_t indexCount = 0;
-
- TextureVertex vertices[totalVertices];
- TextureVertex* vertex = &vertices[0];
-
- // Create a mesh that contains the transformed vertices for all the
- // 9-patch objects that are part of the batch. Note that onDefer()
- // enforces ops drawn by this function to have a pure translate or
- // identity matrix
- for (unsigned int i = 0; i < ops.size(); i++) {
- DrawPatchOp* patchOp = (DrawPatchOp*) ops[i].op;
- const DeferredDisplayState* state = ops[i].state;
- const Patch* opMesh = patchOp->getMesh(renderer);
- uint32_t vertexCount = opMesh->verticesCount;
- if (vertexCount == 0) continue;
-
- // We use the bounds to know where to translate our vertices
- // Using patchOp->state.mBounds wouldn't work because these
- // bounds are clipped
- const float tx = (int) floorf(state->mMatrix.getTranslateX() +
- patchOp->mLocalBounds.left + 0.5f);
- const float ty = (int) floorf(state->mMatrix.getTranslateY() +
- patchOp->mLocalBounds.top + 0.5f);
-
- // Copy & transform all the vertices for the current operation
- TextureVertex* opVertices = opMesh->vertices.get();
- for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
- TextureVertex::set(vertex++,
- opVertices->x + tx, opVertices->y + ty,
- opVertices->u, opVertices->v);
- }
-
- // Dirty the current layer if possible. When the 9-patch does not
- // contain empty quads we can take a shortcut and simply set the
- // dirty rect to the object's bounds.
- if (hasLayer) {
- if (!opMesh->hasEmptyQuads) {
- renderer.dirtyLayer(tx, ty,
- tx + patchOp->mLocalBounds.getWidth(),
- ty + patchOp->mLocalBounds.getHeight());
- } else {
- const size_t count = opMesh->quads.size();
- for (size_t i = 0; i < count; i++) {
- const Rect& quadBounds = opMesh->quads[i];
- const float x = tx + quadBounds.left;
- const float y = ty + quadBounds.top;
- renderer.dirtyLayer(x, y,
- x + quadBounds.getWidth(), y + quadBounds.getHeight());
- }
- }
- }
-
- indexCount += opMesh->indexCount;
- }
-
- renderer.drawPatches(mBitmap, getAtlasEntry(renderer),
- &vertices[0], indexCount, mPaint);
- }
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- // We're not calling the public variant of drawPatch() here
- // This method won't perform the quickReject() since we've already done it at this point
- renderer.drawPatch(mBitmap, getMesh(renderer), getAtlasEntry(renderer),
- mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
- mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw patch " RECT_STRING "%s", RECT_ARGS(mLocalBounds),
- mEntry ? " with AssetAtlas" : "");
- }
-
- virtual const char* name() override { return "DrawPatch"; }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch;
- deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap;
- deferInfo.mergeable = state.mMatrix.isPureTranslate() &&
- PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
- deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mBitmap->isOpaque();
- }
-
-private:
- const SkBitmap* mBitmap;
- const Res_png_9patch* mPatch;
-
- uint32_t mGenerationId;
- const Patch* mMesh;
-
- bool mEntryValid;
- AssetAtlas::Entry* mEntry;
-};
-
-class DrawColorOp : public DrawOp {
-public:
- DrawColorOp(int color, SkXfermode::Mode mode)
- : DrawOp(nullptr), mColor(color), mMode(mode) {};
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawColor(mColor, mMode);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw color %#x, mode %d", mColor, mMode);
- }
-
- virtual const char* name() override { return "DrawColor"; }
-
-private:
- int mColor;
- SkXfermode::Mode mMode;
-};
-
-class DrawStrokableOp : public DrawBoundedOp {
-public:
- DrawStrokableOp(float left, float top, float right, float bottom, const SkPaint* paint)
- : DrawBoundedOp(left, top, right, bottom, paint) {};
- DrawStrokableOp(const Rect& localBounds, const SkPaint* paint)
- : DrawBoundedOp(localBounds, paint) {};
-
- virtual bool getLocalBounds(Rect& localBounds) override {
- localBounds.set(mLocalBounds);
- if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) {
- localBounds.outset(strokeWidthOutset());
- }
- return true;
- }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- if (mPaint->getPathEffect()) {
- deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
- } else {
- deferInfo.batchId = mPaint->isAntiAlias() ?
- DeferredDisplayList::kOpBatch_AlphaVertices :
- DeferredDisplayList::kOpBatch_Vertices;
- }
- }
-};
-
-class DrawRectOp : public DrawStrokableOp {
-public:
- DrawRectOp(float left, float top, float right, float bottom, const SkPaint* paint)
- : DrawStrokableOp(left, top, right, bottom, paint) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawRect(mLocalBounds.left, mLocalBounds.top,
- mLocalBounds.right, mLocalBounds.bottom, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Rect " RECT_STRING, RECT_ARGS(mLocalBounds));
- }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- DrawStrokableOp::onDefer(renderer, deferInfo, state);
- deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) &&
- mPaint->getStyle() == SkPaint::kFill_Style;
- }
-
- virtual const char* name() override { return "DrawRect"; }
-};
-
-class DrawRectsOp : public DrawBoundedOp {
-public:
- DrawRectsOp(const float* rects, int count, const SkPaint* paint)
- : DrawBoundedOp(rects, count, paint),
- mRects(rects), mCount(count) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawRects(mRects, mCount, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Rects count %d", mCount);
- }
-
- virtual const char* name() override { return "DrawRects"; }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- deferInfo.batchId = DeferredDisplayList::kOpBatch_Vertices;
- }
-
-private:
- const float* mRects;
- int mCount;
-};
-
-class DrawRoundRectOp : public DrawStrokableOp {
-public:
- DrawRoundRectOp(float left, float top, float right, float bottom,
- float rx, float ry, const SkPaint* paint)
- : DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawRoundRect(mLocalBounds.left, mLocalBounds.top,
- mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
- }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- DrawStrokableOp::onDefer(renderer, deferInfo, state);
- if (!mPaint->getPathEffect()) {
- renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, *mPaint,
- mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy);
- }
- }
-
- virtual const char* name() override { return "DrawRoundRect"; }
-
-private:
- float mRx;
- float mRy;
-};
-
-class DrawRoundRectPropsOp : public DrawOp {
-public:
- DrawRoundRectPropsOp(float* left, float* top, float* right, float* bottom,
- float *rx, float *ry, const SkPaint* paint)
- : DrawOp(paint), mLeft(left), mTop(top), mRight(right), mBottom(bottom),
- mRx(rx), mRy(ry) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawRoundRect(*mLeft, *mTop, *mRight, *mBottom,
- *mRx, *mRy, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw RoundRect Props " RECT_STRING ", rx %f, ry %f",
- *mLeft, *mTop, *mRight, *mBottom, *mRx, *mRy);
- }
-
- virtual const char* name() override { return "DrawRoundRectProps"; }
-
-private:
- float* mLeft;
- float* mTop;
- float* mRight;
- float* mBottom;
- float* mRx;
- float* mRy;
-};
-
-class DrawCircleOp : public DrawStrokableOp {
-public:
- DrawCircleOp(float x, float y, float radius, const SkPaint* paint)
- : DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint),
- mX(x), mY(y), mRadius(radius) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawCircle(mX, mY, mRadius, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Circle x %f, y %f, r %f", mX, mY, mRadius);
- }
-
- virtual const char* name() override { return "DrawCircle"; }
-
-private:
- float mX;
- float mY;
- float mRadius;
-};
-
-class DrawCirclePropsOp : public DrawOp {
-public:
- DrawCirclePropsOp(float* x, float* y, float* radius, const SkPaint* paint)
- : DrawOp(paint), mX(x), mY(y), mRadius(radius) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawCircle(*mX, *mY, *mRadius, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Circle Props x %p, y %p, r %p", mX, mY, mRadius);
- }
-
- virtual const char* name() override { return "DrawCircleProps"; }
-
-private:
- float* mX;
- float* mY;
- float* mRadius;
-};
-
-class DrawVectorDrawableOp : public DrawOp {
-public:
- DrawVectorDrawableOp(VectorDrawableRoot* tree, const SkRect& bounds)
- : DrawOp(nullptr), mTree(tree), mDst(bounds) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- const SkBitmap& bitmap = mTree->getBitmapUpdateIfDirty();
- SkPaint* paint = mTree->getPaint();
- renderer.drawBitmap(&bitmap, Rect(0, 0, bitmap.width(), bitmap.height()),
- mDst, paint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Vector Drawable %p", mTree);
- }
-
- virtual const char* name() override { return "DrawVectorDrawable"; }
-
-private:
- VectorDrawableRoot* mTree;
- SkRect mDst;
-
-};
-
-class DrawOvalOp : public DrawStrokableOp {
-public:
- DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint)
- : DrawStrokableOp(left, top, right, bottom, paint) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawOval(mLocalBounds.left, mLocalBounds.top,
- mLocalBounds.right, mLocalBounds.bottom, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds));
- }
-
- virtual const char* name() override { return "DrawOval"; }
-};
-
-class DrawArcOp : public DrawStrokableOp {
-public:
- DrawArcOp(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint)
- : DrawStrokableOp(left, top, right, bottom, paint),
- mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawArc(mLocalBounds.left, mLocalBounds.top,
- mLocalBounds.right, mLocalBounds.bottom,
- mStartAngle, mSweepAngle, mUseCenter, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Arc " RECT_STRING ", start %f, sweep %f, useCenter %d",
- RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
- }
-
- virtual const char* name() override { return "DrawArc"; }
-
-private:
- float mStartAngle;
- float mSweepAngle;
- bool mUseCenter;
-};
-
-class DrawPathOp : public DrawBoundedOp {
-public:
- DrawPathOp(const SkPath* path, const SkPaint* paint)
- : DrawBoundedOp(paint), mPath(path) {
- float left, top, offset;
- uint32_t width, height;
- PathCache::computePathBounds(path, paint, left, top, offset, width, height);
- left -= offset;
- top -= offset;
- mLocalBounds.set(left, top, left + width, top + height);
- }
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawPath(mPath, mPaint);
- }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- renderer.getCaches().pathCache.precache(mPath, mPaint);
-
- deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Path %p in " RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
- }
-
- virtual const char* name() override { return "DrawPath"; }
-
-private:
- const SkPath* mPath;
-};
-
-class DrawLinesOp : public DrawBoundedOp {
-public:
- DrawLinesOp(const float* points, int count, const SkPaint* paint)
- : DrawBoundedOp(points, count, paint),
- mPoints(points), mCount(count) {
- mLocalBounds.outset(strokeWidthOutset());
- }
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawLines(mPoints, mCount, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Lines count %d", mCount);
- }
-
- virtual const char* name() override { return "DrawLines"; }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- deferInfo.batchId = mPaint->isAntiAlias() ?
- DeferredDisplayList::kOpBatch_AlphaVertices :
- DeferredDisplayList::kOpBatch_Vertices;
- }
-
-protected:
- const float* mPoints;
- int mCount;
-};
-
-class DrawPointsOp : public DrawLinesOp {
-public:
- DrawPointsOp(const float* points, int count, const SkPaint* paint)
- : DrawLinesOp(points, count, paint) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawPoints(mPoints, mCount, mPaint);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Points count %d", mCount);
- }
-
- virtual const char* name() override { return "DrawPoints"; }
-};
-
-class DrawSomeTextOp : public DrawOp {
-public:
- DrawSomeTextOp(const glyph_t* text, int bytesCount, int count, const SkPaint* paint)
- : DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw some text, %d bytes", mBytesCount);
- }
-
- virtual bool hasTextShadow() const override {
- return PaintUtils::hasTextShadow(mPaint);
- }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- FontRenderer& fontRenderer = renderer.getCaches().fontRenderer.getFontRenderer();
- fontRenderer.precache(mPaint, mText, mCount, SkMatrix::I());
-
- deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
- DeferredDisplayList::kOpBatch_Text :
- DeferredDisplayList::kOpBatch_ColorText;
- }
-
-protected:
- const glyph_t* mText;
- int mBytesCount;
- int mCount;
-};
-
-class DrawTextOnPathOp : public DrawSomeTextOp {
-public:
- DrawTextOnPathOp(const glyph_t* text, int bytesCount, int count,
- const SkPath* path, float hOffset, float vOffset, const SkPaint* paint)
- : DrawSomeTextOp(text, bytesCount, count, paint),
- mPath(path), mHOffset(hOffset), mVOffset(vOffset) {
- /* TODO: inherit from DrawBounded and init mLocalBounds */
- }
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath,
- mHOffset, mVOffset, mPaint);
- }
-
- virtual const char* name() override { return "DrawTextOnPath"; }
-
-private:
- const SkPath* mPath;
- float mHOffset;
- float mVOffset;
-};
-
-class DrawTextOp : public DrawStrokableOp {
-public:
- DrawTextOp(const glyph_t* text, int bytesCount, int count, float x, float y,
- const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds)
- : DrawStrokableOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count),
- mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) {
- mPrecacheTransform = SkMatrix::InvalidMatrix();
- }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- FontRenderer& fontRenderer = renderer.getCaches().fontRenderer.getFontRenderer();
- SkMatrix transform;
- renderer.findBestFontTransform(state.mMatrix, &transform);
- if (mPrecacheTransform != transform) {
- fontRenderer.precache(mPaint, mText, mCount, transform);
- mPrecacheTransform = transform;
- }
- deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
- DeferredDisplayList::kOpBatch_Text :
- DeferredDisplayList::kOpBatch_ColorText;
-
- deferInfo.mergeId = reinterpret_cast<mergeid_t>(mPaint->getColor());
-
- // don't merge decorated text - the decorations won't draw in order
- bool hasDecorations = mPaint->getFlags()
- & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag);
-
- deferInfo.mergeable = state.mMatrix.isPureTranslate()
- && !hasDecorations
- && PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
- }
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- Rect bounds;
- getLocalBounds(bounds);
- renderer.drawText(mText, mBytesCount, mCount, mX, mY,
- mPositions, mPaint, mTotalAdvance, bounds);
- }
-
- virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const std::vector<OpStatePair>& ops, const Rect& bounds) override {
- for (unsigned int i = 0; i < ops.size(); i++) {
- const DeferredDisplayState& state = *(ops[i].state);
- DrawOpMode drawOpMode = (i == ops.size() - 1) ? DrawOpMode::kFlush : DrawOpMode::kDefer;
- renderer.restoreDisplayState(state, true); // restore all but the clip
-
- DrawTextOp& op = *((DrawTextOp*)ops[i].op);
- // quickReject() will not occure in drawText() so we can use mLocalBounds
- // directly, we do not need to account for shadow by calling getLocalBounds()
- renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY,
- op.mPositions, op.mPaint, op.mTotalAdvance, op.mLocalBounds,
- drawOpMode);
- }
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount);
- }
-
- virtual const char* name() override { return "DrawText"; }
-
-private:
- const glyph_t* mText;
- int mBytesCount;
- int mCount;
- float mX;
- float mY;
- const float* mPositions;
- float mTotalAdvance;
- SkMatrix mPrecacheTransform;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// SPECIAL DRAW OPERATIONS
-///////////////////////////////////////////////////////////////////////////////
-
-class DrawFunctorOp : public DrawOp {
-public:
- explicit DrawFunctorOp(Functor* functor)
- : DrawOp(nullptr), mFunctor(functor) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.startMark("GL functor");
- renderer.callDrawGLFunction(mFunctor, dirty);
- renderer.endMark();
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Functor %p", mFunctor);
- }
-
- virtual const char* name() override { return "DrawFunctor"; }
-
-private:
- Functor* mFunctor;
-};
-
-class DrawRenderNodeOp : public DrawBoundedOp {
- friend class RenderNode; // grant RenderNode access to info of child
- friend class DisplayList; // grant DisplayList access to info of child
- friend class DisplayListCanvas;
- friend class TestUtils;
-public:
- DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent, bool clipIsSimple)
- : DrawBoundedOp(0, 0,
- renderNode->stagingProperties().getWidth(),
- renderNode->stagingProperties().getHeight(),
- nullptr)
- , renderNode(renderNode)
- , mRecordedWithPotentialStencilClip(!clipIsSimple || !transformFromParent.isSimple())
- , localMatrix(transformFromParent)
- , skipInOrderDraw(false) {}
-
- virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
- bool useQuickReject) override {
- if (renderNode->isRenderable() && !skipInOrderDraw) {
- renderNode->defer(deferStruct, level + 1);
- }
- }
-
- virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
- bool useQuickReject) override {
- if (renderNode->isRenderable() && !skipInOrderDraw) {
- renderNode->replay(replayStruct, level + 1);
- }
- }
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- LOG_ALWAYS_FATAL("should not be called, because replay() is overridden");
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw RenderNode %p %s", renderNode, renderNode->getName());
- if (renderNode && (logFlags & kOpLogFlag_Recurse)) {
- renderNode->output(level + 1);
- }
- }
-
- virtual const char* name() override { return "DrawRenderNode"; }
-
-private:
- RenderNode* renderNode;
-
- /**
- * This RenderNode was drawn into a DisplayList with the canvas in a state that will likely
- * require rendering with stencil clipping. Either:
- *
- * 1) A path clip or rotated rect clip was in effect on the canvas at record time
- * 2) The RenderNode was recorded with a non-simple canvas transform (e.g. rotation)
- *
- * Note: even if this is false, non-rect clipping may still be applied applied either due to
- * property-driven rotation (either in this RenderNode, or any ancestor), or record time
- * clipping in an ancestor. These are handled in RenderNode::prepareTreeImpl since they are
- * dynamic (relative to a static DisplayList of a parent), and don't affect this flag.
- */
- bool mRecordedWithPotentialStencilClip;
-
- ///////////////////////////
- // Properties below are used by RenderNode::computeOrderingImpl() and issueOperations()
- ///////////////////////////
- /**
- * Records transform vs parent, used for computing total transform without rerunning DL contents
- */
- const mat4 localMatrix;
-
- /**
- * Holds the transformation between the projection surface ViewGroup and this RenderNode
- * drawing instance. Represents any translations / transformations done within the drawing of
- * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this
- * DisplayList draw instance.
- *
- * Note: doesn't include transformation within the RenderNode, or its properties.
- */
- mat4 transformFromCompositingAncestor;
- bool skipInOrderDraw;
-};
-
-/**
- * Not a canvas operation, used only by 3d / z ordering logic in RenderNode::iterate()
- */
-class DrawShadowOp : public DrawOp {
-public:
- DrawShadowOp(const mat4& transformXY, const mat4& transformZ,
- float casterAlpha, const SkPath* casterOutline)
- : DrawOp(nullptr)
- , mTransformXY(transformXY)
- , mTransformZ(transformZ)
- , mCasterAlpha(casterAlpha)
- , mCasterOutline(casterOutline) {
- }
-
- virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
- const DeferredDisplayState& state) override {
- renderer.getCaches().tessellationCache.precacheShadows(&state.mMatrix,
- renderer.getLocalClipBounds(), isCasterOpaque(), mCasterOutline,
- &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius());
- }
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- TessellationCache::vertexBuffer_pair_t buffers;
- Matrix4 drawTransform(*(renderer.currentTransform()));
- renderer.getCaches().tessellationCache.getShadowBuffers(&drawTransform,
- renderer.getLocalClipBounds(), isCasterOpaque(), mCasterOutline,
- &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius(),
- buffers);
-
- renderer.drawShadow(mCasterAlpha, buffers.first, buffers.second);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOGS("DrawShadow");
- }
-
- virtual const char* name() override { return "DrawShadow"; }
-
-private:
- bool isCasterOpaque() { return mCasterAlpha >= 1.0f; }
-
- const mat4 mTransformXY;
- const mat4 mTransformZ;
- const float mCasterAlpha;
- const SkPath* mCasterOutline;
-};
-
-class DrawLayerOp : public DrawOp {
-public:
- DrawLayerOp(Layer* layer)
- : DrawOp(nullptr), mLayer(layer) {}
-
- virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawLayer(mLayer);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Layer %p", mLayer);
- }
-
- virtual const char* name() override { return "DrawLayer"; }
-
-private:
- Layer* mLayer;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_DISPLAY_OPERATION_H
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
deleted file mode 100644
index ec2013e27401..000000000000
--- a/libs/hwui/Dither.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Caches.h"
-#include "Dither.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Lifecycle
-///////////////////////////////////////////////////////////////////////////////
-
-Dither::Dither(Caches& caches)
- : mCaches(caches)
- , mInitialized(false)
- , mDitherTexture(0) {
-}
-
-void Dither::bindDitherTexture() {
- if (!mInitialized) {
- glGenTextures(1, &mDitherTexture);
- mCaches.textureState().bindTexture(mDitherTexture);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-
- if (mCaches.extensions().hasFloatTextures()) {
- // We use a R16F texture, let's remap the alpha channel to the
- // red channel to avoid changing the shader sampling code on GL ES 3.0+
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
-
- float dither = 1.0f / (255.0f * DITHER_KERNEL_SIZE * DITHER_KERNEL_SIZE);
- const GLfloat pattern[] = {
- 0 * dither, 8 * dither, 2 * dither, 10 * dither,
- 12 * dither, 4 * dither, 14 * dither, 6 * dither,
- 3 * dither, 11 * dither, 1 * dither, 9 * dither,
- 15 * dither, 7 * dither, 13 * dither, 5 * dither
- };
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
- GL_RED, GL_FLOAT, &pattern);
- } else {
- const uint8_t pattern[] = {
- 0, 8, 2, 10,
- 12, 4, 14, 6,
- 3, 11, 1, 9,
- 15, 7, 13, 5
- };
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, &pattern);
- }
-
- mInitialized = true;
- } else {
- mCaches.textureState().bindTexture(mDitherTexture);
- }
-}
-
-void Dither::clear() {
- if (mInitialized) {
- mCaches.textureState().deleteTexture(mDitherTexture);
- mInitialized = false;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Program management
-///////////////////////////////////////////////////////////////////////////////
-
-void Dither::setupProgram(Program& program, GLuint* textureUnit) {
- GLuint textureSlot = (*textureUnit)++;
- mCaches.textureState().activateTexture(textureSlot);
-
- bindDitherTexture();
-
- glUniform1i(program.getUniform("ditherSampler"), textureSlot);
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
deleted file mode 100644
index 6af3e8384472..000000000000
--- a/libs/hwui/Dither.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_DITHER_H
-#define ANDROID_HWUI_DITHER_H
-
-#include <GLES3/gl3.h>
-
-namespace android {
-namespace uirenderer {
-
-class Caches;
-class Extensions;
-class Program;
-
-// Must be a power of two
-#define DITHER_KERNEL_SIZE 4
-// These must not use the .0f notation as they are used from GLSL
-#define DITHER_KERNEL_SIZE_INV (1.0 / 4.0)
-#define DITHER_KERNEL_SIZE_INV_SQUARE (1.0 / 16.0)
-
-/**
- * Handles dithering for programs.
- */
-class Dither {
-public:
- explicit Dither(Caches& caches);
-
- void clear();
- void setupProgram(Program& program, GLuint* textureUnit);
-
-private:
- void bindDitherTexture();
-
- Caches& mCaches;
- bool mInitialized;
- GLuint mDitherTexture;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_DITHER_H
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 02caaa49e99c..00238a25ebde 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -22,6 +22,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+
#include <utils/Log.h>
namespace android {
@@ -36,15 +37,6 @@ namespace uirenderer {
Extensions::Extensions() {
- auto extensions = StringUtils::split((const char*) glGetString(GL_EXTENSIONS));
- mHasNPot = extensions.has("GL_OES_texture_npot");
- mHasFramebufferFetch = extensions.has("GL_NV_shader_framebuffer_fetch");
- mHasDiscardFramebuffer = extensions.has("GL_EXT_discard_framebuffer");
- mHasDebugMarker = extensions.has("GL_EXT_debug_marker");
- mHas1BitStencil = extensions.has("GL_OES_stencil1");
- mHas4BitStencil = extensions.has("GL_OES_stencil4");
- mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage");
-
const char* version = (const char*) glGetString(GL_VERSION);
// Section 6.1.5 of the OpenGL ES specification indicates the GL version
@@ -64,6 +56,28 @@ Extensions::Extensions() {
mVersionMajor = 2;
mVersionMinor = 0;
}
+
+ auto extensions = StringUtils::split((const char*) glGetString(GL_EXTENSIONS));
+ mHasNPot = extensions.has("GL_OES_texture_npot");
+ mHasFramebufferFetch = extensions.has("GL_NV_shader_framebuffer_fetch");
+ mHasDiscardFramebuffer = extensions.has("GL_EXT_discard_framebuffer");
+ mHasDebugMarker = extensions.has("GL_EXT_debug_marker");
+ mHas1BitStencil = extensions.has("GL_OES_stencil1");
+ mHas4BitStencil = extensions.has("GL_OES_stencil4");
+ mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage");
+
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
+ mHasSRGB = mVersionMajor >= 3 || extensions.has("GL_EXT_sRGB");
+ mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control");
+
+ // If linear blending is enabled, the device must have (ES3.0 or EXT_sRGB)
+ // and EXT_sRGB_write_control
+ LOG_ALWAYS_FATAL_IF(!mHasSRGB, "Linear blending requires ES 3.0 or EXT_sRGB");
+ LOG_ALWAYS_FATAL_IF(!mHasSRGBWriteControl, "Linear blending requires EXT_sRGB_write_control");
+#else
+ mHasSRGB = false;
+ mHasSRGBWriteControl = false;
+#endif
}
}; // namespace uirenderer
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 67cc747015e0..2c38507bd79a 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -43,6 +43,8 @@ public:
inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
+ inline bool hasSRGB() const { return mHasSRGB; }
+ inline bool hasSRGBWriteControl() const { return hasSRGB() && mHasSRGBWriteControl; }
inline int getMajorGlVersion() const { return mVersionMajor; }
inline int getMinorGlVersion() const { return mVersionMinor; }
@@ -55,6 +57,8 @@ private:
bool mHas1BitStencil;
bool mHas4BitStencil;
bool mHasUnpackSubImage;
+ bool mHasSRGB;
+ bool mHasSRGBWriteControl;
int mVersionMajor;
int mVersionMinor;
diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h
index 9a39ec28aa3d..d8afa35d32bf 100644
--- a/libs/hwui/FloatColor.h
+++ b/libs/hwui/FloatColor.h
@@ -16,6 +16,7 @@
#ifndef FLOATCOLOR_H
#define FLOATCOLOR_H
+#include "utils/Color.h"
#include "utils/Macros.h"
#include "utils/MathUtils.h"
@@ -25,11 +26,25 @@ namespace android {
namespace uirenderer {
struct FloatColor {
+ // "color" is a gamma-encoded sRGB color
+ // After calling this method, the color is stored as a pre-multiplied linear color
+ // if linear blending is enabled. Otherwise, the color is stored as a pre-multiplied
+ // gamma-encoded sRGB color
void set(uint32_t color) {
a = ((color >> 24) & 0xff) / 255.0f;
- r = a * ((color >> 16) & 0xff) / 255.0f;
- g = a * ((color >> 8) & 0xff) / 255.0f;
- b = a * ((color ) & 0xff) / 255.0f;
+ r = a * EOCF(((color >> 16) & 0xff) / 255.0f);
+ g = a * EOCF(((color >> 8) & 0xff) / 255.0f);
+ b = a * EOCF(((color ) & 0xff) / 255.0f);
+ }
+
+ // "color" is a gamma-encoded sRGB color
+ // After calling this method, the color is stored as a linear color. The color
+ // is not pre-multiplied.
+ void setUnPreMultipliedSRGB(uint32_t color) {
+ a = ((color >> 24) & 0xff) / 255.0f;
+ r = EOCF_sRGB(((color >> 16) & 0xff) / 255.0f);
+ g = EOCF_sRGB(((color >> 8) & 0xff) / 255.0f);
+ b = EOCF_sRGB(((color ) & 0xff) / 255.0f);
}
bool isNotBlack() {
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 681cf55066b4..4f9a3de64fc2 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -16,9 +16,13 @@
#include "FontRenderer.h"
+#include "BakedOpDispatcher.h"
+#include "BakedOpRenderer.h"
+#include "BakedOpState.h"
#include "Caches.h"
#include "Debug.h"
#include "Extensions.h"
+#include "font/Font.h"
#include "Glop.h"
#include "GlopBuilder.h"
#include "PixelBuffer.h"
@@ -27,15 +31,6 @@
#include "utils/Blur.h"
#include "utils/Timing.h"
-
-#if HWUI_NEW_OPS
-#include "BakedOpDispatcher.h"
-#include "BakedOpRenderer.h"
-#include "BakedOpState.h"
-#else
-#include "OpenGLRenderer.h"
-#endif
-
#include <algorithm>
#include <cutils/properties.h>
#include <SkGlyph.h>
@@ -66,27 +61,22 @@ void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) {
}
int transformFlags = pureTranslate
? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
+ bool gammaCorrection = true;
+#else
+ bool gammaCorrection = false;
+#endif
Glop glop;
-#if HWUI_NEW_OPS
GlopBuilder(renderer->renderState(), renderer->caches(), &glop)
.setRoundRectClipState(bakedState->roundRectClipState)
.setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
.setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha)
+ .setGammaCorrection(gammaCorrection)
.setTransform(bakedState->computedState.transform, transformFlags)
.setModelViewIdentityEmptyBounds()
.build();
// Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer
renderer->renderGlop(nullptr, clip, glop);
-#else
- GlopBuilder(renderer->mRenderState, renderer->mCaches, &glop)
- .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState)
- .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
- .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha)
- .setTransform(*(renderer->currentSnapshot()), transformFlags)
- .setModelViewOffsetRect(0, 0, Rect())
- .build();
- renderer->renderGlop(glop);
-#endif
}
///////////////////////////////////////////////////////////////////////////////
@@ -304,24 +294,23 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
// Copy the glyph image, taking the mask format into account
switch (format) {
case SkMask::kA8_Format: {
- uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
- TEXTURE_BORDER_SIZE;
// write leading border line
memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
// write glyph data
if (mGammaTable) {
- for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
+ for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
row = cacheY * cacheWidth;
cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
- for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
+ for (uint32_t cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
uint8_t tempCol = bitmapBuffer[bY + bX];
cacheBuffer[row + cacheX] = mGammaTable[tempCol];
}
cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
}
} else {
- for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
+ for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
row = cacheY * cacheWidth;
memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 1e59a966750e..dd9c40f89ad0 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_FONT_RENDERER_H
-#define ANDROID_HWUI_FONT_RENDERER_H
+#pragma once
#include "font/FontUtil.h"
#include "font/CacheTexture.h"
@@ -48,31 +47,21 @@ namespace RSC {
namespace android {
namespace uirenderer {
-#if HWUI_NEW_OPS
class BakedOpState;
class BakedOpRenderer;
struct ClipBase;
-#else
-class OpenGLRenderer;
-#endif
class TextDrawFunctor {
public:
TextDrawFunctor(
-#if HWUI_NEW_OPS
BakedOpRenderer* renderer,
const BakedOpState* bakedState,
const ClipBase* clip,
-#else
- OpenGLRenderer* renderer,
-#endif
float x, float y, bool pureTranslate,
- int alpha, SkXfermode::Mode mode, const SkPaint* paint)
+ int alpha, SkBlendMode mode, const SkPaint* paint)
: renderer(renderer)
-#if HWUI_NEW_OPS
, bakedState(bakedState)
, clip(clip)
-#endif
, x(x)
, y(y)
, pureTranslate(pureTranslate)
@@ -83,18 +72,14 @@ public:
void draw(CacheTexture& texture, bool linearFiltering);
-#if HWUI_NEW_OPS
BakedOpRenderer* renderer;
const BakedOpState* bakedState;
const ClipBase* clip;
-#else
- OpenGLRenderer* renderer;
-#endif
float x;
float y;
bool pureTranslate;
int alpha;
- SkXfermode::Mode mode;
+ SkBlendMode mode;
const SkPaint* paint;
};
@@ -235,5 +220,3 @@ private:
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_FONT_RENDERER_H
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 7524ba0dcea6..a53a55a06964 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -120,7 +120,7 @@ void FrameBuilder::deferRenderNode(float tx, float ty, Rect clipRect, RenderNode
mCanvasState.save(SaveFlags::MatrixClip);
mCanvasState.translate(tx, ty);
mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
- SkRegion::kIntersect_Op);
+ SkClipOp::kIntersect);
deferNodePropsAndOps(renderNode);
mCanvasState.restore();
}
@@ -262,7 +262,7 @@ void FrameBuilder::deferNodePropsAndOps(RenderNode& node) {
Rect clipRect;
properties.getClippingRectForFlags(clipFlags, &clipRect);
mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
- SkRegion::kIntersect_Op);
+ SkClipOp::kIntersect);
}
if (properties.getRevealClip().willClip()) {
@@ -477,7 +477,7 @@ void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
projectionReceiverOutline->transform(
skCurrentTransform,
&transformedMaskPath);
- mCanvasState.setProjectionPathMask(mAllocator, &transformedMaskPath);
+ mCanvasState.setProjectionPathMask(&transformedMaskPath);
}
for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) {
@@ -608,11 +608,10 @@ void FrameBuilder::deferBitmapOp(const BitmapOp& op) {
// MergingDrawBatch::canMergeWith()
if (bakedState->computedState.transform.isSimple()
&& bakedState->computedState.transform.positiveScale()
- && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
+ && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver
&& op.bitmap->colorType() != kAlpha_8_SkColorType
&& hasMergeableClip(*bakedState)) {
mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
- // TODO: AssetAtlas in mergeId
currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId);
} else {
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
@@ -632,7 +631,7 @@ void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) {
}
void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) {
- const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
+ Bitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
SkPaint* paint = op.vectorDrawable->getPaint();
const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>(op.unmappedBounds,
op.localMatrix,
@@ -684,10 +683,9 @@ void FrameBuilder::deferPatchOp(const PatchOp& op) {
if (!bakedState) return; // quick rejected
if (bakedState->computedState.transform.isPureTranslate()
- && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
+ && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver
&& hasMergeableClip(*bakedState)) {
mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
- // TODO: AssetAtlas in mergeId
// Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together
currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId);
@@ -752,7 +750,7 @@ void FrameBuilder::deferTextOp(const TextOp& op) {
batchid_t batchId = textBatchId(*(op.paint));
if (bakedState->computedState.transform.isPureTranslate()
- && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
+ && PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver
&& hasMergeableClip(*bakedState)) {
mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor());
currentLayer().deferMergeableOp(mAllocator, bakedState, batchId, mergeId);
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index adadd32a2fc0..d3adc32da848 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -15,11 +15,8 @@
*/
#include "FrameInfoVisualizer.h"
-#if HWUI_NEW_OPS
#include "BakedOpRenderer.h"
-#else
-#include "OpenGLRenderer.h"
-#endif
+#include "IProfileRenderer.h"
#include "utils/Color.h"
#include <cutils/compiler.h>
@@ -92,7 +89,7 @@ void FrameInfoVisualizer::unionDirty(SkRect* dirty) {
}
}
-void FrameInfoVisualizer::draw(ContentRenderer* renderer) {
+void FrameInfoVisualizer::draw(IProfileRenderer& renderer) {
RETURN_IF_DISABLED();
if (mShowDirtyRegions) {
@@ -100,8 +97,8 @@ void FrameInfoVisualizer::draw(ContentRenderer* renderer) {
if (mFlashToggle) {
SkPaint paint;
paint.setColor(0x7fff0000);
- renderer->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
- mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint);
+ renderer.drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
+ mDirtyRegion.fRight, mDirtyRegion.fBottom, paint);
}
}
@@ -115,7 +112,7 @@ void FrameInfoVisualizer::draw(ContentRenderer* renderer) {
info.markSwapBuffers();
info.markFrameCompleted();
- initializeRects(renderer->getViewportHeight(), renderer->getViewportWidth());
+ initializeRects(renderer.getViewportHeight(), renderer.getViewportWidth());
drawGraph(renderer);
drawThreshold(renderer);
}
@@ -198,26 +195,26 @@ void FrameInfoVisualizer::nextBarSegment(FrameInfoIndex start, FrameInfoIndex en
}
}
-void FrameInfoVisualizer::drawGraph(ContentRenderer* renderer) {
+void FrameInfoVisualizer::drawGraph(IProfileRenderer& renderer) {
SkPaint paint;
for (size_t i = 0; i < Bar.size(); i++) {
nextBarSegment(Bar[i].start, Bar[i].end);
paint.setColor(Bar[i].color & BAR_FAST_MASK);
- renderer->drawRects(mFastRects.get(), mNumFastRects * 4, &paint);
+ renderer.drawRects(mFastRects.get(), mNumFastRects * 4, paint);
paint.setColor(Bar[i].color & BAR_JANKY_MASK);
- renderer->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint);
+ renderer.drawRects(mJankyRects.get(), mNumJankyRects * 4, paint);
}
}
-void FrameInfoVisualizer::drawThreshold(ContentRenderer* renderer) {
+void FrameInfoVisualizer::drawThreshold(IProfileRenderer& renderer) {
SkPaint paint;
paint.setColor(THRESHOLD_COLOR);
- float yLocation = renderer->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
- renderer->drawRect(0.0f,
+ float yLocation = renderer.getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
+ renderer.drawRect(0.0f,
yLocation - mThresholdStroke/2,
- renderer->getViewportWidth(),
+ renderer.getViewportWidth(),
yLocation + mThresholdStroke/2,
- &paint);
+ paint);
}
bool FrameInfoVisualizer::consumeProperties() {
diff --git a/libs/hwui/FrameInfoVisualizer.h b/libs/hwui/FrameInfoVisualizer.h
index 719d0f8c5032..b98f50101483 100644
--- a/libs/hwui/FrameInfoVisualizer.h
+++ b/libs/hwui/FrameInfoVisualizer.h
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef DRAWPROFILER_H
-#define DRAWPROFILER_H
+
+#pragma once
#include "FrameInfo.h"
#include "Properties.h"
@@ -28,13 +28,7 @@
namespace android {
namespace uirenderer {
-#if HWUI_NEW_OPS
-class BakedOpRenderer;
-typedef BakedOpRenderer ContentRenderer;
-#else
-class OpenGLRenderer;
-typedef OpenGLRenderer ContentRenderer;
-#endif
+class IProfileRenderer;
// TODO: This is a bit awkward as it needs to match the thing in CanvasContext
// A better abstraction here would be nice but iterators are painful
@@ -52,7 +46,7 @@ public:
void setDensity(float density);
void unionDirty(SkRect* dirty);
- void draw(ContentRenderer* renderer);
+ void draw(IProfileRenderer& renderer);
void dumpData(int fd);
@@ -62,8 +56,8 @@ private:
void initializeRects(const int baseline, const int width);
void nextBarSegment(FrameInfoIndex start, FrameInfoIndex end);
- void drawGraph(ContentRenderer* renderer);
- void drawThreshold(ContentRenderer* renderer);
+ void drawGraph(IProfileRenderer& renderer);
+ void drawThreshold(IProfileRenderer& renderer);
inline float durationMS(size_t index, FrameInfoIndex start, FrameInfoIndex end) {
float duration = mFrameSource[index].duration(start, end) * 0.000001f;
@@ -93,5 +87,3 @@ private:
} /* namespace uirenderer */
} /* namespace android */
-
-#endif /* DRAWPROFILER_H */
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
index 96cac86386b5..8aff0a24b4f0 100644
--- a/libs/hwui/GammaFontRenderer.cpp
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -24,12 +24,13 @@ namespace uirenderer {
GammaFontRenderer::GammaFontRenderer() {
INIT_LOGD("Creating lookup gamma font renderer");
+#ifndef ANDROID_ENABLE_LINEAR_BLENDING
// Compute the gamma tables
const float gamma = 1.0f / Properties::textGamma;
-
for (uint32_t i = 0; i <= 255; i++) {
mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f));
}
+#endif
}
void GammaFontRenderer::endPrecaching() {
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index bd27a1a72060..c9cf69bc7d9f 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -18,11 +18,6 @@
#define ANDROID_HWUI_GAMMA_FONT_RENDERER_H
#include "FontRenderer.h"
-#include "Program.h"
-
-#include <SkPaint.h>
-
-#include <utils/String8.h>
namespace android {
namespace uirenderer {
@@ -43,7 +38,11 @@ public:
FontRenderer& getFontRenderer() {
if (!mRenderer) {
- mRenderer.reset(new FontRenderer(&mGammaTable[0]));
+ const uint8_t* table = nullptr;
+#ifndef ANDROID_ENABLE_LINEAR_BLENDING
+ table = &mGammaTable[0];
+#endif
+ mRenderer.reset(new FontRenderer(table));
}
return *mRenderer;
}
@@ -64,7 +63,9 @@ public:
private:
std::unique_ptr<FontRenderer> mRenderer;
+#ifndef ANDROID_ENABLE_LINEAR_BLENDING
uint8_t mGammaTable[256];
+#endif
};
}; // namespace uirenderer
diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp
new file mode 100644
index 000000000000..c0ab895260ab
--- /dev/null
+++ b/libs/hwui/GlLayer.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GlLayer.h"
+
+#include "Caches.h"
+#include "RenderNode.h"
+#include "renderstate/RenderState.h"
+#include "utils/TraceUtils.h"
+
+#include <utils/Log.h>
+
+#define ATRACE_LAYER_WORK(label) \
+ ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", \
+ label, \
+ (renderNode.get() != NULL) ? renderNode->getName() : "", \
+ getWidth(), getHeight())
+
+namespace android {
+namespace uirenderer {
+
+GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight)
+ : Layer(renderState, Api::OpenGL)
+ , caches(Caches::getInstance())
+ , texture(caches) {
+ texture.mWidth = layerWidth;
+ texture.mHeight = layerHeight;
+}
+
+GlLayer::~GlLayer() {
+ if (texture.mId) {
+ texture.deleteTexture();
+ }
+}
+
+void GlLayer::onGlContextLost() {
+ texture.deleteTexture();
+}
+
+void GlLayer::bindTexture() const {
+ if (texture.mId) {
+ caches.textureState().bindTexture(texture.target(), texture.mId);
+ }
+}
+
+void GlLayer::generateTexture() {
+ if (!texture.mId) {
+ glGenTextures(1, &texture.mId);
+ }
+}
+
+void GlLayer::clearTexture() {
+ // There's a rare possibility that Caches could have been destroyed already
+ // since this method is queued up as a task.
+ // Since this is a reset method, treat this as non-fatal.
+ if (caches.isInitialized()) {
+ caches.textureState().unbindTexture(texture.mId);
+ }
+ texture.mId = 0;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h
new file mode 100644
index 000000000000..54bf5adc3ace
--- /dev/null
+++ b/libs/hwui/GlLayer.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Layer.h"
+
+#include "Texture.h"
+
+namespace android {
+namespace uirenderer {
+
+// Forward declarations
+class Caches;
+
+/**
+ * A layer has dimensions and is backed by an OpenGL texture or FBO.
+ */
+class GlLayer : public Layer {
+public:
+ GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight);
+ virtual ~GlLayer();
+
+ uint32_t getWidth() const override {
+ return texture.mWidth;
+ }
+
+ uint32_t getHeight() const override {
+ return texture.mHeight;
+ }
+
+ void setSize(uint32_t width, uint32_t height) override {
+ texture.updateSize(width, height, texture.internalFormat(), texture.format(),
+ texture.target());
+ }
+
+ void setBlend(bool blend) override {
+ texture.blend = blend;
+ }
+
+ bool isBlend() const override {
+ return texture.blend;
+ }
+
+ inline GLuint getTextureId() const {
+ return texture.id();
+ }
+
+ inline Texture& getTexture() {
+ return texture;
+ }
+
+ inline GLenum getRenderTarget() const {
+ return texture.target();
+ }
+
+ inline void setRenderTarget(GLenum renderTarget) {
+ texture.mTarget = renderTarget;
+ }
+
+ inline bool isRenderable() const {
+ return texture.target() != GL_NONE;
+ }
+
+ void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) {
+ texture.setWrap(wrap, bindTexture, force);
+ }
+
+ void setFilter(GLenum filter, bool bindTexture = false, bool force = false) {
+ texture.setFilter(filter, bindTexture, force);
+ }
+
+ void bindTexture() const;
+ void generateTexture();
+
+ /**
+ * When the caller frees the texture itself, the caller
+ * must call this method to tell this layer that it lost
+ * the texture.
+ */
+ void clearTexture();
+
+ /**
+ * Lost the GL context but the layer is still around, mark it invalid internally
+ * so the dtor knows not to do any GL work
+ */
+ void onGlContextLost();
+
+private:
+ Caches& caches;
+
+ /**
+ * The texture backing this layer.
+ */
+ Texture texture;
+}; // struct GlLayer
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index 6433c86908e7..34c7934db198 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_GLOP_H
-#define ANDROID_HWUI_GLOP_H
+#pragma once
#include "FloatColor.h"
#include "Matrix.h"
@@ -26,7 +25,6 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-#include <SkXfermode.h>
namespace android {
namespace uirenderer {
@@ -68,7 +66,7 @@ namespace TransformFlags {
OffsetByFudgeFactor = 1 << 0,
// Canvas transform isn't applied to the mesh at draw time,
- //since it's already built in.
+ // since it's already built in.
MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove for HWUI_NEW_OPS
};
};
@@ -120,7 +118,6 @@ public:
struct TextureData {
Texture* texture;
- GLenum target;
GLenum filter;
GLenum clamp;
Matrix4* textureTransform;
@@ -168,14 +165,6 @@ public:
GLenum dst;
} blend;
-#if !HWUI_NEW_OPS
- /**
- * Bounds of the drawing command in layer space. Only mapped into layer
- * space once GlopBuilder::build() is called.
- */
- Rect bounds; // TODO: remove for HWUI_NEW_OPS
-#endif
-
/**
* Additional render state to enumerate:
* - scissor + (bits for whether each of LTRB needed?)
@@ -185,5 +174,3 @@ public:
} /* namespace uirenderer */
} /* namespace android */
-
-#endif // ANDROID_HWUI_GLOP_H
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index e502725ddb09..8a6e038d8e0d 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -16,9 +16,12 @@
#include "GlopBuilder.h"
#include "Caches.h"
+#include "GlLayer.h"
#include "Glop.h"
+#include "Layer.h"
#include "Matrix.h"
#include "Patch.h"
+#include "PathCache.h"
#include "renderstate/MeshState.h"
#include "renderstate/RenderState.h"
#include "SkiaShader.h"
@@ -165,20 +168,6 @@ GlopBuilder& GlopBuilder::setMeshTexturedIndexedQuads(TextureVertex* vertexData,
return *this;
}
-GlopBuilder& GlopBuilder::setMeshTexturedMesh(TextureVertex* vertexData, int elementCount) {
- TRIGGER_STAGE(kMeshStage);
-
- mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
- mOutGlop->mesh.indices = { 0, nullptr };
- mOutGlop->mesh.vertices = {
- 0,
- VertexAttribFlags::TextureCoord,
- &vertexData[0].x, &vertexData[0].u, nullptr,
- kTextureVertexStride };
- mOutGlop->mesh.elementCount = elementCount;
- return *this;
-}
-
GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount) {
TRIGGER_STAGE(kMeshStage);
@@ -232,19 +221,19 @@ GlopBuilder& GlopBuilder::setMeshPatchQuads(const Patch& patch) {
////////////////////////////////////////////////////////////////////////////////
void GlopBuilder::setFill(int color, float alphaScale,
- SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
+ SkBlendMode mode, Blend::ModeOrderSwap modeUsage,
const SkShader* shader, const SkColorFilter* colorFilter) {
- if (mode != SkXfermode::kClear_Mode) {
- float alpha = (SkColorGetA(color) / 255.0f) * alphaScale;
+ if (mode != SkBlendMode::kClear) {
if (!shader) {
- float colorScale = alpha / 255.0f;
- mOutGlop->fill.color = {
- colorScale * SkColorGetR(color),
- colorScale * SkColorGetG(color),
- colorScale * SkColorGetB(color),
- alpha
- };
+ FloatColor c;
+ c.set(color);
+ c.r *= alphaScale;
+ c.g *= alphaScale;
+ c.b *= alphaScale;
+ c.a *= alphaScale;
+ mOutGlop->fill.color = c;
} else {
+ float alpha = (SkColorGetA(color) / 255.0f) * alphaScale;
mOutGlop->fill.color = { 1, 1, 1, alpha };
}
} else {
@@ -258,8 +247,8 @@ void GlopBuilder::setFill(int color, float alphaScale,
|| mOutGlop->roundRectClipState
|| PaintUtils::isBlendedShader(shader)
|| PaintUtils::isBlendedColorFilter(colorFilter)
- || mode != SkXfermode::kSrcOver_Mode) {
- if (CC_LIKELY(mode <= SkXfermode::kScreen_Mode)) {
+ || mode != SkBlendMode::kSrcOver) {
+ if (CC_LIKELY(mode <= SkBlendMode::kScreen)) {
Blend::getFactors(mode, modeUsage,
&mOutGlop->blend.src, &mOutGlop->blend.dst);
} else {
@@ -274,7 +263,7 @@ void GlopBuilder::setFill(int color, float alphaScale,
// blending in shader, don't enable
} else {
// unsupported
- Blend::getFactors(SkXfermode::kSrcOver_Mode, modeUsage,
+ Blend::getFactors(SkBlendMode::kSrcOver, modeUsage,
&mOutGlop->blend.src, &mOutGlop->blend.dst);
}
}
@@ -283,20 +272,12 @@ void GlopBuilder::setFill(int color, float alphaScale,
if (colorFilter) {
SkColor color;
- SkXfermode::Mode mode;
+ SkBlendMode bmode;
SkScalar srcColorMatrix[20];
- if (colorFilter->asColorMode(&color, &mode)) {
+ if (colorFilter->asColorMode(&color, &bmode)) {
mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Blend;
- mDescription.colorMode = mode;
-
- const float alpha = SkColorGetA(color) / 255.0f;
- float colorScale = alpha / 255.0f;
- mOutGlop->fill.filter.color = {
- colorScale * SkColorGetR(color),
- colorScale * SkColorGetG(color),
- colorScale * SkColorGetB(color),
- alpha,
- };
+ mDescription.colorMode = bmode;
+ mOutGlop->fill.filter.color.set(color);
} else if (colorFilter->asColorMatrix(srcColorMatrix)) {
mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Matrix;
@@ -309,10 +290,10 @@ void GlopBuilder::setFill(int color, float alphaScale,
// Skia uses the range [0..255] for the addition vector, but we need
// the [0..1] range to apply the vector in GLSL
float* colorVector = mOutGlop->fill.filter.matrix.vector;
- colorVector[0] = srcColorMatrix[4] / 255.0f;
- colorVector[1] = srcColorMatrix[9] / 255.0f;
- colorVector[2] = srcColorMatrix[14] / 255.0f;
- colorVector[3] = srcColorMatrix[19] / 255.0f;
+ colorVector[0] = EOCF(srcColorMatrix[4] / 255.0f);
+ colorVector[1] = EOCF(srcColorMatrix[9] / 255.0f);
+ colorVector[2] = EOCF(srcColorMatrix[14] / 255.0f);
+ colorVector[3] = srcColorMatrix[19] / 255.0f; // alpha is linear
} else {
LOG_ALWAYS_FATAL("unsupported ColorFilter");
}
@@ -328,8 +309,7 @@ GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture,
GLenum filter = (textureFillFlags & TextureFillFlags::ForceFilter)
? GL_LINEAR : PaintUtils::getFilter(paint);
- mOutGlop->fill.texture = { &texture,
- GL_TEXTURE_2D, filter, GL_CLAMP_TO_EDGE, nullptr };
+ mOutGlop->fill.texture = { &texture, filter, GL_CLAMP_TO_EDGE, nullptr };
if (paint) {
int color = paint->getColor();
@@ -341,7 +321,7 @@ GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture,
shader = nullptr;
}
setFill(color, alphaScale,
- PaintUtils::getXfermode(paint->getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ paint->getBlendMode(), Blend::ModeOrderSwap::NoSwap,
shader, paint->getColorFilter());
} else {
mOutGlop->fill.color = { alphaScale, alphaScale, alphaScale, alphaScale };
@@ -350,7 +330,7 @@ GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture,
|| (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha)
|| texture.blend
|| mOutGlop->roundRectClipState) {
- Blend::getFactors(SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
+ Blend::getFactors(SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap,
&mOutGlop->blend.src, &mOutGlop->blend.dst);
} else {
mOutGlop->blend = { GL_ZERO, GL_ZERO };
@@ -372,15 +352,15 @@ GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale, b
if (CC_LIKELY(!shadowInterp)) {
mOutGlop->fill.texture = {
- nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+ nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
} else {
mOutGlop->fill.texture = {
- mCaches.textureState().getShadowLutTexture(), GL_TEXTURE_2D,
+ mCaches.textureState().getShadowLutTexture(),
GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
}
setFill(paint.getColor(), alphaScale,
- PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
paint.getShader(), paint.getColorFilter());
mDescription.useShadowAlphaInterp = shadowInterp;
mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
@@ -393,10 +373,10 @@ GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture,
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
//specify invalid filter/clamp, since these are always static for PathTextures
- mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+ mOutGlop->fill.texture = { &texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
setFill(paint.getColor(), alphaScale,
- PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
paint.getShader(), paint.getColorFilter());
mDescription.hasAlpha8Texture = true;
@@ -410,7 +390,7 @@ GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
//specify invalid filter/clamp, since these are always static for ShadowTextures
- mOutGlop->fill.texture = { &texture, GL_TEXTURE_2D, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+ mOutGlop->fill.texture = { &texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
const int ALPHA_BITMASK = SK_ColorBLACK;
const int COLOR_BITMASK = ~ALPHA_BITMASK;
@@ -420,7 +400,7 @@ GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int
}
setFill(shadowColor, alphaScale,
- PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
+ paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
paint.getShader(), paint.getColorFilter());
mDescription.hasAlpha8Texture = true;
@@ -432,8 +412,8 @@ GlopBuilder& GlopBuilder::setFillBlack() {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
- mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
- setFill(SK_ColorBLACK, 1.0f, SkXfermode::kSrcOver_Mode, Blend::ModeOrderSwap::NoSwap,
+ mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+ setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap,
nullptr, nullptr);
return *this;
}
@@ -442,19 +422,18 @@ GlopBuilder& GlopBuilder::setFillClear() {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
- mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
- setFill(SK_ColorBLACK, 1.0f, SkXfermode::kClear_Mode, Blend::ModeOrderSwap::NoSwap,
+ mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+ setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kClear, Blend::ModeOrderSwap::NoSwap,
nullptr, nullptr);
return *this;
}
GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
- float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage) {
+ float alpha, SkBlendMode mode, Blend::ModeOrderSwap modeUsage) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
- mOutGlop->fill.texture = { &texture,
- GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
+ mOutGlop->fill.texture = { &texture, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter);
@@ -462,12 +441,12 @@ GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* co
return *this;
}
-GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) {
+GlopBuilder& GlopBuilder::setFillTextureLayer(GlLayer& layer, float alpha) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
mOutGlop->fill.texture = { &(layer.getTexture()),
- layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
+ GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap,
nullptr, layer.getColorFilter());
@@ -481,11 +460,9 @@ GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& text
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
- mOutGlop->fill.texture = { &texture,
- GL_TEXTURE_EXTERNAL_OES, GL_LINEAR, GL_CLAMP_TO_EDGE,
- &textureTransform };
+ mOutGlop->fill.texture = { &texture, GL_LINEAR, GL_CLAMP_TO_EDGE, &textureTransform };
- setFill(SK_ColorWHITE, 1.0f, SkXfermode::kSrc_Mode, Blend::ModeOrderSwap::NoSwap,
+ setFill(SK_ColorWHITE, 1.0f, SkBlendMode::kSrc, Blend::ModeOrderSwap::NoSwap,
nullptr, nullptr);
mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
@@ -493,6 +470,13 @@ GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& text
return *this;
}
+GlopBuilder& GlopBuilder::setGammaCorrection(bool enabled) {
+ REQUIRE_STAGES(kFillStage);
+
+ mDescription.hasGammaCorrection = enabled;
+ return *this;
+}
+
////////////////////////////////////////////////////////////////////////////////
// Transform
////////////////////////////////////////////////////////////////////////////////
@@ -514,9 +498,6 @@ GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) {
mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f);
mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
-#if !HWUI_NEW_OPS
- mOutGlop->bounds = destination;
-#endif
return *this;
}
@@ -540,9 +521,6 @@ GlopBuilder& GlopBuilder::setModelViewMapUnitToRectSnap(const Rect destination)
mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f);
mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
-#if !HWUI_NEW_OPS
- mOutGlop->bounds = destination;
-#endif
return *this;
}
@@ -550,10 +528,6 @@ GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, c
TRIGGER_STAGE(kModelViewStage);
mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
-#if !HWUI_NEW_OPS
- mOutGlop->bounds = source;
- mOutGlop->bounds.translate(offsetX, offsetY);
-#endif
return *this;
}
@@ -573,10 +547,6 @@ GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offset
}
mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
-#if !HWUI_NEW_OPS
- mOutGlop->bounds = source;
- mOutGlop->bounds.translate(offsetX, offsetY);
-#endif
return *this;
}
@@ -630,7 +600,7 @@ void verify(const ProgramDescription& description, const Glop& glop) {
void GlopBuilder::build() {
REQUIRE_STAGES(kAllStages);
if (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) {
- if (mOutGlop->fill.texture.target == GL_TEXTURE_2D) {
+ if (mOutGlop->fill.texture.texture->target() == GL_TEXTURE_2D) {
mDescription.hasTexture = true;
} else {
mDescription.hasExternalTexture = true;
@@ -676,9 +646,6 @@ void GlopBuilder::build() {
// Final step: populate program and map bounds into render target space
mOutGlop->fill.program = mCaches.programCache.get(mDescription);
-#if !HWUI_NEW_OPS
- mOutGlop->transform.meshTransform().mapRect(mOutGlop->bounds);
-#endif
}
void GlopBuilder::dump(const Glop& glop) {
@@ -698,7 +665,8 @@ void GlopBuilder::dump(const Glop& glop) {
ALOGD(" program %p", fill.program);
if (fill.texture.texture) {
ALOGD(" texture %p, target %d, filter %d, clamp %d",
- fill.texture.texture, fill.texture.target, fill.texture.filter, fill.texture.clamp);
+ fill.texture.texture, fill.texture.texture->target(),
+ fill.texture.filter, fill.texture.clamp);
if (fill.texture.textureTransform) {
fill.texture.textureTransform->dump("texture transform");
}
@@ -718,9 +686,6 @@ void GlopBuilder::dump(const Glop& glop) {
ALOGD_IF(glop.roundRectClipState, "Glop RRCS %p", glop.roundRectClipState);
ALOGD("Glop blend %d %d", glop.blend.src, glop.blend.dst);
-#if !HWUI_NEW_OPS
- ALOGD("Glop bounds " RECT_STRING, RECT_ARGS(glop.bounds));
-#endif
}
} /* namespace uirenderer */
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 1c520c26110e..87b1568ed72b 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -13,11 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef RENDERSTATE_GLOPBUILDER_H
-#define RENDERSTATE_GLOPBUILDER_H
+
+#pragma once
#include "Glop.h"
-#include "OpenGLRenderer.h"
#include "Program.h"
#include "renderstate/Blend.h"
#include "utils/Macros.h"
@@ -29,10 +28,15 @@ namespace android {
namespace uirenderer {
class Caches;
+class GlLayer;
class Matrix4;
+class Patch;
class RenderState;
class Texture;
+class UvMapper;
class VertexBuffer;
+struct PathTexture;
+struct ShadowTexture;
namespace TextureFillFlags {
enum {
@@ -53,7 +57,6 @@ public:
GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs);
GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer);
GlopBuilder& setMeshIndexedQuads(Vertex* vertexData, int quadCount);
- GlopBuilder& setMeshTexturedMesh(TextureVertex* vertexData, int elementCount); // TODO: delete
GlopBuilder& setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount); // TODO: use indexed quads
GlopBuilder& setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount); // TODO: take quadCount
GlopBuilder& setMeshPatchQuads(const Patch& patch);
@@ -68,16 +71,12 @@ public:
GlopBuilder& setFillBlack();
GlopBuilder& setFillClear();
GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
- float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage);
- GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
- // TODO: Texture should probably know and own its target.
- // setFillLayer() forces it to GL_TEXTURE which isn't always correct.
- // Similarly setFillLayer normally forces its own wrap & filter mode
+ float alpha, SkBlendMode mode, Blend::ModeOrderSwap modeUsage);
+ GlopBuilder& setFillTextureLayer(GlLayer& layer, float alpha);
+ // TODO: setFillLayer normally forces its own wrap & filter mode,
+ // which isn't always correct.
GlopBuilder& setFillExternalTexture(Texture& texture, Matrix4& textureTransform);
- GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) {
- return setTransform(*snapshot.transform, transformFlags);
- }
GlopBuilder& setTransform(const Matrix4& canvas, const int transformFlags);
GlopBuilder& setModelViewMapUnitToRect(const Rect destination);
@@ -106,12 +105,14 @@ public:
GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState);
+ GlopBuilder& setGammaCorrection(bool enabled);
+
void build();
static void dump(const Glop& glop);
private:
void setFill(int color, float alphaScale,
- SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
+ SkBlendMode mode, Blend::ModeOrderSwap modeUsage,
const SkShader* shader, const SkColorFilter* colorFilter);
enum StageFlags {
@@ -133,5 +134,3 @@ private:
} /* namespace uirenderer */
} /* namespace android */
-
-#endif // RENDERSTATE_GLOPBUILDER_H
diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp
index 4fb57019264d..a52ec8738015 100644
--- a/libs/hwui/GpuMemoryTracker.cpp
+++ b/libs/hwui/GpuMemoryTracker.cpp
@@ -67,13 +67,13 @@ void GpuMemoryTracker::stopTrackingObject() {
gObjectStats[static_cast<int>(mType)].count--;
}
-void GpuMemoryTracker::onGLContextCreated() {
- LOG_ALWAYS_FATAL_IF(gGpuThread != 0, "We already have a GL thread? "
- "current = %lu, gl thread = %lu", pthread_self(), gGpuThread);
+void GpuMemoryTracker::onGpuContextCreated() {
+ LOG_ALWAYS_FATAL_IF(gGpuThread != 0, "We already have a gpu thread? "
+ "current = %lu, gpu thread = %lu", pthread_self(), gGpuThread);
gGpuThread = pthread_self();
}
-void GpuMemoryTracker::onGLContextDestroyed() {
+void GpuMemoryTracker::onGpuContextDestroyed() {
gGpuThread = 0;
if (CC_UNLIKELY(gObjectSet.size() > 0)) {
std::stringstream os;
diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h
index 352f3d785c54..18e2330668b0 100644
--- a/libs/hwui/GpuMemoryTracker.h
+++ b/libs/hwui/GpuMemoryTracker.h
@@ -44,8 +44,8 @@ public:
GpuObjectType objectType() { return mType; }
int objectSize() { return mSize; }
- static void onGLContextCreated();
- static void onGLContextDestroyed();
+ static void onGpuContextCreated();
+ static void onGpuContextDestroyed();
static void dump();
static void dump(std::ostream& stream);
static int getInstanceCount(GpuObjectType type);
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index c8f5e9435594..1dad58fd64b9 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -67,7 +67,8 @@ GradientCache::GradientCache(Extensions& extensions)
, mSize(0)
, mMaxSize(Properties::gradientCacheSize)
, mUseFloatTexture(extensions.hasFloatTextures())
- , mHasNpot(extensions.hasNPot()){
+ , mHasNpot(extensions.hasNPot())
+ , mHasSRGB(extensions.hasSRGB()) {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
mCache.setOnEntryRemovedListener(this);
@@ -176,71 +177,61 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
size_t GradientCache::bytesPerPixel() const {
// We use 4 channels (RGBA)
- return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
-}
-
-void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const {
- outColor.r = (inColor >> 16) & 0xff;
- outColor.g = (inColor >> 8) & 0xff;
- outColor.b = (inColor >> 0) & 0xff;
- outColor.a = (inColor >> 24) & 0xff;
+ return 4 * (mUseFloatTexture ? /* fp16 */ 2 : sizeof(uint8_t));
}
-void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const {
- outColor.r = ((inColor >> 16) & 0xff) / 255.0f;
- outColor.g = ((inColor >> 8) & 0xff) / 255.0f;
- outColor.b = ((inColor >> 0) & 0xff) / 255.0f;
- outColor.a = ((inColor >> 24) & 0xff) / 255.0f;
+size_t GradientCache::sourceBytesPerPixel() const {
+ // We use 4 channels (RGBA) and upload from floats (not half floats)
+ return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
}
-void GradientCache::mixBytes(GradientColor& start, GradientColor& end, float amount,
- uint8_t*& dst) const {
+void GradientCache::mixBytes(const FloatColor& start, const FloatColor& end,
+ float amount, uint8_t*& dst) const {
float oppAmount = 1.0f - amount;
- const float alpha = start.a * oppAmount + end.a * amount;
- const float a = alpha / 255.0f;
-
- *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount));
- *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount));
- *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount));
- *dst++ = uint8_t(alpha);
+ float a = start.a * oppAmount + end.a * amount;
+ *dst++ = uint8_t(a * OECF_sRGB((start.r * oppAmount + end.r * amount)) * 255.0f);
+ *dst++ = uint8_t(a * OECF_sRGB((start.g * oppAmount + end.g * amount)) * 255.0f);
+ *dst++ = uint8_t(a * OECF_sRGB((start.b * oppAmount + end.b * amount)) * 255.0f);
+ *dst++ = uint8_t(a * 255.0f);
}
-void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float amount,
- uint8_t*& dst) const {
+void GradientCache::mixFloats(const FloatColor& start, const FloatColor& end,
+ float amount, uint8_t*& dst) const {
float oppAmount = 1.0f - amount;
- const float a = start.a * oppAmount + end.a * amount;
-
+ float a = start.a * oppAmount + end.a * amount;
float* d = (float*) dst;
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
*d++ = a * (start.r * oppAmount + end.r * amount);
*d++ = a * (start.g * oppAmount + end.g * amount);
*d++ = a * (start.b * oppAmount + end.b * amount);
+#else
+ *d++ = a * OECF_sRGB(start.r * oppAmount + end.r * amount);
+ *d++ = a * OECF_sRGB(start.g * oppAmount + end.g * amount);
+ *d++ = a * OECF_sRGB(start.b * oppAmount + end.b * amount);
+#endif
*d++ = a;
-
dst += 4 * sizeof(float);
}
void GradientCache::generateTexture(uint32_t* colors, float* positions,
const uint32_t width, const uint32_t height, Texture* texture) {
- const GLsizei rowBytes = width * bytesPerPixel();
+ const GLsizei rowBytes = width * sourceBytesPerPixel();
uint8_t pixels[rowBytes * height];
- static ChannelSplitter gSplitters[] = {
- &android::uirenderer::GradientCache::splitToBytes,
- &android::uirenderer::GradientCache::splitToFloats,
- };
- ChannelSplitter split = gSplitters[mUseFloatTexture];
-
static ChannelMixer gMixers[] = {
+ // colors are stored gamma-encoded
&android::uirenderer::GradientCache::mixBytes,
+ // colors are stored in linear (linear blending on)
+ // or gamma-encoded (linear blending off)
&android::uirenderer::GradientCache::mixFloats,
};
ChannelMixer mix = gMixers[mUseFloatTexture];
- GradientColor start;
- (this->*split)(colors[0], start);
+ FloatColor start;
+ start.setUnPreMultipliedSRGB(colors[0]);
- GradientColor end;
- (this->*split)(colors[1], end);
+ FloatColor end;
+ end.setUnPreMultipliedSRGB(colors[1]);
int currentPos = 1;
float startPos = positions[0];
@@ -255,7 +246,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions,
currentPos++;
- (this->*split)(colors[currentPos], end);
+ end.setUnPreMultipliedSRGB(colors[currentPos]);
distance = positions[currentPos] - startPos;
}
@@ -266,10 +257,10 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions,
memcpy(pixels + rowBytes, pixels, rowBytes);
if (mUseFloatTexture) {
- // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering
texture->upload(GL_RGBA16F, width, height, GL_RGBA, GL_FLOAT, pixels);
} else {
- texture->upload(GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ GLint internalFormat = mHasSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA;
+ texture->upload(internalFormat, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
}
texture->setFilter(GL_LINEAR);
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 49be19ab1c81..5e35435ed64c 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -26,6 +26,8 @@
#include <utils/LruCache.h>
#include <utils/Mutex.h>
+#include "FloatColor.h"
+
namespace android {
namespace uirenderer {
@@ -150,25 +152,15 @@ private:
void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info);
size_t bytesPerPixel() const;
+ size_t sourceBytesPerPixel() const;
- struct GradientColor {
- float r;
- float g;
- float b;
- float a;
- };
-
- typedef void (GradientCache::*ChannelSplitter)(uint32_t inColor,
- GradientColor& outColor) const;
-
- void splitToBytes(uint32_t inColor, GradientColor& outColor) const;
- void splitToFloats(uint32_t inColor, GradientColor& outColor) const;
-
- typedef void (GradientCache::*ChannelMixer)(GradientColor& start, GradientColor& end,
+ typedef void (GradientCache::*ChannelMixer)(const FloatColor& start, const FloatColor& end,
float amount, uint8_t*& dst) const;
- void mixBytes(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const;
- void mixFloats(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const;
+ void mixBytes(const FloatColor& start, const FloatColor& end,
+ float amount, uint8_t*& dst) const;
+ void mixFloats(const FloatColor& start, const FloatColor& end,
+ float amount, uint8_t*& dst) const;
LruCache<GradientCacheEntry, Texture*> mCache;
@@ -178,6 +170,7 @@ private:
GLint mMaxTextureSize;
bool mUseFloatTexture;
bool mHasNpot;
+ bool mHasSRGB;
mutable Mutex mLock;
}; // class GradientCache
diff --git a/libs/hwui/IProfileRenderer.h b/libs/hwui/IProfileRenderer.h
new file mode 100644
index 000000000000..947ed34cc070
--- /dev/null
+++ b/libs/hwui/IProfileRenderer.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkPaint.h"
+
+namespace android {
+namespace uirenderer {
+
+class IProfileRenderer {
+public:
+ virtual void drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) = 0;
+ virtual void drawRects(const float* rects, int count, const SkPaint& paint) = 0;
+ virtual uint32_t getViewportWidth() = 0;
+ virtual uint32_t getViewportHeight() = 0;
+
+ virtual ~IProfileRenderer() {}
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index b5a33afccdc7..d740c038f36d 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -89,6 +89,39 @@ float OvershootInterpolator::interpolate(float t) {
return t * t * ((mTension + 1) * t + mTension) + 1.0f;
}
+float PathInterpolator::interpolate(float t) {
+ if (t <= 0) {
+ return 0;
+ } else if (t >= 1) {
+ return 1;
+ }
+ // Do a binary search for the correct x to interpolate between.
+ size_t startIndex = 0;
+ size_t endIndex = mX.size() - 1;
+
+ while (endIndex > startIndex + 1) {
+ int midIndex = (startIndex + endIndex) / 2;
+ if (t < mX[midIndex]) {
+ endIndex = midIndex;
+ } else {
+ startIndex = midIndex;
+ }
+ }
+
+ float xRange = mX[endIndex] - mX[startIndex];
+ if (xRange == 0) {
+ return mY[startIndex];
+ }
+
+ float tInRange = t - mX[startIndex];
+ float fraction = tInRange / xRange;
+
+ float startY = mY[startIndex];
+ float endY = mY[endIndex];
+ return startY + (fraction * (endY - startY));
+
+}
+
LUTInterpolator::LUTInterpolator(float* values, size_t size)
: mValues(values)
, mSize(size) {
diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h
index 65120087ff60..224cee70acc7 100644
--- a/libs/hwui/Interpolator.h
+++ b/libs/hwui/Interpolator.h
@@ -20,6 +20,7 @@
#include <memory>
#include <cutils/compiler.h>
+#include <vector>
namespace android {
namespace uirenderer {
@@ -100,6 +101,16 @@ private:
const float mTension;
};
+class ANDROID_API PathInterpolator : public Interpolator {
+public:
+ explicit PathInterpolator(std::vector<float>&& x, std::vector<float>&& y)
+ : mX (x), mY(y) {}
+ virtual float interpolate(float input) override;
+private:
+ std::vector<float> mX;
+ std::vector<float> mY;
+};
+
class ANDROID_API LUTInterpolator : public Interpolator {
public:
LUTInterpolator(float* values, size_t size);
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index cdbbbab7730d..331bb81208b1 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -16,278 +16,36 @@
#include "Layer.h"
-#include "Caches.h"
-#include "DeferredDisplayList.h"
-#include "LayerRenderer.h"
-#include "OpenGLRenderer.h"
-#include "RenderNode.h"
#include "renderstate/RenderState.h"
-#include "utils/TraceUtils.h"
-#include <utils/Log.h>
-
-#define ATRACE_LAYER_WORK(label) \
- ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", \
- label, \
- (renderNode.get() != NULL) ? renderNode->getName() : "", \
- getWidth(), getHeight())
+#include <SkColorFilter.h>
namespace android {
namespace uirenderer {
-Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight)
+Layer::Layer(RenderState& renderState, Api api)
: GpuMemoryTracker(GpuObjectType::Layer)
- , state(State::Uncached)
- , caches(Caches::getInstance())
- , renderState(renderState)
- , texture(caches)
- , type(layerType) {
+ , mRenderState(renderState)
+ , mApi(api) {
// TODO: This is a violation of Android's typical ref counting, but it
// preserves the old inc/dec ref locations. This should be changed...
incStrong(nullptr);
- renderTarget = GL_TEXTURE_2D;
- texture.mWidth = layerWidth;
- texture.mHeight = layerHeight;
+
renderState.registerLayer(this);
}
Layer::~Layer() {
- renderState.unregisterLayer(this);
SkSafeUnref(colorFilter);
- if (stencil || fbo || texture.mId) {
- removeFbo();
- texture.deleteTexture();
- }
-
- delete[] mesh;
-}
-
-void Layer::onGlContextLost() {
- removeFbo();
- texture.deleteTexture();
-}
-
-uint32_t Layer::computeIdealWidth(uint32_t layerWidth) {
- return uint32_t(ceilf(layerWidth / float(LAYER_SIZE)) * LAYER_SIZE);
-}
-
-uint32_t Layer::computeIdealHeight(uint32_t layerHeight) {
- return uint32_t(ceilf(layerHeight / float(LAYER_SIZE)) * LAYER_SIZE);
-}
-
-void Layer::requireRenderer() {
- if (!renderer) {
- renderer.reset(new LayerRenderer(renderState, this));
- renderer->initProperties();
- }
-}
-
-void Layer::updateLightPosFromRenderer(const OpenGLRenderer& rootRenderer) {
- if (renderer && rendererLightPosDirty) {
- // re-init renderer's light position, based upon last cached location in window
- Vector3 lightPos = rootRenderer.getLightCenter();
- cachedInvTransformInWindow.mapPoint3d(lightPos);
- renderer->initLight(rootRenderer.getLightRadius(),
- rootRenderer.getAmbientShadowAlpha(),
- rootRenderer.getSpotShadowAlpha());
- renderer->setLightCenter(lightPos);
- rendererLightPosDirty = false;
- }
-}
-
-bool Layer::resize(const uint32_t width, const uint32_t height) {
- uint32_t desiredWidth = computeIdealWidth(width);
- uint32_t desiredHeight = computeIdealWidth(height);
-
- if (desiredWidth <= getWidth() && desiredHeight <= getHeight()) {
- return true;
- }
-
- ATRACE_NAME("resizeLayer");
-
- const uint32_t maxTextureSize = caches.maxTextureSize;
- if (desiredWidth > maxTextureSize || desiredHeight > maxTextureSize) {
- ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
- desiredWidth, desiredHeight, maxTextureSize, maxTextureSize);
- return false;
- }
-
- uint32_t oldWidth = getWidth();
- uint32_t oldHeight = getHeight();
-
- setSize(desiredWidth, desiredHeight);
-
- if (fbo) {
- caches.textureState().activateTexture(0);
- bindTexture();
- allocateTexture();
-
- if (glGetError() != GL_NO_ERROR) {
- setSize(oldWidth, oldHeight);
- return false;
- }
- }
-
- if (stencil) {
- stencil->bind();
- stencil->resize(desiredWidth, desiredHeight);
-
- if (glGetError() != GL_NO_ERROR) {
- setSize(oldWidth, oldHeight);
- return false;
- }
- }
-
- return true;
-}
-
-void Layer::removeFbo(bool flush) {
- if (stencil) {
- GLuint previousFbo = renderState.getFramebuffer();
- renderState.bindFramebuffer(fbo);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
- renderState.bindFramebuffer(previousFbo);
-
- caches.renderBufferCache.put(stencil);
- stencil = nullptr;
- }
-
- if (fbo) {
- if (flush) LayerRenderer::flushLayer(renderState, this);
- renderState.deleteFramebuffer(fbo);
- fbo = 0;
- }
-}
-
-void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom) {
- requireRenderer();
- this->renderNode = renderNode;
- const Rect r(left, top, right, bottom);
- dirtyRect.unionWith(r);
- deferredUpdateScheduled = true;
-}
-
-void Layer::setPaint(const SkPaint* paint) {
- alpha = PaintUtils::getAlphaDirect(paint);
- mode = PaintUtils::getXfermodeDirect(paint);
- setColorFilter((paint) ? paint->getColorFilter() : nullptr);
+ mRenderState.unregisterLayer(this);
}
void Layer::setColorFilter(SkColorFilter* filter) {
SkRefCnt_SafeAssign(colorFilter, filter);
}
-void Layer::bindTexture() const {
- if (texture.mId) {
- caches.textureState().bindTexture(renderTarget, texture.mId);
- }
-}
-
-void Layer::bindStencilRenderBuffer() const {
- if (stencil) {
- stencil->bind();
- }
-}
-
-void Layer::generateTexture() {
- if (!texture.mId) {
- glGenTextures(1, &texture.mId);
- }
-}
-
-void Layer::clearTexture() {
- // There's a rare possibility that Caches could have been destroyed already
- // since this method is queued up as a task.
- // Since this is a reset method, treat this as non-fatal.
- if (caches.isInitialized()) {
- caches.textureState().unbindTexture(texture.mId);
- }
- texture.mId = 0;
-}
-
-void Layer::allocateTexture() {
-#if DEBUG_LAYERS
- ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
-#endif
- if (texture.mId) {
- texture.updateSize(getWidth(), getHeight(), GL_RGBA);
- glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
- GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
- }
-}
-
-void Layer::defer(const OpenGLRenderer& rootRenderer) {
- ATRACE_LAYER_WORK("Optimize");
-
- updateLightPosFromRenderer(rootRenderer);
- const float width = layer.getWidth();
- const float height = layer.getHeight();
-
- if (dirtyRect.isEmpty() || (dirtyRect.left <= 0 && dirtyRect.top <= 0 &&
- dirtyRect.right >= width && dirtyRect.bottom >= height)) {
- dirtyRect.set(0, 0, width, height);
- }
-
- deferredList.reset(new DeferredDisplayList(dirtyRect));
-
- DeferStateStruct deferredState(*deferredList, *renderer,
- RenderNode::kReplayFlag_ClipChildren);
-
- renderer->setupFrameState(width, height, dirtyRect.left, dirtyRect.top,
- dirtyRect.right, dirtyRect.bottom, !isBlend());
-
- renderNode->computeOrdering();
- renderNode->defer(deferredState, 0);
-
- deferredUpdateScheduled = false;
-}
-
-void Layer::cancelDefer() {
- renderNode = nullptr;
- deferredUpdateScheduled = false;
- deferredList.reset(nullptr);
-}
-
-void Layer::flush() {
- // renderer is checked as layer may be destroyed/put in layer cache with flush scheduled
- if (deferredList && renderer) {
- ATRACE_LAYER_WORK("Issue");
- renderer->startMark((renderNode.get() != nullptr) ? renderNode->getName() : "Layer");
-
- renderer->prepareDirty(layer.getWidth(), layer.getHeight(),
- dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend());
-
- deferredList->flush(*renderer, dirtyRect);
-
- renderer->finish();
-
- dirtyRect.setEmpty();
- renderNode = nullptr;
-
- renderer->endMark();
- }
-}
-
-void Layer::render(const OpenGLRenderer& rootRenderer) {
- ATRACE_LAYER_WORK("Direct-Issue");
-
- updateLightPosFromRenderer(rootRenderer);
- renderer->prepareDirty(layer.getWidth(), layer.getHeight(),
- dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend());
-
- renderer->drawRenderNode(renderNode.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren);
-
- renderer->finish();
-
- dirtyRect.setEmpty();
-
- deferredUpdateScheduled = false;
- renderNode = nullptr;
-}
-
void Layer::postDecStrong() {
- renderState.postDecStrong(this);
+ mRenderState.postDecStrong(this);
}
}; // namespace uirenderer
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 1e5498bb3d21..3b639ee49334 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -14,28 +14,15 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_LAYER_H
-#define ANDROID_HWUI_LAYER_H
+#pragma once
-#include <cutils/compiler.h>
-#include <sys/types.h>
-#include <utils/StrongPointer.h>
#include <utils/RefBase.h>
-#include <memory>
-
-#include <GLES2/gl2.h>
#include <GpuMemoryTracker.h>
-#include <ui/Region.h>
-
#include <SkPaint.h>
-#include <SkXfermode.h>
+#include <SkBlendMode.h>
#include "Matrix.h"
-#include "Rect.h"
-#include "RenderBuffer.h"
-#include "Texture.h"
-#include "Vertex.h"
namespace android {
namespace uirenderer {
@@ -44,106 +31,33 @@ namespace uirenderer {
// Layers
///////////////////////////////////////////////////////////////////////////////
-// Forward declarations
-class Caches;
-class RenderNode;
class RenderState;
-class OpenGLRenderer;
-class DeferredDisplayList;
-struct DeferStateStruct;
/**
- * A layer has dimensions and is backed by an OpenGL texture or FBO.
+ * A layer has dimensions and is backed by a backend specific texture or framebuffer.
*/
class Layer : public VirtualLightRefBase, GpuMemoryTracker {
public:
- enum class Type {
- Texture,
- DisplayList,
- };
-
- // layer lifecycle, controlled from outside
- enum class State {
- Uncached = 0,
- InCache = 1,
- FailedToCache = 2,
- RemovedFromCache = 3,
- DeletedFromCache = 4,
- InGarbageList = 5,
+ enum class Api {
+ OpenGL = 0,
+ Vulkan = 1,
};
- State state; // public for logging/debugging purposes
-
- Layer(Type type, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight);
- ~Layer();
-
- static uint32_t computeIdealWidth(uint32_t layerWidth);
- static uint32_t computeIdealHeight(uint32_t layerHeight);
-
- /**
- * Calling this method will remove (either by recycling or
- * destroying) the associated FBO, if present, and any render
- * buffer (stencil for instance.)
- */
- void removeFbo(bool flush = true);
-
- /**
- * Sets this layer's region to a rectangle. Computes the appropriate
- * texture coordinates.
- */
- void setRegionAsRect() {
- const android::Rect& bounds = region.getBounds();
- regionRect.set(bounds.leftTop().x, bounds.leftTop().y,
- bounds.rightBottom().x, bounds.rightBottom().y);
-
- const float texX = 1.0f / float(texture.mWidth);
- const float texY = 1.0f / float(texture.mHeight);
- const float height = layer.getHeight();
- texCoords.set(
- regionRect.left * texX, (height - regionRect.top) * texY,
- regionRect.right * texX, (height - regionRect.bottom) * texY);
-
- regionRect.translate(layer.left, layer.top);
- }
-
- void setWindowTransform(Matrix4& windowTransform) {
- cachedInvTransformInWindow.loadInverse(windowTransform);
- rendererLightPosDirty = true;
- }
- void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom);
-
- inline uint32_t getWidth() const {
- return texture.mWidth;
+ Api getApi() const {
+ return mApi;
}
- inline uint32_t getHeight() const {
- return texture.mHeight;
- }
+ ~Layer();
- /**
- * Resize the layer and its texture if needed.
- *
- * @param width The new width of the layer
- * @param height The new height of the layer
- *
- * @return True if the layer was resized or nothing happened, false if
- * a failure occurred during the resizing operation
- */
- bool resize(const uint32_t width, const uint32_t height);
+ virtual uint32_t getWidth() const = 0;
- void setSize(uint32_t width, uint32_t height) {
- texture.updateSize(width, height, texture.format());
- }
+ virtual uint32_t getHeight() const = 0;
- ANDROID_API void setPaint(const SkPaint* paint);
+ virtual void setSize(uint32_t width, uint32_t height) = 0;
- inline void setBlend(bool blend) {
- texture.blend = blend;
- }
+ virtual void setBlend(bool blend) = 0;
- inline bool isBlend() const {
- return texture.blend;
- }
+ virtual bool isBlend() const = 0;
inline void setForceFilter(bool forceFilter) {
this->forceFilter = forceFilter;
@@ -157,7 +71,7 @@ public:
this->alpha = alpha;
}
- inline void setAlpha(int alpha, SkXfermode::Mode mode) {
+ inline void setAlpha(int alpha, SkBlendMode mode) {
this->alpha = alpha;
this->mode = mode;
}
@@ -166,114 +80,15 @@ public:
return alpha;
}
- inline SkXfermode::Mode getMode() const {
+ inline SkBlendMode getMode() const {
return mode;
}
- inline void setEmpty(bool empty) {
- this->empty = empty;
- }
-
- inline bool isEmpty() const {
- return empty;
- }
-
- inline void setFbo(GLuint fbo) {
- this->fbo = fbo;
- }
-
- inline GLuint getFbo() const {
- return fbo;
- }
-
- inline void setStencilRenderBuffer(RenderBuffer* renderBuffer) {
- if (RenderBuffer::isStencilBuffer(renderBuffer->getFormat())) {
- this->stencil = renderBuffer;
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, stencil->getName());
- } else {
- ALOGE("The specified render buffer is not a stencil buffer");
- }
- }
-
- inline RenderBuffer* getStencilRenderBuffer() const {
- return stencil;
- }
-
- inline GLuint getTextureId() const {
- return texture.id();
- }
-
- inline Texture& getTexture() {
- return texture;
- }
-
- inline GLenum getRenderTarget() const {
- return renderTarget;
- }
-
- inline void setRenderTarget(GLenum renderTarget) {
- this->renderTarget = renderTarget;
- }
-
- inline bool isRenderable() const {
- return renderTarget != GL_NONE;
- }
-
- void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) {
- texture.setWrap(wrap, bindTexture, force, renderTarget);
- }
-
- void setFilter(GLenum filter, bool bindTexture = false, bool force = false) {
- texture.setFilter(filter, bindTexture, force, renderTarget);
- }
-
- inline bool isCacheable() const {
- return cacheable;
- }
-
- inline void setCacheable(bool cacheable) {
- this->cacheable = cacheable;
- }
-
- inline bool isDirty() const {
- return dirty;
- }
-
- inline void setDirty(bool dirty) {
- this->dirty = dirty;
- }
-
- inline bool isTextureLayer() const {
- return type == Type::Texture;
- }
-
inline SkColorFilter* getColorFilter() const {
return colorFilter;
}
- ANDROID_API void setColorFilter(SkColorFilter* filter);
-
- inline void setConvexMask(const SkPath* convexMask) {
- this->convexMask = convexMask;
- }
-
- inline const SkPath* getConvexMask() {
- return convexMask;
- }
-
- void bindStencilRenderBuffer() const;
-
- void bindTexture() const;
- void generateTexture();
- void allocateTexture();
-
- /**
- * When the caller frees the texture itself, the caller
- * must call this method to tell this layer that it lost
- * the texture.
- */
- ANDROID_API void clearTexture();
+ void setColorFilter(SkColorFilter* filter);
inline mat4& getTexTransform() {
return texTransform;
@@ -283,113 +98,19 @@ public:
return transform;
}
- void defer(const OpenGLRenderer& rootRenderer);
- void cancelDefer();
- void flush();
- void render(const OpenGLRenderer& rootRenderer);
-
/**
* Posts a decStrong call to the appropriate thread.
* Thread-safe.
*/
void postDecStrong();
- /**
- * Lost the GL context but the layer is still around, mark it invalid internally
- * so the dtor knows not to do any GL work
- */
- void onGlContextLost();
+protected:
+ Layer(RenderState& renderState, Api api);
- /**
- * Bounds of the layer.
- */
- Rect layer;
- /**
- * Texture coordinates of the layer.
- */
- Rect texCoords;
- /**
- * Clipping rectangle.
- */
- Rect clipRect;
-
- /**
- * Dirty region indicating what parts of the layer
- * have been drawn.
- */
- Region region;
- /**
- * If the region is a rectangle, coordinates of the
- * region are stored here.
- */
- Rect regionRect;
-
- /**
- * If the layer can be rendered as a mesh, this is non-null.
- */
- TextureVertex* mesh = nullptr;
- GLsizei meshElementCount = 0;
-
- /**
- * Used for deferred updates.
- */
- bool deferredUpdateScheduled = false;
- std::unique_ptr<OpenGLRenderer> renderer;
- sp<RenderNode> renderNode;
- Rect dirtyRect;
- bool debugDrawUpdate = false;
- bool hasDrawnSinceUpdate = false;
- bool wasBuildLayered = false;
+ RenderState& mRenderState;
private:
- void requireRenderer();
- void updateLightPosFromRenderer(const OpenGLRenderer& rootRenderer);
-
- Caches& caches;
-
- RenderState& renderState;
-
- /**
- * Name of the FBO used to render the layer. If the name is 0
- * this layer is not backed by an FBO, but a simple texture.
- */
- GLuint fbo = 0;
-
- /**
- * The render buffer used as the stencil buffer.
- */
- RenderBuffer* stencil = nullptr;
-
- /**
- * Indicates whether this layer has been used already.
- */
- bool empty = true;
-
- /**
- * The texture backing this layer.
- */
- Texture texture;
-
- /**
- * If set to true (by default), the layer can be reused.
- */
- bool cacheable = true;
-
- /**
- * Denotes whether the layer is a DisplayList, or Texture layer.
- */
- const Type type;
-
- /**
- * When set to true, this layer is dirty and should be cleared
- * before any rendering occurs.
- */
- bool dirty = false;
-
- /**
- * Indicates the render target.
- */
- GLenum renderTarget = GL_TEXTURE_2D;
+ Api mApi;
/**
* Color filter used to draw this layer. Optional.
@@ -409,7 +130,7 @@ private:
/**
* Blending mode of the layer.
*/
- SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
+ SkBlendMode mode = SkBlendMode::kSrcOver;
/**
* Optional texture coordinates transform.
@@ -421,28 +142,7 @@ private:
*/
mat4 transform;
- /**
- * Cached transform of layer in window, updated only on creation / resize
- */
- mat4 cachedInvTransformInWindow;
- bool rendererLightPosDirty = true;
-
- /**
- * Used to defer display lists when the layer is updated with a
- * display list.
- */
- std::unique_ptr<DeferredDisplayList> deferredList;
-
- /**
- * This convex path should be used to mask the layer's draw to the screen.
- *
- * Data not owned/managed by layer object.
- */
- const SkPath* convexMask = nullptr;
-
}; // struct Layer
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_LAYER_H
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index 66413dcf0d97..c5d5492d4fd1 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -274,7 +274,7 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
// One or more unclipped saveLayers have been enqueued, with deferred clears.
// Flush all of these clears with a single draw
SkPaint* paint = allocator.create<SkPaint>();
- paint->setXfermodeMode(SkXfermode::kClear_Mode);
+ paint->setBlendMode(SkBlendMode::kClear);
SimpleRectsOp* op = allocator.create_trivial<SimpleRectsOp>(bounds,
Matrix4::identity(), nullptr, paint,
verts, vertCount);
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
deleted file mode 100644
index f5681ce712d5..000000000000
--- a/libs/hwui/LayerCache.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LayerCache.h"
-
-#include "Caches.h"
-#include "Properties.h"
-
-#include <utils/Log.h>
-
-#include <GLES2/gl2.h>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-LayerCache::LayerCache()
- : mSize(0)
- , mMaxSize(Properties::layerPoolSize) {}
-
-LayerCache::~LayerCache() {
- clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Size management
-///////////////////////////////////////////////////////////////////////////////
-
-size_t LayerCache::getCount() {
- return mCache.size();
-}
-
-uint32_t LayerCache::getSize() {
- return mSize;
-}
-
-uint32_t LayerCache::getMaxSize() {
- return mMaxSize;
-}
-
-void LayerCache::setMaxSize(uint32_t maxSize) {
- clear();
- mMaxSize = maxSize;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-int LayerCache::LayerEntry::compare(const LayerCache::LayerEntry& lhs,
- const LayerCache::LayerEntry& rhs) {
- int deltaInt = int(lhs.mWidth) - int(rhs.mWidth);
- if (deltaInt != 0) return deltaInt;
-
- return int(lhs.mHeight) - int(rhs.mHeight);
-}
-
-void LayerCache::deleteLayer(Layer* layer) {
- if (layer) {
- LAYER_LOGD("Destroying layer %dx%d, fbo %d", layer->getWidth(), layer->getHeight(),
- layer->getFbo());
- mSize -= layer->getWidth() * layer->getHeight() * 4;
- layer->state = Layer::State::DeletedFromCache;
- layer->decStrong(nullptr);
- }
-}
-
-void LayerCache::clear() {
- for (auto entry : mCache) {
- deleteLayer(entry.mLayer);
- }
- mCache.clear();
-}
-
-Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uint32_t height) {
- Layer* layer = nullptr;
-
- LayerEntry entry(width, height);
- auto iter = mCache.find(entry);
-
- if (iter != mCache.end()) {
- entry = *iter;
- mCache.erase(iter);
-
- layer = entry.mLayer;
- layer->state = Layer::State::RemovedFromCache;
- mSize -= layer->getWidth() * layer->getHeight() * 4;
-
- LAYER_LOGD("Reusing layer %dx%d", layer->getWidth(), layer->getHeight());
- } else {
- LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight);
-
- layer = new Layer(Layer::Type::DisplayList, renderState, entry.mWidth, entry.mHeight);
- layer->setBlend(true);
- layer->generateTexture();
- layer->bindTexture();
- layer->setFilter(GL_NEAREST);
- layer->setWrap(GL_CLAMP_TO_EDGE, false);
-
-#if DEBUG_LAYERS
- dump();
-#endif
- }
-
- return layer;
-}
-
-void LayerCache::dump() {
- for (auto entry : mCache) {
- ALOGD(" Layer size %dx%d", entry.mWidth, entry.mHeight);
- }
-}
-
-bool LayerCache::put(Layer* layer) {
- if (!layer->isCacheable()) return false;
-
- const uint32_t size = layer->getWidth() * layer->getHeight() * 4;
- // Don't even try to cache a layer that's bigger than the cache
- if (size < mMaxSize) {
- // TODO: Use an LRU
- while (mSize + size > mMaxSize) {
- Layer* victim = mCache.begin()->mLayer;
- deleteLayer(victim);
- mCache.erase(mCache.begin());
-
- LAYER_LOGD(" Deleting layer %.2fx%.2f", victim->layer.getWidth(),
- victim->layer.getHeight());
- }
-
- layer->cancelDefer();
-
- LayerEntry entry(layer);
-
- mCache.insert(entry);
- mSize += size;
-
- layer->state = Layer::State::InCache;
- return true;
- }
-
- layer->state = Layer::State::FailedToCache;
- return false;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
deleted file mode 100644
index 7a1a9ae3dd5d..000000000000
--- a/libs/hwui/LayerCache.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_LAYER_CACHE_H
-#define ANDROID_HWUI_LAYER_CACHE_H
-
-#include "Debug.h"
-#include "Layer.h"
-
-#include <set>
-
-namespace android {
-namespace uirenderer {
-
-class RenderState;
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#if DEBUG_LAYERS
- #define LAYER_LOGD(...) ALOGD(__VA_ARGS__)
-#else
- #define LAYER_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Cache
-///////////////////////////////////////////////////////////////////////////////
-
-class LayerCache {
-public:
- LayerCache();
- ~LayerCache();
-
- /**
- * Returns a layer large enough for the specified dimensions. If no suitable
- * layer can be found, a new one is created and returned. If creating a new
- * layer fails, NULL is returned.
- *
- * When a layer is obtained from the cache, it is removed and the total
- * size of the cache goes down.
- *
- * @param width The desired width of the layer
- * @param height The desired height of the layer
- */
- Layer* get(RenderState& renderState, const uint32_t width, const uint32_t height);
-
- /**
- * Adds the layer to the cache. The layer will not be added if there is
- * not enough space available. Adding a layer can cause other layers to
- * be removed from the cache.
- *
- * @param layer The layer to add to the cache
- *
- * @return True if the layer was added, false otherwise.
- */
- bool put(Layer* layer);
- /**
- * Clears the cache. This causes all layers to be deleted.
- */
- void clear();
-
- /**
- * Sets the maximum size of the cache in bytes.
- */
- void setMaxSize(uint32_t maxSize);
- /**
- * Returns the maximum size of the cache in bytes.
- */
- uint32_t getMaxSize();
- /**
- * Returns the current size of the cache in bytes.
- */
- uint32_t getSize();
-
- size_t getCount();
-
- /**
- * Prints out the content of the cache.
- */
- void dump();
-
-private:
- struct LayerEntry {
- LayerEntry():
- mLayer(nullptr), mWidth(0), mHeight(0) {
- }
-
- LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(nullptr) {
- mWidth = Layer::computeIdealWidth(layerWidth);
- mHeight = Layer::computeIdealHeight(layerHeight);
- }
-
- explicit LayerEntry(Layer* layer):
- mLayer(layer), mWidth(layer->getWidth()), mHeight(layer->getHeight()) {
- }
-
- static int compare(const LayerEntry& lhs, const LayerEntry& rhs);
-
- bool operator==(const LayerEntry& other) const {
- return compare(*this, other) == 0;
- }
-
- bool operator!=(const LayerEntry& other) const {
- return compare(*this, other) != 0;
- }
-
- bool operator<(const LayerEntry& other) const {
- return LayerEntry::compare(*this, other) < 0;
- }
-
- Layer* mLayer;
- uint32_t mWidth;
- uint32_t mHeight;
- }; // struct LayerEntry
-
- void deleteLayer(Layer* layer);
-
- std::multiset<LayerEntry> mCache;
-
- uint32_t mSize;
- uint32_t mMaxSize;
-}; // class LayerCache
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_LAYER_CACHE_H
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
deleted file mode 100644
index 137316f57725..000000000000
--- a/libs/hwui/LayerRenderer.cpp
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LayerCache.h"
-#include "LayerRenderer.h"
-#include "Matrix.h"
-#include "Properties.h"
-#include "Rect.h"
-#include "renderstate/RenderState.h"
-#include "utils/GLUtils.h"
-#include "utils/TraceUtils.h"
-
-#include <ui/Rect.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Rendering
-///////////////////////////////////////////////////////////////////////////////
-
-LayerRenderer::LayerRenderer(RenderState& renderState, Layer* layer)
- : OpenGLRenderer(renderState)
- , mLayer(layer) {
-}
-
-LayerRenderer::~LayerRenderer() {
-}
-
-void LayerRenderer::prepareDirty(int viewportWidth, int viewportHeight,
- float left, float top, float right, float bottom, bool opaque) {
- LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo());
-
- mRenderState.bindFramebuffer(mLayer->getFbo());
-
- const float width = mLayer->layer.getWidth();
- const float height = mLayer->layer.getHeight();
-
- Rect dirty(left, top, right, bottom);
- if (dirty.isEmpty() || (dirty.left <= 0 && dirty.top <= 0 &&
- dirty.right >= width && dirty.bottom >= height)) {
- mLayer->region.clear();
- dirty.set(0.0f, 0.0f, width, height);
- } else {
- dirty.doIntersect(0.0f, 0.0f, width, height);
- android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom);
- mLayer->region.subtractSelf(r);
- }
- mLayer->clipRect.set(dirty);
-
- OpenGLRenderer::prepareDirty(viewportWidth, viewportHeight,
- dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
-}
-
-void LayerRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
- if (mLayer->isDirty()) {
- mRenderState.scissor().setEnabled(false);
- glClear(GL_COLOR_BUFFER_BIT);
-
- mRenderState.scissor().reset();
- mLayer->setDirty(false);
- } else {
- OpenGLRenderer::clear(left, top, right, bottom, opaque);
- }
-}
-
-bool LayerRenderer::finish() {
- bool retval = OpenGLRenderer::finish();
-
- generateMesh();
-
- LAYER_RENDERER_LOGD("Finished rendering into layer, fbo = %d", mLayer->getFbo());
-
- // No need to unbind our FBO, this will be taken care of by the caller
- // who will invoke OpenGLRenderer::resume()
- return retval;
-}
-
-GLuint LayerRenderer::getTargetFbo() const {
- return mLayer->getFbo();
-}
-
-bool LayerRenderer::suppressErrorChecks() const {
- return true;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Layer support
-///////////////////////////////////////////////////////////////////////////////
-
-bool LayerRenderer::hasLayer() const {
- return true;
-}
-
-void LayerRenderer::ensureStencilBuffer() {
- attachStencilBufferToLayer(mLayer);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Dirty region tracking
-///////////////////////////////////////////////////////////////////////////////
-
-Region* LayerRenderer::getRegion() const {
- if (mState.currentFlags() & Snapshot::kFlagFboTarget) {
- return OpenGLRenderer::getRegion();
- }
- return &mLayer->region;
-}
-
-// TODO: This implementation uses a very simple approach to fixing T-junctions which keeps the
-// results as rectangles, and is thus not necessarily efficient in the geometry
-// produced. Eventually, it may be better to develop triangle-based mechanism.
-void LayerRenderer::generateMesh() {
- if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
- if (mLayer->mesh) {
- delete[] mLayer->mesh;
- mLayer->mesh = nullptr;
- mLayer->meshElementCount = 0;
- }
-
- mLayer->setRegionAsRect();
- return;
- }
-
- // avoid T-junctions as they cause artifacts in between the resultant
- // geometry when complex transforms occur.
- // TODO: generate the safeRegion only if necessary based on drawing transform (see
- // OpenGLRenderer::composeLayerRegion())
- Region safeRegion = Region::createTJunctionFreeRegion(mLayer->region);
-
- size_t count;
- const android::Rect* rects = safeRegion.getArray(&count);
-
- GLsizei elementCount = count * 6;
-
- if (mLayer->mesh && mLayer->meshElementCount < elementCount) {
- delete[] mLayer->mesh;
- mLayer->mesh = nullptr;
- }
-
- if (!mLayer->mesh) {
- mLayer->mesh = new TextureVertex[count * 4];
- }
- mLayer->meshElementCount = elementCount;
-
- const float texX = 1.0f / float(mLayer->getWidth());
- const float texY = 1.0f / float(mLayer->getHeight());
- const float height = mLayer->layer.getHeight();
-
- TextureVertex* mesh = mLayer->mesh;
-
- for (size_t i = 0; i < count; i++) {
- const android::Rect* r = &rects[i];
-
- const float u1 = r->left * texX;
- const float v1 = (height - r->top) * texY;
- const float u2 = r->right * texX;
- const float v2 = (height - r->bottom) * texY;
-
- TextureVertex::set(mesh++, r->left, r->top, u1, v1);
- TextureVertex::set(mesh++, r->right, r->top, u2, v1);
- TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
- TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Layers management
-///////////////////////////////////////////////////////////////////////////////
-
-Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height) {
- ATRACE_FORMAT("Allocate %ux%u HW Layer", width, height);
- LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height);
-
- Caches& caches = Caches::getInstance();
- GLuint fbo = renderState.createFramebuffer();
- if (!fbo) {
- ALOGW("Could not obtain an FBO");
- return nullptr;
- }
-
- caches.textureState().activateTexture(0);
- Layer* layer = caches.layerCache.get(renderState, width, height);
- if (!layer) {
- ALOGW("Could not obtain a layer");
- return nullptr;
- }
-
- // We first obtain a layer before comparing against the max texture size
- // because layers are not allocated at the exact desired size. They are
- // always created slightly larger to improve recycling
- const uint32_t maxTextureSize = caches.maxTextureSize;
- if (layer->getWidth() > maxTextureSize || layer->getHeight() > maxTextureSize) {
- ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
- width, height, maxTextureSize, maxTextureSize);
-
- // Creating a new layer always increment its refcount by 1, this allows
- // us to destroy the layer object if one was created for us
- layer->decStrong(nullptr);
-
- return nullptr;
- }
-
- layer->setFbo(fbo);
- layer->layer.set(0.0f, 0.0f, width, height);
- layer->texCoords.set(0.0f, height / float(layer->getHeight()),
- width / float(layer->getWidth()), 0.0f);
- layer->setAlpha(255, SkXfermode::kSrcOver_Mode);
- layer->setColorFilter(nullptr);
- layer->setDirty(true);
- layer->region.clear();
-
- GLuint previousFbo = renderState.getFramebuffer();
-
- renderState.bindFramebuffer(layer->getFbo());
- layer->bindTexture();
-
- // Initialize the texture if needed
- if (layer->isEmpty()) {
- layer->setEmpty(false);
- layer->allocateTexture();
-
- // This should only happen if we run out of memory
- if (CC_UNLIKELY(GLUtils::dumpGLErrors())) {
- LOG_ALWAYS_FATAL("Could not allocate texture for layer (fbo=%d %dx%d)",
- fbo, width, height);
- renderState.bindFramebuffer(previousFbo);
- layer->decStrong(nullptr);
- return nullptr;
- }
- }
-
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- layer->getTextureId(), 0);
-
- renderState.bindFramebuffer(previousFbo);
-
- return layer;
-}
-
-bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
- if (layer) {
- LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->getFbo(), width, height);
-
- if (layer->resize(width, height)) {
- layer->layer.set(0.0f, 0.0f, width, height);
- layer->texCoords.set(0.0f, height / float(layer->getHeight()),
- width / float(layer->getWidth()), 0.0f);
- } else {
- return false;
- }
- }
-
- return true;
-}
-
-Layer* LayerRenderer::createTextureLayer(RenderState& renderState) {
- LAYER_RENDERER_LOGD("Creating new texture layer");
-
- Layer* layer = new Layer(Layer::Type::Texture, renderState, 0, 0);
- layer->setCacheable(false);
- layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f);
- layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f);
- layer->region.clear();
- layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer()
-
- Caches::getInstance().textureState().activateTexture(0);
- layer->generateTexture();
-
- return layer;
-}
-
-void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
- bool isOpaque, bool forceFilter, GLenum renderTarget, const float* textureTransform) {
- if (layer) {
- layer->setBlend(!isOpaque);
- layer->setForceFilter(forceFilter);
- layer->setSize(width, height);
- layer->layer.set(0.0f, 0.0f, width, height);
- layer->region.set(width, height);
- layer->regionRect.set(0.0f, 0.0f, width, height);
- layer->getTexTransform().load(textureTransform);
-
- if (renderTarget != layer->getRenderTarget()) {
- layer->setRenderTarget(renderTarget);
- layer->bindTexture();
- layer->setFilter(GL_NEAREST, false, true);
- layer->setWrap(GL_CLAMP_TO_EDGE, false, true);
- }
- }
-}
-
-void LayerRenderer::destroyLayer(Layer* layer) {
- if (layer) {
- ATRACE_FORMAT("Destroy %ux%u HW Layer", layer->getWidth(), layer->getHeight());
- LAYER_RENDERER_LOGD("Recycling layer, %dx%d fbo = %d",
- layer->getWidth(), layer->getHeight(), layer->getFbo());
-
- if (!Caches::getInstance().layerCache.put(layer)) {
- LAYER_RENDERER_LOGD(" Destroyed!");
- layer->decStrong(nullptr);
- } else {
- LAYER_RENDERER_LOGD(" Cached!");
-#if DEBUG_LAYER_RENDERER
- Caches::getInstance().layerCache.dump();
-#endif
- layer->removeFbo();
- layer->region.clear();
- }
- }
-}
-
-void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) {
-#ifdef GL_EXT_discard_framebuffer
- if (!layer) return;
-
- GLuint fbo = layer->getFbo();
- if (fbo) {
- // If possible, discard any enqueud operations on deferred
- // rendering architectures
- if (Caches::getInstance().extensions().hasDiscardFramebuffer()) {
- GLuint previousFbo = renderState.getFramebuffer();
- if (fbo != previousFbo) {
- renderState.bindFramebuffer(fbo);
- }
-
- const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 };
- glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
-
- if (fbo != previousFbo) {
- renderState.bindFramebuffer(previousFbo);
- }
- }
- }
-#endif
-}
-
-bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap) {
- Caches& caches = Caches::getInstance();
- if (layer && layer->isRenderable()
- && bitmap->width() <= caches.maxTextureSize
- && bitmap->height() <= caches.maxTextureSize) {
-
- GLuint fbo = renderState.createFramebuffer();
- if (!fbo) {
- ALOGW("Could not obtain an FBO");
- return false;
- }
-
- SkAutoLockPixels alp(*bitmap);
-
- GLuint texture;
- GLuint previousFbo;
- GLsizei previousViewportWidth;
- GLsizei previousViewportHeight;
-
- GLenum format;
- GLenum type;
-
- bool status = false;
-
- switch (bitmap->colorType()) {
- case kAlpha_8_SkColorType:
- format = GL_ALPHA;
- type = GL_UNSIGNED_BYTE;
- break;
- case kRGB_565_SkColorType:
- format = GL_RGB;
- type = GL_UNSIGNED_SHORT_5_6_5;
- break;
- case kARGB_4444_SkColorType:
- format = GL_RGBA;
- type = GL_UNSIGNED_SHORT_4_4_4_4;
- break;
- case kN32_SkColorType:
- default:
- format = GL_RGBA;
- type = GL_UNSIGNED_BYTE;
- break;
- }
-
- float alpha = layer->getAlpha();
- SkXfermode::Mode mode = layer->getMode();
- GLuint previousLayerFbo = layer->getFbo();
-
- layer->setAlpha(255, SkXfermode::kSrc_Mode);
- layer->setFbo(fbo);
-
- previousFbo = renderState.getFramebuffer();
- renderState.getViewport(&previousViewportWidth, &previousViewportHeight);
- renderState.bindFramebuffer(fbo);
-
- glGenTextures(1, &texture);
-
- caches.textureState().activateTexture(0);
- caches.textureState().bindTexture(texture);
-
- glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(),
- 0, format, type, nullptr);
-
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, texture, 0);
-
- {
- LayerRenderer renderer(renderState, layer);
- renderer.OpenGLRenderer::prepareDirty(bitmap->width(), bitmap->height(),
- 0.0f, 0.0f, bitmap->width(), bitmap->height(), !layer->isBlend());
-
- renderState.scissor().setEnabled(false);
- renderer.translate(0.0f, bitmap->height());
- renderer.scale(1.0f, -1.0f);
-
- {
- Rect bounds;
- bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height());
- renderer.drawTextureLayer(layer, bounds);
-
- glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
- type, bitmap->getPixels());
-
- }
-
- status = true;
- }
-
- renderState.bindFramebuffer(previousFbo);
- layer->setAlpha(alpha, mode);
- layer->setFbo(previousLayerFbo);
- caches.textureState().deleteTexture(texture);
- renderState.deleteFramebuffer(fbo);
- renderState.setViewport(previousViewportWidth, previousViewportHeight);
-
- GL_CHECKPOINT(MODERATE);
-
- return status;
- }
- return false;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
deleted file mode 100644
index 38c3705cfa25..000000000000
--- a/libs/hwui/LayerRenderer.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_LAYER_RENDERER_H
-#define ANDROID_HWUI_LAYER_RENDERER_H
-
-#include <cutils/compiler.h>
-
-#include "OpenGLRenderer.h"
-#include "Layer.h"
-
-#include <SkBitmap.h>
-
-namespace android {
-namespace uirenderer {
-
-class RenderState;
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_LAYER_RENDERER
- #define LAYER_RENDERER_LOGD(...) ALOGD(__VA_ARGS__)
-#else
- #define LAYER_RENDERER_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Renderer
-///////////////////////////////////////////////////////////////////////////////
-
-class LayerRenderer: public OpenGLRenderer {
-public:
- LayerRenderer(RenderState& renderState, Layer* layer);
- virtual ~LayerRenderer();
-
- virtual void onViewportInitialized() override { /* do nothing */ }
- virtual void prepareDirty(int viewportWidth, int viewportHeight,
- float left, float top, float right, float bottom, bool opaque) override;
- virtual void clear(float left, float top, float right, float bottom, bool opaque) override;
- virtual bool finish() override;
-
- static Layer* createTextureLayer(RenderState& renderState);
- static Layer* createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height);
- static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
- static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
- bool isOpaque, bool forceFilter, GLenum renderTarget, const float* textureTransform);
- static void destroyLayer(Layer* layer);
- static bool copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap);
-
- static void flushLayer(RenderState& renderState, Layer* layer);
-
-protected:
- virtual void ensureStencilBuffer() override;
- virtual bool hasLayer() const override;
- virtual Region* getRegion() const override;
- virtual GLuint getTargetFbo() const override;
- virtual bool suppressErrorChecks() const override;
-
-private:
- void generateMesh();
-
- Layer* mLayer;
-}; // class LayerRenderer
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_LAYER_RENDERER_H
diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h
new file mode 100644
index 000000000000..e989a4680a60
--- /dev/null
+++ b/libs/hwui/NinePatchUtils.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+namespace NinePatchUtils {
+
+static inline void SetLatticeDivs(SkCanvas::Lattice* lattice, const Res_png_9patch& chunk,
+ int width, int height) {
+ lattice->fXCount = chunk.numXDivs;
+ lattice->fYCount = chunk.numYDivs;
+ lattice->fXDivs = chunk.getXDivs();
+ lattice->fYDivs = chunk.getYDivs();
+
+ // We'll often see ninepatches where the last div is equal to the width or height.
+ // This doesn't provide any additional information and is not supported by Skia.
+ if (lattice->fXCount > 0 && width == lattice->fXDivs[lattice->fXCount - 1]) {
+ lattice->fXCount--;
+ }
+ if (lattice->fYCount > 0 && height == lattice->fYDivs[lattice->fYCount - 1]) {
+ lattice->fYCount--;
+ }
+}
+
+static inline int NumDistinctRects(const SkCanvas::Lattice& lattice) {
+ int xRects;
+ if (lattice.fXCount > 0) {
+ xRects = (0 == lattice.fXDivs[0]) ? lattice.fXCount : lattice.fXCount + 1;
+ } else {
+ xRects = 1;
+ }
+
+ int yRects;
+ if (lattice.fYCount > 0) {
+ yRects = (0 == lattice.fYDivs[0]) ? lattice.fYCount : lattice.fYCount + 1;
+ } else {
+ yRects = 1;
+ }
+ return xRects * yRects;
+}
+
+static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, SkCanvas::Lattice::Flags* flags,
+ int numFlags, const Res_png_9patch& chunk) {
+ lattice->fFlags = flags;
+ sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::Flags));
+
+ bool needPadRow = lattice->fYCount > 0 && 0 == lattice->fYDivs[0];
+ bool needPadCol = lattice->fXCount > 0 && 0 == lattice->fXDivs[0];
+
+ int yCount = lattice->fYCount;
+ if (needPadRow) {
+ // Skip flags for the degenerate first row of rects.
+ flags += lattice->fXCount + 1;
+ yCount--;
+ }
+
+ int i = 0;
+ bool setFlags = false;
+ for (int y = 0; y < yCount + 1; y++) {
+ for (int x = 0; x < lattice->fXCount + 1; x++) {
+ if (0 == x && needPadCol) {
+ // First rect of each column is degenerate, skip the flag.
+ flags++;
+ continue;
+ }
+
+ if (0 == chunk.getColors()[i++]) {
+ *flags = SkCanvas::Lattice::kTransparent_Flags;
+ setFlags = true;
+ }
+
+ flags++;
+ }
+ }
+
+ if (!setFlags) {
+ lattice->fFlags = nullptr;
+ }
+}
+
+}; // namespace NinePatchUtils
+}; // namespace android
diff --git a/libs/hwui/OpDumper.cpp b/libs/hwui/OpDumper.cpp
index ec9ffdeebb4c..f4b7ee0fe430 100644
--- a/libs/hwui/OpDumper.cpp
+++ b/libs/hwui/OpDumper.cpp
@@ -16,6 +16,7 @@
#include "OpDumper.h"
+#include "ClipArea.h"
#include "RecordedOp.h"
namespace android {
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/OpenGLReadback.cpp
index 0ab247dc8052..c460c0d2dfd4 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include "Readback.h"
+#include "OpenGLReadback.h"
#include "Caches.h"
#include "Image.h"
#include "GlopBuilder.h"
+#include "GlLayer.h"
#include "renderstate/RenderState.h"
#include "renderthread/EglManager.h"
#include "utils/GLUtils.h"
@@ -30,14 +31,98 @@
namespace android {
namespace uirenderer {
-CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
- Surface& surface, SkBitmap* bitmap) {
- // TODO: Clean this up and unify it with LayerRenderer::copyLayer,
- // of which most of this is copied from.
- renderThread.eglManager().initialize();
+CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect,
+ SkBitmap* bitmap) {
+ ATRACE_CALL();
+ // Setup the source
+ sp<GraphicBuffer> sourceBuffer;
+ sp<Fence> sourceFence;
+ Matrix4 texTransform;
+ status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence,
+ texTransform.data);
+ texTransform.invalidateType();
+ if (err != NO_ERROR) {
+ ALOGW("Failed to get last queued buffer, error = %d", err);
+ return CopyResult::UnknownError;
+ }
+ if (!sourceBuffer.get()) {
+ ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
+ return CopyResult::SourceEmpty;
+ }
+ if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
+ ALOGW("Surface is protected, unable to copy from it");
+ return CopyResult::SourceInvalid;
+ }
+ err = sourceFence->wait(500 /* ms */);
+ if (err != NO_ERROR) {
+ ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+ return CopyResult::Timeout;
+ }
- Caches& caches = Caches::getInstance();
- RenderState& renderState = renderThread.renderState();
+ return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap);
+}
+
+CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
+ Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap) {
+ mRenderThread.eglManager().initialize();
+ // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
+ // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
+ // to be able to properly sample from the buffer.
+
+ // Create the EGLImage object that maps the GraphicBuffer
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ EGLClientBuffer clientBuffer = (EGLClientBuffer) graphicBuffer->getNativeBuffer();
+ EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
+
+ EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
+
+ if (sourceImage == EGL_NO_IMAGE_KHR) {
+ ALOGW("eglCreateImageKHR failed (%#x)", eglGetError());
+ return CopyResult::UnknownError;
+ }
+
+ uint32_t width = graphicBuffer->getWidth();
+ uint32_t height = graphicBuffer->getHeight();
+ // If this is a 90 or 270 degree rotation we need to swap width/height
+ // This is a fuzzy way of checking that.
+ if (texTransform[Matrix4::kSkewX] >= 0.5f || texTransform[Matrix4::kSkewX] <= -0.5f) {
+ std::swap(width, height);
+ }
+ CopyResult copyResult = copyImageInto(sourceImage, texTransform, width, height,
+ srcRect, bitmap);
+
+ // All we're flushing & finishing is the deletion of the texture since
+ // copyImageInto already did a major flush & finish as an implicit
+ // part of glReadPixels, so this shouldn't pose any major stalls.
+ glFinish();
+ eglDestroyImageKHR(display, sourceImage);
+ return copyResult;
+}
+
+CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) {
+ Rect srcRect;
+ Matrix4 transform;
+ transform.loadScale(1, -1, 1);
+ transform.translate(0, -1);
+ return copyGraphicBufferInto(graphicBuffer, transform, srcRect, bitmap);
+}
+
+static float sFlipVInit[16] = {
+ 1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 1, 0, 1,
+};
+
+static const Matrix4 sFlipV(sFlipVInit);
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
+ Texture& sourceTexture, const Matrix4& texTransform, const Rect& srcRect,
+ SkBitmap* bitmap) {
int destWidth = bitmap->width();
int destHeight = bitmap->height();
if (destWidth > caches.maxTextureSize
@@ -46,6 +131,13 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
destWidth, destHeight, caches.maxTextureSize);
return CopyResult::DestinationInvalid;
}
+
+ // TODO: Add support for RGBA_F16 destinations
+ if (bitmap->colorType() == kRGBA_F16_SkColorType) {
+ ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
+ return CopyResult::DestinationInvalid;
+ }
+
GLuint fbo = renderState.createFramebuffer();
if (!fbo) {
ALOGW("Could not obtain an FBO");
@@ -98,64 +190,6 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texture, 0);
- // Setup the source
- sp<GraphicBuffer> sourceBuffer;
- sp<Fence> sourceFence;
- Matrix4 texTransform;
- status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence,
- texTransform.data);
- texTransform.invalidateType();
- if (err != NO_ERROR) {
- ALOGW("Failed to get last queued buffer, error = %d", err);
- return CopyResult::UnknownError;
- }
- if (!sourceBuffer.get()) {
- ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
- return CopyResult::SourceEmpty;
- }
- if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
- ALOGW("Surface is protected, unable to copy from it");
- return CopyResult::SourceInvalid;
- }
- err = sourceFence->wait(500 /* ms */);
- if (err != NO_ERROR) {
- ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
- return CopyResult::Timeout;
- }
-
- // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
- // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
- // to be able to properly sample from the buffer.
-
- // Create the EGLImage object that maps the GraphicBuffer
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- EGLClientBuffer clientBuffer = (EGLClientBuffer) sourceBuffer->getNativeBuffer();
- EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
-
- EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT,
- EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
-
- if (sourceImage == EGL_NO_IMAGE_KHR) {
- ALOGW("eglCreateImageKHR failed (%#x)", eglGetError());
- return CopyResult::UnknownError;
- }
- GLuint sourceTexId;
- // Create a 2D texture to sample from the EGLImage
- glGenTextures(1, &sourceTexId);
- Caches::getInstance().textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, sourceImage);
-
- GLenum status = GL_NO_ERROR;
- while ((status = glGetError()) != GL_NO_ERROR) {
- ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status);
- eglDestroyImageKHR(display, sourceImage);
- return CopyResult::UnknownError;
- }
-
- Texture sourceTexture(caches);
- sourceTexture.wrap(sourceTexId,
- sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */);
-
{
// Draw & readback
renderState.setViewport(destWidth, destHeight);
@@ -163,14 +197,25 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
renderState.blend().syncEnabled();
renderState.stencil().disable();
- Rect destRect(destWidth, destHeight);
+ Matrix4 croppedTexTransform(texTransform);
+ if (!srcRect.isEmpty()) {
+ // We flipV to convert to 0,0 top-left for the srcRect
+ // coordinates then flip back to 0,0 bottom-left for
+ // GLES coordinates.
+ croppedTexTransform.multiply(sFlipV);
+ croppedTexTransform.translate(srcRect.left / sourceTexture.width(),
+ srcRect.top / sourceTexture.height(), 0);
+ croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(),
+ srcRect.getHeight() / sourceTexture.height(), 1);
+ croppedTexTransform.multiply(sFlipV);
+ }
Glop glop;
GlopBuilder(renderState, caches, &glop)
.setRoundRectClipState(nullptr)
.setMeshTexturedUnitQuad(nullptr)
- .setFillExternalTexture(sourceTexture, texTransform)
+ .setFillExternalTexture(sourceTexture, croppedTexTransform)
.setTransform(Matrix4::identity(), TransformFlags::None)
- .setModelViewMapUnitToRect(destRect)
+ .setModelViewMapUnitToRect(Rect(destWidth, destHeight))
.build();
Matrix4 ortho;
ortho.loadOrtho(destWidth, destHeight);
@@ -184,17 +229,45 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
caches.textureState().deleteTexture(texture);
renderState.deleteFramebuffer(fbo);
- sourceTexture.deleteTexture();
- // All we're flushing & finishing is the deletion of the texture since
- // copyTextureInto already did a major flush & finish as an implicit
- // part of glReadPixels, so this shouldn't pose any major stalls.
- glFinish();
- eglDestroyImageKHR(display, sourceImage);
-
GL_CHECKPOINT(MODERATE);
return CopyResult::Success;
}
+CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage,
+ const Matrix4& imgTransform, int imgWidth, int imgHeight, const Rect& srcRect,
+ SkBitmap* bitmap) {
+
+ Caches& caches = Caches::getInstance();
+ GLuint sourceTexId;
+ // Create a 2D texture to sample from the EGLImage
+ glGenTextures(1, &sourceTexId);
+ caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
+
+ GLenum status = GL_NO_ERROR;
+ while ((status = glGetError()) != GL_NO_ERROR) {
+ ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status);
+ return CopyResult::UnknownError;
+ }
+
+ Texture sourceTexture(caches);
+ sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */,
+ GL_TEXTURE_EXTERNAL_OES);
+
+ CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(),
+ sourceTexture, imgTransform, srcRect, bitmap);
+ sourceTexture.deleteTexture();
+ return copyResult;
+}
+
+bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread,
+ GlLayer& layer, SkBitmap* bitmap) {
+ return CopyResult::Success == copyTextureInto(Caches::getInstance(),
+ renderThread.renderState(), layer.getTexture(), layer.getTexTransform(),
+ Rect(), bitmap);
+}
+
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/OpenGLReadback.h b/libs/hwui/OpenGLReadback.h
new file mode 100644
index 000000000000..c9222cff51da
--- /dev/null
+++ b/libs/hwui/OpenGLReadback.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Readback.h"
+
+namespace android {
+namespace uirenderer {
+
+class Matrix4;
+class GlLayer;
+
+class OpenGLReadback : public Readback {
+public:
+ virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
+ SkBitmap* bitmap) override;
+ virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
+ SkBitmap* bitmap) override;
+
+protected:
+ explicit OpenGLReadback(renderthread::RenderThread& thread) : Readback(thread) {}
+ virtual ~OpenGLReadback() {}
+
+ virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
+ int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) = 0;
+private:
+ CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform,
+ const Rect& srcRect, SkBitmap* bitmap);
+};
+
+class OpenGLReadbackImpl : public OpenGLReadback {
+public:
+ OpenGLReadbackImpl(renderthread::RenderThread& thread) : OpenGLReadback(thread) {}
+
+ /**
+ * Copies the layer's contents into the provided bitmap.
+ */
+ static bool copyLayerInto(renderthread::RenderThread& renderThread, GlLayer& layer,
+ SkBitmap* bitmap);
+
+protected:
+ virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
+ int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override;
+};
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
deleted file mode 100644
index 9d821f38fab6..000000000000
--- a/libs/hwui/OpenGLRenderer.cpp
+++ /dev/null
@@ -1,2451 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <GpuMemoryTracker.h>
-#include "OpenGLRenderer.h"
-
-#include "DeferredDisplayList.h"
-#include "GammaFontRenderer.h"
-#include "Glop.h"
-#include "GlopBuilder.h"
-#include "Patch.h"
-#include "PathTessellator.h"
-#include "Properties.h"
-#include "RenderNode.h"
-#include "renderstate/MeshState.h"
-#include "renderstate/RenderState.h"
-#include "ShadowTessellator.h"
-#include "SkiaShader.h"
-#include "Vector.h"
-#include "VertexBuffer.h"
-#include "hwui/Canvas.h"
-#include "utils/GLUtils.h"
-#include "utils/PaintUtils.h"
-#include "utils/TraceUtils.h"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <SkColor.h>
-#include <SkPaintDefaults.h>
-#include <SkPathOps.h>
-#include <SkShader.h>
-#include <SkTypeface.h>
-
-#include <utils/Log.h>
-#include <utils/StopWatch.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-#include <ui/Rect.h>
-
-#if DEBUG_DETAILED_EVENTS
- #define EVENT_LOGD(...) eventMarkDEBUG(__VA_ARGS__)
-#else
- #define EVENT_LOGD(...)
-#endif
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
- : mState(*this)
- , mCaches(Caches::getInstance())
- , mRenderState(renderState)
- , mFrameStarted(false)
- , mScissorOptimizationDisabled(false)
- , mDirty(false)
- , mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN})
- , mLightRadius(FLT_MIN)
- , mAmbientShadowAlpha(0)
- , mSpotShadowAlpha(0) {
-}
-
-OpenGLRenderer::~OpenGLRenderer() {
- // The context has already been destroyed at this point, do not call
- // GL APIs. All GL state should be kept in Caches.h
-}
-
-void OpenGLRenderer::initProperties() {
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_DISABLE_SCISSOR_OPTIMIZATION, property, "false")) {
- mScissorOptimizationDisabled = !strcasecmp(property, "true");
- INIT_LOGD(" Scissor optimization %s",
- mScissorOptimizationDisabled ? "disabled" : "enabled");
- } else {
- INIT_LOGD(" Scissor optimization enabled");
- }
-}
-
-void OpenGLRenderer::initLight(float lightRadius, uint8_t ambientShadowAlpha,
- uint8_t spotShadowAlpha) {
- mLightRadius = lightRadius;
- mAmbientShadowAlpha = ambientShadowAlpha;
- mSpotShadowAlpha = spotShadowAlpha;
-}
-
-void OpenGLRenderer::setLightCenter(const Vector3& lightCenter) {
- mLightCenter = lightCenter;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Setup
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::onViewportInitialized() {
- glDisable(GL_DITHER);
- glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-}
-
-void OpenGLRenderer::setupFrameState(int viewportWidth, int viewportHeight,
- float left, float top, float right, float bottom, bool opaque) {
- mCaches.clearGarbage();
- mState.initializeSaveStack(viewportWidth, viewportHeight,
- left, top, right, bottom, mLightCenter);
- mOpaque = opaque;
- mTilingClip.set(left, top, right, bottom);
-}
-
-void OpenGLRenderer::startFrame() {
- if (mFrameStarted) return;
- mFrameStarted = true;
-
- mState.setDirtyClip(true);
-
- discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
-
- mRenderState.setViewport(mState.getWidth(), mState.getHeight());
-
- debugOverdraw(true, true);
-
- clear(mTilingClip.left, mTilingClip.top,
- mTilingClip.right, mTilingClip.bottom, mOpaque);
-}
-
-void OpenGLRenderer::prepareDirty(int viewportWidth, int viewportHeight,
- float left, float top, float right, float bottom, bool opaque) {
-
- setupFrameState(viewportWidth, viewportHeight, left, top, right, bottom, opaque);
-
- // Layer renderers will start the frame immediately
- // The framebuffer renderer will first defer the display list
- // for each layer and wait until the first drawing command
- // to start the frame
- if (currentSnapshot()->fbo == 0) {
- mRenderState.blend().syncEnabled();
- updateLayers();
- } else {
- startFrame();
- }
-}
-
-void OpenGLRenderer::discardFramebuffer(float left, float top, float right, float bottom) {
- // If we know that we are going to redraw the entire framebuffer,
- // perform a discard to let the driver know we don't need to preserve
- // the back buffer for this frame.
- if (mCaches.extensions().hasDiscardFramebuffer() &&
- left <= 0.0f && top <= 0.0f && right >= mState.getWidth() && bottom >= mState.getHeight()) {
- const bool isFbo = getTargetFbo() == 0;
- const GLenum attachments[] = {
- isFbo ? (const GLenum) GL_COLOR_EXT : (const GLenum) GL_COLOR_ATTACHMENT0,
- isFbo ? (const GLenum) GL_STENCIL_EXT : (const GLenum) GL_STENCIL_ATTACHMENT };
- glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
- }
-}
-
-void OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
- if (!opaque) {
- mRenderState.scissor().setEnabled(true);
- mRenderState.scissor().set(left, getViewportHeight() - bottom, right - left, bottom - top);
- glClear(GL_COLOR_BUFFER_BIT);
- mDirty = true;
- return;
- }
-
- mRenderState.scissor().reset();
-}
-
-bool OpenGLRenderer::finish() {
- renderOverdraw();
- mTempPaths.clear();
-
- // When finish() is invoked on FBO 0 we've reached the end
- // of the current frame
- if (getTargetFbo() == 0) {
- mCaches.pathCache.trim();
- mCaches.tessellationCache.trim();
- }
-
- if (!suppressErrorChecks()) {
- GL_CHECKPOINT(MODERATE);
-
-#if DEBUG_MEMORY_USAGE
- mCaches.dumpMemoryUsage();
- GPUMemoryTracker::dump();
-#else
- if (Properties::debugLevel & kDebugMemory) {
- mCaches.dumpMemoryUsage();
- }
-#endif
- }
-
- mFrameStarted = false;
-
- return reportAndClearDirty();
-}
-
-void OpenGLRenderer::resumeAfterLayer() {
- mRenderState.setViewport(getViewportWidth(), getViewportHeight());
- mRenderState.bindFramebuffer(currentSnapshot()->fbo);
- debugOverdraw(true, false);
-
- mRenderState.scissor().reset();
- dirtyClip();
-}
-
-void OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
- if (mState.currentlyIgnored()) return;
-
- Rect clip(mState.currentRenderTargetClip());
- clip.snapToPixelBoundaries();
-
- // Since we don't know what the functor will draw, let's dirty
- // the entire clip region
- if (hasLayer()) {
- dirtyLayerUnchecked(clip, getRegion());
- }
-
- DrawGlInfo info;
- info.clipLeft = clip.left;
- info.clipTop = clip.top;
- info.clipRight = clip.right;
- info.clipBottom = clip.bottom;
- info.isLayer = hasLayer();
- info.width = getViewportWidth();
- info.height = getViewportHeight();
- currentTransform()->copyTo(&info.transform[0]);
-
- bool prevDirtyClip = mState.getDirtyClip();
- // setup GL state for functor
- if (mState.getDirtyClip()) {
- setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
- }
- if (mRenderState.scissor().setEnabled(true) || prevDirtyClip) {
- setScissorFromClip();
- }
-
- mRenderState.invokeFunctor(functor, DrawGlInfo::kModeDraw, &info);
- // Scissor may have been modified, reset dirty clip
- dirtyClip();
-
- mDirty = true;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Debug
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::eventMarkDEBUG(const char* fmt, ...) const {
-#if DEBUG_DETAILED_EVENTS
- const int BUFFER_SIZE = 256;
- va_list ap;
- char buf[BUFFER_SIZE];
-
- va_start(ap, fmt);
- vsnprintf(buf, BUFFER_SIZE, fmt, ap);
- va_end(ap);
-
- eventMark(buf);
-#endif
-}
-
-
-void OpenGLRenderer::eventMark(const char* name) const {
- mCaches.eventMark(0, name);
-}
-
-void OpenGLRenderer::startMark(const char* name) const {
- mCaches.startMark(0, name);
-}
-
-void OpenGLRenderer::endMark() const {
- mCaches.endMark();
-}
-
-void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
- mRenderState.debugOverdraw(enable, clear);
-}
-
-void OpenGLRenderer::renderOverdraw() {
- if (Properties::debugOverdraw && getTargetFbo() == 0) {
- const Rect* clip = &mTilingClip;
-
- mRenderState.scissor().setEnabled(true);
- mRenderState.scissor().set(clip->left,
- mState.firstSnapshot()->getViewportHeight() - clip->bottom,
- clip->right - clip->left,
- clip->bottom - clip->top);
-
- // 1x overdraw
- mRenderState.stencil().enableDebugTest(2);
- drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode);
-
- // 2x overdraw
- mRenderState.stencil().enableDebugTest(3);
- drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode);
-
- // 3x overdraw
- mRenderState.stencil().enableDebugTest(4);
- drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode);
-
- // 4x overdraw and higher
- mRenderState.stencil().enableDebugTest(4, true);
- drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode);
-
- mRenderState.stencil().disable();
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Layers
-///////////////////////////////////////////////////////////////////////////////
-
-bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
- if (layer->deferredUpdateScheduled && layer->renderer
- && layer->renderNode.get() && layer->renderNode->isRenderable()) {
-
- if (inFrame) {
- debugOverdraw(false, false);
- }
-
- if (CC_UNLIKELY(inFrame || Properties::drawDeferDisabled)) {
- layer->render(*this);
- } else {
- layer->defer(*this);
- }
-
- if (inFrame) {
- resumeAfterLayer();
- }
-
- layer->debugDrawUpdate = Properties::debugLayersUpdates;
- layer->hasDrawnSinceUpdate = false;
-
- return true;
- }
-
- return false;
-}
-
-void OpenGLRenderer::updateLayers() {
- // If draw deferring is enabled this method will simply defer
- // the display list of each individual layer. The layers remain
- // in the layer updates list which will be cleared by flushLayers().
- int count = mLayerUpdates.size();
- if (count > 0) {
- if (CC_UNLIKELY(Properties::drawDeferDisabled)) {
- startMark("Layer Updates");
- } else {
- startMark("Defer Layer Updates");
- }
-
- // Note: it is very important to update the layers in order
- for (int i = 0; i < count; i++) {
- Layer* layer = mLayerUpdates[i].get();
- updateLayer(layer, false);
- }
-
- if (CC_UNLIKELY(Properties::drawDeferDisabled)) {
- mLayerUpdates.clear();
- mRenderState.bindFramebuffer(getTargetFbo());
- }
- endMark();
- }
-}
-
-void OpenGLRenderer::flushLayers() {
- int count = mLayerUpdates.size();
- if (count > 0) {
- startMark("Apply Layer Updates");
-
- // Note: it is very important to update the layers in order
- for (int i = 0; i < count; i++) {
- mLayerUpdates[i]->flush();
- }
-
- mLayerUpdates.clear();
- mRenderState.bindFramebuffer(getTargetFbo());
-
- endMark();
- }
-}
-
-void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
- if (layer) {
- // Make sure we don't introduce duplicates.
- // SortedVector would do this automatically but we need to respect
- // the insertion order. The linear search is not an issue since
- // this list is usually very short (typically one item, at most a few)
- for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
- if (mLayerUpdates[i] == layer) {
- return;
- }
- }
- mLayerUpdates.push_back(layer);
- }
-}
-
-void OpenGLRenderer::cancelLayerUpdate(Layer* layer) {
- if (layer) {
- for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
- if (mLayerUpdates[i] == layer) {
- mLayerUpdates.erase(mLayerUpdates.begin() + i);
- break;
- }
- }
- }
-}
-
-void OpenGLRenderer::flushLayerUpdates() {
- ATRACE_NAME("Update HW Layers");
- mRenderState.blend().syncEnabled();
- updateLayers();
- flushLayers();
- // Wait for all the layer updates to be executed
- glFinish();
-}
-
-void OpenGLRenderer::markLayersAsBuildLayers() {
- for (size_t i = 0; i < mLayerUpdates.size(); i++) {
- mLayerUpdates[i]->wasBuildLayered = true;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// State management
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
- bool restoreViewport = removed.flags & Snapshot::kFlagIsFboLayer;
- bool restoreClip = removed.flags & Snapshot::kFlagClipSet;
- bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer;
-
- if (restoreViewport) {
- mRenderState.setViewport(getViewportWidth(), getViewportHeight());
- }
-
- if (restoreClip) {
- dirtyClip();
- }
-
- if (restoreLayer) {
- endMark(); // Savelayer
- ATRACE_END(); // SaveLayer
- startMark("ComposeLayer");
- composeLayer(removed, restored);
- endMark();
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Layers
-///////////////////////////////////////////////////////////////////////////////
-
-int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags, const SkPath* convexMask) {
- // force matrix/clip isolation for layer
- flags |= SaveFlags::MatrixClip;
-
- const int count = mState.saveSnapshot(flags);
-
- if (!mState.currentlyIgnored()) {
- createLayer(left, top, right, bottom, paint, flags, convexMask);
- }
-
- return count;
-}
-
-void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer) {
- const Rect untransformedBounds(bounds);
-
- currentTransform()->mapRect(bounds);
-
- // Layers only make sense if they are in the framebuffer's bounds
- bounds.doIntersect(mState.currentRenderTargetClip());
- if (!bounds.isEmpty()) {
- // We cannot work with sub-pixels in this case
- bounds.snapToPixelBoundaries();
-
- // When the layer is not an FBO, we may use glCopyTexImage so we
- // need to make sure the layer does not extend outside the bounds
- // of the framebuffer
- const Snapshot& previous = *(currentSnapshot()->previous);
- Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
-
- bounds.doIntersect(previousViewport);
- if (!bounds.isEmpty() && fboLayer) {
- clip.set(bounds);
- mat4 inverse;
- inverse.loadInverse(*currentTransform());
- inverse.mapRect(clip);
- clip.snapToPixelBoundaries();
- clip.doIntersect(untransformedBounds);
- if (!clip.isEmpty()) {
- clip.translate(-untransformedBounds.left, -untransformedBounds.top);
- bounds.set(untransformedBounds);
- }
- }
- }
-}
-
-void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect& clip,
- bool fboLayer, int alpha) {
- if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
- bounds.getHeight() > mCaches.maxTextureSize ||
- (fboLayer && clip.isEmpty())) {
- writableSnapshot()->empty = fboLayer;
- } else {
- writableSnapshot()->invisible = writableSnapshot()->invisible || (alpha <= 0 && fboLayer);
- }
-}
-
-int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags) {
- const int count = mState.saveSnapshot(flags);
-
- if (!mState.currentlyIgnored() && (flags & SaveFlags::ClipToLayer)) {
- // initialize the snapshot as though it almost represents an FBO layer so deferred draw
- // operations will be able to store and restore the current clip and transform info, and
- // quick rejection will be correct (for display lists)
-
- Rect bounds(left, top, right, bottom);
- Rect clip;
- calculateLayerBoundsAndClip(bounds, clip, true);
- updateSnapshotIgnoreForLayer(bounds, clip, true, PaintUtils::getAlphaDirect(paint));
-
- if (!mState.currentlyIgnored()) {
- writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
- writableSnapshot()->resetClip(clip.left, clip.top, clip.right, clip.bottom);
- writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight());
- writableSnapshot()->roundRectClipState = nullptr;
- }
- }
-
- return count;
-}
-
-/**
- * Layers are viewed by Skia are slightly different than layers in image editing
- * programs (for instance.) When a layer is created, previously created layers
- * and the frame buffer still receive every drawing command. For instance, if a
- * layer is created and a shape intersecting the bounds of the layers and the
- * framebuffer is draw, the shape will be drawn on both (unless the layer was
- * created with the SaveFlags::ClipToLayer flag.)
- *
- * A way to implement layers is to create an FBO for each layer, backed by an RGBA
- * texture. Unfortunately, this is inefficient as it requires every primitive to
- * be drawn n + 1 times, where n is the number of active layers. In practice this
- * means, for every primitive:
- * - Switch active frame buffer
- * - Change viewport, clip and projection matrix
- * - Issue the drawing
- *
- * Switching rendering target n + 1 times per drawn primitive is extremely costly.
- * To avoid this, layers are implemented in a different way here, at least in the
- * general case. FBOs are used, as an optimization, when the "clip to layer" flag
- * is set. When this flag is set we can redirect all drawing operations into a
- * single FBO.
- *
- * This implementation relies on the frame buffer being at least RGBA 8888. When
- * a layer is created, only a texture is created, not an FBO. The content of the
- * frame buffer contained within the layer's bounds is copied into this texture
- * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame
- * buffer and drawing continues as normal. This technique therefore treats the
- * frame buffer as a scratch buffer for the layers.
- *
- * To compose the layers back onto the frame buffer, each layer texture
- * (containing the original frame buffer data) is drawn as a simple quad over
- * the frame buffer. The trick is that the quad is set as the composition
- * destination in the blending equation, and the frame buffer becomes the source
- * of the composition.
- *
- * Drawing layers with an alpha value requires an extra step before composition.
- * An empty quad is drawn over the layer's region in the frame buffer. This quad
- * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the
- * quad is used to multiply the colors in the frame buffer. This is achieved by
- * changing the GL blend functions for the GL_FUNC_ADD blend equation to
- * GL_ZERO, GL_SRC_ALPHA.
- *
- * Because glCopyTexImage2D() can be slow, an alternative implementation might
- * be use to draw a single clipped layer. The implementation described above
- * is correct in every case.
- *
- * (1) The frame buffer is actually not cleared right away. To allow the GPU
- * to potentially optimize series of calls to glCopyTexImage2D, the frame
- * buffer is left untouched until the first drawing operation. Only when
- * something actually gets drawn are the layers regions cleared.
- */
-bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags, const SkPath* convexMask) {
- LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
- LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
-
- const bool fboLayer = flags & SaveFlags::ClipToLayer;
-
- // Window coordinates of the layer
- Rect clip;
- Rect bounds(left, top, right, bottom);
- calculateLayerBoundsAndClip(bounds, clip, fboLayer);
- updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, PaintUtils::getAlphaDirect(paint));
-
- // Bail out if we won't draw in this snapshot
- if (mState.currentlyIgnored()) {
- return false;
- }
-
- mCaches.textureState().activateTexture(0);
- Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight());
- if (!layer) {
- return false;
- }
-
- layer->setPaint(paint);
- layer->layer.set(bounds);
- layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()),
- bounds.getWidth() / float(layer->getWidth()), 0.0f);
-
- layer->setBlend(true);
- layer->setDirty(false);
- layer->setConvexMask(convexMask); // note: the mask must be cleared before returning to the cache
-
- // Save the layer in the snapshot
- writableSnapshot()->flags |= Snapshot::kFlagIsLayer;
- writableSnapshot()->layer = layer;
-
- ATRACE_FORMAT_BEGIN("%ssaveLayer %ux%u",
- fboLayer ? "" : "unclipped ",
- layer->getWidth(), layer->getHeight());
- startMark("SaveLayer");
- if (fboLayer) {
- return createFboLayer(layer, bounds, clip);
- } else {
- // Copy the framebuffer into the layer
- layer->bindTexture();
- if (!bounds.isEmpty()) {
- if (layer->isEmpty()) {
- // Workaround for some GL drivers. When reading pixels lying outside
- // of the window we should get undefined values for those pixels.
- // Unfortunately some drivers will turn the entire target texture black
- // when reading outside of the window.
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->getWidth(), layer->getHeight(),
- 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
- layer->setEmpty(false);
- }
-
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
- bounds.left, getViewportHeight() - bounds.bottom,
- bounds.getWidth(), bounds.getHeight());
-
- // Enqueue the buffer coordinates to clear the corresponding region later
- mLayers.push_back(Rect(bounds));
- }
- }
-
- return true;
-}
-
-bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
- layer->clipRect.set(clip);
- layer->setFbo(mRenderState.createFramebuffer());
-
- writableSnapshot()->region = &writableSnapshot()->layer->region;
- writableSnapshot()->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
- writableSnapshot()->fbo = layer->getFbo();
- writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
- writableSnapshot()->resetClip(clip.left, clip.top, clip.right, clip.bottom);
- writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight());
- writableSnapshot()->roundRectClipState = nullptr;
-
- debugOverdraw(false, false);
- // Bind texture to FBO
- mRenderState.bindFramebuffer(layer->getFbo());
- layer->bindTexture();
-
- // Initialize the texture if needed
- if (layer->isEmpty()) {
- layer->allocateTexture();
- layer->setEmpty(false);
- }
-
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- layer->getTextureId(), 0);
-
- // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
- mRenderState.scissor().setEnabled(true);
- mRenderState.scissor().set(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
- clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
- glClear(GL_COLOR_BUFFER_BIT);
-
- dirtyClip();
-
- // Change the ortho projection
- mRenderState.setViewport(bounds.getWidth(), bounds.getHeight());
- return true;
-}
-
-/**
- * Read the documentation of createLayer() before doing anything in this method.
- */
-void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& restored) {
- if (!removed.layer) {
- ALOGE("Attempting to compose a layer that does not exist");
- return;
- }
-
- Layer* layer = removed.layer;
- const Rect& rect = layer->layer;
- const bool fboLayer = removed.flags & Snapshot::kFlagIsFboLayer;
-
- bool clipRequired = false;
- mState.calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom,
- &clipRequired, nullptr, false); // safely ignore return, should never be rejected
- mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
-
- if (fboLayer) {
- // Detach the texture from the FBO
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
-
- layer->removeFbo(false);
-
- // Unbind current FBO and restore previous one
- mRenderState.bindFramebuffer(restored.fbo);
- debugOverdraw(true, false);
- }
-
- if (!fboLayer && layer->getAlpha() < 255) {
- SkPaint layerPaint;
- layerPaint.setAlpha(layer->getAlpha());
- layerPaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
- layerPaint.setColorFilter(layer->getColorFilter());
-
- drawColorRect(rect.left, rect.top, rect.right, rect.bottom, &layerPaint, true);
- // Required below, composeLayerRect() will divide by 255
- layer->setAlpha(255);
- }
-
- mRenderState.meshState().unbindMeshBuffer();
-
- mCaches.textureState().activateTexture(0);
-
- // When the layer is stored in an FBO, we can save a bit of fillrate by
- // drawing only the dirty region
- if (fboLayer) {
- dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *restored.transform);
- composeLayerRegion(layer, rect);
- } else if (!rect.isEmpty()) {
- dirtyLayer(rect.left, rect.top, rect.right, rect.bottom);
-
- save(0);
- // the layer contains screen buffer content that shouldn't be alpha modulated
- // (and any necessary alpha modulation was handled drawing into the layer)
- writableSnapshot()->alpha = 1.0f;
- composeLayerRectSwapped(layer, rect);
- restore();
- }
-
- dirtyClip();
-
- // Failing to add the layer to the cache should happen only if the layer is too large
- layer->setConvexMask(nullptr);
- if (!mCaches.layerCache.put(layer)) {
- LAYER_LOGD("Deleting layer");
- layer->decStrong(nullptr);
- }
-}
-
-void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
- const bool tryToSnap = !layer->getForceFilter()
- && layer->getWidth() == (uint32_t) rect.getWidth()
- && layer->getHeight() == (uint32_t) rect.getHeight();
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
- .setFillTextureLayer(*layer, getLayerAlpha(layer))
- .setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewMapUnitToRectOptionalSnap(tryToSnap, rect)
- .build();
- renderGlop(glop);
-}
-
-void OpenGLRenderer::composeLayerRectSwapped(Layer* layer, const Rect& rect) {
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshTexturedUvQuad(nullptr, layer->texCoords)
- .setFillLayer(layer->getTexture(), layer->getColorFilter(),
- getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::Swap)
- .setTransform(*currentSnapshot(), TransformFlags::MeshIgnoresCanvasTransform)
- .setModelViewMapUnitToRect(rect)
- .build();
- renderGlop(glop);
-}
-
-void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect) {
- if (layer->isTextureLayer()) {
- EVENT_LOGD("composeTextureLayerRect");
- drawTextureLayer(layer, rect);
- } else {
- EVENT_LOGD("composeHardwareLayerRect");
-
- const bool tryToSnap = layer->getWidth() == static_cast<uint32_t>(rect.getWidth())
- && layer->getHeight() == static_cast<uint32_t>(rect.getHeight());
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshTexturedUvQuad(nullptr, layer->texCoords)
- .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
- .setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewMapUnitToRectOptionalSnap(tryToSnap, rect)
- .build();
- renderGlop(glop);
- }
-}
-
-/**
- * Issues the command X, and if we're composing a save layer to the fbo or drawing a newly updated
- * hardware layer with overdraw debug on, draws again to the stencil only, so that these draw
- * operations are correctly counted twice for overdraw. NOTE: assumes composeLayerRegion only used
- * by saveLayer's restore
- */
-#define DRAW_DOUBLE_STENCIL_IF(COND, DRAW_COMMAND) { \
- DRAW_COMMAND; \
- if (CC_UNLIKELY(Properties::debugOverdraw && getTargetFbo() == 0 && (COND))) { \
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); \
- DRAW_COMMAND; \
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); \
- } \
- }
-
-#define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
-
-// This class is purely for inspection. It inherits from SkShader, but Skia does not know how to
-// use it. The OpenGLRenderer will look at it to find its Layer and whether it is opaque.
-class LayerShader : public SkShader {
-public:
- LayerShader(Layer* layer, const SkMatrix* localMatrix)
- : INHERITED(localMatrix)
- , mLayer(layer) {
- }
-
- virtual bool asACustomShader(void** data) const override {
- if (data) {
- *data = static_cast<void*>(mLayer);
- }
- return true;
- }
-
- virtual bool isOpaque() const override {
- return !mLayer->isBlend();
- }
-
-protected:
- virtual void shadeSpan(int x, int y, SkPMColor[], int count) {
- LOG_ALWAYS_FATAL("LayerShader should never be drawn with raster backend.");
- }
-
- virtual void flatten(SkWriteBuffer&) const override {
- LOG_ALWAYS_FATAL("LayerShader should never be flattened.");
- }
-
- virtual Factory getFactory() const override {
- LOG_ALWAYS_FATAL("LayerShader should never be created from a stream.");
- return nullptr;
- }
-private:
- // Unowned.
- Layer* mLayer;
- typedef SkShader INHERITED;
-};
-
-void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
- if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw
-
- if (layer->getConvexMask()) {
- save(SaveFlags::MatrixClip);
-
- // clip to the area of the layer the mask can be larger
- clipRect(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kIntersect_Op);
-
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setColor(SkColorSetARGB(int(getLayerAlpha(layer) * 255), 0, 0, 0));
-
- // create LayerShader to map SaveLayer content into subsequent draw
- SkMatrix shaderMatrix;
- shaderMatrix.setTranslate(rect.left, rect.bottom);
- shaderMatrix.preScale(1, -1);
- LayerShader layerShader(layer, &shaderMatrix);
- paint.setShader(&layerShader);
-
- // Since the drawing primitive is defined in local drawing space,
- // we don't need to modify the draw matrix
- const SkPath* maskPath = layer->getConvexMask();
- DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint));
-
- paint.setShader(nullptr);
- restore();
-
- return;
- }
-
- if (layer->region.isRect()) {
- layer->setRegionAsRect();
-
- DRAW_DOUBLE_STENCIL(composeLayerRect(layer, layer->regionRect));
-
- layer->region.clear();
- return;
- }
-
- EVENT_LOGD("composeLayerRegion");
- // standard Region based draw
- size_t count;
- const android::Rect* rects;
- Region safeRegion;
- if (CC_LIKELY(hasRectToRectTransform())) {
- rects = layer->region.getArray(&count);
- } else {
- safeRegion = Region::createTJunctionFreeRegion(layer->region);
- rects = safeRegion.getArray(&count);
- }
-
- const float texX = 1.0f / float(layer->getWidth());
- const float texY = 1.0f / float(layer->getHeight());
- const float height = rect.getHeight();
-
- TextureVertex quadVertices[count * 4];
- TextureVertex* mesh = &quadVertices[0];
- for (size_t i = 0; i < count; i++) {
- const android::Rect* r = &rects[i];
-
- const float u1 = r->left * texX;
- const float v1 = (height - r->top) * texY;
- const float u2 = r->right * texX;
- const float v2 = (height - r->bottom) * texY;
-
- // TODO: Reject quads outside of the clip
- TextureVertex::set(mesh++, r->left, r->top, u1, v1);
- TextureVertex::set(mesh++, r->right, r->top, u2, v1);
- TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
- TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
- }
- Rect modelRect = Rect(rect.getWidth(), rect.getHeight());
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshTexturedIndexedQuads(&quadVertices[0], count * 6)
- .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
- .setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewOffsetRectSnap(rect.left, rect.top, modelRect)
- .build();
- DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
-
-#if DEBUG_LAYERS_AS_REGIONS
- drawRegionRectsDebug(layer->region);
-#endif
-
- layer->region.clear();
-}
-
-#if DEBUG_LAYERS_AS_REGIONS
-void OpenGLRenderer::drawRegionRectsDebug(const Region& region) {
- size_t count;
- const android::Rect* rects = region.getArray(&count);
-
- uint32_t colors[] = {
- 0x7fff0000, 0x7f00ff00,
- 0x7f0000ff, 0x7fff00ff,
- };
-
- int offset = 0;
- int32_t top = rects[0].top;
-
- for (size_t i = 0; i < count; i++) {
- if (top != rects[i].top) {
- offset ^= 0x2;
- top = rects[i].top;
- }
-
- SkPaint paint;
- paint.setColor(colors[offset + (i & 0x1)]);
- Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
- drawColorRect(r.left, r.top, r.right, r.bottom, paint);
- }
-}
-#endif
-
-void OpenGLRenderer::drawRegionRects(const SkRegion& region, const SkPaint& paint, bool dirty) {
- Vector<float> rects;
-
- SkRegion::Iterator it(region);
- while (!it.done()) {
- const SkIRect& r = it.rect();
- rects.push(r.fLeft);
- rects.push(r.fTop);
- rects.push(r.fRight);
- rects.push(r.fBottom);
- it.next();
- }
-
- drawColorRects(rects.array(), rects.size(), &paint, true, dirty, false);
-}
-
-void OpenGLRenderer::dirtyLayer(const float left, const float top,
- const float right, const float bottom, const Matrix4& transform) {
- if (hasLayer()) {
- Rect bounds(left, top, right, bottom);
- transform.mapRect(bounds);
- dirtyLayerUnchecked(bounds, getRegion());
- }
-}
-
-void OpenGLRenderer::dirtyLayer(const float left, const float top,
- const float right, const float bottom) {
- if (hasLayer()) {
- Rect bounds(left, top, right, bottom);
- dirtyLayerUnchecked(bounds, getRegion());
- }
-}
-
-void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
- bounds.doIntersect(mState.currentRenderTargetClip());
- if (!bounds.isEmpty()) {
- bounds.snapToPixelBoundaries();
- android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
- if (!dirty.isEmpty()) {
- region->orSelf(dirty);
- }
- }
-}
-
-void OpenGLRenderer::clearLayerRegions() {
- const size_t quadCount = mLayers.size();
- if (quadCount == 0) return;
-
- if (!mState.currentlyIgnored()) {
- EVENT_LOGD("clearLayerRegions");
- // Doing several glScissor/glClear here can negatively impact
- // GPUs with a tiler architecture, instead we draw quads with
- // the Clear blending mode
-
- // The list contains bounds that have already been clipped
- // against their initial clip rect, and the current clip
- // is likely different so we need to disable clipping here
- bool scissorChanged = mRenderState.scissor().setEnabled(false);
-
- Vertex mesh[quadCount * 4];
- Vertex* vertex = mesh;
-
- for (uint32_t i = 0; i < quadCount; i++) {
- const Rect& bounds = mLayers[i];
-
- Vertex::set(vertex++, bounds.left, bounds.top);
- Vertex::set(vertex++, bounds.right, bounds.top);
- Vertex::set(vertex++, bounds.left, bounds.bottom);
- Vertex::set(vertex++, bounds.right, bounds.bottom);
- }
- // We must clear the list of dirty rects before we
- // call clearLayerRegions() in renderGlop to prevent
- // stencil setup from doing the same thing again
- mLayers.clear();
-
- const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(nullptr) // clear ignores clip state
- .setMeshIndexedQuads(&mesh[0], quadCount)
- .setFillClear()
- .setTransform(*currentSnapshot(), transformFlags)
- .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getRenderTargetClip()))
- .build();
- renderGlop(glop, GlopRenderType::LayerClear);
-
- if (scissorChanged) mRenderState.scissor().setEnabled(true);
- } else {
- mLayers.clear();
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// State Deferral
-///////////////////////////////////////////////////////////////////////////////
-
-bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
- const Rect& currentClip = mState.currentRenderTargetClip();
- const mat4* currentMatrix = currentTransform();
-
- if (stateDeferFlags & kStateDeferFlag_Draw) {
- // state has bounds initialized in local coordinates
- if (!state.mBounds.isEmpty()) {
- currentMatrix->mapRect(state.mBounds);
- Rect clippedBounds(state.mBounds);
- // NOTE: if we ever want to use this clipping info to drive whether the scissor
- // is used, it should more closely duplicate the quickReject logic (in how it uses
- // snapToPixelBoundaries)
-
- clippedBounds.doIntersect(currentClip);
- if (clippedBounds.isEmpty()) {
- // quick rejected
- return true;
- }
-
- state.mClipSideFlags = kClipSide_None;
- if (!currentClip.contains(state.mBounds)) {
- int& flags = state.mClipSideFlags;
- // op partially clipped, so record which sides are clipped for clip-aware merging
- if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left;
- if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top;
- if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right;
- if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
- }
- state.mBounds.set(clippedBounds);
- } else {
- // Empty bounds implies size unknown. Label op as conservatively clipped to disable
- // overdraw avoidance (since we don't know what it overlaps)
- state.mClipSideFlags = kClipSide_ConservativeFull;
- state.mBounds.set(currentClip);
- }
- }
-
- state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip);
- if (state.mClipValid) {
- state.mClip.set(currentClip);
- }
-
- // Transform and alpha always deferred, since they are used by state operations
- // (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything)
- state.mMatrix = *currentMatrix;
- state.mAlpha = currentSnapshot()->alpha;
-
- // always store/restore, since these are just pointers
- state.mRoundRectClipState = currentSnapshot()->roundRectClipState;
-#if !HWUI_NEW_OPS
- state.mProjectionPathMask = currentSnapshot()->projectionPathMask;
-#endif
- return false;
-}
-
-void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
- setGlobalMatrix(state.mMatrix);
- writableSnapshot()->alpha = state.mAlpha;
- writableSnapshot()->roundRectClipState = state.mRoundRectClipState;
-#if !HWUI_NEW_OPS
- writableSnapshot()->projectionPathMask = state.mProjectionPathMask;
-#endif
-
- if (state.mClipValid && !skipClipRestore) {
- writableSnapshot()->setClip(state.mClip.left, state.mClip.top,
- state.mClip.right, state.mClip.bottom);
- dirtyClip();
- }
-}
-
-/**
- * Merged multidraw (such as in drawText and drawBitmaps rely on the fact that no clipping is done
- * in the draw path. Instead, clipping is done ahead of time - either as a single clip rect (when at
- * least one op is clipped), or disabled entirely (because no merged op is clipped)
- *
- * This method should be called when restoreDisplayState() won't be restoring the clip
- */
-void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) {
- if (clipRect != nullptr) {
- writableSnapshot()->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
- } else {
- writableSnapshot()->setClip(0, 0, mState.getWidth(), mState.getHeight());
- }
- dirtyClip();
- bool enableScissor = (clipRect != nullptr) || mScissorOptimizationDisabled;
- mRenderState.scissor().setEnabled(enableScissor);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Clipping
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::setScissorFromClip() {
- Rect clip(mState.currentRenderTargetClip());
- clip.snapToPixelBoundaries();
-
- if (mRenderState.scissor().set(clip.left, getViewportHeight() - clip.bottom,
- clip.getWidth(), clip.getHeight())) {
- mState.setDirtyClip(false);
- }
-}
-
-void OpenGLRenderer::ensureStencilBuffer() {
- // Thanks to the mismatch between EGL and OpenGL ES FBO we
- // cannot attach a stencil buffer to fbo0 dynamically. Let's
- // just hope we have one when hasLayer() returns false.
- if (hasLayer()) {
- attachStencilBufferToLayer(currentSnapshot()->layer);
- }
-}
-
-void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
- // The layer's FBO is already bound when we reach this stage
- if (!layer->getStencilRenderBuffer()) {
- RenderBuffer* buffer = mCaches.renderBufferCache.get(
- Stencil::getLayerStencilFormat(),
- layer->getWidth(), layer->getHeight());
- layer->setStencilRenderBuffer(buffer);
- }
-}
-
-static void handlePoint(std::vector<Vertex>& rectangleVertices, const Matrix4& transform,
- float x, float y) {
- Vertex v;
- v.x = x;
- v.y = y;
- transform.mapPoint(v.x, v.y);
- rectangleVertices.push_back(v);
-}
-
-static void handlePointNoTransform(std::vector<Vertex>& rectangleVertices, float x, float y) {
- Vertex v;
- v.x = x;
- v.y = y;
- rectangleVertices.push_back(v);
-}
-
-void OpenGLRenderer::drawRectangleList(const RectangleList& rectangleList) {
- int quadCount = rectangleList.getTransformedRectanglesCount();
- std::vector<Vertex> rectangleVertices(quadCount * 4);
- Rect scissorBox = rectangleList.calculateBounds();
- scissorBox.snapToPixelBoundaries();
- for (int i = 0; i < quadCount; ++i) {
- const TransformedRectangle& tr(rectangleList.getTransformedRectangle(i));
- const Matrix4& transform = tr.getTransform();
- Rect bounds = tr.getBounds();
- if (transform.rectToRect()) {
- transform.mapRect(bounds);
- bounds.doIntersect(scissorBox);
- if (!bounds.isEmpty()) {
- handlePointNoTransform(rectangleVertices, bounds.left, bounds.top);
- handlePointNoTransform(rectangleVertices, bounds.right, bounds.top);
- handlePointNoTransform(rectangleVertices, bounds.left, bounds.bottom);
- handlePointNoTransform(rectangleVertices, bounds.right, bounds.bottom);
- }
- } else {
- handlePoint(rectangleVertices, transform, bounds.left, bounds.top);
- handlePoint(rectangleVertices, transform, bounds.right, bounds.top);
- handlePoint(rectangleVertices, transform, bounds.left, bounds.bottom);
- handlePoint(rectangleVertices, transform, bounds.right, bounds.bottom);
- }
- }
-
- mRenderState.scissor().set(scissorBox.left, getViewportHeight() - scissorBox.bottom,
- scissorBox.getWidth(), scissorBox.getHeight());
- const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
- Glop glop;
- Vertex* vertices = &rectangleVertices[0];
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshIndexedQuads(vertices, rectangleVertices.size() / 4)
- .setFillBlack()
- .setTransform(*currentSnapshot(), transformFlags)
- .setModelViewOffsetRect(0, 0, scissorBox)
- .build();
- renderGlop(glop);
-}
-
-void OpenGLRenderer::setStencilFromClip() {
- if (!Properties::debugOverdraw) {
- if (!currentSnapshot()->clipIsSimple()) {
- int incrementThreshold;
- EVENT_LOGD("setStencilFromClip - enabling");
-
- // NOTE: The order here is important, we must set dirtyClip to false
- // before any draw call to avoid calling back into this method
- mState.setDirtyClip(false);
-
- ensureStencilBuffer();
-
- const ClipArea& clipArea = currentSnapshot()->getClipArea();
-
- bool isRectangleList = clipArea.isRectangleList();
- if (isRectangleList) {
- incrementThreshold = clipArea.getRectangleList().getTransformedRectanglesCount();
- } else {
- incrementThreshold = 0;
- }
-
- mRenderState.stencil().enableWrite(incrementThreshold);
-
- // Clean and update the stencil, but first make sure we restrict drawing
- // to the region's bounds
- bool resetScissor = mRenderState.scissor().setEnabled(true);
- if (resetScissor) {
- // The scissor was not set so we now need to update it
- setScissorFromClip();
- }
-
- mRenderState.stencil().clear();
-
- // stash and disable the outline clip state, since stencil doesn't account for outline
- bool storedSkipOutlineClip = mSkipOutlineClip;
- mSkipOutlineClip = true;
-
- SkPaint paint;
- paint.setColor(SK_ColorBLACK);
- paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-
- if (isRectangleList) {
- drawRectangleList(clipArea.getRectangleList());
- } else {
- // NOTE: We could use the region contour path to generate a smaller mesh
- // Since we are using the stencil we could use the red book path
- // drawing technique. It might increase bandwidth usage though.
-
- // The last parameter is important: we are not drawing in the color buffer
- // so we don't want to dirty the current layer, if any
- drawRegionRects(clipArea.getClipRegion(), paint, false);
- }
- if (resetScissor) mRenderState.scissor().setEnabled(false);
- mSkipOutlineClip = storedSkipOutlineClip;
-
- mRenderState.stencil().enableTest(incrementThreshold);
-
- // Draw the region used to generate the stencil if the appropriate debug
- // mode is enabled
- // TODO: Implement for rectangle list clip areas
- if (Properties::debugStencilClip == StencilClipDebug::ShowRegion
- && !clipArea.isRectangleList()) {
- paint.setColor(0x7f0000ff);
- paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
- drawRegionRects(currentSnapshot()->getClipRegion(), paint);
- }
- } else {
- EVENT_LOGD("setStencilFromClip - disabling");
- mRenderState.stencil().disable();
- }
- }
-}
-
-/**
- * Returns false and sets scissor enable based upon bounds if drawing won't be clipped out.
- *
- * @param paint if not null, the bounds will be expanded to account for stroke depending on paint
- * style, and tessellated AA ramp
- */
-bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, float bottom,
- const SkPaint* paint) {
- bool snapOut = paint && paint->isAntiAlias();
-
- if (paint && paint->getStyle() != SkPaint::kFill_Style) {
- float outset = paint->getStrokeWidth() * 0.5f;
- left -= outset;
- top -= outset;
- right += outset;
- bottom += outset;
- }
-
- bool clipRequired = false;
- bool roundRectClipRequired = false;
- if (mState.calculateQuickRejectForScissor(left, top, right, bottom,
- &clipRequired, &roundRectClipRequired, snapOut)) {
- return true;
- }
-
- // not quick rejected, so enable the scissor if clipRequired
- mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
- mSkipOutlineClip = !roundRectClipRequired;
- return false;
-}
-
-void OpenGLRenderer::debugClip() {
-#if DEBUG_CLIP_REGIONS
- if (!currentSnapshot()->clipRegion->isEmpty()) {
- SkPaint paint;
- paint.setColor(0x7f00ff00);
- drawRegionRects(*(currentSnapshot()->clipRegion, paint);
-
- }
-#endif
-}
-
-void OpenGLRenderer::renderGlop(const Glop& glop, GlopRenderType type) {
- // TODO: It would be best if we could do this before quickRejectSetupScissor()
- // changes the scissor test state
- if (type != GlopRenderType::LayerClear) {
- // Regular draws need to clear the dirty area on the layer before they start drawing on top
- // of it. If this draw *is* a layer clear, it skips the clear step (since it would
- // infinitely recurse)
- clearLayerRegions();
- }
-
- if (mState.getDirtyClip()) {
- if (mRenderState.scissor().isEnabled()) {
- setScissorFromClip();
- }
-
- setStencilFromClip();
- }
- mRenderState.render(glop, currentSnapshot()->getOrthoMatrix());
- if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) {
- // TODO: specify more clearly when a draw should dirty the layer.
- // is writing to the stencil the only time we should ignore this?
-#if !HWUI_NEW_OPS
- dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom);
-#endif
- mDirty = true;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Drawing
-///////////////////////////////////////////////////////////////////////////////
-
-void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
- // All the usual checks and setup operations (quickReject, setupDraw, etc.)
- // will be performed by the display list itself
- if (renderNode && renderNode->isRenderable()) {
- // compute 3d ordering
- renderNode->computeOrdering();
- if (CC_UNLIKELY(Properties::drawDeferDisabled)) {
- startFrame();
- ReplayStateStruct replayStruct(*this, dirty, replayFlags);
- renderNode->replay(replayStruct, 0);
- return;
- }
-
- DeferredDisplayList deferredList(mState.currentRenderTargetClip());
- DeferStateStruct deferStruct(deferredList, *this, replayFlags);
- renderNode->defer(deferStruct, 0);
-
- flushLayers();
- startFrame();
-
- deferredList.flush(*this, dirty);
- } else {
- // Even if there is no drawing command(Ex: invisible),
- // it still needs startFrame to clear buffer and start tiling.
- startFrame();
- }
-}
-
-/**
- * Important note: this method is intended to draw batches of bitmaps and
- * will not set the scissor enable or dirty the current layer, if any.
- * The caller is responsible for properly dirtying the current layer.
- */
-void OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
- int bitmapCount, TextureVertex* vertices, bool pureTranslate,
- const Rect& bounds, const SkPaint* paint) {
- Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
- if (!texture) return;
-
- const AutoTexture autoCleanup(texture);
-
- // TODO: remove layer dirty in multi-draw callers
- // TODO: snap doesn't need to touch transform, only texture filter.
- bool snap = pureTranslate;
- const float x = floorf(bounds.left + 0.5f);
- const float y = floorf(bounds.top + 0.5f);
-
- const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
- ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
- const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshTexturedMesh(vertices, bitmapCount * 6)
- .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
- .setTransform(*currentSnapshot(), transformFlags)
- .setModelViewOffsetRectOptionalSnap(snap, x, y, Rect(bounds.getWidth(), bounds.getHeight()))
- .build();
- renderGlop(glop, GlopRenderType::Multi);
-}
-
-void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
- if (quickRejectSetupScissor(0, 0, bitmap->width(), bitmap->height())) {
- return;
- }
-
- mCaches.textureState().activateTexture(0);
- Texture* texture = getTexture(bitmap);
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
-
- const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
- ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshTexturedUnitQuad(texture->uvMapper)
- .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
- .setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height()))
- .build();
- renderGlop(glop);
-}
-
-void OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors, const SkPaint* paint) {
- if (!vertices || mState.currentlyIgnored()) {
- return;
- }
-
- float left = FLT_MAX;
- float top = FLT_MAX;
- float right = FLT_MIN;
- float bottom = FLT_MIN;
-
- const uint32_t elementCount = meshWidth * meshHeight * 6;
-
- std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
- ColorTextureVertex* vertex = &mesh[0];
-
- std::unique_ptr<int[]> tempColors;
- if (!colors) {
- uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1);
- tempColors.reset(new int[colorsCount]);
- memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
- colors = tempColors.get();
- }
-
- Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap->pixelRef());
- const UvMapper& mapper(getMapper(texture));
-
- for (int32_t y = 0; y < meshHeight; y++) {
- for (int32_t x = 0; x < meshWidth; x++) {
- uint32_t i = (y * (meshWidth + 1) + x) * 2;
-
- float u1 = float(x) / meshWidth;
- float u2 = float(x + 1) / meshWidth;
- float v1 = float(y) / meshHeight;
- float v2 = float(y + 1) / meshHeight;
-
- mapper.map(u1, v1, u2, v2);
-
- int ax = i + (meshWidth + 1) * 2;
- int ay = ax + 1;
- int bx = i;
- int by = bx + 1;
- int cx = i + 2;
- int cy = cx + 1;
- int dx = i + (meshWidth + 1) * 2 + 2;
- int dy = dx + 1;
-
- ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
- ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
- ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
-
- ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
- ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
- ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
-
- left = std::min(left, std::min(vertices[ax], std::min(vertices[bx], vertices[cx])));
- top = std::min(top, std::min(vertices[ay], std::min(vertices[by], vertices[cy])));
- right = std::max(right, std::max(vertices[ax], std::max(vertices[bx], vertices[cx])));
- bottom = std::max(bottom, std::max(vertices[ay], std::max(vertices[by], vertices[cy])));
- }
- }
-
- if (quickRejectSetupScissor(left, top, right, bottom)) {
- return;
- }
-
- if (!texture) {
- texture = mCaches.textureCache.get(bitmap);
- if (!texture) {
- return;
- }
- }
- const AutoTexture autoCleanup(texture);
-
- /*
- * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
- * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
- */
- const int textureFillFlags = TextureFillFlags::None;
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshColoredTexturedMesh(mesh.get(), elementCount)
- .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
- .setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
- .build();
- renderGlop(glop);
-}
-
-void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst, const SkPaint* paint) {
- if (quickRejectSetupScissor(dst)) {
- return;
- }
-
- Texture* texture = getTexture(bitmap);
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
-
- Rect uv(std::max(0.0f, src.left / texture->width()),
- std::max(0.0f, src.top / texture->height()),
- std::min(1.0f, src.right / texture->width()),
- std::min(1.0f, src.bottom / texture->height()));
-
- const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
- ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
- const bool tryToSnap = MathUtils::areEqual(src.getWidth(), dst.getWidth())
- && MathUtils::areEqual(src.getHeight(), dst.getHeight());
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshTexturedUvQuad(texture->uvMapper, uv)
- .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
- .setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewMapUnitToRectOptionalSnap(tryToSnap, dst)
- .build();
- renderGlop(glop);
-}
-
-void OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh,
- AssetAtlas::Entry* entry, float left, float top, float right, float bottom,
- const SkPaint* paint) {
- if (!mesh || !mesh->verticesCount || quickRejectSetupScissor(left, top, right, bottom)) {
- return;
- }
-
- Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
-
- // 9 patches are built for stretching - always filter
- int textureFillFlags = TextureFillFlags::ForceFilter;
- if (bitmap->colorType() == kAlpha_8_SkColorType) {
- textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
- }
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshPatchQuads(*mesh)
- .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
- .setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewOffsetRectSnap(left, top, Rect(right - left, bottom - top)) // TODO: get minimal bounds from patch
- .build();
- renderGlop(glop);
-}
-
-/**
- * Important note: this method is intended to draw batches of 9-patch objects and
- * will not set the scissor enable or dirty the current layer, if any.
- * The caller is responsible for properly dirtying the current layer.
- */
-void OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
- TextureVertex* vertices, uint32_t elementCount, const SkPaint* paint) {
- mCaches.textureState().activateTexture(0);
- Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
-
- // TODO: get correct bounds from caller
- const int transformFlags = TransformFlags::MeshIgnoresCanvasTransform;
- // 9 patches are built for stretching - always filter
- int textureFillFlags = TextureFillFlags::ForceFilter;
- if (bitmap->colorType() == kAlpha_8_SkColorType) {
- textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
- }
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshTexturedIndexedQuads(vertices, elementCount)
- .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
- .setTransform(*currentSnapshot(), transformFlags)
- .setModelViewOffsetRect(0, 0, Rect())
- .build();
- renderGlop(glop, GlopRenderType::Multi);
-}
-
-void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
- const VertexBuffer& vertexBuffer, const SkPaint* paint, int displayFlags) {
- // not missing call to quickReject/dirtyLayer, always done at a higher level
- if (!vertexBuffer.getVertexCount()) {
- // no vertices to draw
- return;
- }
-
- bool shadowInterp = displayFlags & kVertexBuffer_ShadowInterp;
- const int transformFlags = TransformFlags::OffsetByFudgeFactor;
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshVertexBuffer(vertexBuffer)
- .setFillPaint(*paint, currentSnapshot()->alpha, shadowInterp)
- .setTransform(*currentSnapshot(), transformFlags)
- .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
- .build();
- renderGlop(glop);
-}
-
-/**
- * Renders a convex path via tessellation. For AA paths, this function uses a similar approach to
- * that of AA lines in the drawLines() function. We expand the convex path by a half pixel in
- * screen space in all directions. However, instead of using a fragment shader to compute the
- * translucency of the color from its position, we simply use a varying parameter to define how far
- * a given pixel is from the edge. For non-AA paths, the expansion and alpha varying are not used.
- *
- * Doesn't yet support joins, caps, or path effects.
- */
-void OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) {
- VertexBuffer vertexBuffer;
- // TODO: try clipping large paths to viewport
-
- PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer);
- drawVertexBuffer(vertexBuffer, paint);
-}
-
-/**
- * We create tristrips for the lines much like shape stroke tessellation, using a per-vertex alpha
- * and additional geometry for defining an alpha slope perimeter.
- *
- * Using GL_LINES can be difficult because the rasterization rules for those lines produces some
- * unexpected results, and may vary between hardware devices. Previously we used a varying-base
- * in-shader alpha region, but found it to be taxing on some GPUs.
- *
- * TODO: try using a fixed input buffer for non-capped lines as in text rendering. this may reduce
- * memory transfer by removing need for degenerate vertices.
- */
-void OpenGLRenderer::drawLines(const float* points, int count, const SkPaint* paint) {
- if (mState.currentlyIgnored() || count < 4) return;
-
- count &= ~0x3; // round down to nearest four
-
- VertexBuffer buffer;
- PathTessellator::tessellateLines(points, count, paint, *currentTransform(), buffer);
- const Rect& bounds = buffer.getBounds();
-
- if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
- return;
- }
-
- int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset;
- drawVertexBuffer(buffer, paint, displayFlags);
-}
-
-void OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) {
- if (mState.currentlyIgnored() || count < 2) return;
-
- count &= ~0x1; // round down to nearest two
-
- VertexBuffer buffer;
- PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), buffer);
-
- const Rect& bounds = buffer.getBounds();
- if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
- return;
- }
-
- int displayFlags = paint->isAntiAlias() ? 0 : kVertexBuffer_Offset;
- drawVertexBuffer(buffer, paint, displayFlags);
-
- mDirty = true;
-}
-
-void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
- // No need to check against the clip, we fill the clip region
- if (mState.currentlyIgnored()) return;
-
- Rect clip(mState.currentRenderTargetClip());
- clip.snapToPixelBoundaries();
-
- SkPaint paint;
- paint.setColor(color);
- paint.setXfermodeMode(mode);
-
- drawColorRect(clip.left, clip.top, clip.right, clip.bottom, &paint, true);
-
- mDirty = true;
-}
-
-void OpenGLRenderer::drawShape(float left, float top, PathTexture* texture,
- const SkPaint* paint) {
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
-
- const float x = left + texture->left - texture->offset;
- const float y = top + texture->top - texture->offset;
-
- drawPathTexture(texture, x, y, paint);
-
- mDirty = true;
-}
-
-void OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, const SkPaint* p) {
- if (mState.currentlyIgnored()
- || quickRejectSetupScissor(left, top, right, bottom, p)
- || PaintUtils::paintWillNotDraw(*p)) {
- return;
- }
-
- if (p->getPathEffect() != nullptr) {
- mCaches.textureState().activateTexture(0);
- PathTexture* texture = mCaches.pathCache.getRoundRect(
- right - left, bottom - top, rx, ry, p);
- drawShape(left, top, texture, p);
- } else {
- const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(
- *currentTransform(), *p, right - left, bottom - top, rx, ry);
- drawVertexBuffer(left, top, *vertexBuffer, p);
- }
-}
-
-void OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) {
- if (mState.currentlyIgnored()
- || quickRejectSetupScissor(x - radius, y - radius, x + radius, y + radius, p)
- || PaintUtils::paintWillNotDraw(*p)) {
- return;
- }
-
- if (p->getPathEffect() != nullptr) {
- mCaches.textureState().activateTexture(0);
- PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
- drawShape(x - radius, y - radius, texture, p);
- return;
- }
-
- SkPath path;
- if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
- path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
- } else {
- path.addCircle(x, y, radius);
- }
-
-#if !HWUI_NEW_OPS
- if (CC_UNLIKELY(currentSnapshot()->projectionPathMask != nullptr)) {
- // mask ripples with projection mask
- SkPath maskPath = *(currentSnapshot()->projectionPathMask->projectionMask);
-
- Matrix4 screenSpaceTransform;
- currentSnapshot()->buildScreenSpaceTransform(&screenSpaceTransform);
-
- Matrix4 totalTransform;
- totalTransform.loadInverse(screenSpaceTransform);
- totalTransform.multiply(currentSnapshot()->projectionPathMask->projectionMaskTransform);
-
- SkMatrix skTotalTransform;
- totalTransform.copyTo(skTotalTransform);
- maskPath.transform(skTotalTransform);
-
- // Mask the ripple path by the projection mask, now that it's
- // in local space. Note that this can create CCW paths.
- Op(path, maskPath, kIntersect_SkPathOp, &path);
- }
-#endif
- drawConvexPath(path, p);
-}
-
-void OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
- const SkPaint* p) {
- if (mState.currentlyIgnored()
- || quickRejectSetupScissor(left, top, right, bottom, p)
- || PaintUtils::paintWillNotDraw(*p)) {
- return;
- }
-
- if (p->getPathEffect() != nullptr) {
- mCaches.textureState().activateTexture(0);
- PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
- drawShape(left, top, texture, p);
- } else {
- SkPath path;
- SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
- rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
- }
- path.addOval(rect);
- drawConvexPath(path, p);
- }
-}
-
-void OpenGLRenderer::drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, const SkPaint* p) {
- if (mState.currentlyIgnored()
- || quickRejectSetupScissor(left, top, right, bottom, p)
- || PaintUtils::paintWillNotDraw(*p)) {
- return;
- }
-
- // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
- if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != nullptr || useCenter) {
- mCaches.textureState().activateTexture(0);
- PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
- startAngle, sweepAngle, useCenter, p);
- drawShape(left, top, texture, p);
- return;
- }
- SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
- rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
- }
-
- SkPath path;
- if (useCenter) {
- path.moveTo(rect.centerX(), rect.centerY());
- }
- path.arcTo(rect, startAngle, sweepAngle, !useCenter);
- if (useCenter) {
- path.close();
- }
- drawConvexPath(path, p);
-}
-
-void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
- const SkPaint* p) {
- if (mState.currentlyIgnored()
- || quickRejectSetupScissor(left, top, right, bottom, p)
- || PaintUtils::paintWillNotDraw(*p)) {
- return;
- }
-
- if (p->getStyle() != SkPaint::kFill_Style) {
- // only fill style is supported by drawConvexPath, since others have to handle joins
- static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
- if (p->getPathEffect() != nullptr || p->getStrokeJoin() != SkPaint::kMiter_Join ||
- p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
- mCaches.textureState().activateTexture(0);
- PathTexture* texture =
- mCaches.pathCache.getRect(right - left, bottom - top, p);
- drawShape(left, top, texture, p);
- } else {
- SkPath path;
- SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
- rect.outset(p->getStrokeWidth() / 2, p->getStrokeWidth() / 2);
- }
- path.addRect(rect);
- drawConvexPath(path, p);
- }
- } else {
- if (p->isAntiAlias() && !currentTransform()->isSimple()) {
- SkPath path;
- path.addRect(left, top, right, bottom);
- drawConvexPath(path, p);
- } else {
- drawColorRect(left, top, right, bottom, p);
-
- mDirty = true;
- }
- }
-}
-
-void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const glyph_t* glyphs,
- int count, const float* positions,
- FontRenderer& fontRenderer, int alpha, float x, float y) {
- mCaches.textureState().activateTexture(0);
-
- PaintUtils::TextShadow textShadow;
- if (!PaintUtils::getTextShadow(paint, &textShadow)) {
- LOG_ALWAYS_FATAL("failed to query shadow attributes");
- }
-
- // NOTE: The drop shadow will not perform gamma correction
- // if shader-based correction is enabled
- mCaches.dropShadowCache.setFontRenderer(fontRenderer);
- ShadowTexture* texture = mCaches.dropShadowCache.get(
- paint, glyphs, count, textShadow.radius, positions);
- // If the drop shadow exceeds the max texture size or couldn't be
- // allocated, skip drawing
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
-
- const float sx = x - texture->left + textShadow.dx;
- const float sy = y - texture->top + textShadow.dy;
-
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshTexturedUnitQuad(nullptr)
- .setFillShadowTexturePaint(*texture, textShadow.color, *paint, currentSnapshot()->alpha)
- .setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
- .build();
- renderGlop(glop);
-}
-
-// TODO: remove this, once mState.currentlyIgnored captures snapshot alpha
-bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
- float alpha = (PaintUtils::hasTextShadow(paint)
- ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
- return MathUtils::isZero(alpha)
- && PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
-}
-
-bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const {
- if (CC_LIKELY(transform.isPureTranslate())) {
- outMatrix->setIdentity();
- return false;
- } else if (CC_UNLIKELY(transform.isPerspective())) {
- outMatrix->setIdentity();
- return true;
- }
-
- /**
- * Input is a non-perspective, scaling transform. Generate a scale-only transform,
- * with values rounded to the nearest int.
- */
- float sx, sy;
- transform.decomposeScale(sx, sy);
- outMatrix->setScale(
- roundf(std::max(1.0f, sx)),
- roundf(std::max(1.0f, sy)));
- return true;
-}
-
-int OpenGLRenderer::getSaveCount() const {
- return mState.getSaveCount();
-}
-
-int OpenGLRenderer::save(int flags) {
- return mState.save(flags);
-}
-
-void OpenGLRenderer::restore() {
- mState.restore();
-}
-
-void OpenGLRenderer::restoreToCount(int saveCount) {
- mState.restoreToCount(saveCount);
-}
-
-
-void OpenGLRenderer::translate(float dx, float dy, float dz) {
- mState.translate(dx, dy, dz);
-}
-
-void OpenGLRenderer::rotate(float degrees) {
- mState.rotate(degrees);
-}
-
-void OpenGLRenderer::scale(float sx, float sy) {
- mState.scale(sx, sy);
-}
-
-void OpenGLRenderer::skew(float sx, float sy) {
- mState.skew(sx, sy);
-}
-
-void OpenGLRenderer::setLocalMatrix(const Matrix4& matrix) {
- mState.setMatrix(mBaseTransform);
- mState.concatMatrix(matrix);
-}
-
-void OpenGLRenderer::setLocalMatrix(const SkMatrix& matrix) {
- mState.setMatrix(mBaseTransform);
- mState.concatMatrix(matrix);
-}
-
-void OpenGLRenderer::concatMatrix(const Matrix4& matrix) {
- mState.concatMatrix(matrix);
-}
-
-bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
- return mState.clipRect(left, top, right, bottom, op);
-}
-
-bool OpenGLRenderer::clipPath(const SkPath* path, SkRegion::Op op) {
- return mState.clipPath(path, op);
-}
-
-bool OpenGLRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
- return mState.clipRegion(region, op);
-}
-
-void OpenGLRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
- mState.setClippingOutline(allocator, outline);
-}
-
-void OpenGLRenderer::setClippingRoundRect(LinearAllocator& allocator,
- const Rect& rect, float radius, bool highPriority) {
- mState.setClippingRoundRect(allocator, rect, radius, highPriority);
-}
-
-void OpenGLRenderer::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
- mState.setProjectionPathMask(allocator, path);
-}
-
-void OpenGLRenderer::drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y,
- const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
- DrawOpMode drawOpMode) {
-
- if (drawOpMode == DrawOpMode::kImmediate) {
- // The checks for corner-case ignorable text and quick rejection is only done for immediate
- // drawing as ops from DeferredDisplayList are already filtered for these
- if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) ||
- quickRejectSetupScissor(bounds)) {
- return;
- }
- }
-
- const float oldX = x;
- const float oldY = y;
-
- const mat4& transform = *currentTransform();
- const bool pureTranslate = transform.isPureTranslate();
-
- if (CC_LIKELY(pureTranslate)) {
- x = floorf(x + transform.getTranslateX() + 0.5f);
- y = floorf(y + transform.getTranslateY() + 0.5f);
- }
-
- int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
- SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
-
- FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
-
- if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
- fontRenderer.setFont(paint, SkMatrix::I());
- drawTextShadow(paint, glyphs, count, positions, fontRenderer,
- alpha, oldX, oldY);
- }
-
- const bool hasActiveLayer = hasLayer();
-
- // We only pass a partial transform to the font renderer. That partial
- // matrix defines how glyphs are rasterized. Typically we want glyphs
- // to be rasterized at their final size on screen, which means the partial
- // matrix needs to take the scale factor into account.
- // When a partial matrix is used to transform glyphs during rasterization,
- // the mesh is generated with the inverse transform (in the case of scale,
- // the mesh is generated at 1.0 / scale for instance.) This allows us to
- // apply the full transform matrix at draw time in the vertex shader.
- // Applying the full matrix in the shader is the easiest way to handle
- // rotation and perspective and allows us to always generated quads in the
- // font renderer which greatly simplifies the code, clipping in particular.
- SkMatrix fontTransform;
- bool linearFilter = findBestFontTransform(transform, &fontTransform)
- || fabs(y - (int) y) > 0.0f
- || fabs(x - (int) x) > 0.0f;
- fontRenderer.setFont(paint, fontTransform);
- fontRenderer.setTextureFiltering(linearFilter);
-
- // TODO: Implement better clipping for scaled/rotated text
- const Rect* clip = !pureTranslate ? nullptr : &mState.currentRenderTargetClip();
- Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
-
- bool status;
-#if HWUI_NEW_OPS
- LOG_ALWAYS_FATAL("unsupported");
- TextDrawFunctor functor(nullptr, nullptr, nullptr, x, y, pureTranslate, alpha, mode, paint);
-#else
- TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
-#endif
-
- // don't call issuedrawcommand, do it at end of batch
- bool forceFinish = (drawOpMode != DrawOpMode::kDefer);
- if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
- SkPaint paintCopy(*paint);
- paintCopy.setTextAlign(SkPaint::kLeft_Align);
- status = fontRenderer.renderPosText(&paintCopy, clip, glyphs, count, x, y,
- positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
- } else {
- status = fontRenderer.renderPosText(paint, clip, glyphs, count, x, y,
- positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
- }
-
- if ((status || drawOpMode != DrawOpMode::kImmediate) && hasActiveLayer) {
- if (!pureTranslate) {
- transform.mapRect(layerBounds);
- }
- dirtyLayerUnchecked(layerBounds, getRegion());
- }
-
- mDirty = true;
-}
-
-void OpenGLRenderer::drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count,
- const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) {
- if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
- return;
- }
-
- // TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
- mRenderState.scissor().setEnabled(true);
-
- FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
- fontRenderer.setFont(paint, SkMatrix::I());
- fontRenderer.setTextureFiltering(true);
-
- int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
- SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
-#if HWUI_NEW_OPS
- LOG_ALWAYS_FATAL("unsupported");
- TextDrawFunctor functor(nullptr, nullptr, nullptr, 0.0f, 0.0f, false, alpha, mode, paint);
-#else
- TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
-#endif
-
- const Rect* clip = &writableSnapshot()->getLocalClip();
- Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
-
- if (fontRenderer.renderTextOnPath(paint, clip, glyphs, count, path,
- hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) {
- dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
- mDirty = true;
- }
-}
-
-void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
- if (mState.currentlyIgnored()) return;
-
- mCaches.textureState().activateTexture(0);
-
- PathTexture* texture = mCaches.pathCache.get(path, paint);
- if (!texture) return;
-
- const float x = texture->left - texture->offset;
- const float y = texture->top - texture->offset;
-
- drawPathTexture(texture, x, y, paint);
-
- if (texture->cleanup) {
- mCaches.pathCache.remove(path, paint);
- }
- mDirty = true;
-}
-
-void OpenGLRenderer::drawLayer(Layer* layer) {
- if (!layer) {
- return;
- }
-
- mat4* transform = nullptr;
- if (layer->isTextureLayer()) {
- transform = &layer->getTransform();
- if (!transform->isIdentity()) {
- save(SaveFlags::Matrix);
- concatMatrix(*transform);
- }
- }
-
- bool clipRequired = false;
- const bool rejected = mState.calculateQuickRejectForScissor(
- 0, 0, layer->layer.getWidth(), layer->layer.getHeight(),
- &clipRequired, nullptr, false);
-
- if (rejected) {
- if (transform && !transform->isIdentity()) {
- restore();
- }
- return;
- }
-
- EVENT_LOGD("drawLayer," RECT_STRING ", clipRequired %d", x, y,
- x + layer->layer.getWidth(), y + layer->layer.getHeight(), clipRequired);
-
- updateLayer(layer, true);
-
- mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
- mCaches.textureState().activateTexture(0);
-
- if (CC_LIKELY(!layer->region.isEmpty())) {
- if (layer->region.isRect()) {
- DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
- composeLayerRect(layer, layer->regionRect));
- } else if (layer->mesh) {
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount)
- .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
- .setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewOffsetRectSnap(0, 0, Rect(layer->layer.getWidth(), layer->layer.getHeight()))
- .build();
- DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
-#if DEBUG_LAYERS_AS_REGIONS
- drawRegionRectsDebug(layer->region);
-#endif
- }
-
- if (layer->debugDrawUpdate) {
- layer->debugDrawUpdate = false;
-
- SkPaint paint;
- paint.setColor(0x7f00ff00);
- drawColorRect(0, 0, layer->layer.getWidth(), layer->layer.getHeight(), &paint);
- }
- }
- layer->hasDrawnSinceUpdate = true;
-
- if (transform && !transform->isIdentity()) {
- restore();
- }
-
- mDirty = true;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Draw filters
-///////////////////////////////////////////////////////////////////////////////
-void OpenGLRenderer::setDrawFilter(SkDrawFilter* filter) {
- // We should never get here since we apply the draw filter when stashing
- // the paints in the DisplayList.
- LOG_ALWAYS_FATAL("OpenGLRenderer does not directly support DrawFilters");
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Drawing implementation
-///////////////////////////////////////////////////////////////////////////////
-
-Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) {
- Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap->pixelRef());
- if (!texture) {
- return mCaches.textureCache.get(bitmap);
- }
- return texture;
-}
-
-void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y,
- const SkPaint* paint) {
- if (quickRejectSetupScissor(x, y, x + texture->width(), y + texture->height())) {
- return;
- }
-
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshTexturedUnitQuad(nullptr)
- .setFillPathTexturePaint(*texture, *paint, currentSnapshot()->alpha)
- .setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewMapUnitToRect(Rect(x, y, x + texture->width(), y + texture->height()))
- .build();
- renderGlop(glop);
-}
-
-void OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
- if (mState.currentlyIgnored()) {
- return;
- }
-
- drawColorRects(rects, count, paint, false, true, true);
-}
-
-void OpenGLRenderer::drawShadow(float casterAlpha,
- const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
- if (mState.currentlyIgnored()) return;
-
- // TODO: use quickRejectWithScissor. For now, always force enable scissor.
- mRenderState.scissor().setEnabled(true);
-
- SkPaint paint;
- paint.setAntiAlias(true); // want to use AlphaVertex
-
- // The caller has made sure casterAlpha > 0.
- float ambientShadowAlpha = mAmbientShadowAlpha;
- if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
- ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
- }
- if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
- paint.setARGB(casterAlpha * ambientShadowAlpha, 0, 0, 0);
- drawVertexBuffer(*ambientShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp);
- }
-
- float spotShadowAlpha = mSpotShadowAlpha;
- if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
- spotShadowAlpha = Properties::overrideSpotShadowStrength;
- }
- if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
- paint.setARGB(casterAlpha * spotShadowAlpha, 0, 0, 0);
- drawVertexBuffer(*spotShadowVertexBuffer, &paint, kVertexBuffer_ShadowInterp);
- }
-
- mDirty=true;
-}
-
-void OpenGLRenderer::drawColorRects(const float* rects, int count, const SkPaint* paint,
- bool ignoreTransform, bool dirty, bool clip) {
- if (count == 0) {
- return;
- }
-
- float left = FLT_MAX;
- float top = FLT_MAX;
- float right = FLT_MIN;
- float bottom = FLT_MIN;
-
- Vertex mesh[count];
- Vertex* vertex = mesh;
-
- for (int index = 0; index < count; index += 4) {
- float l = rects[index + 0];
- float t = rects[index + 1];
- float r = rects[index + 2];
- float b = rects[index + 3];
-
- Vertex::set(vertex++, l, t);
- Vertex::set(vertex++, r, t);
- Vertex::set(vertex++, l, b);
- Vertex::set(vertex++, r, b);
-
- left = std::min(left, l);
- top = std::min(top, t);
- right = std::max(right, r);
- bottom = std::max(bottom, b);
- }
-
- if (clip && quickRejectSetupScissor(left, top, right, bottom)) {
- return;
- }
-
- const int transformFlags = ignoreTransform
- ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshIndexedQuads(&mesh[0], count / 4)
- .setFillPaint(*paint, currentSnapshot()->alpha)
- .setTransform(*currentSnapshot(), transformFlags)
- .setModelViewOffsetRect(0, 0, Rect(left, top, right, bottom))
- .build();
- renderGlop(glop);
-}
-
-void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
- const SkPaint* paint, bool ignoreTransform) {
- const int transformFlags = ignoreTransform
- ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(currentSnapshot()->roundRectClipState)
- .setMeshUnitQuad()
- .setFillPaint(*paint, currentSnapshot()->alpha)
- .setTransform(*currentSnapshot(), transformFlags)
- .setModelViewMapUnitToRect(Rect(left, top, right, bottom))
- .build();
- renderGlop(glop);
-}
-
-float OpenGLRenderer::getLayerAlpha(const Layer* layer) const {
- return (layer->getAlpha() / 255.0f) * currentSnapshot()->alpha;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
deleted file mode 100644
index ec450bd63296..000000000000
--- a/libs/hwui/OpenGLRenderer.h
+++ /dev/null
@@ -1,785 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_OPENGL_RENDERER_H
-#define ANDROID_HWUI_OPENGL_RENDERER_H
-
-#include "CanvasState.h"
-#include "Debug.h"
-#include "Extensions.h"
-#include "Matrix.h"
-#include "Program.h"
-#include "Rect.h"
-#include "Snapshot.h"
-#include "UvMapper.h"
-#include "Vertex.h"
-#include "Caches.h"
-#include "utils/PaintUtils.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkColorFilter.h>
-#include <SkDrawLooper.h>
-#include <SkMatrix.h>
-#include <SkPaint.h>
-#include <SkRegion.h>
-#include <SkXfermode.h>
-
-#include <utils/Blur.h>
-#include <utils/Functor.h>
-#include <utils/RefBase.h>
-#include <utils/SortedVector.h>
-
-#include <cutils/compiler.h>
-
-#include <androidfw/ResourceTypes.h>
-
-#include <vector>
-
-class SkShader;
-
-namespace android {
-namespace uirenderer {
-
-enum class DrawOpMode {
- kImmediate,
- kDefer,
- kFlush
-};
-
-class DeferredDisplayState;
-struct Glop;
-class RenderState;
-class RenderNode;
-class TextDrawFunctor;
-class VertexBuffer;
-
-enum StateDeferFlags {
- kStateDeferFlag_Draw = 0x1,
- kStateDeferFlag_Clip = 0x2
-};
-
-enum ClipSideFlags {
- kClipSide_None = 0x0,
- kClipSide_Left = 0x1,
- kClipSide_Top = 0x2,
- kClipSide_Right = 0x4,
- kClipSide_Bottom = 0x8,
- kClipSide_Full = 0xF,
- kClipSide_ConservativeFull = 0x1F
-};
-
-enum VertexBufferDisplayFlags {
- kVertexBuffer_Offset = 0x1,
- kVertexBuffer_ShadowInterp = 0x2,
-};
-
-/**
- * Defines additional transformation that should be applied by the model view matrix, beyond that of
- * the currentTransform()
- */
-enum ModelViewMode {
- /**
- * Used when the model view should simply translate geometry passed to the shader. The resulting
- * matrix will be a simple translation.
- */
- kModelViewMode_Translate = 0,
-
- /**
- * Used when the model view should translate and scale geometry. The resulting matrix will be a
- * translation + scale. This is frequently used together with VBO 0, the (0,0,1,1) rect.
- */
- kModelViewMode_TranslateAndScale = 1,
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Renderer
-///////////////////////////////////////////////////////////////////////////////
-/**
- * OpenGL Renderer implementation.
- */
-class OpenGLRenderer : public CanvasStateClient {
-public:
- explicit OpenGLRenderer(RenderState& renderState);
- virtual ~OpenGLRenderer();
-
- void initProperties();
- void initLight(float lightRadius, uint8_t ambientShadowAlpha,
- uint8_t spotShadowAlpha);
- void setLightCenter(const Vector3& lightCenter);
-
- /*
- * Prepares the renderer to draw a frame. This method must be invoked
- * at the beginning of each frame. Only the specified rectangle of the
- * frame is assumed to be dirty. A clip will automatically be set to
- * the specified rectangle.
- *
- * @param opaque If true, the target surface is considered opaque
- * and will not be cleared. If false, the target surface
- * will be cleared
- */
- virtual void prepareDirty(int viewportWidth, int viewportHeight,
- float left, float top, float right, float bottom, bool opaque);
-
- /**
- * Indicates the end of a frame. This method must be invoked whenever
- * the caller is done rendering a frame.
- * Returns true if any drawing was done during the frame (the output
- * has changed / is "dirty" and should be displayed to the user).
- */
- virtual bool finish();
-
- void callDrawGLFunction(Functor* functor, Rect& dirty);
-
- void pushLayerUpdate(Layer* layer);
- void cancelLayerUpdate(Layer* layer);
- void flushLayerUpdates();
- void markLayersAsBuildLayers();
-
- virtual int saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags) {
- return saveLayer(left, top, right, bottom, paint, flags, nullptr);
- }
-
- // Specialized saveLayer implementation, which will pass the convexMask to an FBO layer, if
- // created, which will in turn clip to that mask when drawn back/restored.
- int saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags, const SkPath* convexMask);
-
- int saveLayerDeferred(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags);
-
- void drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1);
- void drawLayer(Layer* layer);
- void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
- void drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
- TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint);
- void drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst,
- const SkPaint* paint);
- void drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors, const SkPaint* paint);
- void drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
- TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint);
- void drawPatch(const SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
- float left, float top, float right, float bottom, const SkPaint* paint);
- void drawColor(int color, SkXfermode::Mode mode);
- void drawRect(float left, float top, float right, float bottom,
- const SkPaint* paint);
- void drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, const SkPaint* paint);
- void drawCircle(float x, float y, float radius, const SkPaint* paint);
- void drawOval(float left, float top, float right, float bottom,
- const SkPaint* paint);
- void drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint);
- void drawPath(const SkPath* path, const SkPaint* paint);
- void drawLines(const float* points, int count, const SkPaint* paint);
- void drawPoints(const float* points, int count, const SkPaint* paint);
- void drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count, const SkPath* path,
- float hOffset, float vOffset, const SkPaint* paint);
- void drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y,
- const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
- DrawOpMode drawOpMode = DrawOpMode::kImmediate);
- void drawRects(const float* rects, int count, const SkPaint* paint);
-
- void drawShadow(float casterAlpha,
- const VertexBuffer* ambientShadowVertexBuffer,
- const VertexBuffer* spotShadowVertexBuffer);
-
- void setDrawFilter(SkDrawFilter* filter);
-
- /**
- * Store the current display state (most importantly, the current clip and transform), and
- * additionally map the state's bounds from local to window coordinates.
- *
- * Returns true if quick-rejected
- */
- bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags);
- void restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore = false);
- void setupMergedMultiDraw(const Rect* clipRect);
-
- bool isCurrentTransformSimple() {
- return currentTransform()->isSimple();
- }
-
- Caches& getCaches() {
- return mCaches;
- }
-
- RenderState& renderState() {
- return mRenderState;
- }
-
- int getViewportWidth() { return mState.getViewportWidth(); }
- int getViewportHeight() { return mState.getViewportHeight(); }
-
- /**
- * Scales the alpha on the current snapshot. This alpha value will be modulated
- * with other alpha values when drawing primitives.
- */
- void scaleAlpha(float alpha) { mState.scaleAlpha(alpha); }
-
- /**
- * Inserts a named event marker in the stream of GL commands.
- */
- void eventMark(const char* name) const;
-
- /**
- * Inserts a formatted event marker in the stream of GL commands.
- */
- void eventMarkDEBUG(const char *fmt, ...) const;
-
- /**
- * Inserts a named group marker in the stream of GL commands. This marker
- * can be used by tools to group commands into logical groups. A call to
- * this method must always be followed later on by a call to endMark().
- */
- void startMark(const char* name) const;
-
- /**
- * Closes the last group marker opened by startMark().
- */
- void endMark() const;
-
- /**
- * Build the best transform to use to rasterize text given a full
- * transform matrix, and whether filteration is needed.
- *
- * Returns whether filtration is needed
- */
- bool findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const;
-
-#if DEBUG_MERGE_BEHAVIOR
- void drawScreenSpaceColorRect(float left, float top, float right, float bottom, int color) {
- mCaches.setScissorEnabled(false);
-
- // should only be called outside of other draw ops, so stencil can only be in test state
- bool stencilWasEnabled = mCaches.stencil.isTestEnabled();
- mCaches.stencil.disable();
-
- drawColorRect(left, top, right, bottom, color, SkXfermode::kSrcOver_Mode, true);
-
- if (stencilWasEnabled) mCaches.stencil.enableTest();
- mDirty = true;
- }
-#endif
-
- const Vector3& getLightCenter() const { return mState.currentLightCenter(); }
- float getLightRadius() const { return mLightRadius; }
- uint8_t getAmbientShadowAlpha() const { return mAmbientShadowAlpha; }
- uint8_t getSpotShadowAlpha() const { return mSpotShadowAlpha; }
-
- ///////////////////////////////////////////////////////////////////
- /// State manipulation
-
- int getSaveCount() const;
- int save(int flags);
- void restore();
- void restoreToCount(int saveCount);
-
- void setGlobalMatrix(const Matrix4& matrix) {
- mState.setMatrix(matrix);
- }
- void setLocalMatrix(const Matrix4& matrix);
- void setLocalMatrix(const SkMatrix& matrix);
- void concatMatrix(const SkMatrix& matrix) { mState.concatMatrix(matrix); }
-
- void translate(float dx, float dy, float dz = 0.0f);
- void rotate(float degrees);
- void scale(float sx, float sy);
- void skew(float sx, float sy);
-
- void setMatrix(const Matrix4& matrix); // internal only convenience method
- void concatMatrix(const Matrix4& matrix); // internal only convenience method
-
- const Rect& getLocalClipBounds() const { return mState.getLocalClipBounds(); }
- const Rect& getRenderTargetClipBounds() const { return mState.getRenderTargetClipBounds(); }
- bool quickRejectConservative(float left, float top,
- float right, float bottom) const {
- return mState.quickRejectConservative(left, top, right, bottom);
- }
-
- bool clipRect(float left, float top,
- float right, float bottom, SkRegion::Op op);
- bool clipPath(const SkPath* path, SkRegion::Op op);
- bool clipRegion(const SkRegion* region, SkRegion::Op op);
-
- /**
- * Does not support different clipping Ops (that is, every call to setClippingOutline is
- * effectively using SkRegion::kReplaceOp)
- *
- * The clipping outline is independent from the regular clip.
- */
- void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
- void setClippingRoundRect(LinearAllocator& allocator,
- const Rect& rect, float radius, bool highPriority = true);
- void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
-
- inline bool hasRectToRectTransform() const { return mState.hasRectToRectTransform(); }
- inline const mat4* currentTransform() const { return mState.currentTransform(); }
-
- ///////////////////////////////////////////////////////////////////
- /// CanvasStateClient interface
-
- virtual void onViewportInitialized() override;
- virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
- virtual GLuint getTargetFbo() const override { return 0; }
-
- SkPath* allocPathForFrame() {
- std::unique_ptr<SkPath> path(new SkPath());
- SkPath* returnPath = path.get();
- mTempPaths.push_back(std::move(path));
- return returnPath;
- }
-
- void setBaseTransform(const Matrix4& matrix) { mBaseTransform = matrix; }
-
-protected:
- /**
- * Perform the setup specific to a frame. This method does not
- * issue any OpenGL commands.
- */
- void setupFrameState(int viewportWidth, int viewportHeight,
- float left, float top, float right, float bottom, bool opaque);
-
- /**
- * Indicates the start of rendering. This method will setup the
- * initial OpenGL state (viewport, clearing the buffer, etc.)
- */
- void startFrame();
-
- /**
- * Clears the underlying surface if needed.
- */
- virtual void clear(float left, float top, float right, float bottom, bool opaque);
-
- /**
- * Call this method after updating a layer during a drawing pass.
- */
- void resumeAfterLayer();
-
- /**
- * This method is called whenever a stencil buffer is required. Subclasses
- * should override this method and call attachStencilBufferToLayer() on the
- * appropriate layer(s).
- */
- virtual void ensureStencilBuffer();
-
- /**
- * Obtains a stencil render buffer (allocating it if necessary) and
- * attaches it to the specified layer.
- */
- void attachStencilBufferToLayer(Layer* layer);
-
- /**
- * Draw a rectangle list. Currently only used for the the stencil buffer so that the stencil
- * will have a value of 'n' in every unclipped pixel, where 'n' is the number of rectangles
- * in the list.
- */
- void drawRectangleList(const RectangleList& rectangleList);
-
- bool quickRejectSetupScissor(float left, float top, float right, float bottom,
- const SkPaint* paint = nullptr);
- bool quickRejectSetupScissor(const Rect& bounds, const SkPaint* paint = nullptr) {
- return quickRejectSetupScissor(bounds.left, bounds.top,
- bounds.right, bounds.bottom, paint);
- }
-
- /**
- * Compose the layer defined in the current snapshot with the layer
- * defined by the previous snapshot.
- *
- * The current snapshot *must* be a layer (flag kFlagIsLayer set.)
- *
- * @param curent The current snapshot containing the layer to compose
- * @param previous The previous snapshot to compose the current layer with
- */
- virtual void composeLayer(const Snapshot& current, const Snapshot& previous);
-
- /**
- * Marks the specified region as dirty at the specified bounds.
- */
- void dirtyLayerUnchecked(Rect& bounds, Region* region);
-
- /**
- * Returns the region of the current layer.
- */
- virtual Region* getRegion() const {
- return mState.currentRegion();
- }
-
- /**
- * Indicates whether rendering is currently targeted at a layer.
- */
- virtual bool hasLayer() const {
- return (mState.currentFlags() & Snapshot::kFlagFboTarget) && mState.currentRegion();
- }
-
- /**
- * Renders the specified layer as a textured quad.
- *
- * @param layer The layer to render
- * @param rect The bounds of the layer
- */
- void drawTextureLayer(Layer* layer, const Rect& rect);
-
- /**
- * Gets the alpha from a layer, accounting for snapshot alpha
- *
- * @param layer The layer from which the alpha is extracted
- */
- inline float getLayerAlpha(const Layer* layer) const;
-
- /**
- * Set to true to suppress error checks at the end of a frame.
- */
- virtual bool suppressErrorChecks() const {
- return false;
- }
-
- CanvasState mState;
- Caches& mCaches;
- RenderState& mRenderState;
-
-private:
- enum class GlopRenderType {
- Standard,
- Multi,
- LayerClear
- };
-
- void renderGlop(const Glop& glop, GlopRenderType type = GlopRenderType::Standard);
-
- /**
- * Discards the content of the framebuffer if supported by the driver.
- * This method should be called at the beginning of a frame to optimize
- * rendering on some tiler architectures.
- */
- void discardFramebuffer(float left, float top, float right, float bottom);
-
- /**
- * Sets the clipping rectangle using glScissor. The clip is defined by
- * the current snapshot's clipRect member.
- */
- void setScissorFromClip();
-
- /**
- * Sets the clipping region using the stencil buffer. The clip region
- * is defined by the current snapshot's clipRegion member.
- */
- void setStencilFromClip();
-
- /**
- * Given the local bounds of the layer, calculates ...
- */
- void calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer);
-
- /**
- * Given the local bounds + clip of the layer, updates current snapshot's empty/invisible
- */
- void updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect& clip,
- bool fboLayer, int alpha);
-
- /**
- * Creates a new layer stored in the specified snapshot.
- *
- * @param snapshot The snapshot associated with the new layer
- * @param left The left coordinate of the layer
- * @param top The top coordinate of the layer
- * @param right The right coordinate of the layer
- * @param bottom The bottom coordinate of the layer
- * @param alpha The translucency of the layer
- * @param mode The blending mode of the layer
- * @param flags The layer save flags
- * @param mask A mask to use when drawing the layer back, may be empty
- *
- * @return True if the layer was successfully created, false otherwise
- */
- bool createLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, int flags, const SkPath* convexMask);
-
- /**
- * Creates a new layer stored in the specified snapshot as an FBO.
- *
- * @param layer The layer to store as an FBO
- * @param snapshot The snapshot associated with the new layer
- * @param bounds The bounds of the layer
- */
- bool createFboLayer(Layer* layer, Rect& bounds, Rect& clip);
-
- /**
- * Compose the specified layer as a region.
- *
- * @param layer The layer to compose
- * @param rect The layer's bounds
- */
- void composeLayerRegion(Layer* layer, const Rect& rect);
-
- /**
- * Restores the content in layer to the screen, swapping the blend mode,
- * specifically used in the restore() of a saveLayerAlpha().
- *
- * This allows e.g. a layer that would have been drawn on top of existing content (with SrcOver)
- * to be drawn underneath.
- *
- * This will always ignore the canvas transform.
- */
- void composeLayerRectSwapped(Layer* layer, const Rect& rect);
-
- /**
- * Draws the content in layer to the screen.
- */
- void composeLayerRect(Layer* layer, const Rect& rect);
-
- /**
- * Clears all the regions corresponding to the current list of layers.
- * This method MUST be invoked before any drawing operation.
- */
- void clearLayerRegions();
-
- /**
- * Mark the layer as dirty at the specified coordinates. The coordinates
- * are transformed with the supplied matrix.
- */
- void dirtyLayer(const float left, const float top,
- const float right, const float bottom, const Matrix4& transform);
-
- /**
- * Mark the layer as dirty at the specified coordinates.
- */
- void dirtyLayer(const float left, const float top,
- const float right, const float bottom);
-
- /**
- * Draws a colored rectangle with the specified color. The specified coordinates
- * are transformed by the current snapshot's transform matrix unless specified
- * otherwise.
- *
- * @param left The left coordinate of the rectangle
- * @param top The top coordinate of the rectangle
- * @param right The right coordinate of the rectangle
- * @param bottom The bottom coordinate of the rectangle
- * @param paint The paint containing the color, blending mode, etc.
- * @param ignoreTransform True if the current transform should be ignored
- */
- void drawColorRect(float left, float top, float right, float bottom,
- const SkPaint* paint, bool ignoreTransform = false);
-
- /**
- * Draws a series of colored rectangles with the specified color. The specified
- * coordinates are transformed by the current snapshot's transform matrix unless
- * specified otherwise.
- *
- * @param rects A list of rectangles, 4 floats (left, top, right, bottom)
- * per rectangle
- * @param paint The paint containing the color, blending mode, etc.
- * @param ignoreTransform True if the current transform should be ignored
- * @param dirty True if calling this method should dirty the current layer
- * @param clip True if the rects should be clipped, false otherwise
- */
- void drawColorRects(const float* rects, int count, const SkPaint* paint,
- bool ignoreTransform = false, bool dirty = true, bool clip = true);
-
- /**
- * Draws the shape represented by the specified path texture.
- * This method invokes drawPathTexture() but takes into account
- * the extra left/top offset and the texture offset to correctly
- * position the final shape.
- *
- * @param left The left coordinate of the shape to render
- * @param top The top coordinate of the shape to render
- * @param texture The texture reprsenting the shape
- * @param paint The paint to draw the shape with
- */
- void drawShape(float left, float top, PathTexture* texture, const SkPaint* paint);
-
- /**
- * Renders a strip of polygons with the specified paint, used for tessellated geometry.
- *
- * @param vertexBuffer The VertexBuffer to be drawn
- * @param paint The paint to render with
- * @param flags flags with which to draw
- */
- void drawVertexBuffer(float translateX, float translateY, const VertexBuffer& vertexBuffer,
- const SkPaint* paint, int flags = 0);
-
- /**
- * Convenience for translating method
- */
- void drawVertexBuffer(const VertexBuffer& vertexBuffer,
- const SkPaint* paint, int flags = 0) {
- drawVertexBuffer(0.0f, 0.0f, vertexBuffer, paint, flags);
- }
-
- /**
- * Renders the convex hull defined by the specified path as a strip of polygons.
- *
- * @param path The hull of the path to draw
- * @param paint The paint to render with
- */
- void drawConvexPath(const SkPath& path, const SkPaint* paint);
-
- /**
- * Draws shadow layer on text (with optional positions).
- *
- * @param paint The paint to draw the shadow with
- * @param text The text to draw
- * @param count The number of glyphs in the text
- * @param positions The x, y positions of individual glyphs (or NULL)
- * @param fontRenderer The font renderer object
- * @param alpha The alpha value for drawing the shadow
- * @param x The x coordinate where the shadow will be drawn
- * @param y The y coordinate where the shadow will be drawn
- */
- void drawTextShadow(const SkPaint* paint, const glyph_t* glyphs, int count,
- const float* positions, FontRenderer& fontRenderer, int alpha,
- float x, float y);
-
- /**
- * Draws a path texture. Path textures are alpha8 bitmaps that need special
- * compositing to apply colors/filters/etc.
- *
- * @param texture The texture to render
- * @param x The x coordinate where the texture will be drawn
- * @param y The y coordinate where the texture will be drawn
- * @param paint The paint to draw the texture with
- */
- void drawPathTexture(PathTexture* texture, float x, float y, const SkPaint* paint);
-
- /**
- * Resets the texture coordinates stored in mMeshVertices. Setting the values
- * back to default is achieved by calling:
- *
- * resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
- *
- * @param u1 The left coordinate of the texture
- * @param v1 The bottom coordinate of the texture
- * @param u2 The right coordinate of the texture
- * @param v2 The top coordinate of the texture
- */
- void resetDrawTextureTexCoords(float u1, float v1, float u2, float v2);
-
- /**
- * Returns true if the specified paint will draw invisible text.
- */
- bool canSkipText(const SkPaint* paint) const;
-
- bool updateLayer(Layer* layer, bool inFrame);
- void updateLayers();
- void flushLayers();
-
-#if DEBUG_LAYERS_AS_REGIONS
- /**
- * Renders the specified region as a series of rectangles. This method
- * is used for debugging only.
- */
- void drawRegionRectsDebug(const Region& region);
-#endif
-
- /**
- * Renders the specified region as a series of rectangles. The region
- * must be in screen-space coordinates.
- */
- void drawRegionRects(const SkRegion& region, const SkPaint& paint, bool dirty = false);
-
- /**
- * Draws the current clip region if any. Only when DEBUG_CLIP_REGIONS
- * is turned on.
- */
- void debugClip();
-
- void debugOverdraw(bool enable, bool clear);
- void renderOverdraw();
- void countOverdraw();
-
- /**
- * Should be invoked every time the glScissor is modified.
- */
- inline void dirtyClip() { mState.setDirtyClip(true); }
-
- inline const UvMapper& getMapper(const Texture* texture) {
- return texture && texture->uvMapper ? *texture->uvMapper : mUvMapper;
- }
-
- /**
- * Returns a texture object for the specified bitmap. The texture can
- * come from the texture cache or an atlas. If this method returns
- * NULL, the texture could not be found and/or allocated.
- */
- Texture* getTexture(const SkBitmap* bitmap);
-
- bool reportAndClearDirty() { bool ret = mDirty; mDirty = false; return ret; }
- inline Snapshot* writableSnapshot() { return mState.writableSnapshot(); }
- inline const Snapshot* currentSnapshot() const { return mState.currentSnapshot(); }
-
- // State used to define the clipping region
- Rect mTilingClip;
- // Is the target render surface opaque
- bool mOpaque;
- // Is a frame currently being rendered
- bool mFrameStarted;
-
- // Default UV mapper
- const UvMapper mUvMapper;
-
- // List of rectangles to clear after saveLayer() is invoked
- std::vector<Rect> mLayers;
- // List of layers to update at the beginning of a frame
- std::vector< sp<Layer> > mLayerUpdates;
-
- // See PROPERTY_DISABLE_SCISSOR_OPTIMIZATION in
- // Properties.h
- bool mScissorOptimizationDisabled;
-
- bool mSkipOutlineClip;
-
- // True if anything has been drawn since the last call to
- // reportAndClearDirty()
- bool mDirty;
-
- // Lighting + shadows
- Vector3 mLightCenter;
- float mLightRadius;
- uint8_t mAmbientShadowAlpha;
- uint8_t mSpotShadowAlpha;
-
- // Paths kept alive for the duration of the frame
- std::vector<std::unique_ptr<SkPath>> mTempPaths;
-
- /**
- * Initial transform for a rendering pass; transform from global device
- * coordinates to the current RenderNode's drawing content coordinates,
- * with the RenderNode's RenderProperty transforms already applied.
- * Calling setMatrix(mBaseTransform) will result in drawing at the origin
- * of the DisplayList's recorded surface prior to any Canvas
- * transformation.
- */
- Matrix4 mBaseTransform;
-
- friend class Layer;
- friend class TextDrawFunctor;
- friend class DrawBitmapOp;
- friend class DrawPatchOp;
-
-}; // class OpenGLRenderer
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_OPENGL_RENDERER_H
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index bd6feb9fc762..983c17e92266 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -36,28 +36,12 @@ PatchCache::PatchCache(RenderState& renderState)
, mSize(0)
, mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
, mMeshBuffer(0)
- , mFreeBlocks(nullptr)
- , mGenerationId(0) {}
+ , mFreeBlocks(nullptr) {}
PatchCache::~PatchCache() {
clear();
}
-void PatchCache::init() {
- bool created = false;
- if (!mMeshBuffer) {
- glGenBuffers(1, &mMeshBuffer);
- created = true;
- }
-
- mRenderState.meshState().bindMeshBuffer(mMeshBuffer);
- mRenderState.meshState().resetVertexPointers();
-
- if (created) {
- createVertexBuffer();
- }
-}
-
///////////////////////////////////////////////////////////////////////////////
// Caching
///////////////////////////////////////////////////////////////////////////////
@@ -80,8 +64,7 @@ void PatchCache::clear() {
clearCache();
if (mMeshBuffer) {
- mRenderState.meshState().unbindMeshBuffer();
- glDeleteBuffers(1, &mMeshBuffer);
+ mRenderState.meshState().deleteMeshBuffer(mMeshBuffer);
mMeshBuffer = 0;
mSize = 0;
}
@@ -170,10 +153,10 @@ void PatchCache::clearGarbage() {
}
void PatchCache::createVertexBuffer() {
- glBufferData(GL_ARRAY_BUFFER, mMaxSize, nullptr, GL_DYNAMIC_DRAW);
+ mRenderState.meshState().genOrUpdateMeshBuffer(&mMeshBuffer,
+ mMaxSize, nullptr, GL_DYNAMIC_DRAW);
mSize = 0;
mFreeBlocks = new BufferBlock(0, mMaxSize);
- mGenerationId++;
}
/**
@@ -182,7 +165,9 @@ void PatchCache::createVertexBuffer() {
*/
void PatchCache::setupMesh(Patch* newMesh) {
// This call ensures the VBO exists and that it is bound
- init();
+ if (!mMeshBuffer) {
+ createVertexBuffer();
+ }
// If we're running out of space, let's clear the entire cache
uint32_t size = newMesh->getSize();
@@ -215,7 +200,9 @@ void PatchCache::setupMesh(Patch* newMesh) {
// Copy the 9patch mesh in the VBO
newMesh->positionOffset = (GLintptr) (block->offset);
newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset;
- glBufferSubData(GL_ARRAY_BUFFER, newMesh->positionOffset, size, newMesh->vertices.get());
+
+ mRenderState.meshState().updateMeshBufferSubData(mMeshBuffer, newMesh->positionOffset, size,
+ newMesh->vertices.get());
// Remove the block since we've used it entirely
if (block->size == size) {
@@ -236,17 +223,15 @@ void PatchCache::setupMesh(Patch* newMesh) {
static const UvMapper sIdentity;
-const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
- const uint32_t bitmapWidth, const uint32_t bitmapHeight,
+const Patch* PatchCache::get( const uint32_t bitmapWidth, const uint32_t bitmapHeight,
const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
const Patch* mesh = mCache.get(description);
if (!mesh) {
- const UvMapper& mapper = entry ? entry->uvMapper : sIdentity;
Patch* newMesh = new Patch(bitmapWidth, bitmapHeight,
- pixelWidth, pixelHeight, mapper, patch);
+ pixelWidth, pixelHeight, sIdentity, patch);
if (newMesh->vertices) {
setupMesh(newMesh);
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index bc5981d53457..aa746c7dfa15 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_PATCH_CACHE_H
-#define ANDROID_HWUI_PATCH_CACHE_H
+#pragma once
#include <GLES2/gl2.h>
@@ -23,7 +22,6 @@
#include <androidfw/ResourceTypes.h>
-#include "AssetAtlas.h"
#include "Debug.h"
#include "utils/Pair.h"
@@ -48,15 +46,14 @@ class Patch;
///////////////////////////////////////////////////////////////////////////////
class Caches;
+class RenderState;
class PatchCache {
public:
explicit PatchCache(RenderState& renderState);
~PatchCache();
- void init();
- const Patch* get(const AssetAtlas::Entry* entry,
- const uint32_t bitmapWidth, const uint32_t bitmapHeight,
+ const Patch* get(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch);
void clear();
@@ -72,10 +69,6 @@ public:
return mMeshBuffer;
}
- uint32_t getGenerationId() const {
- return mGenerationId;
- }
-
/**
* Removes the entries associated with the specified 9-patch. This is meant
* to be called from threads that are not the EGL context thread (GC thread
@@ -178,8 +171,6 @@ private:
// First available free block inside the mesh buffer
BufferBlock* mFreeBlocks;
- uint32_t mGenerationId;
-
// Garbage tracking, required to handle GC events on the VM side
Vector<Res_png_9patch*> mGarbage;
mutable Mutex mLock;
@@ -187,5 +178,3 @@ private:
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_PATCH_CACHE_H
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 972ff81ad31f..cc96de71df82 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -17,6 +17,8 @@
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkColor.h>
+#include <SkColorFilter.h>
+#include <SkMaskFilter.h>
#include <SkPaint.h>
#include <SkPath.h>
#include <SkPathEffect.h>
@@ -121,34 +123,19 @@ bool PathDescription::operator==(const PathDescription& rhs) const {
// Utilities
///////////////////////////////////////////////////////////////////////////////
-bool PathCache::canDrawAsConvexPath(SkPath* path, const SkPaint* paint) {
- // NOTE: This should only be used after PathTessellator handles joins properly
- return paint->getPathEffect() == nullptr && path->getConvexity() == SkPath::kConvex_Convexity;
-}
-
-void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint,
- float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+static void computePathBounds(const SkPath* path, const SkPaint* paint, PathTexture* texture,
+ uint32_t& width, uint32_t& height) {
const SkRect& bounds = path->getBounds();
- PathCache::computeBounds(bounds, paint, left, top, offset, width, height);
-}
-
-void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint,
- float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
const float pathWidth = std::max(bounds.width(), 1.0f);
const float pathHeight = std::max(bounds.height(), 1.0f);
- left = bounds.fLeft;
- top = bounds.fTop;
-
- offset = (int) floorf(std::max(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
+ texture->left = floorf(bounds.fLeft);
+ texture->top = floorf(bounds.fTop);
- width = uint32_t(pathWidth + offset * 2.0 + 0.5);
- height = uint32_t(pathHeight + offset * 2.0 + 0.5);
-}
+ texture->offset = (int) floorf(std::max(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
-static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
- bitmap.allocPixels(SkImageInfo::MakeA8(width, height));
- bitmap.eraseColor(0);
+ width = uint32_t(pathWidth + texture->offset * 2.0 + 0.5);
+ height = uint32_t(pathHeight + texture->offset * 2.0 + 0.5);
}
static void initPaint(SkPaint& paint) {
@@ -159,20 +146,30 @@ static void initPaint(SkPaint& paint) {
paint.setColorFilter(nullptr);
paint.setMaskFilter(nullptr);
paint.setShader(nullptr);
- SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
- SkSafeUnref(paint.setXfermode(mode));
+ paint.setBlendMode(SkBlendMode::kSrc);
}
-static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
- float left, float top, float offset, uint32_t width, uint32_t height) {
- initBitmap(bitmap, width, height);
+static sk_sp<Bitmap> drawPath(const SkPath* path, const SkPaint* paint, PathTexture* texture,
+ uint32_t maxTextureSize) {
+ uint32_t width, height;
+ computePathBounds(path, paint, texture, width, height);
+ if (width > maxTextureSize || height > maxTextureSize) {
+ ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)",
+ width, height, maxTextureSize, maxTextureSize);
+ return nullptr;
+ }
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(width, height));
SkPaint pathPaint(*paint);
initPaint(pathPaint);
- SkCanvas canvas(bitmap);
- canvas.translate(-left + offset, -top + offset);
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ skBitmap.eraseColor(0);
+ SkCanvas canvas(skBitmap);
+ canvas.translate(-texture->left + texture->offset, -texture->top + texture->offset);
canvas.drawPath(*path, pathPaint);
+ return bitmap;
}
///////////////////////////////////////////////////////////////////////////////
@@ -182,8 +179,7 @@ static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
PathCache::PathCache()
: mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity)
, mSize(0)
- , mMaxSize(Properties::pathCacheSize)
- , mTexNum(0) {
+ , mMaxSize(Properties::pathCacheSize) {
mCache.setOnEntryRemovedListener(this);
GLint maxTextureSize;
@@ -227,7 +223,7 @@ void PathCache::removeTexture(PathTexture* texture) {
// If there is a pending task we must wait for it to return
// before attempting our cleanup
- const sp<Task<SkBitmap*> >& task = texture->task();
+ const sp<PathTask>& task = texture->task();
if (task != nullptr) {
task->getResult();
texture->clearTask();
@@ -239,7 +235,6 @@ void PathCache::removeTexture(PathTexture* texture) {
"the cache in an inconsistent state", size);
}
mSize -= size;
- mTexNum--;
}
PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d",
@@ -264,7 +259,14 @@ void PathCache::purgeCache(uint32_t width, uint32_t height) {
}
void PathCache::trim() {
- while (mSize > mMaxSize || mTexNum > DEFAULT_PATH_TEXTURE_CAP) {
+ // 25 is just an arbitrary lower bound to ensure we aren't in weird edge cases
+ // of things like a cap of 0 or 1 as that's going to break things.
+ // It does not represent a reasonable minimum value
+ static_assert(DEFAULT_PATH_TEXTURE_CAP > 25, "Path cache texture cap is too small");
+
+ while (mSize > mMaxSize || mCache.size() > DEFAULT_PATH_TEXTURE_CAP) {
+ LOG_ALWAYS_FATAL_IF(!mCache.size(), "Inconsistent mSize! Ran out of items to remove!"
+ " mSize = %u, mMaxSize = %u", mSize, mMaxSize);
mCache.removeOldest();
}
}
@@ -273,27 +275,21 @@ PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *p
const SkPaint* paint) {
ATRACE_NAME("Generate Path Texture");
- float left, top, offset;
- uint32_t width, height;
- computePathBounds(path, paint, left, top, offset, width, height);
-
- if (!checkTextureSize(width, height)) return nullptr;
-
- purgeCache(width, height);
-
- SkBitmap bitmap;
- drawPath(path, paint, bitmap, left, top, offset, width, height);
-
- PathTexture* texture = new PathTexture(Caches::getInstance(),
- left, top, offset, path->getGenerationID());
- generateTexture(entry, &bitmap, texture);
+ PathTexture* texture = new PathTexture(Caches::getInstance(), path->getGenerationID());
+ sk_sp<Bitmap> bitmap(drawPath(path, paint, texture, mMaxTextureSize));
+ if (!bitmap) {
+ delete texture;
+ return nullptr;
+ }
+ purgeCache(bitmap->width(), bitmap->height());
+ generateTexture(entry, *bitmap, texture);
return texture;
}
-void PathCache::generateTexture(const PathDescription& entry, SkBitmap* bitmap,
+void PathCache::generateTexture(const PathDescription& entry, Bitmap& bitmap,
PathTexture* texture, bool addToCache) {
- generateTexture(*bitmap, texture);
+ generateTexture(bitmap, texture);
// Note here that we upload to a texture even if it's bigger than mMaxSize.
// Such an entry in mCache will only be temporary, since it will be evicted
@@ -314,11 +310,10 @@ void PathCache::clear() {
mCache.clear();
}
-void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
+void PathCache::generateTexture(Bitmap& bitmap, Texture* texture) {
ATRACE_NAME("Upload Path Texture");
texture->upload(bitmap);
texture->setFilter(GL_LINEAR);
- mTexNum++;
}
///////////////////////////////////////////////////////////////////////////////
@@ -326,29 +321,14 @@ void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
///////////////////////////////////////////////////////////////////////////////
PathCache::PathProcessor::PathProcessor(Caches& caches):
- TaskProcessor<SkBitmap*>(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) {
+ TaskProcessor<sk_sp<Bitmap> >(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) {
}
-void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
+void PathCache::PathProcessor::onProcess(const sp<Task<sk_sp<Bitmap> > >& task) {
PathTask* t = static_cast<PathTask*>(task.get());
ATRACE_NAME("pathPrecache");
- float left, top, offset;
- uint32_t width, height;
- PathCache::computePathBounds(&t->path, &t->paint, left, top, offset, width, height);
-
- PathTexture* texture = t->texture;
- texture->left = left;
- texture->top = top;
- texture->offset = offset;
-
- if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
- SkBitmap* bitmap = new SkBitmap();
- drawPath(&t->path, &t->paint, *bitmap, left, top, offset, width, height);
- t->setResult(bitmap);
- } else {
- t->setResult(nullptr);
- }
+ t->setResult(drawPath(&t->path, &t->paint, t->texture, mMaxTextureSize));
}
///////////////////////////////////////////////////////////////////////////////
@@ -393,16 +373,15 @@ PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) {
} else {
// A bitmap is attached to the texture, this means we need to
// upload it as a GL texture
- const sp<Task<SkBitmap*> >& task = texture->task();
+ const sp<PathTask>& task = texture->task();
if (task != nullptr) {
// But we must first wait for the worker thread to be done
// producing the bitmap, so let's wait
- SkBitmap* bitmap = task->getResult();
+ sk_sp<Bitmap> bitmap = task->getResult();
if (bitmap) {
- generateTexture(entry, bitmap, texture, false);
+ generateTexture(entry, *bitmap, texture, false);
texture->clearTask();
} else {
- ALOGW("Path too large to be rendered into a texture");
texture->clearTask();
texture = nullptr;
mCache.remove(entry);
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index e925848a25b8..7bd190df951b 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -19,6 +19,7 @@
#include "Debug.h"
#include "Texture.h"
+#include "hwui/Bitmap.h"
#include "thread/Task.h"
#include "thread/TaskProcessor.h"
#include "utils/Macros.h"
@@ -32,7 +33,6 @@
#include <vector>
-class SkBitmap;
class SkCanvas;
class SkPaint;
struct SkRect;
@@ -41,7 +41,6 @@ namespace android {
namespace uirenderer {
class Caches;
-
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
@@ -57,18 +56,25 @@ class Caches;
// Classes
///////////////////////////////////////////////////////////////////////////////
+struct PathTexture;
+class PathTask: public Task<sk_sp<Bitmap>> {
+public:
+ PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture):
+ path(*path), paint(*paint), texture(texture) {
+ }
+
+ // copied, since input path not guaranteed to survive for duration of task
+ const SkPath path;
+
+ // copied, since input paint may not be immutable
+ const SkPaint paint;
+ PathTexture* texture;
+};
+
/**
* Alpha texture used to represent a path.
*/
struct PathTexture: public Texture {
- PathTexture(Caches& caches, float left, float top,
- float offset, int generation)
- : Texture(caches)
- , left(left)
- , top(top)
- , offset(offset) {
- this->generation = generation;
- }
PathTexture(Caches& caches, int generation)
: Texture(caches) {
this->generation = generation;
@@ -91,11 +97,11 @@ struct PathTexture: public Texture {
*/
float offset = 0;
- sp<Task<SkBitmap*> > task() const {
+ sp<PathTask> task() const {
return mTask;
}
- void setTask(const sp<Task<SkBitmap*> >& task) {
+ void setTask(const sp<PathTask>& task) {
mTask = task;
}
@@ -106,7 +112,7 @@ struct PathTexture: public Texture {
}
private:
- sp<Task<SkBitmap*> > mTask;
+ sp<PathTask> mTask;
}; // struct PathTexture
enum class ShapeType {
@@ -227,22 +233,15 @@ public:
*/
void precache(const SkPath* path, const SkPaint* paint);
- static bool canDrawAsConvexPath(SkPath* path, const SkPaint* paint);
- static void computePathBounds(const SkPath* path, const SkPaint* paint,
- float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
- static void computeBounds(const SkRect& bounds, const SkPaint* paint,
- float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
-
private:
PathTexture* addTexture(const PathDescription& entry,
const SkPath *path, const SkPaint* paint);
- PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap);
/**
* Generates the texture from a bitmap into the specified texture structure.
*/
- void generateTexture(SkBitmap& bitmap, Texture* texture);
- void generateTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture,
+ void generateTexture(Bitmap& bitmap, Texture* texture);
+ void generateTexture(const PathDescription& entry, Bitmap& bitmap, PathTexture* texture,
bool addToCache = true);
PathTexture* get(const PathDescription& entry) {
@@ -257,41 +256,15 @@ private:
void removeTexture(PathTexture* texture);
- bool checkTextureSize(uint32_t width, uint32_t height) {
- if (width > mMaxTextureSize || height > mMaxTextureSize) {
- ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)",
- width, height, mMaxTextureSize, mMaxTextureSize);
- return false;
- }
- return true;
- }
-
void init();
- class PathTask: public Task<SkBitmap*> {
- public:
- PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture):
- path(*path), paint(*paint), texture(texture) {
- }
-
- ~PathTask() {
- delete future()->get();
- }
-
- // copied, since input path not guaranteed to survive for duration of task
- const SkPath path;
- // copied, since input paint may not be immutable
- const SkPaint paint;
- PathTexture* texture;
- };
-
- class PathProcessor: public TaskProcessor<SkBitmap*> {
+ class PathProcessor: public TaskProcessor<sk_sp<Bitmap> > {
public:
explicit PathProcessor(Caches& caches);
~PathProcessor() { }
- virtual void onProcess(const sp<Task<SkBitmap*> >& task) override;
+ virtual void onProcess(const sp<Task<sk_sp<Bitmap> > >& task) override;
private:
uint32_t mMaxTextureSize;
@@ -304,12 +277,6 @@ private:
bool mDebugEnabled;
- /**
- * Driver allocated 4k/8k/16k memory for small path cache,
- * limit the number of PathTexture in case occupy too much memory in hardware.
- */
- uint32_t mTexNum;
-
sp<PathProcessor> mProcessor;
std::vector<uint32_t> mGarbage;
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
index 165c7db4c85f..2a96b6914afc 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -37,8 +37,6 @@ public:
uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override;
- uint8_t* getMappedPointer() const override;
-
void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override;
protected:
@@ -64,10 +62,6 @@ void CpuPixelBuffer::unmap() {
mAccessMode = kAccessMode_None;
}
-uint8_t* CpuPixelBuffer::getMappedPointer() const {
- return mAccessMode == kAccessMode_None ? nullptr : mBuffer.get();
-}
-
void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
mFormat, GL_UNSIGNED_BYTE, &mBuffer[offset]);
@@ -84,8 +78,6 @@ public:
uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override;
- uint8_t* getMappedPointer() const override;
-
void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override;
protected:
@@ -142,10 +134,6 @@ void GpuPixelBuffer::unmap() {
}
}
-uint8_t* GpuPixelBuffer::getMappedPointer() const {
- return mMappedPointer;
-}
-
void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
// If the buffer is not mapped, unmap() will not bind it
mCaches.pixelBufferState().bind(mBuffer);
diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h
index 280af877b93d..77d5e413cb36 100644
--- a/libs/hwui/PixelBuffer.h
+++ b/libs/hwui/PixelBuffer.h
@@ -100,12 +100,6 @@ public:
}
/**
- * Returns the currently mapped pointer. Returns NULL if the buffer
- * is not mapped.
- */
- virtual uint8_t* getMappedPointer() const = 0;
-
- /**
* Upload the specified rectangle of this pixel buffer as a
* GL_TEXTURE_2D texture. Calling this method will trigger
* an unmap() if necessary.
@@ -200,8 +194,7 @@ protected:
/**
* Unmaps this buffer, if needed. After the buffer is unmapped,
* the pointer previously returned by map() becomes invalid and
- * should not be used. After calling this method, getMappedPointer()
- * will always return NULL.
+ * should not be used.
*/
virtual void unmap() = 0;
diff --git a/libs/hwui/ProfileRenderer.cpp b/libs/hwui/ProfileRenderer.cpp
new file mode 100644
index 000000000000..0ad484ce0687
--- /dev/null
+++ b/libs/hwui/ProfileRenderer.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ProfileRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+void ProfileRenderer::drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) {
+ mRenderer.drawRect(left, top, right, bottom, &paint);
+}
+
+void ProfileRenderer::drawRects(const float* rects, int count, const SkPaint& paint) {
+ mRenderer.drawRects(rects, count, &paint);
+}
+
+uint32_t ProfileRenderer::getViewportWidth() {
+ return mRenderer.getViewportWidth();
+}
+
+uint32_t ProfileRenderer::getViewportHeight() {
+ return mRenderer.getViewportHeight();
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/ProfileRenderer.h b/libs/hwui/ProfileRenderer.h
new file mode 100644
index 000000000000..b9e586f592e8
--- /dev/null
+++ b/libs/hwui/ProfileRenderer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "IProfileRenderer.h"
+
+#include "BakedOpRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+class ProfileRenderer : public IProfileRenderer {
+public:
+ ProfileRenderer(BakedOpRenderer& renderer)
+ : mRenderer(renderer) {
+ }
+
+ void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
+ void drawRects(const float* rects, int count, const SkPaint& paint) override;
+ uint32_t getViewportWidth() override;
+ uint32_t getViewportHeight() override;
+
+ virtual ~ProfileRenderer() {}
+
+private:
+ BakedOpRenderer& mRenderer;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index e5200a516777..e70982f5444a 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -22,7 +22,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-#include <SkXfermode.h>
+#include <SkBlendMode.h>
#include "Debug.h"
#include "FloatColor.h"
@@ -54,6 +54,7 @@ namespace uirenderer {
#define PROGRAM_KEY_COLOR_MATRIX 0x20
#define PROGRAM_KEY_COLOR_BLEND 0x40
#define PROGRAM_KEY_BITMAP_NPOT 0x80
+#define PROGRAM_KEY_BITMAP_EXTERNAL 0x100
#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
@@ -85,6 +86,8 @@ namespace uirenderer {
#define PROGRAM_HAS_DEBUG_HIGHLIGHT 42
#define PROGRAM_HAS_ROUND_RECT_CLIP 43
+#define PROGRAM_HAS_GAMMA_CORRECTION 44
+
///////////////////////////////////////////////////////////////////////////////
// Types
///////////////////////////////////////////////////////////////////////////////
@@ -131,7 +134,8 @@ struct ProgramDescription {
// Shaders
bool hasBitmap;
- bool isBitmapNpot;
+ bool isShaderBitmapExternal;
+ bool useShaderBasedWrap;
bool hasVertexAlpha;
bool useShadowAlphaInterp;
@@ -140,7 +144,7 @@ struct ProgramDescription {
Gradient gradientType;
bool isSimpleGradient;
- SkXfermode::Mode shadersMode;
+ SkBlendMode shadersMode;
bool isBitmapFirst;
GLenum bitmapWrapS;
@@ -148,16 +152,18 @@ struct ProgramDescription {
// Color operations
ColorFilterMode colorOp;
- SkXfermode::Mode colorMode;
+ SkBlendMode colorMode;
// Framebuffer blending (requires Extensions.hasFramebufferFetch())
- // Ignored for all values < SkXfermode::kPlus_Mode
- SkXfermode::Mode framebufferMode;
+ // Ignored for all values < SkBlendMode::kPlus
+ SkBlendMode framebufferMode;
bool swapSrcDst;
bool hasDebugHighlight;
bool hasRoundRectClip;
+ bool hasGammaCorrection;
+
/**
* Resets this description. All fields are reset back to the default
* values they hold after building a new instance.
@@ -176,26 +182,29 @@ struct ProgramDescription {
modulate = false;
hasBitmap = false;
- isBitmapNpot = false;
+ isShaderBitmapExternal = false;
+ useShaderBasedWrap = false;
hasGradient = false;
gradientType = kGradientLinear;
isSimpleGradient = false;
- shadersMode = SkXfermode::kClear_Mode;
+ shadersMode = SkBlendMode::kClear;
isBitmapFirst = false;
bitmapWrapS = GL_CLAMP_TO_EDGE;
bitmapWrapT = GL_CLAMP_TO_EDGE;
colorOp = ColorFilterMode::None;
- colorMode = SkXfermode::kClear_Mode;
+ colorMode = SkBlendMode::kClear;
- framebufferMode = SkXfermode::kClear_Mode;
+ framebufferMode = SkBlendMode::kClear;
swapSrcDst = false;
hasDebugHighlight = false;
hasRoundRectClip = false;
+
+ hasGammaCorrection = false;
}
/**
@@ -228,17 +237,20 @@ struct ProgramDescription {
if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
if (hasBitmap) {
key |= PROGRAM_KEY_BITMAP;
- if (isBitmapNpot) {
+ if (useShaderBasedWrap) {
key |= PROGRAM_KEY_BITMAP_NPOT;
key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT;
key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT;
}
+ if (isShaderBitmapExternal) {
+ key |= PROGRAM_KEY_BITMAP_EXTERNAL;
+ }
}
if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT;
if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST;
if (hasBitmap && hasGradient) {
- key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
+ key |= ((int)shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
}
switch (colorOp) {
case ColorFilterMode::Matrix:
@@ -246,12 +258,12 @@ struct ProgramDescription {
break;
case ColorFilterMode::Blend:
key |= PROGRAM_KEY_COLOR_BLEND;
- key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
+ key |= ((int)colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
break;
case ColorFilterMode::None:
break;
}
- key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
+ key |= ((int)framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
if (hasVertexAlpha) key |= programid(0x1) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT;
@@ -262,6 +274,7 @@ struct ProgramDescription {
if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS;
if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP;
+ if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
return key;
}
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 59225e108ac7..71076791cf7f 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -17,8 +17,8 @@
#include <utils/String8.h>
#include "Caches.h"
-#include "Dither.h"
#include "ProgramCache.h"
+#include "Properties.h"
namespace android {
namespace uirenderer {
@@ -69,22 +69,16 @@ const char* gVS_Header_Varyings_HasBitmap =
"varying highp vec2 outBitmapTexCoords;\n";
const char* gVS_Header_Varyings_HasGradient[6] = {
// Linear
- "varying highp vec2 linear;\n"
- "varying vec2 ditherTexCoords;\n",
- "varying float linear;\n"
- "varying vec2 ditherTexCoords;\n",
+ "varying highp vec2 linear;\n",
+ "varying float linear;\n",
// Circular
- "varying highp vec2 circular;\n"
- "varying vec2 ditherTexCoords;\n",
- "varying highp vec2 circular;\n"
- "varying vec2 ditherTexCoords;\n",
+ "varying highp vec2 circular;\n",
+ "varying highp vec2 circular;\n",
// Sweep
- "varying highp vec2 sweep;\n"
- "varying vec2 ditherTexCoords;\n",
- "varying highp vec2 sweep;\n"
- "varying vec2 ditherTexCoords;\n",
+ "varying highp vec2 sweep;\n",
+ "varying highp vec2 sweep;\n",
};
const char* gVS_Header_Varyings_HasRoundRectClip =
"varying highp vec2 roundRectPos;\n";
@@ -98,22 +92,16 @@ const char* gVS_Main_OutTransformedTexCoords =
" outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n";
const char* gVS_Main_OutGradient[6] = {
// Linear
- " linear = vec2((screenSpace * position).x, 0.5);\n"
- " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
- " linear = (screenSpace * position).x;\n"
- " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
+ " linear = vec2((screenSpace * position).x, 0.5);\n",
+ " linear = (screenSpace * position).x;\n",
// Circular
- " circular = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
- " circular = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
+ " circular = (screenSpace * position).xy;\n",
+ " circular = (screenSpace * position).xy;\n",
// Sweep
+ " sweep = (screenSpace * position).xy;\n",
" sweep = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
- " sweep = (screenSpace * position).xy;\n"
- " ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
};
const char* gVS_Main_OutBitmapTexCoords =
" outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
@@ -147,17 +135,18 @@ const char* gFS_Uniforms_TextureSampler =
"uniform sampler2D baseSampler;\n";
const char* gFS_Uniforms_ExternalTextureSampler =
"uniform samplerExternalOES baseSampler;\n";
-const char* gFS_Uniforms_Dither =
- "uniform sampler2D ditherSampler;";
const char* gFS_Uniforms_GradientSampler[2] = {
- "%s\n"
+ "uniform vec2 screenSize;\n"
"uniform sampler2D gradientSampler;\n",
- "%s\n"
+
+ "uniform vec2 screenSize;\n"
"uniform vec4 startColor;\n"
"uniform vec4 endColor;\n"
};
const char* gFS_Uniforms_BitmapSampler =
"uniform sampler2D bitmapSampler;\n";
+const char* gFS_Uniforms_BitmapExternalSampler =
+ "uniform samplerExternalOES bitmapSampler;\n";
const char* gFS_Uniforms_ColorOp[3] = {
// None
"",
@@ -172,18 +161,59 @@ const char* gFS_Uniforms_HasRoundRectClip =
"uniform vec4 roundRectInnerRectLTRB;\n"
"uniform float roundRectRadius;\n";
+// Dithering must be done in the quantization space
+// When we are writing to an sRGB framebuffer, we must do the following:
+// EOCF(OECF(color) + dither)
+// We approximate the transfer functions with gamma 2.0 to avoid branches and pow()
+// The dithering pattern is generated with a triangle noise generator in the range [-0.0,1.0]
+// TODO: Handle linear fp16 render targets
+const char* gFS_Gradient_Functions =
+ "\nfloat triangleNoise(const highp vec2 n) {\n"
+ " highp vec2 p = fract(n * vec2(5.3987, 5.4421));\n"
+ " p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));\n"
+ " highp float xy = p.x * p.y;\n"
+ " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n"
+ "}\n";
+const char* gFS_Gradient_Preamble[2] = {
+ // Linear framebuffer
+ "\nvec4 dither(const vec4 color) {\n"
+ " return vec4(color.rgb + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0), color.a);\n"
+ "}\n"
+ "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n"
+ " vec4 c = pow(mix(a, b, v), vec4(vec3(1.0 / 2.2), 1.0));\n"
+ " return vec4(c.rgb * c.a, c.a);\n"
+ "}\n",
+ // sRGB framebuffer
+ "\nvec4 dither(const vec4 color) {\n"
+ " vec3 dithered = sqrt(color.rgb) + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);\n"
+ " return vec4(dithered * dithered, color.a);\n"
+ "}\n"
+ "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n"
+ " vec4 c = mix(a, b, v);\n"
+ " return vec4(c.rgb * c.a, c.a);\n"
+ "}\n"
+};
+
+// Uses luminance coefficients from Rec.709 to choose the appropriate gamma
+// The gamma() function assumes that bright text will be displayed on a dark
+// background and that dark text will be displayed on bright background
+// The gamma coefficient is chosen to thicken or thin the text accordingly
+// The dot product used to compute the luminance could be approximated with
+// a simple max(color.r, color.g, color.b)
+const char* gFS_Gamma_Preamble =
+ "\n#define GAMMA (%.2f)\n"
+ "#define GAMMA_INV (%.2f)\n"
+ "\nfloat gamma(float a, const vec3 color) {\n"
+ " float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));\n"
+ " return pow(a, luminance < 0.5 ? GAMMA_INV : GAMMA);\n"
+ "}\n";
+
const char* gFS_Main =
"\nvoid main(void) {\n"
- " lowp vec4 fragColor;\n";
+ " vec4 fragColor;\n";
-const char* gFS_Main_Dither[2] = {
- // ES 2.0
- "texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE),
- // ES 3.0
- "texture2D(ditherSampler, ditherTexCoords).a"
-};
-const char* gFS_Main_AddDitherToGradient =
- " gradientColor += %s;\n";
+const char* gFS_Main_AddDither =
+ " fragColor = dither(fragColor);\n";
// Fast cases
const char* gFS_Fast_SingleColor =
@@ -202,24 +232,32 @@ const char* gFS_Fast_SingleA8Texture =
"\nvoid main(void) {\n"
" gl_FragColor = texture2D(baseSampler, outTexCoords);\n"
"}\n\n";
+const char* gFS_Fast_SingleA8Texture_ApplyGamma =
+ "\nvoid main(void) {\n"
+ " gl_FragColor = vec4(0.0, 0.0, 0.0, pow(texture2D(baseSampler, outTexCoords).a, GAMMA));\n"
+ "}\n\n";
const char* gFS_Fast_SingleModulateA8Texture =
"\nvoid main(void) {\n"
" gl_FragColor = color * texture2D(baseSampler, outTexCoords).a;\n"
"}\n\n";
+const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
+ "\nvoid main(void) {\n"
+ " gl_FragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n"
+ "}\n\n";
const char* gFS_Fast_SingleGradient[2] = {
"\nvoid main(void) {\n"
- " gl_FragColor = %s + texture2D(gradientSampler, linear);\n"
+ " gl_FragColor = dither(texture2D(gradientSampler, linear));\n"
"}\n\n",
"\nvoid main(void) {\n"
- " gl_FragColor = %s + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
+ " gl_FragColor = dither(gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n"
"}\n\n",
};
const char* gFS_Fast_SingleModulateGradient[2] = {
"\nvoid main(void) {\n"
- " gl_FragColor = %s + color.a * texture2D(gradientSampler, linear);\n"
+ " gl_FragColor = dither(color.a * texture2D(gradientSampler, linear));\n"
"}\n\n",
"\nvoid main(void) {\n"
- " gl_FragColor = %s + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
+ " gl_FragColor = dither(color.a * gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n"
"}\n\n"
};
@@ -239,29 +277,31 @@ const char* gFS_Main_FetchTexture[2] = {
// Modulate
" fragColor = color * texture2D(baseSampler, outTexCoords);\n"
};
-const char* gFS_Main_FetchA8Texture[2] = {
+const char* gFS_Main_FetchA8Texture[4] = {
// Don't modulate
" fragColor = texture2D(baseSampler, outTexCoords);\n",
+ " fragColor = texture2D(baseSampler, outTexCoords);\n",
// Modulate
" fragColor = color * texture2D(baseSampler, outTexCoords).a;\n",
+ " fragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n",
};
const char* gFS_Main_FetchGradient[6] = {
// Linear
" vec4 gradientColor = texture2D(gradientSampler, linear);\n",
- " vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
+ " vec4 gradientColor = gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
// Circular
" vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n",
- " vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
+ " vec4 gradientColor = gammaMix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
// Sweep
" highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
" vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n",
" highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
- " vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
+ " vec4 gradientColor = gammaMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
};
const char* gFS_Main_FetchBitmap =
" vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
@@ -271,29 +311,38 @@ const char* gFS_Main_BlendShadersBG =
" fragColor = blendShaders(gradientColor, bitmapColor)";
const char* gFS_Main_BlendShadersGB =
" fragColor = blendShaders(bitmapColor, gradientColor)";
-const char* gFS_Main_BlendShaders_Modulate[3] = {
+const char* gFS_Main_BlendShaders_Modulate[6] = {
// Don't modulate
";\n",
+ ";\n",
// Modulate
" * color.a;\n",
+ " * color.a;\n",
// Modulate with alpha 8 texture
" * texture2D(baseSampler, outTexCoords).a;\n",
+ " * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n",
};
-const char* gFS_Main_GradientShader_Modulate[3] = {
+const char* gFS_Main_GradientShader_Modulate[6] = {
// Don't modulate
" fragColor = gradientColor;\n",
+ " fragColor = gradientColor;\n",
// Modulate
" fragColor = gradientColor * color.a;\n",
+ " fragColor = gradientColor * color.a;\n",
// Modulate with alpha 8 texture
" fragColor = gradientColor * texture2D(baseSampler, outTexCoords).a;\n",
+ " fragColor = gradientColor * gamma(texture2D(baseSampler, outTexCoords).a, gradientColor.rgb);\n",
};
-const char* gFS_Main_BitmapShader_Modulate[3] = {
+const char* gFS_Main_BitmapShader_Modulate[6] = {
// Don't modulate
" fragColor = bitmapColor;\n",
+ " fragColor = bitmapColor;\n",
// Modulate
" fragColor = bitmapColor * color.a;\n",
+ " fragColor = bitmapColor * color.a;\n",
// Modulate with alpha 8 texture
" fragColor = bitmapColor * texture2D(baseSampler, outTexCoords).a;\n",
+ " fragColor = bitmapColor * gamma(texture2D(baseSampler, outTexCoords).a, bitmapColor.rgb);\n",
};
const char* gFS_Main_FragColor =
" gl_FragColor = fragColor;\n";
@@ -385,7 +434,8 @@ const char* gBlendOps[18] = {
///////////////////////////////////////////////////////////////////////////////
ProgramCache::ProgramCache(Extensions& extensions)
- : mHasES3(extensions.getMajorGlVersion() >= 3) {
+ : mHasES3(extensions.getMajorGlVersion() >= 3)
+ , mHasSRGB(extensions.hasSRGB()) {
}
ProgramCache::~ProgramCache() {
@@ -518,6 +568,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
static bool shaderOp(const ProgramDescription& description, String8& shader,
const int modulateOp, const char** snippets) {
int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
+ op = op * 2 + description.hasGammaCorrection;
shader.append(snippets[op]);
return description.hasAlpha8Texture;
}
@@ -525,11 +576,12 @@ static bool shaderOp(const ProgramDescription& description, String8& shader,
String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
String8 shader(gFS_Header_Start);
- const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode;
+ const bool blendFramebuffer = description.framebufferMode >= SkBlendMode::kPlus;
if (blendFramebuffer) {
shader.append(gFS_Header_Extension_FramebufferFetch);
}
- if (description.hasExternalTexture) {
+ if (description.hasExternalTexture
+ || (description.hasBitmap && description.isShaderBitmapExternal)) {
shader.append(gFS_Header_Extension_ExternalTexture);
}
@@ -570,13 +622,16 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gFS_Uniforms_ExternalTextureSampler);
}
if (description.hasGradient) {
- shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
- gFS_Uniforms_Dither);
+ shader.append(gFS_Uniforms_GradientSampler[description.isSimpleGradient]);
}
if (description.hasRoundRectClip) {
shader.append(gFS_Uniforms_HasRoundRectClip);
}
+ if (description.hasGammaCorrection) {
+ shader.appendFormat(gFS_Gamma_Preamble, Properties::textGamma, 1.0f / Properties::textGamma);
+ }
+
// Optimization for common cases
if (!description.hasVertexAlpha
&& !blendFramebuffer
@@ -607,18 +662,26 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
fast = true;
} else if (singleA8Texture) {
if (!description.modulate) {
- shader.append(gFS_Fast_SingleA8Texture);
+ if (description.hasGammaCorrection) {
+ shader.append(gFS_Fast_SingleA8Texture_ApplyGamma);
+ } else {
+ shader.append(gFS_Fast_SingleA8Texture);
+ }
} else {
- shader.append(gFS_Fast_SingleModulateA8Texture);
+ if (description.hasGammaCorrection) {
+ shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma);
+ } else {
+ shader.append(gFS_Fast_SingleModulateA8Texture);
+ }
}
fast = true;
} else if (singleGradient) {
+ shader.append(gFS_Gradient_Functions);
+ shader.append(gFS_Gradient_Preamble[mHasSRGB]);
if (!description.modulate) {
- shader.appendFormat(gFS_Fast_SingleGradient[description.isSimpleGradient],
- gFS_Main_Dither[mHasES3]);
+ shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]);
} else {
- shader.appendFormat(gFS_Fast_SingleModulateGradient[description.isSimpleGradient],
- gFS_Main_Dither[mHasES3]);
+ shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]);
}
fast = true;
}
@@ -635,7 +698,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
}
if (description.hasBitmap) {
- shader.append(gFS_Uniforms_BitmapSampler);
+ if (description.isShaderBitmapExternal) {
+ shader.append(gFS_Uniforms_BitmapExternalSampler);
+ } else {
+ shader.append(gFS_Uniforms_BitmapSampler);
+ }
}
shader.append(gFS_Uniforms_ColorOp[static_cast<int>(description.colorOp)]);
@@ -649,9 +716,13 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (blendFramebuffer) {
generateBlend(shader, "blendFramebuffer", description.framebufferMode);
}
- if (description.isBitmapNpot) {
+ if (description.useShaderBasedWrap) {
generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
}
+ if (description.hasGradient) {
+ shader.append(gFS_Gradient_Functions);
+ shader.append(gFS_Gradient_Preamble[mHasSRGB]);
+ }
// Begin the shader
shader.append(gFS_Main); {
@@ -659,7 +730,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasTexture || description.hasExternalTexture) {
if (description.hasAlpha8Texture) {
if (!description.hasGradient && !description.hasBitmap) {
- shader.append(gFS_Main_FetchA8Texture[modulateOp]);
+ shader.append(
+ gFS_Main_FetchA8Texture[modulateOp * 2 + description.hasGammaCorrection]);
}
} else {
shader.append(gFS_Main_FetchTexture[modulateOp]);
@@ -671,10 +743,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
}
if (description.hasGradient) {
shader.append(gFS_Main_FetchGradient[gradientIndex(description)]);
- shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]);
}
if (description.hasBitmap) {
- if (!description.isBitmapNpot) {
+ if (!description.useShaderBasedWrap) {
shader.append(gFS_Main_FetchBitmap);
} else {
shader.append(gFS_Main_FetchBitmapNpot);
@@ -715,6 +786,10 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
}
}
+ if (description.hasGradient) {
+ shader.append(gFS_Main_AddDither);
+ }
+
// Output the fragment
if (!blendFramebuffer) {
shader.append(gFS_Main_FragColor);
@@ -743,12 +818,12 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
return shader;
}
-void ProgramCache::generateBlend(String8& shader, const char* name, SkXfermode::Mode mode) {
+void ProgramCache::generateBlend(String8& shader, const char* name, SkBlendMode mode) {
shader.append("\nvec4 ");
shader.append(name);
shader.append("(vec4 src, vec4 dst) {\n");
shader.append(" ");
- shader.append(gBlendOps[mode]);
+ shader.append(gBlendOps[(int)mode]);
shader.append("}\n");
}
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 9ac885b665e7..c2f715de70c3 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -51,7 +51,7 @@ private:
Program* generateProgram(const ProgramDescription& description, programid key);
String8 generateVertexShader(const ProgramDescription& description);
String8 generateFragmentShader(const ProgramDescription& description);
- void generateBlend(String8& shader, const char* name, SkXfermode::Mode mode);
+ void generateBlend(String8& shader, const char* name, SkBlendMode mode);
void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT);
void printLongString(const String8& shader) const;
@@ -59,6 +59,7 @@ private:
std::map<programid, std::unique_ptr<Program>> mCache;
const bool mHasES3;
+ const bool mHasSRGB;
}; // class ProgramCache
}; // namespace uirenderer
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index c1e2e5e3058a..09e34bf5ca15 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -63,8 +63,10 @@ int Properties::overrideSpotShadowStrength = -1;
ProfileType Properties::sProfileType = ProfileType::None;
bool Properties::sDisableProfileBars = false;
+RenderPipelineType Properties::sRenderPipelineType = RenderPipelineType::NotInitialized;
bool Properties::waitForGpuCompletion = false;
+bool Properties::forceDrawFrame = false;
bool Properties::filterOutTestOverhead = false;
@@ -204,5 +206,33 @@ ProfileType Properties::getProfileType() {
return sProfileType;
}
+RenderPipelineType Properties::getRenderPipelineType() {
+ if (RenderPipelineType::NotInitialized != sRenderPipelineType) {
+ return sRenderPipelineType;
+ }
+ char prop[PROPERTY_VALUE_MAX];
+ property_get(PROPERTY_DEFAULT_RENDERER, prop, "opengl");
+ if (!strcmp(prop, "skiagl") ) {
+ sRenderPipelineType = RenderPipelineType::SkiaGL;
+ } else if (!strcmp(prop, "skiavk") ) {
+ sRenderPipelineType = RenderPipelineType::SkiaVulkan;
+ } else { //"opengl"
+ sRenderPipelineType = RenderPipelineType::OpenGL;
+ }
+ return sRenderPipelineType;
+}
+
+#ifdef HWUI_GLES_WRAP_ENABLED
+void Properties::overrideRenderPipelineType(RenderPipelineType type) {
+ sRenderPipelineType = type;
+}
+#endif
+
+bool Properties::isSkiaEnabled() {
+ auto renderType = getRenderPipelineType();
+ return RenderPipelineType::SkiaGL == renderType
+ || RenderPipelineType::SkiaVulkan == renderType;
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 881bd5f57714..6dc0cb30a25a 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -20,8 +20,7 @@
#include <cutils/properties.h>
/**
- * This file contains the list of system properties used to configure
- * the OpenGLRenderer.
+ * This file contains the list of system properties used to configure libhwui.
*/
namespace android {
@@ -153,6 +152,12 @@ enum DebugLevel {
#define PROPERTY_FILTER_TEST_OVERHEAD "debug.hwui.filter_test_overhead"
+/**
+ * Allows to set rendering pipeline mode to OpenGL (default), Skia OpenGL
+ * or Vulkan.
+ */
+#define PROPERTY_DEFAULT_RENDERER "debug.hwui.default_renderer"
+
///////////////////////////////////////////////////////////////////////////////
// Runtime configuration properties
///////////////////////////////////////////////////////////////////////////////
@@ -161,7 +166,7 @@ enum DebugLevel {
* Used to enable/disable scissor optimization. The accepted values are
* "true" and "false". The default value is "false".
*
- * When scissor optimization is enabled, OpenGLRenderer will attempt to
+ * When scissor optimization is enabled, libhwui will attempt to
* minimize the use of scissor by selectively enabling and disabling the
* GL scissor test.
* When the optimization is disabled, OpenGLRenderer will keep the GL
@@ -198,7 +203,7 @@ enum DebugLevel {
#define PROPERTY_TEXT_LARGE_CACHE_WIDTH "ro.hwui.text_large_cache_width"
#define PROPERTY_TEXT_LARGE_CACHE_HEIGHT "ro.hwui.text_large_cache_height"
-// Gamma (>= 1.0, <= 10.0)
+// Gamma (>= 1.0, <= 3.0)
#define PROPERTY_TEXT_GAMMA "hwui.text_gamma"
///////////////////////////////////////////////////////////////////////////////
@@ -217,7 +222,7 @@ enum DebugLevel {
#define DEFAULT_TEXTURE_CACHE_FLUSH_RATE 0.6f
-#define DEFAULT_TEXT_GAMMA 1.4f
+#define DEFAULT_TEXT_GAMMA 1.45f // Match design tools
// cap to 256 to limite paths in the path cache
#define DEFAULT_PATH_TEXTURE_CAP 256
@@ -248,6 +253,13 @@ enum class StencilClipDebug {
ShowRegion
};
+enum class RenderPipelineType {
+ OpenGL = 0,
+ SkiaGL,
+ SkiaVulkan,
+ NotInitialized = 128
+};
+
/**
* Renderthread-only singleton which manages several static rendering properties. Most of these
* are driven by system properties which are queried once at initialization, and again if init()
@@ -295,18 +307,26 @@ public:
static int overrideSpotShadowStrength;
static ProfileType getProfileType();
+ static RenderPipelineType getRenderPipelineType();
+ static bool isSkiaEnabled();
// Should be used only by test apps
static bool waitForGpuCompletion;
+ static bool forceDrawFrame;
// Should only be set by automated tests to try and filter out
// any overhead they add
static bool filterOutTestOverhead;
+ // Used for testing only to change the render pipeline.
+#ifdef HWUI_GLES_WRAP_ENABLED
+ static void overrideRenderPipelineType(RenderPipelineType);
+#endif
+
private:
static ProfileType sProfileType;
static bool sDisableProfileBars;
-
+ static RenderPipelineType sRenderPipelineType;
}; // class Caches
}; // namespace uirenderer
diff --git a/libs/hwui/PropertyValuesHolder.cpp b/libs/hwui/PropertyValuesHolder.cpp
index 6ba0ab59a88c..2a03e6a3ebc5 100644
--- a/libs/hwui/PropertyValuesHolder.cpp
+++ b/libs/hwui/PropertyValuesHolder.cpp
@@ -16,6 +16,7 @@
#include "PropertyValuesHolder.h"
+#include "utils/Color.h"
#include "utils/VectorDrawableUtils.h"
#include <utils/Log.h>
@@ -25,18 +26,26 @@ namespace uirenderer {
using namespace VectorDrawable;
-inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) {
- return (U8CPU) (fromValue * (1 - fraction) + toValue * fraction);
+inline constexpr float lerp(float fromValue, float toValue, float fraction) {
+ return float (fromValue * (1 - fraction) + toValue * fraction);
+}
+
+inline constexpr float linearize(U8CPU component) {
+ return EOCF_sRGB(component / 255.0f);
}
// TODO: Add a test for this
void ColorEvaluator::evaluate(SkColor* outColor,
const SkColor& fromColor, const SkColor& toColor, float fraction) const {
- U8CPU alpha = lerp(SkColorGetA(fromColor), SkColorGetA(toColor), fraction);
- U8CPU red = lerp(SkColorGetR(fromColor), SkColorGetR(toColor), fraction);
- U8CPU green = lerp(SkColorGetG(fromColor), SkColorGetG(toColor), fraction);
- U8CPU blue = lerp(SkColorGetB(fromColor), SkColorGetB(toColor), fraction);
- *outColor = SkColorSetARGB(alpha, red, green, blue);
+ float a = lerp(SkColorGetA(fromColor) / 255.0f, SkColorGetA(toColor) / 255.0f, fraction);
+ float r = lerp(linearize(SkColorGetR(fromColor)), linearize(SkColorGetR(toColor)), fraction);
+ float g = lerp(linearize(SkColorGetG(fromColor)), linearize(SkColorGetG(toColor)), fraction);
+ float b = lerp(linearize(SkColorGetB(fromColor)), linearize(SkColorGetB(toColor)), fraction);
+ *outColor = SkColorSetARGB(
+ (U8CPU) roundf(a * 255.0f),
+ (U8CPU) roundf(OECF_sRGB(r) * 255.0f),
+ (U8CPU) roundf(OECF_sRGB(g) * 255.0f),
+ (U8CPU) roundf(OECF_sRGB(b) * 255.0f));
}
void PathEvaluator::evaluate(PathData* out,
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index a112c42988c0..b76395301a21 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -17,11 +17,13 @@
#pragma once
#include "renderthread/RenderThread.h"
+#include "Rect.h"
#include <SkBitmap.h>
#include <gui/Surface.h>
namespace android {
+class GraphicBuffer;
namespace uirenderer {
// Keep in sync with PixelCopy.java codes
@@ -36,8 +38,18 @@ enum class CopyResult {
class Readback {
public:
- static CopyResult copySurfaceInto(renderthread::RenderThread& renderThread,
- Surface& surface, SkBitmap* bitmap);
+ /**
+ * Copies the surface's most recently queued buffer into the provided bitmap.
+ */
+ virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
+ SkBitmap* bitmap) = 0;
+ virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) = 0;
+
+protected:
+ explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
+ virtual ~Readback() {}
+
+ renderthread::RenderThread& mRenderThread;
};
} // namespace uirenderer
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 5497f863b357..dea2be68c8db 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -14,20 +14,19 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_RECORDED_OP_H
-#define ANDROID_HWUI_RECORDED_OP_H
+#pragma once
-#include "RecordedOp.h"
#include "font/FontUtil.h"
+#include "GlLayer.h"
#include "Matrix.h"
#include "Rect.h"
#include "RenderNode.h"
#include "TessellationCache.h"
#include "utils/LinearAllocator.h"
+#include "utils/PaintUtils.h"
#include "Vector.h"
#include <androidfw/ResourceTypes.h>
-#include <SkXfermode.h>
class SkBitmap;
class SkPaint;
@@ -213,15 +212,14 @@ struct ArcOp : RecordedOp {
};
struct BitmapOp : RecordedOp {
- BitmapOp(BASE_PARAMS, const SkBitmap* bitmap)
+ BitmapOp(BASE_PARAMS, Bitmap* bitmap)
: SUPER(BitmapOp)
, bitmap(bitmap) {}
- const SkBitmap* bitmap;
- // TODO: asset atlas/texture id lookup?
+ Bitmap* bitmap;
};
struct BitmapMeshOp : RecordedOp {
- BitmapMeshOp(BASE_PARAMS, const SkBitmap* bitmap, int meshWidth, int meshHeight,
+ BitmapMeshOp(BASE_PARAMS, Bitmap* bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors)
: SUPER(BitmapMeshOp)
, bitmap(bitmap)
@@ -229,7 +227,7 @@ struct BitmapMeshOp : RecordedOp {
, meshHeight(meshHeight)
, vertices(vertices)
, colors(colors) {}
- const SkBitmap* bitmap;
+ Bitmap* bitmap;
const int meshWidth;
const int meshHeight;
const float* vertices;
@@ -237,11 +235,11 @@ struct BitmapMeshOp : RecordedOp {
};
struct BitmapRectOp : RecordedOp {
- BitmapRectOp(BASE_PARAMS, const SkBitmap* bitmap, const Rect& src)
+ BitmapRectOp(BASE_PARAMS, Bitmap* bitmap, const Rect& src)
: SUPER(BitmapRectOp)
, bitmap(bitmap)
, src(src) {}
- const SkBitmap* bitmap;
+ Bitmap* bitmap;
const Rect src;
};
@@ -259,12 +257,12 @@ struct CirclePropsOp : RecordedOp {
struct ColorOp : RecordedOp {
// Note: unbounded op that will fillclip, so no bounds/matrix needed
- ColorOp(const ClipBase* localClip, int color, SkXfermode::Mode mode)
+ ColorOp(const ClipBase* localClip, int color, SkBlendMode mode)
: RecordedOp(RecordedOpId::ColorOp, Rect(), Matrix4::identity(), localClip, nullptr)
, color(color)
, mode(mode) {}
const int color;
- const SkXfermode::Mode mode;
+ const SkBlendMode mode;
};
struct FunctorOp : RecordedOp {
@@ -291,11 +289,11 @@ struct OvalOp : RecordedOp {
};
struct PatchOp : RecordedOp {
- PatchOp(BASE_PARAMS, const SkBitmap* bitmap, const Res_png_9patch* patch)
+ PatchOp(BASE_PARAMS, Bitmap* bitmap, const Res_png_9patch* patch)
: SUPER(PatchOp)
, bitmap(bitmap)
, patch(patch) {}
- const SkBitmap* bitmap;
+ Bitmap* bitmap;
const Res_png_9patch* patch;
};
@@ -416,7 +414,7 @@ struct TextOnPathOp : RecordedOp {
};
struct TextureLayerOp : RecordedOp {
- TextureLayerOp(BASE_PARAMS_PAINTLESS, Layer* layer)
+ TextureLayerOp(BASE_PARAMS_PAINTLESS, GlLayer* layer)
: SUPER_PAINTLESS(TextureLayerOp)
, layer(layer) {}
@@ -427,7 +425,7 @@ struct TextureLayerOp : RecordedOp {
, layer(op.layer) {
}
- Layer* layer;
+ GlLayer* layer;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -506,7 +504,7 @@ struct LayerOp : RecordedOp {
: SUPER_PAINTLESS(LayerOp)
, layerHandle(layerHandle)
, alpha(paint ? paint->getAlpha() / 255.0f : 1.0f)
- , mode(PaintUtils::getXfermodeDirect(paint))
+ , mode(PaintUtils::getBlendModeDirect(paint))
, colorFilter(paint ? paint->getColorFilter() : nullptr) {}
explicit LayerOp(RenderNode& node)
@@ -520,7 +518,7 @@ struct LayerOp : RecordedOp {
// constructed until after this operation is constructed.
OffscreenBuffer** layerHandle;
const float alpha;
- const SkXfermode::Mode mode;
+ const SkBlendMode mode;
// pointer to object owned by either LayerProperties, or a recorded Paint object in a
// BeginLayerOp. Lives longer than LayerOp in either case, so no skia ref counting is used.
@@ -529,5 +527,3 @@ struct LayerOp : RecordedOp {
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_RECORDED_OP_H
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 0c552bac1d7f..b5e5d6801f99 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -20,6 +20,7 @@
#include "RecordedOp.h"
#include "RenderNode.h"
#include "VectorDrawable.h"
+#include "hwui/MinikinUtils.h"
namespace android {
namespace uirenderer {
@@ -35,7 +36,7 @@ RecordingCanvas::~RecordingCanvas() {
"Destroyed a RecordingCanvas during a record!");
}
-void RecordingCanvas::resetRecording(int width, int height) {
+void RecordingCanvas::resetRecording(int width, int height, RenderNode* node) {
LOG_ALWAYS_FATAL_IF(mDisplayList,
"prepareDirty called a second time during a recording!");
mDisplayList = new DisplayList();
@@ -43,7 +44,6 @@ void RecordingCanvas::resetRecording(int width, int height) {
mState.initializeRecordingSaveStack(width, height);
mDeferredBarrierType = DeferredBarrierType::InOrder;
- mState.setDirtyClip(false);
}
DisplayList* RecordingCanvas::finishRecording() {
@@ -234,20 +234,17 @@ bool RecordingCanvas::quickRejectPath(const SkPath& path) const {
SkRect bounds = path.getBounds();
return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
}
-bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
return mState.clipRect(left, top, right, bottom, op);
}
-bool RecordingCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
+bool RecordingCanvas::clipPath(const SkPath* path, SkClipOp op) {
return mState.clipPath(path, op);
}
-bool RecordingCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
- return mState.clipRegion(region, op);
-}
// ----------------------------------------------------------------------------
// android/graphics/Canvas draw operations
// ----------------------------------------------------------------------------
-void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) {
+void RecordingCanvas::drawColor(int color, SkBlendMode mode) {
addOp(alloc().create_trivial<ColorOp>(
getRecordedClip(),
color,
@@ -468,17 +465,17 @@ void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
}
// Bitmap-based
-void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
+void RecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
save(SaveFlags::Matrix);
translate(left, top);
- drawBitmap(&bitmap, paint);
+ drawBitmap(bitmap, paint);
restore();
}
-void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
const SkPaint* paint) {
if (matrix.isIdentity()) {
- drawBitmap(&bitmap, paint);
+ drawBitmap(bitmap, paint);
} else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))
&& MathUtils::isPositive(matrix.getScaleX())
&& MathUtils::isPositive(matrix.getScaleY())) {
@@ -492,12 +489,12 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
} else {
save(SaveFlags::Matrix);
concat(matrix);
- drawBitmap(&bitmap, paint);
+ drawBitmap(bitmap, paint);
restore();
}
}
-void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+void RecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint) {
if (srcLeft == 0 && srcTop == 0
@@ -508,7 +505,7 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float sr
// transform simple rect to rect drawing case into position bitmap ops, since they merge
save(SaveFlags::Matrix);
translate(dstLeft, dstTop);
- drawBitmap(&bitmap, paint);
+ drawBitmap(bitmap, paint);
restore();
} else {
addOp(alloc().create_trivial<BitmapRectOp>(
@@ -520,7 +517,7 @@ void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float sr
}
}
-void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+void RecordingCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) {
int vertexCount = (meshWidth + 1) * (meshHeight + 1);
addOp(alloc().create_trivial<BitmapMeshOp>(
@@ -532,7 +529,7 @@ void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int
refBuffer<int>(colors, vertexCount))); // 1 color per vertex
}
-void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& patch,
+void RecordingCanvas::drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& patch,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) {
addOp(alloc().create_trivial<PatchOp>(
@@ -559,22 +556,28 @@ void RecordingCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions,
drawTextDecorations(x, y, totalAdvance, paint);
}
-void RecordingCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) {
- if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
- glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
- addOp(alloc().create_trivial<TextOnPathOp>(
- *(mState.currentSnapshot()->transform),
- getRecordedClip(),
- refPaint(&paint), glyphs, glyphCount, refPath(&path), hOffset, vOffset));
+void RecordingCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
+ const SkPaint& paint, const SkPath& path, size_t start, size_t end) {
+ uint16_t glyphs[1];
+ for (size_t i = start; i < end; i++) {
+ glyphs[0] = layout.getGlyphId(i);
+ float x = hOffset + layout.getX(i);
+ float y = vOffset + layout.getY(i);
+ if (PaintUtils::paintWillNotDrawText(paint)) return;
+ const uint16_t* tempGlyphs = refBuffer<glyph_t>(glyphs, 1);
+ addOp(alloc().create_trivial<TextOnPathOp>(
+ *(mState.currentSnapshot()->transform),
+ getRecordedClip(),
+ refPaint(&paint), tempGlyphs, 1, refPath(&path), x, y));
+ }
}
-void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
+void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkPaint* paint) {
addOp(alloc().create_trivial<BitmapOp>(
- Rect(bitmap->width(), bitmap->height()),
+ Rect(bitmap.width(), bitmap.height()),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
- refPaint(paint), refBitmap(*bitmap)));
+ refPaint(paint), refBitmap(bitmap)));
}
void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
@@ -603,13 +606,14 @@ void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) {
// We ref the DeferredLayerUpdater due to its thread-safe ref-counting semantics.
mDisplayList->ref(layerHandle);
+ LOG_ALWAYS_FATAL_IF(layerHandle->backingLayer()->getApi() != Layer::Api::OpenGL);
// Note that the backing layer has *not* yet been updated, so don't trust
// its width, height, transform, etc...!
addOp(alloc().create_trivial<TextureLayerOp>(
Rect(layerHandle->getWidth(), layerHandle->getHeight()),
*(mState.currentSnapshot()->transform),
getRecordedClip(),
- layerHandle->backingLayer()));
+ static_cast<GlLayer*>(layerHandle->backingLayer())));
}
void RecordingCanvas::callDrawGLFunction(Functor* functor,
@@ -660,7 +664,8 @@ void RecordingCanvas::refBitmapsInShader(const SkShader* shader) {
SkBitmap bitmap;
SkShader::TileMode xy[2];
if (shader->isABitmap(&bitmap, nullptr, xy)) {
- refBitmap(bitmap);
+ Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef());
+ refBitmap(*hwuiBitmap);
return;
}
SkShader::ComposeRec rec;
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 337e97bf450b..44181bd22397 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -22,10 +22,10 @@
#include "ResourceCache.h"
#include "SkiaCanvasProxy.h"
#include "Snapshot.h"
+#include "hwui/Bitmap.h"
#include "hwui/Canvas.h"
#include "utils/LinearAllocator.h"
#include "utils/Macros.h"
-#include "utils/NinePatch.h"
#include <SkDrawFilter.h>
#include <SkPaint.h>
@@ -50,7 +50,7 @@ public:
RecordingCanvas(size_t width, size_t height);
virtual ~RecordingCanvas();
- virtual void resetRecording(int width, int height) override;
+ virtual void resetRecording(int width, int height, RenderNode* node = nullptr) override;
virtual WARN_UNUSED_RESULT DisplayList* finishRecording() override;
// ----------------------------------------------------------------------------
// MISC HWUI OPERATIONS - TODO: CATEGORIZE
@@ -131,9 +131,9 @@ public:
virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
virtual bool quickRejectPath(const SkPath& path) const override;
- virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) override;
- virtual bool clipPath(const SkPath* path, SkRegion::Op op) override;
- virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override;
+ virtual bool clipRect(float left, float top, float right, float bottom,
+ SkClipOp op) override;
+ virtual bool clipPath(const SkPath* path, SkClipOp op) override;
// Misc
virtual SkDrawFilter* getDrawFilter() override { return mDrawFilter.get(); }
@@ -144,7 +144,7 @@ public:
// ----------------------------------------------------------------------------
// android/graphics/Canvas draw operations
// ----------------------------------------------------------------------------
- virtual void drawColor(int color, SkXfermode::Mode mode) override;
+ virtual void drawColor(int color, SkBlendMode mode) override;
virtual void drawPaint(const SkPaint& paint) override;
// Geometry
@@ -176,15 +176,14 @@ public:
virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
// Bitmap-based
- virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
- const SkPaint* paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint) override;
- virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) override;
- virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) override;
@@ -196,15 +195,15 @@ protected:
const SkPaint& paint, float x, float y,
float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
float totalAdvance) override;
- virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) override;
+ virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
+ const SkPaint& paint, const SkPath& path, size_t start, size_t end) override;
private:
const ClipBase* getRecordedClip() {
return mState.writableSnapshot()->mutateClipArea().serializeClip(alloc());
}
- void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
+ void drawBitmap(Bitmap& bitmap, const SkPaint* paint);
void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint);
@@ -286,14 +285,17 @@ private:
return cachedRegion;
}
- inline const SkBitmap* refBitmap(const SkBitmap& bitmap) {
+ inline Bitmap* refBitmap(Bitmap& bitmap) {
// Note that this assumes the bitmap is immutable. There are cases this won't handle
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
// contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case.
- SkBitmap* localBitmap = alloc().create<SkBitmap>(bitmap);
- mDisplayList->bitmapResources.push_back(localBitmap);
- return localBitmap;
+
+ // this is required because sk_sp's ctor adopts the pointer,
+ // but does not increment the refcount,
+ bitmap.ref();
+ mDisplayList->bitmapResources.emplace_back(&bitmap);
+ return &bitmap;
}
inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
@@ -313,7 +315,7 @@ private:
const ClipBase* mDeferredBarrierClip = nullptr;
DisplayList* mDisplayList = nullptr;
bool mHighContrastText = false;
- SkAutoTUnref<SkDrawFilter> mDrawFilter;
+ sk_sp<SkDrawFilter> mDrawFilter;
}; // class RecordingCanvas
}; // namespace uirenderer
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index bdcad798f05e..a5443d9ff6f1 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -16,19 +16,17 @@
#include "RenderNode.h"
+#include "BakedOpRenderer.h"
#include "DamageAccumulator.h"
#include "Debug.h"
-#if HWUI_NEW_OPS
-#include "BakedOpRenderer.h"
-#include "RecordedOp.h"
#include "OpDumper.h"
-#endif
-#include "DisplayListOp.h"
-#include "LayerRenderer.h"
-#include "OpenGLRenderer.h"
+#include "RecordedOp.h"
#include "TreeInfo.h"
#include "utils/MathUtils.h"
+#include "utils/StringUtils.h"
#include "utils/TraceUtils.h"
+#include "VectorDrawable.h"
+#include "renderstate/RenderState.h"
#include "renderthread/CanvasContext.h"
#include "protos/hwui.pb.h"
@@ -41,23 +39,6 @@
namespace android {
namespace uirenderer {
-void RenderNode::debugDumpLayers(const char* prefix) {
-#if HWUI_NEW_OPS
- LOG_ALWAYS_FATAL("TODO: dump layer");
-#else
- if (mLayer) {
- ALOGD("%sNode %p (%s) has layer %p (fbo = %u, wasBuildLayered = %s)",
- prefix, this, getName(), mLayer, mLayer->getFbo(),
- mLayer->wasBuildLayered ? "true" : "false");
- }
-#endif
- if (mDisplayList) {
- for (auto&& child : mDisplayList->getChildren()) {
- child->renderNode->debugDumpLayers(prefix);
- }
- }
-}
-
RenderNode::RenderNode()
: mDirtyPropertyFields(0)
, mNeedsDisplayListSync(false)
@@ -70,15 +51,7 @@ RenderNode::RenderNode()
RenderNode::~RenderNode() {
deleteDisplayList(nullptr);
delete mStagingDisplayList;
-#if HWUI_NEW_OPS
- LOG_ALWAYS_FATAL_IF(mLayer, "layer missed detachment!");
-#else
- if (mLayer) {
- ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer);
- mLayer->postDecStrong();
- mLayer = nullptr;
- }
-#endif
+ LOG_ALWAYS_FATAL_IF(hasLayer(), "layer missed detachment!");
}
void RenderNode::setStagingDisplayList(DisplayList* displayList, TreeObserver* observer) {
@@ -96,53 +69,37 @@ void RenderNode::setStagingDisplayList(DisplayList* displayList, TreeObserver* o
* This function is a simplified version of replay(), where we simply retrieve and log the
* display list. This function should remain in sync with the replay() function.
*/
-#if HWUI_NEW_OPS
-void RenderNode::output(uint32_t level, const char* label) {
- ALOGD("%s (%s %p%s%s%s%s%s)",
- label,
- getName(),
- this,
- (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : ""),
- (properties().hasShadow() ? ", casting shadow" : ""),
- (isRenderable() ? "" : ", empty"),
- (properties().getProjectBackwards() ? ", projected" : ""),
- (mLayer != nullptr ? ", on HW Layer" : ""));
- properties().debugOutputProperties(level + 1);
+void RenderNode::output() {
+ LogcatStream strout;
+ strout << "Root";
+ output(strout, 0);
+}
+
+void RenderNode::output(std::ostream& output, uint32_t level) {
+ output << " (" << getName() << " " << this
+ << (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : "")
+ << (properties().hasShadow() ? ", casting shadow" : "")
+ << (isRenderable() ? "" : ", empty")
+ << (properties().getProjectBackwards() ? ", projected" : "")
+ << (hasLayer() ? ", on HW Layer" : "")
+ << ")" << std::endl;
+
+ properties().debugOutputProperties(output, level + 1);
if (mDisplayList) {
for (auto&& op : mDisplayList->getOps()) {
- std::stringstream strout;
- OpDumper::dump(*op, strout, level + 1);
+ OpDumper::dump(*op, output, level + 1);
if (op->opId == RecordedOpId::RenderNodeOp) {
auto rnOp = reinterpret_cast<const RenderNodeOp*>(op);
- rnOp->renderNode->output(level + 1, strout.str().c_str());
+ rnOp->renderNode->output(output, level + 1);
} else {
- ALOGD("%s", strout.str().c_str());
+ output << std::endl;
}
}
}
- ALOGD("%*s/RenderNode(%s %p)", level * 2, "", getName(), this);
+ output << std::string(level * 2, ' ') << "/RenderNode(" << getName() << " " << this << ")";
+ output << std::endl;
}
-#else
-void RenderNode::output(uint32_t level) {
- ALOGD("%*sStart display list (%p, %s%s%s%s%s%s)", (level - 1) * 2, "", this,
- getName(),
- (MathUtils::isZero(properties().getAlpha()) ? ", zero alpha" : ""),
- (properties().hasShadow() ? ", casting shadow" : ""),
- (isRenderable() ? "" : ", empty"),
- (properties().getProjectBackwards() ? ", projected" : ""),
- (mLayer != nullptr ? ", on HW Layer" : ""));
- ALOGD("%*s%s %d", level * 2, "", "Save", SaveFlags::MatrixClip);
- properties().debugOutputProperties(level);
- if (mDisplayList) {
- // TODO: consider printing the chunk boundaries here
- for (auto&& op : mDisplayList->getOps()) {
- op->output(level, DisplayListOp::kOpLogFlag_Recurse);
- }
- }
- ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
- }
-#endif
void RenderNode::copyTo(proto::RenderNode *pnode) {
pnode->set_id(static_cast<uint64_t>(
@@ -231,8 +188,9 @@ void RenderNode::prepareTree(TreeInfo& info) {
ATRACE_CALL();
LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
- // Functors don't correctly handle stencil usage of overdraw debugging - shove 'em in a layer.
- bool functorsNeedLayer = Properties::debugOverdraw;
+ // The OpenGL renderer reserves the stencil buffer for overdraw debugging. Functors
+ // will need to be drawn in a layer.
+ bool functorsNeedLayer = Properties::debugOverdraw && !Properties::isSkiaEnabled();
prepareTreeImpl(info, functorsNeedLayer);
}
@@ -272,31 +230,6 @@ void RenderNode::prepareLayer(TreeInfo& info, uint32_t dirtyMask) {
}
}
-static layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
-#if HWUI_NEW_OPS
- return renderState.layerPool().get(renderState, width, height);
-#else
- return LayerRenderer::createRenderLayer(renderState, width, height);
-#endif
-}
-
-static void destroyLayer(layer_t* layer) {
-#if HWUI_NEW_OPS
- RenderState& renderState = layer->renderState;
- renderState.layerPool().putOrDelete(layer);
-#else
- LayerRenderer::destroyLayer(layer);
-#endif
-}
-
-static bool layerMatchesWidthAndHeight(layer_t* layer, int width, int height) {
-#if HWUI_NEW_OPS
- return layer->viewportWidth == (uint32_t) width && layer->viewportHeight == (uint32_t)height;
-#else
- return layer->layer.getWidth() == width && layer->layer.getHeight() == height;
-#endif
-}
-
void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().effectiveLayerType();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
@@ -305,43 +238,17 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
|| CC_UNLIKELY(!isRenderable())
|| CC_UNLIKELY(properties().getWidth() == 0)
|| CC_UNLIKELY(properties().getHeight() == 0)) {
- if (CC_UNLIKELY(mLayer)) {
- destroyLayer(mLayer);
- mLayer = nullptr;
+ if (CC_UNLIKELY(hasLayer())) {
+ renderthread::CanvasContext::destroyLayer(this);
}
return;
}
- bool transformUpdateNeeded = false;
- if (!mLayer) {
- mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
-#if !HWUI_NEW_OPS
- applyLayerPropertiesToLayer(info);
-#endif
+ if(info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator)) {
damageSelf(info);
- transformUpdateNeeded = true;
- } else if (!layerMatchesWidthAndHeight(mLayer, getWidth(), getHeight())) {
-#if HWUI_NEW_OPS
- // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
- // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
- RenderState& renderState = mLayer->renderState;
- if (properties().fitsOnLayer()) {
- mLayer = renderState.layerPool().resize(mLayer, getWidth(), getHeight());
- } else {
-#else
- if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
-#endif
- destroyLayer(mLayer);
- mLayer = nullptr;
- }
- damageSelf(info);
- transformUpdateNeeded = true;
}
- SkRect dirty;
- info.damageAccumulator->peekAtDirty(&dirty);
-
- if (!mLayer) {
+ if (!hasLayer()) {
Caches::getInstance().dumpMemoryUsage();
if (info.errorHandler) {
std::ostringstream err;
@@ -358,26 +265,9 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
return;
}
- if (transformUpdateNeeded && mLayer) {
- // update the transform in window of the layer to reset its origin wrt light source position
- Matrix4 windowTransform;
- info.damageAccumulator->computeCurrentTransform(&windowTransform);
- mLayer->setWindowTransform(windowTransform);
- }
-
-#if HWUI_NEW_OPS
+ SkRect dirty;
+ info.damageAccumulator->peekAtDirty(&dirty);
info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
-#else
- if (dirty.intersect(0, 0, getWidth(), getHeight())) {
- dirty.roundOut(&dirty);
- mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
- }
- // This is not inside the above if because we may have called
- // updateDeferred on a previous prepare pass that didn't have a renderer
- if (info.renderer && mLayer->deferredUpdateScheduled) {
- info.renderer->pushLayerUpdate(mLayer);
- }
-#endif
// There might be prefetched layers that need to be accounted for.
// That might be us, so tell CanvasContext that this layer is in the
@@ -406,9 +296,9 @@ void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
bool willHaveFunctor = false;
if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) {
- willHaveFunctor = !mStagingDisplayList->getFunctors().empty();
+ willHaveFunctor = mStagingDisplayList->hasFunctor();
} else if (mDisplayList) {
- willHaveFunctor = !mDisplayList->getFunctors().empty();
+ willHaveFunctor = mDisplayList->hasFunctor();
}
bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
willHaveFunctor, functorsNeedLayer);
@@ -421,15 +311,15 @@ void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
if (info.mode == TreeInfo::MODE_FULL) {
pushStagingDisplayListChanges(info);
}
- prepareSubTree(info, childFunctorsNeedLayer, mDisplayList);
if (mDisplayList) {
- for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) {
- // If any vector drawable in the display list needs update, damage the node.
- if (vectorDrawable->isDirty()) {
- damageSelf(info);
- }
- vectorDrawable->setPropertyChangeWillBeConsumed(true);
+ info.out.hasFunctors |= mDisplayList->hasFunctor();
+ bool isDirty = mDisplayList->prepareListAndChildren(info, childFunctorsNeedLayer,
+ [](RenderNode* child, TreeInfo& info, bool functorsNeedLayer) {
+ child->prepareTreeImpl(info, functorsNeedLayer);
+ });
+ if (isDirty) {
+ damageSelf(info);
}
}
pushLayerUpdate(info);
@@ -453,9 +343,6 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
damageSelf(info);
info.damageAccumulator->popTransform();
syncProperties();
-#if !HWUI_NEW_OPS
- applyLayerPropertiesToLayer(info);
-#endif
// We could try to be clever and only re-damage if the matrix changed.
// However, we don't need to worry about that. The cost of over-damaging
// here is only going to be a single additional map rect of this node
@@ -466,35 +353,19 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
}
}
-#if !HWUI_NEW_OPS
-void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
- if (CC_LIKELY(!mLayer)) return;
-
- const LayerProperties& props = properties().layerProperties();
- mLayer->setAlpha(props.alpha(), props.xferMode());
- mLayer->setColorFilter(props.colorFilter());
- mLayer->setBlend(props.needsBlending());
-}
-#endif
-
void RenderNode::syncDisplayList(TreeInfo* info) {
// Make sure we inc first so that we don't fluctuate between 0 and 1,
// which would thrash the layer cache
if (mStagingDisplayList) {
- for (auto&& child : mStagingDisplayList->getChildren()) {
- child->renderNode->incParentRefCount();
- }
+ mStagingDisplayList->updateChildren([](RenderNode* child) {
+ child->incParentRefCount();
+ });
}
deleteDisplayList(info ? info->observer : nullptr, info);
mDisplayList = mStagingDisplayList;
mStagingDisplayList = nullptr;
if (mDisplayList) {
- for (auto& iter : mDisplayList->getFunctors()) {
- (*iter.functor)(DrawGlInfo::kModeSync, nullptr);
- }
- for (auto& vectorDrawable : mDisplayList->getVectorDrawables()) {
- vectorDrawable->syncProperties();
- }
+ mDisplayList->syncContents();
}
}
@@ -511,48 +382,24 @@ void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
void RenderNode::deleteDisplayList(TreeObserver* observer, TreeInfo* info) {
if (mDisplayList) {
- for (auto&& child : mDisplayList->getChildren()) {
- child->renderNode->decParentRefCount(observer, info);
+ mDisplayList->updateChildren([observer, info](RenderNode* child) {
+ child->decParentRefCount(observer, info);
+ });
+ if (!mDisplayList->reuseDisplayList(this, info ? &info->canvasContext : nullptr)) {
+ delete mDisplayList;
}
}
- delete mDisplayList;
mDisplayList = nullptr;
}
-void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree) {
- if (subtree) {
- TextureCache& cache = Caches::getInstance().textureCache;
- info.out.hasFunctors |= subtree->getFunctors().size();
- for (auto&& bitmapResource : subtree->getBitmapResources()) {
- void* ownerToken = &info.canvasContext;
- info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource);
- }
- for (auto&& op : subtree->getChildren()) {
- RenderNode* childNode = op->renderNode;
-#if HWUI_NEW_OPS
- info.damageAccumulator->pushTransform(&op->localMatrix);
- bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip;
-#else
- info.damageAccumulator->pushTransform(&op->localMatrix);
- bool childFunctorsNeedLayer = functorsNeedLayer
- // Recorded with non-rect clip, or canvas-rotated by parent
- || op->mRecordedWithPotentialStencilClip;
-#endif
- childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
- info.damageAccumulator->popTransform();
- }
- }
-}
-
void RenderNode::destroyHardwareResources(TreeObserver* observer, TreeInfo* info) {
- if (mLayer) {
- destroyLayer(mLayer);
- mLayer = nullptr;
+ if (hasLayer()) {
+ renderthread::CanvasContext::destroyLayer(this);
}
if (mDisplayList) {
- for (auto&& child : mDisplayList->getChildren()) {
- child->renderNode->destroyHardwareResources(observer, info);
- }
+ mDisplayList->updateChildren([observer, info](RenderNode* child) {
+ child->destroyHardwareResources(observer, info);
+ });
if (mNeedsDisplayListSync) {
// Next prepare tree we are going to push a new display list, so we can
// drop our current one now
@@ -579,84 +426,6 @@ void RenderNode::decParentRefCount(TreeObserver* observer, TreeInfo* info) {
}
}
-/*
- * For property operations, we pass a savecount of 0, since the operations aren't part of the
- * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
- * base saveCount (i.e., how RestoreToCount uses saveCount + properties().getCount())
- */
-#define PROPERTY_SAVECOUNT 0
-
-template <class T>
-void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
-#if DEBUG_DISPLAY_LIST
- properties().debugOutputProperties(handler.level() + 1);
-#endif
- if (properties().getLeft() != 0 || properties().getTop() != 0) {
- renderer.translate(properties().getLeft(), properties().getTop());
- }
- if (properties().getStaticMatrix()) {
- renderer.concatMatrix(*properties().getStaticMatrix());
- } else if (properties().getAnimationMatrix()) {
- renderer.concatMatrix(*properties().getAnimationMatrix());
- }
- if (properties().hasTransformMatrix()) {
- if (properties().isTransformTranslateOnly()) {
- renderer.translate(properties().getTranslationX(), properties().getTranslationY());
- } else {
- renderer.concatMatrix(*properties().getTransformMatrix());
- }
- }
- const bool isLayer = properties().effectiveLayerType() != LayerType::None;
- int clipFlags = properties().getClippingFlags();
- if (properties().getAlpha() < 1) {
- if (isLayer) {
- clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
- }
- if (CC_LIKELY(isLayer || !properties().getHasOverlappingRendering())) {
- // simply scale rendering content's alpha
- renderer.scaleAlpha(properties().getAlpha());
- } else {
- // savelayer needed to create an offscreen buffer
- Rect layerBounds(0, 0, getWidth(), getHeight());
- if (clipFlags) {
- properties().getClippingRectForFlags(clipFlags, &layerBounds);
- clipFlags = 0; // all clipping done by savelayer
- }
- SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
- layerBounds.left, layerBounds.top,
- layerBounds.right, layerBounds.bottom,
- (int) (properties().getAlpha() * 255),
- SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer);
- handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
- }
-
- if (CC_UNLIKELY(ATRACE_ENABLED() && properties().promotedToLayer())) {
- // pretend alpha always causes savelayer to warn about
- // performance problem affecting old versions
- ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", getName(),
- static_cast<int>(getWidth()),
- static_cast<int>(getHeight()));
- }
- }
- if (clipFlags) {
- Rect clipRect;
- properties().getClippingRectForFlags(clipFlags, &clipRect);
- ClipRectOp* op = new (handler.allocator()) ClipRectOp(
- clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
- SkRegion::kIntersect_Op);
- handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
- }
-
- // TODO: support nesting round rect clips
- if (mProperties.getRevealClip().willClip()) {
- Rect bounds;
- mProperties.getRevealClip().getBounds(&bounds);
- renderer.setClippingRoundRect(handler.allocator(), bounds, mProperties.getRevealClip().getRadius());
- } else if (mProperties.getOutline().willClip()) {
- renderer.setClippingOutline(handler.allocator(), &(mProperties.getOutline()));
- }
-}
-
/**
* Apply property-based transformations to input matrix
*
@@ -717,14 +486,14 @@ void RenderNode::computeOrdering() {
// transform properties are applied correctly to top level children
if (mDisplayList == nullptr) return;
for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
- renderNodeOp_t* childOp = mDisplayList->getChildren()[i];
+ RenderNodeOp* childOp = mDisplayList->getChildren()[i];
childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity());
}
}
void RenderNode::computeOrderingImpl(
- renderNodeOp_t* opState,
- std::vector<renderNodeOp_t*>* compositedChildrenOfProjectionSurface,
+ RenderNodeOp* opState,
+ std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface) {
mProjectedNodes.clear();
if (mDisplayList == nullptr || mDisplayList->isEmpty()) return;
@@ -748,10 +517,10 @@ void RenderNode::computeOrderingImpl(
const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0;
bool haveAppliedPropertiesToProjection = false;
for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
- renderNodeOp_t* childOp = mDisplayList->getChildren()[i];
+ RenderNodeOp* childOp = mDisplayList->getChildren()[i];
RenderNode* child = childOp->renderNode;
- std::vector<renderNodeOp_t*>* projectionChildren = nullptr;
+ std::vector<RenderNodeOp*>* projectionChildren = nullptr;
const mat4* projectionTransform = nullptr;
if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
// if receiving projections, collect projecting descendant
@@ -774,372 +543,5 @@ void RenderNode::computeOrderingImpl(
}
}
-class DeferOperationHandler {
-public:
- DeferOperationHandler(DeferStateStruct& deferStruct, int level)
- : mDeferStruct(deferStruct), mLevel(level) {}
- inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
- operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
- }
- inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
- inline void startMark(const char* name) {} // do nothing
- inline void endMark() {}
- inline int level() { return mLevel; }
- inline int replayFlags() { return mDeferStruct.mReplayFlags; }
- inline SkPath* allocPathForFrame() { return mDeferStruct.allocPathForFrame(); }
-
-private:
- DeferStateStruct& mDeferStruct;
- const int mLevel;
-};
-
-void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
- DeferOperationHandler handler(deferStruct, level);
- issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
-}
-
-class ReplayOperationHandler {
-public:
- ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
- : mReplayStruct(replayStruct), mLevel(level) {}
- inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
-#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
- mReplayStruct.mRenderer.eventMark(operation->name());
-#endif
- operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
- }
- inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
- inline void startMark(const char* name) {
- mReplayStruct.mRenderer.startMark(name);
- }
- inline void endMark() {
- mReplayStruct.mRenderer.endMark();
- }
- inline int level() { return mLevel; }
- inline int replayFlags() { return mReplayStruct.mReplayFlags; }
- inline SkPath* allocPathForFrame() { return mReplayStruct.allocPathForFrame(); }
-
-private:
- ReplayStateStruct& mReplayStruct;
- const int mLevel;
-};
-
-void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
- ReplayOperationHandler handler(replayStruct, level);
- issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
-}
-
-void RenderNode::buildZSortedChildList(const DisplayList::Chunk& chunk,
- std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
-#if !HWUI_NEW_OPS
- if (chunk.beginChildIndex == chunk.endChildIndex) return;
-
- for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
- DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
- RenderNode* child = childOp->renderNode;
- float childZ = child->properties().getZ();
-
- if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
- zTranslatedNodes.push_back(ZDrawRenderNodeOpPair(childZ, childOp));
- childOp->skipInOrderDraw = true;
- } else if (!child->properties().getProjectBackwards()) {
- // regular, in order drawing DisplayList
- childOp->skipInOrderDraw = false;
- }
- }
-
- // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
- std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
-#endif
-}
-
-template <class T>
-void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
- if (properties().getAlpha() <= 0.0f
- || properties().getOutline().getAlpha() <= 0.0f
- || !properties().getOutline().getPath()
- || properties().getScaleX() == 0
- || properties().getScaleY() == 0) {
- // no shadow to draw
- return;
- }
-
- mat4 shadowMatrixXY(transformFromParent);
- applyViewPropertyTransforms(shadowMatrixXY);
-
- // Z matrix needs actual 3d transformation, so mapped z values will be correct
- mat4 shadowMatrixZ(transformFromParent);
- applyViewPropertyTransforms(shadowMatrixZ, true);
-
- const SkPath* casterOutlinePath = properties().getOutline().getPath();
- const SkPath* revealClipPath = properties().getRevealClip().getPath();
- if (revealClipPath && revealClipPath->isEmpty()) return;
-
- float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha();
-
-
- // holds temporary SkPath to store the result of intersections
- SkPath* frameAllocatedPath = nullptr;
- const SkPath* outlinePath = casterOutlinePath;
-
- // intersect the outline with the reveal clip, if present
- if (revealClipPath) {
- frameAllocatedPath = handler.allocPathForFrame();
-
- Op(*outlinePath, *revealClipPath, kIntersect_SkPathOp, frameAllocatedPath);
- outlinePath = frameAllocatedPath;
- }
-
- // intersect the outline with the clipBounds, if present
- if (properties().getClippingFlags() & CLIP_TO_CLIP_BOUNDS) {
- if (!frameAllocatedPath) {
- frameAllocatedPath = handler.allocPathForFrame();
- }
-
- Rect clipBounds;
- properties().getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
- SkPath clipBoundsPath;
- clipBoundsPath.addRect(clipBounds.left, clipBounds.top,
- clipBounds.right, clipBounds.bottom);
-
- Op(*outlinePath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath);
- outlinePath = frameAllocatedPath;
- }
-
- DisplayListOp* shadowOp = new (handler.allocator()) DrawShadowOp(
- shadowMatrixXY, shadowMatrixZ, casterAlpha, outlinePath);
- handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
-}
-
-#define SHADOW_DELTA 0.1f
-
-template <class T>
-void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
- const Matrix4& initialTransform, const std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
- OpenGLRenderer& renderer, T& handler) {
- const int size = zTranslatedNodes.size();
- if (size == 0
- || (mode == ChildrenSelectMode::NegativeZChildren && zTranslatedNodes[0].key > 0.0f)
- || (mode == ChildrenSelectMode::PositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
- // no 3d children to draw
- return;
- }
-
- // Apply the base transform of the parent of the 3d children. This isolates
- // 3d children of the current chunk from transformations made in previous chunks.
- int rootRestoreTo = renderer.save(SaveFlags::Matrix);
- renderer.setGlobalMatrix(initialTransform);
-
- /**
- * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
- * with very similar Z heights to draw together.
- *
- * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
- * underneath both, and neither's shadow is drawn on top of the other.
- */
- const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
- size_t drawIndex, shadowIndex, endIndex;
- if (mode == ChildrenSelectMode::NegativeZChildren) {
- drawIndex = 0;
- endIndex = nonNegativeIndex;
- shadowIndex = endIndex; // draw no shadows
- } else {
- drawIndex = nonNegativeIndex;
- endIndex = size;
- shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
- }
-
- DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "",
- endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive");
-
- float lastCasterZ = 0.0f;
- while (shadowIndex < endIndex || drawIndex < endIndex) {
- if (shadowIndex < endIndex) {
- DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value;
- RenderNode* caster = casterOp->renderNode;
- const float casterZ = zTranslatedNodes[shadowIndex].key;
- // attempt to render the shadow if the caster about to be drawn is its caster,
- // OR if its caster's Z value is similar to the previous potential caster
- if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
- caster->issueDrawShadowOperation(casterOp->localMatrix, handler);
-
- lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
- shadowIndex++;
- continue;
- }
- }
-
- // only the actual child DL draw needs to be in save/restore,
- // since it modifies the renderer's matrix
- int restoreTo = renderer.save(SaveFlags::Matrix);
-
- DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
-
- renderer.concatMatrix(childOp->localMatrix);
- childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone
- handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
- childOp->skipInOrderDraw = true;
-
- renderer.restoreToCount(restoreTo);
- drawIndex++;
- }
- renderer.restoreToCount(rootRestoreTo);
-}
-
-template <class T>
-void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
- DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size());
- const SkPath* projectionReceiverOutline = properties().getOutline().getPath();
- int restoreTo = renderer.getSaveCount();
-
- LinearAllocator& alloc = handler.allocator();
- handler(new (alloc) SaveOp(SaveFlags::MatrixClip),
- PROPERTY_SAVECOUNT, properties().getClipToBounds());
-
- // Transform renderer to match background we're projecting onto
- // (by offsetting canvas by translationX/Y of background rendernode, since only those are set)
- const DisplayListOp* op =
-#if HWUI_NEW_OPS
- nullptr;
- LOG_ALWAYS_FATAL("unsupported");
-#else
- (mDisplayList->getOps()[mDisplayList->projectionReceiveIndex]);
-#endif
- const DrawRenderNodeOp* backgroundOp = reinterpret_cast<const DrawRenderNodeOp*>(op);
- const RenderProperties& backgroundProps = backgroundOp->renderNode->properties();
- renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
-
- // If the projection receiver has an outline, we mask projected content to it
- // (which we know, apriori, are all tessellated paths)
- renderer.setProjectionPathMask(alloc, projectionReceiverOutline);
-
- // draw projected nodes
- for (size_t i = 0; i < mProjectedNodes.size(); i++) {
- renderNodeOp_t* childOp = mProjectedNodes[i];
-
- // matrix save, concat, and restore can be done safely without allocating operations
- int restoreTo = renderer.save(SaveFlags::Matrix);
- renderer.concatMatrix(childOp->transformFromCompositingAncestor);
- childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone
- handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds());
- childOp->skipInOrderDraw = true;
- renderer.restoreToCount(restoreTo);
- }
-
- handler(new (alloc) RestoreToCountOp(restoreTo),
- PROPERTY_SAVECOUNT, properties().getClipToBounds());
-}
-
-/**
- * This function serves both defer and replay modes, and will organize the displayList's component
- * operations for a single frame:
- *
- * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
- * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
- * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
- * defer vs replay logic, per operation
- */
-template <class T>
-void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
- if (mDisplayList->isEmpty()) {
- DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", handler.level() * 2, "",
- this, getName());
- return;
- }
-
-#if HWUI_NEW_OPS
- const bool drawLayer = false;
-#else
- const bool drawLayer = (mLayer && (&renderer != mLayer->renderer.get()));
-#endif
- // If we are updating the contents of mLayer, we don't want to apply any of
- // the RenderNode's properties to this issueOperations pass. Those will all
- // be applied when the layer is drawn, aka when this is true.
- const bool useViewProperties = (!mLayer || drawLayer);
- if (useViewProperties) {
- const Outline& outline = properties().getOutline();
- if (properties().getAlpha() <= 0
- || (outline.getShouldClip() && outline.isEmpty())
- || properties().getScaleX() == 0
- || properties().getScaleY() == 0) {
- DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "",
- this, getName());
- return;
- }
- }
-
- handler.startMark(getName());
-
-#if DEBUG_DISPLAY_LIST
- const Rect& clipRect = renderer.getLocalClipBounds();
- DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f",
- handler.level() * 2, "", this, getName(),
- clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
-#endif
-
- LinearAllocator& alloc = handler.allocator();
- int restoreTo = renderer.getSaveCount();
- handler(new (alloc) SaveOp(SaveFlags::MatrixClip),
- PROPERTY_SAVECOUNT, properties().getClipToBounds());
-
- DISPLAY_LIST_LOGD("%*sSave %d %d", (handler.level() + 1) * 2, "",
- SaveFlags::MatrixClip, restoreTo);
-
- if (useViewProperties) {
- setViewProperties<T>(renderer, handler);
- }
-
-#if HWUI_NEW_OPS
- LOG_ALWAYS_FATAL("legacy op traversal not supported");
-#else
- bool quickRejected = properties().getClipToBounds()
- && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
- if (!quickRejected) {
- Matrix4 initialTransform(*(renderer.currentTransform()));
- renderer.setBaseTransform(initialTransform);
-
- if (drawLayer) {
- handler(new (alloc) DrawLayerOp(mLayer),
- renderer.getSaveCount() - 1, properties().getClipToBounds());
- } else {
- const int saveCountOffset = renderer.getSaveCount() - 1;
- const int projectionReceiveIndex = mDisplayList->projectionReceiveIndex;
- for (size_t chunkIndex = 0; chunkIndex < mDisplayList->getChunks().size(); chunkIndex++) {
- const DisplayList::Chunk& chunk = mDisplayList->getChunks()[chunkIndex];
-
- std::vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
- buildZSortedChildList(chunk, zTranslatedNodes);
-
- issueOperationsOf3dChildren(ChildrenSelectMode::NegativeZChildren,
- initialTransform, zTranslatedNodes, renderer, handler);
-
- for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
- DisplayListOp *op = mDisplayList->getOps()[opIndex];
-#if DEBUG_DISPLAY_LIST
- op->output(handler.level() + 1);
-#endif
- handler(op, saveCountOffset, properties().getClipToBounds());
-
- if (CC_UNLIKELY(!mProjectedNodes.empty() && projectionReceiveIndex >= 0 &&
- opIndex == static_cast<size_t>(projectionReceiveIndex))) {
- issueOperationsOfProjectedChildren(renderer, handler);
- }
- }
-
- issueOperationsOf3dChildren(ChildrenSelectMode::PositiveZChildren,
- initialTransform, zTranslatedNodes, renderer, handler);
- }
- }
- }
-#endif
-
- DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (handler.level() + 1) * 2, "", restoreTo);
- handler(new (alloc) RestoreToCountOp(restoreTo),
- PROPERTY_SAVECOUNT, properties().getClipToBounds());
-
- DISPLAY_LIST_LOGD("%*sDone (%p, %s)", handler.level() * 2, "", this, getName());
- handler.endMark();
-}
-
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index f9735a231d7a..b8964f0f16a0 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef RENDERNODE_H
-#define RENDERNODE_H
+
+#pragma once
#include <SkCamera.h>
#include <SkMatrix.h>
@@ -32,6 +32,8 @@
#include "DisplayList.h"
#include "Matrix.h"
#include "RenderProperties.h"
+#include "pipeline/skia/SkiaDisplayList.h"
+#include "pipeline/skia/SkiaLayer.h"
#include <vector>
@@ -39,34 +41,19 @@ class SkBitmap;
class SkPaint;
class SkPath;
class SkRegion;
+class SkSurface;
namespace android {
namespace uirenderer {
class CanvasState;
-class DisplayListCanvas;
class DisplayListOp;
-class OpenGLRenderer;
-class Rect;
-class SkiaShader;
-
-#if HWUI_NEW_OPS
class FrameBuilder;
class OffscreenBuffer;
+class Rect;
+class SkiaShader;
struct RenderNodeOp;
-typedef OffscreenBuffer layer_t;
-typedef RenderNodeOp renderNodeOp_t;
-#else
-class Layer;
-typedef Layer layer_t;
-typedef DrawRenderNodeOp renderNodeOp_t;
-#endif
-
-class ClipRectOp;
-class DrawRenderNodeOp;
-class SaveLayerOp;
-class SaveOp;
-class RestoreToCountOp;
+
class TreeInfo;
class TreeObserver;
@@ -78,9 +65,8 @@ class RenderNode;
* Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
*
* Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
- * functionality is split between DisplayListCanvas (which manages the recording), DisplayList
- * (which holds the actual data), and DisplayList (which holds properties and performs playback onto
- * a renderer).
+ * functionality is split between RecordingCanvas (which manages the recording), DisplayList
+ * (which holds the actual data), and RenderNode (which holds properties used for render playback).
*
* Note that DisplayList is swapped out from beneath an individual RenderNode when a view's
* recorded stream of canvas operations is refreshed. The RenderNode (and its properties) stay
@@ -115,20 +101,11 @@ public:
kReplayFlag_ClipChildren = 0x1
};
- void debugDumpLayers(const char* prefix);
-
ANDROID_API void setStagingDisplayList(DisplayList* newData, TreeObserver* observer);
void computeOrdering();
- void defer(DeferStateStruct& deferStruct, const int level);
- void replay(ReplayStateStruct& replayStruct, const int level);
-
-#if HWUI_NEW_OPS
- ANDROID_API void output(uint32_t level = 0, const char* label = "Root");
-#else
- ANDROID_API void output(uint32_t level = 1);
-#endif
+ ANDROID_API void output();
ANDROID_API int getDebugSize();
void copyTo(proto::RenderNode* node);
@@ -223,10 +200,9 @@ public:
const DisplayList* getDisplayList() const {
return mDisplayList;
}
-#if HWUI_NEW_OPS
OffscreenBuffer* getLayer() const { return mLayer; }
OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh...
-#endif
+ void setLayer(OffscreenBuffer* layer) { mLayer = layer; }
// Note: The position callbacks are relying on the listener using
// the frameNumber to appropriately batch/synchronize these transactions.
@@ -257,73 +233,16 @@ public:
}
private:
- typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
-
- static size_t findNonNegativeIndex(const std::vector<ZDrawRenderNodeOpPair>& nodes) {
- for (size_t i = 0; i < nodes.size(); i++) {
- if (nodes[i].key >= 0.0f) return i;
- }
- return nodes.size();
- }
-
- enum class ChildrenSelectMode {
- NegativeZChildren,
- PositiveZChildren
- };
-
- void computeOrderingImpl(renderNodeOp_t* opState,
- std::vector<renderNodeOp_t*>* compositedChildrenOfProjectionSurface,
+ void computeOrderingImpl(RenderNodeOp* opState,
+ std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface);
- template <class T>
- inline void setViewProperties(OpenGLRenderer& renderer, T& handler);
-
- void buildZSortedChildList(const DisplayList::Chunk& chunk,
- std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes);
-
- template<class T>
- inline void issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler);
-
- template <class T>
- inline void issueOperationsOf3dChildren(ChildrenSelectMode mode,
- const Matrix4& initialTransform, const std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
- OpenGLRenderer& renderer, T& handler);
-
- template <class T>
- inline void issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler);
-
- /**
- * Issue the RenderNode's operations into a handler, recursing for subtrees through
- * DrawRenderNodeOp's defer() or replay() methods
- */
- template <class T>
- inline void issueOperations(OpenGLRenderer& renderer, T& handler);
-
- class TextContainer {
- public:
- size_t length() const {
- return mByteLength;
- }
-
- const char* text() const {
- return (const char*) mText;
- }
-
- size_t mByteLength;
- const char* mText;
- };
-
-
void syncProperties();
void syncDisplayList(TreeInfo* info);
void prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer);
void pushStagingPropertiesChanges(TreeInfo& info);
void pushStagingDisplayListChanges(TreeInfo& info);
- void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree);
-#if !HWUI_NEW_OPS
- void applyLayerPropertiesToLayer(TreeInfo& info);
-#endif
void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
void pushLayerUpdate(TreeInfo& info);
void deleteDisplayList(TreeObserver* observer, TreeInfo* info = nullptr);
@@ -331,6 +250,7 @@ private:
void incParentRefCount() { mParentCount++; }
void decParentRefCount(TreeObserver* observer, TreeInfo* info = nullptr);
+ void output(std::ostream& output, uint32_t level);
String8 mName;
sp<VirtualLightRefBase> mUserContext;
@@ -349,14 +269,14 @@ private:
// Owned by RT. Lifecycle is managed by prepareTree(), with the exception
// being in ~RenderNode() which may happen on any thread.
- layer_t* mLayer = nullptr;
+ OffscreenBuffer* mLayer = nullptr;
/**
* Draw time state - these properties are only set and used during rendering
*/
// for projection surfaces, contains a list of all children items
- std::vector<renderNodeOp_t*> mProjectedNodes;
+ std::vector<RenderNodeOp*> mProjectedNodes;
// How many references our parent(s) have to us. Typically this should alternate
// between 2 and 1 (when a staging push happens we inc first then dec)
@@ -367,9 +287,79 @@ private:
uint32_t mParentCount;
sp<PositionListener> mPositionListener;
+
+// METHODS & FIELDS ONLY USED BY THE SKIA RENDERER
+public:
+ /**
+ * Detach and transfer ownership of an already allocated displayList for use
+ * in recording updated content for this renderNode
+ */
+ std::unique_ptr<skiapipeline::SkiaDisplayList> detachAvailableList() {
+ return std::move(mAvailableDisplayList);
+ }
+
+ /**
+ * Attach unused displayList to this node for potential future reuse.
+ */
+ void attachAvailableList(skiapipeline::SkiaDisplayList* skiaDisplayList) {
+ mAvailableDisplayList.reset(skiaDisplayList);
+ }
+
+ /**
+ * Returns true if an offscreen layer from any renderPipeline is attached
+ * to this node.
+ */
+ bool hasLayer() const { return mLayer || mSkiaLayer.get(); }
+
+ /**
+ * Used by the RenderPipeline to attach an offscreen surface to the RenderNode.
+ * The surface is then will be used to store the contents of a layer.
+ */
+ void setLayerSurface(sk_sp<SkSurface> layer) {
+ if (layer.get()) {
+ if (!mSkiaLayer.get()) {
+ mSkiaLayer = std::make_unique<skiapipeline::SkiaLayer>();
+ }
+ mSkiaLayer->layerSurface = std::move(layer);
+ mSkiaLayer->inverseTransformInWindow.loadIdentity();
+ } else {
+ mSkiaLayer.reset();
+ }
+ }
+
+ /**
+ * If the RenderNode is of type LayerType::RenderLayer then this method will
+ * return the an offscreen rendering surface that is used to both render into
+ * the layer and composite the layer into its parent. If the type is not
+ * LayerType::RenderLayer then it will return a nullptr.
+ *
+ * NOTE: this function is only guaranteed to return accurate results after
+ * prepareTree has been run for this RenderNode
+ */
+ SkSurface* getLayerSurface() const {
+ return mSkiaLayer.get() ? mSkiaLayer->layerSurface.get() : nullptr;
+ }
+
+ skiapipeline::SkiaLayer* getSkiaLayer() const {
+ return mSkiaLayer.get();
+ }
+
+private:
+ /**
+ * If this RenderNode has been used in a previous frame then the SkiaDisplayList
+ * from that frame is cached here until one of the following conditions is met:
+ * 1) The RenderNode is deleted (causing this to be deleted)
+ * 2) It is replaced with the displayList from the next completed frame
+ * 3) It is detached and used to to record a new displayList for a later frame
+ */
+ std::unique_ptr<skiapipeline::SkiaDisplayList> mAvailableDisplayList;
+
+ /**
+ * An offscreen rendering target used to contain the contents this RenderNode
+ * when it has been set to draw as a LayerType::RenderLayer.
+ */
+ std::unique_ptr<skiapipeline::SkiaLayer> mSkiaLayer;
}; // class RenderNode
} /* namespace uirenderer */
} /* namespace android */
-
-#endif /* RENDERNODE_H */
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 5ebf5458da18..146fbe73a48a 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -24,7 +24,6 @@
#include <SkPathOps.h>
#include "Matrix.h"
-#include "OpenGLRenderer.h"
#include "hwui/Canvas.h"
#include "utils/MathUtils.h"
@@ -53,7 +52,7 @@ bool LayerProperties::setColorFilter(SkColorFilter* filter) {
bool LayerProperties::setFromPaint(const SkPaint* paint) {
bool changed = false;
changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
- changed |= setXferMode(PaintUtils::getXfermodeDirect(paint));
+ changed |= setXferMode(PaintUtils::getBlendModeDirect(paint));
changed |= setColorFilter(paint ? paint->getColorFilter() : nullptr);
return changed;
}
@@ -100,26 +99,34 @@ RenderProperties& RenderProperties::operator=(const RenderProperties& other) {
return *this;
}
-void RenderProperties::debugOutputProperties(const int level) const {
- if (mPrimitiveFields.mLeft != 0 || mPrimitiveFields.mTop != 0) {
- ALOGD("%*s(Translate (left, top) %d, %d)", level * 2, "",
- mPrimitiveFields.mLeft, mPrimitiveFields.mTop);
- }
- if (mStaticMatrix) {
- ALOGD("%*s(ConcatMatrix (static) %p: " SK_MATRIX_STRING ")",
- level * 2, "", mStaticMatrix, SK_MATRIX_ARGS(mStaticMatrix));
+static void dumpMatrix(std::ostream& output, std::string& indent,
+ const char* label, SkMatrix* matrix) {
+ if (matrix) {
+ output << indent << "(" << label << " " << matrix << ": ";
+ output << std::fixed << std::setprecision(2);
+ output << "[" << matrix->get(0) << " "<< matrix->get(1) << " " << matrix->get(2) << "]";
+ output << " [" << matrix->get(3) << " "<< matrix->get(4) << " " << matrix->get(5) << "]";
+ output << " [" << matrix->get(6) << " "<< matrix->get(7) << " " << matrix->get(8) << "]";
+ output << ")" << std::endl;
}
- if (mAnimationMatrix) {
- ALOGD("%*s(ConcatMatrix (animation) %p: " SK_MATRIX_STRING ")",
- level * 2, "", mAnimationMatrix, SK_MATRIX_ARGS(mAnimationMatrix));
+}
+
+void RenderProperties::debugOutputProperties(std::ostream& output, const int level) const {
+ auto indent = std::string(level * 2, ' ');
+ if (mPrimitiveFields.mLeft != 0 || mPrimitiveFields.mTop != 0) {
+ output << indent << "(Translate (left, top) " << mPrimitiveFields.mLeft
+ << ", " << mPrimitiveFields.mTop << ")" << std::endl;
}
+ dumpMatrix(output, indent, "ConcatMatrix (static)", mStaticMatrix);
+ dumpMatrix(output, indent, "ConcatMatrix (animation)", mAnimationMatrix);
+
+ output << std::fixed << std::setprecision(2);
if (hasTransformMatrix()) {
if (isTransformTranslateOnly()) {
- ALOGD("%*s(Translate %.2f, %.2f, %.2f)",
- level * 2, "", getTranslationX(), getTranslationY(), getZ());
+ output << indent << "(Translate " << getTranslationX() << ", " << getTranslationY()
+ << ", " << getZ() << ")" << std::endl;
} else {
- ALOGD("%*s(ConcatMatrix %p: " SK_MATRIX_STRING ")",
- level * 2, "", mComputedFields.mTransformMatrix, SK_MATRIX_ARGS(mComputedFields.mTransformMatrix));
+ dumpMatrix(output, indent, "ConcatMatrix ", mComputedFields.mTransformMatrix);
}
}
@@ -133,7 +140,7 @@ void RenderProperties::debugOutputProperties(const int level) const {
if (CC_LIKELY(isLayer || !getHasOverlappingRendering())) {
// simply scale rendering content's alpha
- ALOGD("%*s(ScaleAlpha %.2f)", level * 2, "", mPrimitiveFields.mAlpha);
+ output << indent << "(ScaleAlpha " << mPrimitiveFields.mAlpha << ")" << std::endl;
} else {
// savelayeralpha to create an offscreen buffer to apply alpha
Rect layerBounds(0, 0, getWidth(), getHeight());
@@ -141,35 +148,40 @@ void RenderProperties::debugOutputProperties(const int level) const {
getClippingRectForFlags(clipFlags, &layerBounds);
clipFlags = 0; // all clipping done by savelayer
}
- ALOGD("%*s(SaveLayerAlpha %d, %d, %d, %d, %d, 0x%x)", level * 2, "",
- (int)layerBounds.left, (int)layerBounds.top,
- (int)layerBounds.right, (int)layerBounds.bottom,
- (int)(mPrimitiveFields.mAlpha * 255),
- SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer);
+ output << indent << "(SaveLayerAlpha "
+ << (int)layerBounds.left << ", " << (int)layerBounds.top << ", "
+ << (int)layerBounds.right << ", " << (int)layerBounds.bottom << ", "
+ << (int)(mPrimitiveFields.mAlpha * 255) << ", 0x" << std::hex
+ << (SaveFlags::HasAlphaLayer | SaveFlags::ClipToLayer) << ")" << std::dec
+ << std::endl;
}
}
if (clipFlags) {
Rect clipRect;
getClippingRectForFlags(clipFlags, &clipRect);
- ALOGD("%*s(ClipRect %d, %d, %d, %d)", level * 2, "",
- (int)clipRect.left, (int)clipRect.top, (int)clipRect.right, (int)clipRect.bottom);
+ output << indent << "(ClipRect "
+ << (int)clipRect.left << ", " << (int)clipRect.top << ", "
+ << (int)clipRect.right << ", " << (int)clipRect.bottom << ")" << std::endl;
}
if (getRevealClip().willClip()) {
Rect bounds;
getRevealClip().getBounds(&bounds);
- ALOGD("%*s(Clip to reveal clip with bounds %.2f %.2f %.2f %.2f)", level * 2, "",
- RECT_ARGS(bounds));
+ output << indent << "(Clip to reveal clip with bounds "
+ << bounds.left << ", " << bounds.top << ", "
+ << bounds.right << ", " << bounds.bottom << ")" << std::endl;
}
auto& outline = mPrimitiveFields.mOutline;
if (outline.getShouldClip()) {
if (outline.isEmpty()) {
- ALOGD("%*s(Clip to empty outline)", level * 2, "");
+ output << indent << "(Clip to empty outline)";
} else if (outline.willClip()) {
- ALOGD("%*s(Clip to outline with bounds %.2f %.2f %.2f %.2f)", level * 2, "",
- RECT_ARGS(outline.getBounds()));
+ const Rect& bounds = outline.getBounds();
+ output << indent << "(Clip to outline with bounds "
+ << bounds.left << ", " << bounds.top << ", "
+ << bounds.right << ", " << bounds.bottom << ")" << std::endl;
}
}
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 6a6e8dbb3fcd..9ee2f9c69343 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef RENDERNODEPROPERTIES_H
-#define RENDERNODEPROPERTIES_H
+
+#pragma once
#include "Caches.h"
#include "DeviceInfo.h"
@@ -22,11 +22,12 @@
#include "RevealClip.h"
#include "Outline.h"
#include "utils/MathUtils.h"
+#include "utils/PaintUtils.h"
+#include <SkBlendMode.h>
#include <SkCamera.h>
#include <SkMatrix.h>
#include <SkRegion.h>
-#include <SkXfermode.h>
#include <algorithm>
#include <stddef.h>
@@ -34,6 +35,7 @@
#include <cutils/compiler.h>
#include <androidfw/ResourceTypes.h>
#include <utils/Log.h>
+#include <ostream>
class SkBitmap;
class SkColorFilter;
@@ -91,11 +93,11 @@ public:
return mAlpha;
}
- bool setXferMode(SkXfermode::Mode mode) {
+ bool setXferMode(SkBlendMode mode) {
return RP_SET(mMode, mode);
}
- SkXfermode::Mode xferMode() const {
+ SkBlendMode xferMode() const {
return mMode;
}
@@ -131,7 +133,7 @@ private:
// Whether or not that Layer's content is opaque, doesn't include alpha
bool mOpaque;
uint8_t mAlpha;
- SkXfermode::Mode mMode;
+ SkBlendMode mMode;
SkColorFilter* mColorFilter = nullptr;
};
@@ -573,7 +575,7 @@ public:
return mPrimitiveFields.mProjectBackwards;
}
- void debugOutputProperties(const int level) const;
+ void debugOutputProperties(std::ostream& output, const int level) const;
void updateMatrix();
@@ -678,5 +680,3 @@ private:
} /* namespace uirenderer */
} /* namespace android */
-
-#endif /* RENDERNODEPROPERTIES_H */
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index 5f4c9c53a9d8..2eaf187a1af1 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -80,8 +80,6 @@ public:
static Vector2 centroid2d(const Vector2* poly, int polyLength);
- static bool isClockwise(const Vector2* polygon, int len);
-
static Vector2 calculateNormal(const Vector2& p1, const Vector2& p2);
static int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2,
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 3b6fae08773d..89e2a01a5ebe 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -14,181 +14,31 @@
* limitations under the License.
*/
+#include "SkiaCanvas.h"
+
#include "CanvasProperty.h"
-#include "Layer.h"
-#include "RenderNode.h"
-#include "hwui/Canvas.h"
+#include "NinePatchUtils.h"
+#include "VectorDrawable.h"
+#include "hwui/Bitmap.h"
+#include "hwui/MinikinUtils.h"
+#include "pipeline/skia/AnimatedDrawables.h"
-#include <SkCanvas.h>
-#include <SkClipStack.h>
#include <SkDrawable.h>
-#include <SkDevice.h>
#include <SkDeque.h>
#include <SkDrawFilter.h>
#include <SkGraphics.h>
#include <SkImage.h>
+#include <SkImagePriv.h>
+#include <SkRSXform.h>
#include <SkShader.h>
-#include <SkTArray.h>
-#include <SkTLazy.h>
#include <SkTemplates.h>
-
-#include "VectorDrawable.h"
+#include <SkTextBlob.h>
#include <memory>
namespace android {
-// Holds an SkCanvas reference plus additional native data.
-class SkiaCanvas : public Canvas {
-public:
- explicit SkiaCanvas(const SkBitmap& bitmap);
-
- /**
- * Create a new SkiaCanvas.
- *
- * @param canvas SkCanvas to handle calls made to this SkiaCanvas. Must
- * not be NULL. This constructor will ref() the SkCanvas, and unref()
- * it in its destructor.
- */
- explicit SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {
- SkASSERT(canvas);
- canvas->ref();
- }
-
- virtual SkCanvas* asSkCanvas() override {
- return mCanvas.get();
- }
-
- virtual void resetRecording(int width, int height) override {
- LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas");
- }
-
- virtual uirenderer::DisplayList* finishRecording() override {
- LOG_ALWAYS_FATAL("SkiaCanvas does not produce a DisplayList");
- return nullptr;
- }
- virtual void insertReorderBarrier(bool enableReorder) override {
- LOG_ALWAYS_FATAL("SkiaCanvas does not support reordering barriers");
- }
-
- virtual void setBitmap(const SkBitmap& bitmap) override;
-
- virtual bool isOpaque() override;
- virtual int width() override;
- virtual int height() override;
-
- virtual void setHighContrastText(bool highContrastText) override {
- mHighContrastText = highContrastText;
- }
- virtual bool isHighContrastText() override { return mHighContrastText; }
-
- virtual int getSaveCount() const override;
- virtual int save(SaveFlags::Flags flags) override;
- virtual void restore() override;
- virtual void restoreToCount(int saveCount) override;
-
- virtual int saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, SaveFlags::Flags flags) override;
- virtual int saveLayerAlpha(float left, float top, float right, float bottom,
- int alpha, SaveFlags::Flags flags) override;
-
- virtual void getMatrix(SkMatrix* outMatrix) const override;
- virtual void setMatrix(const SkMatrix& matrix) override;
- virtual void concat(const SkMatrix& matrix) override;
- virtual void rotate(float degrees) override;
- virtual void scale(float sx, float sy) override;
- virtual void skew(float sx, float sy) override;
- virtual void translate(float dx, float dy) override;
-
- virtual bool getClipBounds(SkRect* outRect) const override;
- virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
- virtual bool quickRejectPath(const SkPath& path) const override;
- virtual bool clipRect(float left, float top, float right, float bottom,
- SkRegion::Op op) override;
- virtual bool clipPath(const SkPath* path, SkRegion::Op op) override;
- virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override;
-
- virtual SkDrawFilter* getDrawFilter() override;
- virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
-
- virtual void drawColor(int color, SkXfermode::Mode mode) override;
- virtual void drawPaint(const SkPaint& paint) override;
-
- virtual void drawPoint(float x, float y, const SkPaint& paint) override;
- virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
- virtual void drawLine(float startX, float startY, float stopX, float stopY,
- const SkPaint& paint) override;
- virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
- virtual void drawRect(float left, float top, float right, float bottom,
- const SkPaint& paint) override;
- virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
- virtual void drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, const SkPaint& paint) override;
- virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
- virtual void drawOval(float left, float top, float right, float bottom,
- const SkPaint& paint) override;
- virtual void drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) override;
- virtual void drawPath(const SkPath& path, const SkPaint& paint) override;
- virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
- const float* verts, const float* tex, const int* colors,
- const uint16_t* indices, int indexCount, const SkPaint& paint) override;
-
- virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
- const SkPaint* paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
- const SkPaint* paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
- float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, const SkPaint* paint) override;
- virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors, const SkPaint* paint) override;
- virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
- float dstLeft, float dstTop, float dstRight, float dstBottom,
- const SkPaint* paint) override;
-
- virtual bool drawTextAbsolutePos() const override { return true; }
- virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
-
- virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
- uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
- uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
- uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) override;
- virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x,
- uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
- uirenderer::CanvasPropertyPaint* paint) override;
-
- virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
- virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
- virtual void callDrawGLFunction(Functor* functor,
- uirenderer::GlFunctorLifecycleListener* listener) override;
-
-protected:
- virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
- const SkPaint& paint, float x, float y,
- float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
- float totalAdvance) override;
- virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) override;
-
-private:
- struct SaveRec {
- int saveCount;
- SaveFlags::Flags saveFlags;
- };
-
- bool mHighContrastText = false;
-
- void recordPartialSave(SaveFlags::Flags flags);
- void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount);
- void applyClips(const SkTArray<SkClipStack::Element>& clips);
-
- void drawPoints(const float* points, int count, const SkPaint& paint,
- SkCanvas::PointMode mode);
-
- SkAutoTUnref<SkCanvas> mCanvas;
- std::unique_ptr<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
-};
+using uirenderer::PaintUtils;
Canvas* Canvas::create_canvas(const SkBitmap& bitmap) {
return new SkiaCanvas(bitmap);
@@ -198,8 +48,25 @@ Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) {
return new SkiaCanvas(skiaCanvas);
}
+SkiaCanvas::SkiaCanvas() {}
+
+SkiaCanvas::SkiaCanvas(SkCanvas* canvas)
+ : mCanvas(canvas) {}
+
SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
- mCanvas.reset(new SkCanvas(bitmap));
+ mCanvasOwned = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap));
+ mCanvas = mCanvasOwned.get();
+}
+
+SkiaCanvas::~SkiaCanvas() {}
+
+void SkiaCanvas::reset(SkCanvas* skiaCanvas) {
+ if (mCanvas != skiaCanvas) {
+ mCanvas = skiaCanvas;
+ mCanvasOwned.reset();
+ }
+ mSaveStack.reset(nullptr);
+ mHighContrastText = false;
}
// ----------------------------------------------------------------------------
@@ -210,13 +77,13 @@ class ClipCopier : public SkCanvas::ClipVisitor {
public:
explicit ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
- virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
+ virtual void clipRect(const SkRect& rect, SkClipOp op, bool antialias) {
m_dstCanvas->clipRect(rect, op, antialias);
}
- virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) {
+ virtual void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) {
m_dstCanvas->clipRRect(rrect, op, antialias);
}
- virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
+ virtual void clipPath(const SkPath& path, SkClipOp op, bool antialias) {
m_dstCanvas->clipPath(path, op, antialias);
}
@@ -235,11 +102,12 @@ void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
mCanvas->replayClips(&copier);
}
- // unrefs the existing canvas
- mCanvas.reset(newCanvas);
+ // deletes the previously owned canvas (if any)
+ mCanvasOwned = std::unique_ptr<SkCanvas>(newCanvas);
+ mCanvas = newCanvas;
// clean up the old save stack
- mSaveStack.reset(NULL);
+ mSaveStack.reset(nullptr);
}
// ----------------------------------------------------------------------------
@@ -277,13 +145,8 @@ int SkiaCanvas::save(SaveFlags::Flags flags) {
// operation. It does this by explicitly saving off the clip & matrix state
// when requested and playing it back after the SkCanvas::restore.
void SkiaCanvas::restore() {
- const SaveRec* rec = (NULL == mSaveStack.get())
- ? NULL
- : static_cast<SaveRec*>(mSaveStack->back());
- int currentSaveCount = mCanvas->getSaveCount();
- SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
-
- if (NULL == rec || rec->saveCount != currentSaveCount) {
+ const auto* rec = this->currentSaveRec();
+ if (!rec) {
// Fast path - no record for this frame.
mCanvas->restore();
return;
@@ -297,27 +160,18 @@ void SkiaCanvas::restore() {
savedMatrix = mCanvas->getTotalMatrix();
}
- SkTArray<SkClipStack::Element> savedClips;
- int topClipStackFrame = mCanvas->getClipStack()->getSaveCount();
- if (preserveClip) {
- saveClipsForFrame(savedClips, topClipStackFrame);
- }
+ const size_t clipIndex = rec->clipIndex;
mCanvas->restore();
+ mSaveStack->pop_back();
if (preserveMatrix) {
mCanvas->setMatrix(savedMatrix);
}
- if (preserveClip && !savedClips.empty() &&
- topClipStackFrame != mCanvas->getClipStack()->getSaveCount()) {
- // Only reapply the saved clips if the top clip stack frame was actually
- // popped by restore(). If it wasn't, it means it doesn't belong to the
- // restored canvas frame (SkCanvas lazy save/restore kicked in).
- applyClips(savedClips);
+ if (preserveClip) {
+ this->applyPersistentClips(clipIndex);
}
-
- mSaveStack->pop_back();
}
void SkiaCanvas::restoreToCount(int restoreCount) {
@@ -344,22 +198,71 @@ static inline SkCanvas::SaveLayerFlags layerFlags(SaveFlags::Flags flags) {
int SkiaCanvas::saveLayer(float left, float top, float right, float bottom,
const SkPaint* paint, SaveFlags::Flags flags) {
const SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
- const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags));
+ //always save matrix and clip to match the behaviour of Skia and HWUI pipelines and to ensure
+ //android state tracking behavior matches that of the Skia API (partial save is not supported)
+ const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags | SaveFlags::MatrixClip));
- int count = mCanvas->saveLayer(rec);
- recordPartialSave(flags);
- return count;
+ return mCanvas->saveLayer(rec);
}
int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom,
int alpha, SaveFlags::Flags flags) {
- SkTLazy<SkPaint> alphaPaint;
if (static_cast<unsigned>(alpha) < 0xFF) {
- alphaPaint.init()->setAlpha(alpha);
+ SkPaint alphaPaint;
+ alphaPaint.setAlpha(alpha);
+ return this->saveLayer(left, top, right, bottom, &alphaPaint, flags);
}
+ return this->saveLayer(left, top, right, bottom, nullptr, flags);
+}
- return this->saveLayer(left, top, right, bottom, alphaPaint.getMaybeNull(),
- flags);
+class SkiaCanvas::Clip {
+public:
+ Clip(const SkRect& rect, SkClipOp op, const SkMatrix& m)
+ : mType(Type::Rect), mOp(op), mMatrix(m), mRRect(SkRRect::MakeRect(rect)) {}
+ Clip(const SkRRect& rrect, SkClipOp op, const SkMatrix& m)
+ : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {}
+ Clip(const SkPath& path, SkClipOp op, const SkMatrix& m)
+ : mType(Type::Path), mOp(op), mMatrix(m), mPath(&path) {}
+
+ void apply(SkCanvas* canvas) const {
+ canvas->setMatrix(mMatrix);
+ switch (mType) {
+ case Type::Rect:
+ canvas->clipRect(mRRect.rect(), mOp);
+ break;
+ case Type::RRect:
+ canvas->clipRRect(mRRect, mOp);
+ break;
+ case Type::Path:
+ canvas->clipPath(*mPath.get(), mOp);
+ break;
+ }
+ }
+
+private:
+ enum class Type {
+ Rect,
+ RRect,
+ Path,
+ };
+
+ Type mType;
+ SkClipOp mOp;
+ SkMatrix mMatrix;
+
+ // These are logically a union (tracked separately due to non-POD path).
+ SkTLazy<SkPath> mPath;
+ SkRRect mRRect;
+};
+
+const SkiaCanvas::SaveRec* SkiaCanvas::currentSaveRec() const {
+ const SaveRec* rec = mSaveStack
+ ? static_cast<const SaveRec*>(mSaveStack->back())
+ : nullptr;
+ int currentSaveCount = mCanvas->getSaveCount();
+ SkASSERT(!rec || currentSaveCount >= rec->saveCount);
+
+ return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr;
}
// ----------------------------------------------------------------------------
@@ -378,45 +281,48 @@ void SkiaCanvas::recordPartialSave(SaveFlags::Flags flags) {
return;
}
- if (NULL == mSaveStack.get()) {
+ if (!mSaveStack) {
mSaveStack.reset(new SkDeque(sizeof(struct SaveRec), 8));
}
SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
rec->saveCount = mCanvas->getSaveCount();
rec->saveFlags = flags;
+ rec->clipIndex = mClipStack.size();
}
-void SkiaCanvas::saveClipsForFrame(SkTArray<SkClipStack::Element>& clips,
- int saveCountToBackup) {
- // Each SkClipStack::Element stores the index of the canvas save
- // with which it is associated. Backup only those Elements that
- // are associated with 'saveCountToBackup'
- SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
- SkClipStack::Iter::kTop_IterStart);
- while (const SkClipStack::Element* elem = clipIterator.prev()) {
- if (elem->getSaveCount() < saveCountToBackup) {
- // done with the target save count.
- break;
- }
- SkASSERT(elem->getSaveCount() == saveCountToBackup);
- clips.push_back(*elem);
+template <typename T>
+void SkiaCanvas::recordClip(const T& clip, SkClipOp op) {
+ // Only need tracking when in a partial save frame which
+ // doesn't restore the clip.
+ const SaveRec* rec = this->currentSaveRec();
+ if (rec && !(rec->saveFlags & SaveFlags::Clip)) {
+ mClipStack.emplace_back(clip, op, mCanvas->getTotalMatrix());
}
}
-void SkiaCanvas::applyClips(const SkTArray<SkClipStack::Element>& clips) {
- ClipCopier clipCopier(mCanvas);
+// Applies and optionally removes all clips >= index.
+void SkiaCanvas::applyPersistentClips(size_t clipStartIndex) {
+ SkASSERT(clipStartIndex <= mClipStack.size());
+ const auto begin = mClipStack.cbegin() + clipStartIndex;
+ const auto end = mClipStack.cend();
- // The clip stack stores clips in device space.
- SkMatrix origMatrix = mCanvas->getTotalMatrix();
- mCanvas->resetMatrix();
+ // Clip application mutates the CTM.
+ const SkMatrix saveMatrix = mCanvas->getTotalMatrix();
- // We pushed the clips in reverse order.
- for (int i = clips.count() - 1; i >= 0; --i) {
- clips[i].replay(&clipCopier);
+ for (auto clip = begin; clip != end; ++clip) {
+ clip->apply(mCanvas);
}
- mCanvas->setMatrix(origMatrix);
+ mCanvas->setMatrix(saveMatrix);
+
+ // If the current/post-restore save rec is also persisting clips, we
+ // leave them on the stack to be reapplied part of the next restore().
+ // Otherwise we're done and just pop them.
+ const auto* rec = this->currentSaveRec();
+ if (!rec || (rec->saveFlags & SaveFlags::Clip)) {
+ mClipStack.erase(begin, end);
+ }
}
// ----------------------------------------------------------------------------
@@ -490,27 +396,21 @@ bool SkiaCanvas::quickRejectPath(const SkPath& path) const {
return mCanvas->quickReject(path);
}
-bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ this->recordClip(rect, op);
mCanvas->clipRect(rect, op);
return !mCanvas->isClipEmpty();
}
-bool SkiaCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
- mCanvas->clipPath(*path, op);
- return !mCanvas->isClipEmpty();
-}
-
-bool SkiaCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
- SkPath rgnPath;
- if (region->getBoundaryPath(&rgnPath)) {
- // The region is specified in device space.
- SkMatrix savedMatrix = mCanvas->getTotalMatrix();
- mCanvas->resetMatrix();
- mCanvas->clipPath(rgnPath, op);
- mCanvas->setMatrix(savedMatrix);
+bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) {
+ SkRRect roundRect;
+ if (path->isRRect(&roundRect)) {
+ this->recordClip(roundRect, op);
+ mCanvas->clipRRect(roundRect, op);
} else {
- mCanvas->clipRect(SkRect::MakeEmpty(), op);
+ this->recordClip(*path, op);
+ mCanvas->clipPath(*path, op);
}
return !mCanvas->isClipEmpty();
}
@@ -531,7 +431,7 @@ void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) {
// Canvas draw operations
// ----------------------------------------------------------------------------
-void SkiaCanvas::drawColor(int color, SkXfermode::Mode mode) {
+void SkiaCanvas::drawColor(int color, SkBlendMode mode) {
mCanvas->drawColor(color, mode);
}
@@ -545,6 +445,7 @@ void SkiaCanvas::drawPaint(const SkPaint& paint) {
void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint,
SkCanvas::PointMode mode) {
+ if (CC_UNLIKELY(count < 2 || PaintUtils::paintWillNotDraw(paint))) return;
// convert the floats into SkPoints
count >>= 1; // now it is the number of points
std::unique_ptr<SkPoint[]> pts(new SkPoint[count]);
@@ -570,46 +471,58 @@ void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
}
void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
+ if (CC_UNLIKELY(count < 4 || PaintUtils::paintWillNotDraw(paint))) return;
this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
}
void SkiaCanvas::drawRect(float left, float top, float right, float bottom,
const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
mCanvas->drawRectCoords(left, top, right, bottom, paint);
}
void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
- SkRegion::Iterator it(region);
- while (!it.done()) {
- mCanvas->drawRect(SkRect::Make(it.rect()), paint);
- it.next();
- }
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+ mCanvas->drawRegion(region, paint);
}
void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
mCanvas->drawRoundRect(rect, rx, ry, paint);
}
void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
+ if (CC_UNLIKELY(radius <= 0 || PaintUtils::paintWillNotDraw(paint))) return;
mCanvas->drawCircle(x, y, radius, paint);
}
void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
mCanvas->drawOval(oval, paint);
}
void SkiaCanvas::drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
}
void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
- mCanvas->drawPath(path, paint);
+ if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+ SkRect rect;
+ SkRRect roundRect;
+ if (path.isOval(&rect)) {
+ mCanvas->drawOval(rect, paint);
+ } else if (path.isRRect(&roundRect)) {
+ mCanvas->drawRRect(roundRect, paint);
+ } else {
+ mCanvas->drawPath(path, paint);
+ }
}
void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
@@ -620,34 +533,41 @@ void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
#endif
const int ptCount = vertexCount >> 1;
mCanvas->drawVertices(vertexMode, ptCount, (SkPoint*)verts, (SkPoint*)texs,
- (SkColor*)colors, NULL, indices, indexCount, paint);
+ (SkColor*)colors, indices, indexCount, paint);
}
// ----------------------------------------------------------------------------
// Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
-void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
- mCanvas->drawBitmap(bitmap, left, top, paint);
+void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
+ SkBitmap skBitmap;
+ bitmap.getSkBitmap(&skBitmap);
+ mCanvas->drawBitmap(skBitmap, left, top, paint);
}
-void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
+void SkiaCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix, const SkPaint* paint) {
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
SkAutoCanvasRestore acr(mCanvas, true);
mCanvas->concat(matrix);
mCanvas->drawBitmap(bitmap, 0, 0, paint);
}
-void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+void SkiaCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint) {
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
mCanvas->drawBitmapRect(bitmap, srcRect, dstRect, paint);
}
-void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) {
-
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
const int ptCount = (meshWidth + 1) * (meshHeight + 1);
const int indexCount = meshWidth * meshHeight * 6;
@@ -732,20 +652,40 @@ void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshH
if (paint) {
tmpPaint = *paint;
}
- SkShader* shader = SkShader::CreateBitmapShader(bitmap,
- SkShader::kClamp_TileMode,
- SkShader::kClamp_TileMode);
- SkSafeUnref(tmpPaint.setShader(shader));
+
+ sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+ tmpPaint.setShader(image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode));
mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices,
- texs, (const SkColor*)colors, NULL, indices,
+ texs, (const SkColor*)colors, indices,
indexCount, tmpPaint);
}
-void SkiaCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& chunk,
+void SkiaCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk,
float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
- SkRect bounds = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- NinePatch::Draw(mCanvas, bounds, bitmap, chunk, paint, nullptr);
+
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
+
+ SkCanvas::Lattice lattice;
+ NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
+
+ lattice.fFlags = nullptr;
+ int numFlags = 0;
+ if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
+ // We can expect the framework to give us a color for every distinct rect.
+ // Skia requires a flag for every rect.
+ numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
+ }
+
+ SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags);
+ if (numFlags > 0) {
+ NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk);
+ }
+
+ lattice.fBounds = nullptr;
+ SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+ mCanvas->drawBitmapLattice(bitmap, lattice, dst, paint);
}
void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
@@ -760,83 +700,72 @@ void SkiaCanvas::drawGlyphs(const uint16_t* text, const float* positions, int co
const SkPaint& paint, float x, float y,
float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
float totalAdvance) {
- static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
- mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paint);
- drawTextDecorations(x, y, totalAdvance, paint);
-}
+ if (!text || !positions || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
+ // Set align to left for drawing, as we don't want individual
+ // glyphs centered or right-aligned; the offset above takes
+ // care of all alignment.
+ SkPaint paintCopy(paint);
+ paintCopy.setTextAlign(SkPaint::kLeft_Align);
+
+ SkRect bounds = SkRect::MakeLTRB(boundsLeft + x, boundsTop + y,
+ boundsRight + x, boundsBottom + y);
+
+ SkTextBlobBuilder builder;
+ const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(paintCopy, count, &bounds);
+ // TODO: we could reduce the number of memcpy's if the this were exposed further up
+ // in the architecture.
+ memcpy(buffer.glyphs, text, count * sizeof(uint16_t));
+ memcpy(buffer.pos, positions, (count << 1) * sizeof(float));
+
+ sk_sp<SkTextBlob> textBlob(builder.make());
+ mCanvas->drawTextBlob(textBlob, 0, 0, paintCopy);
+ drawTextDecorations(x, y, totalAdvance, paintCopy);
+}
+
+void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
+ const SkPaint& paint, const SkPath& path, size_t start, size_t end) {
+ const int N = end - start;
+ SkAutoSTMalloc<1024, uint8_t> storage(N * (sizeof(uint16_t) + sizeof(SkRSXform)));
+ SkRSXform* xform = (SkRSXform*)storage.get();
+ uint16_t* glyphs = (uint16_t*)(xform + N);
+ SkPathMeasure meas(path, false);
+
+ for (size_t i = start; i < end; i++) {
+ glyphs[i - start] = layout.getGlyphId(i);
+ float x = hOffset + layout.getX(i);
+ float y = vOffset + layout.getY(i);
+
+ SkPoint pos;
+ SkVector tan;
+ if (!meas.getPosTan(x, &pos, &tan)) {
+ pos.set(x, y);
+ tan.set(1, 0);
+ }
+ xform[i - start].fSCos = tan.x();
+ xform[i - start].fSSin = tan.y();
+ xform[i - start].fTx = pos.x() - tan.y() * y;
+ xform[i - start].fTy = pos.y() + tan.x() * y;
+ }
-void SkiaCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) {
- mCanvas->drawTextOnPathHV(glyphs, count << 1, path, hOffset, vOffset, paint);
+ this->asSkCanvas()->drawTextRSXform(glyphs, sizeof(uint16_t) * N, xform, nullptr, paint);
}
// ----------------------------------------------------------------------------
// Canvas draw operations: Animations
// ----------------------------------------------------------------------------
-class AnimatedRoundRect : public SkDrawable {
- public:
- AnimatedRoundRect(uirenderer::CanvasPropertyPrimitive* left,
- uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
- uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
- uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* p) :
- mLeft(left), mTop(top), mRight(right), mBottom(bottom), mRx(rx), mRy(ry), mPaint(p) {}
-
- protected:
- virtual SkRect onGetBounds() override {
- return SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
- }
- virtual void onDraw(SkCanvas* canvas) override {
- SkRect rect = SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
- canvas->drawRoundRect(rect, mRx->value, mRy->value, mPaint->value);
- }
-
- private:
- sp<uirenderer::CanvasPropertyPrimitive> mLeft;
- sp<uirenderer::CanvasPropertyPrimitive> mTop;
- sp<uirenderer::CanvasPropertyPrimitive> mRight;
- sp<uirenderer::CanvasPropertyPrimitive> mBottom;
- sp<uirenderer::CanvasPropertyPrimitive> mRx;
- sp<uirenderer::CanvasPropertyPrimitive> mRy;
- sp<uirenderer::CanvasPropertyPaint> mPaint;
-};
-
-class AnimatedCircle : public SkDrawable {
- public:
- AnimatedCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
- uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint) :
- mX(x), mY(y), mRadius(radius), mPaint(paint) {}
-
- protected:
- virtual SkRect onGetBounds() override {
- const float x = mX->value;
- const float y = mY->value;
- const float radius = mRadius->value;
- return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius);
- }
- virtual void onDraw(SkCanvas* canvas) override {
- canvas->drawCircle(mX->value, mY->value, mRadius->value, mPaint->value);
- }
-
- private:
- sp<uirenderer::CanvasPropertyPrimitive> mX;
- sp<uirenderer::CanvasPropertyPrimitive> mY;
- sp<uirenderer::CanvasPropertyPrimitive> mRadius;
- sp<uirenderer::CanvasPropertyPaint> mPaint;
-};
-
void SkiaCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) {
- SkAutoTUnref<AnimatedRoundRect> drawable(
- new AnimatedRoundRect(left, top, right, bottom, rx, ry, paint));
+ sk_sp<uirenderer::skiapipeline::AnimatedRoundRect> drawable(
+ new uirenderer::skiapipeline::AnimatedRoundRect(left, top, right, bottom, rx, ry, paint));
mCanvas->drawDrawable(drawable.get());
}
void SkiaCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint) {
- SkAutoTUnref<AnimatedCircle> drawable(new AnimatedCircle(x, y, radius, paint));
+ sk_sp<uirenderer::skiapipeline::AnimatedCircle> drawable(new uirenderer::skiapipeline::AnimatedCircle(x, y, radius, paint));
mCanvas->drawDrawable(drawable.get());
}
@@ -844,11 +773,17 @@ void SkiaCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::
// Canvas draw operations: View System
// ----------------------------------------------------------------------------
-void SkiaCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layer) { }
+void SkiaCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
+ LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw Layers");
+}
-void SkiaCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { }
+void SkiaCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
+ LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw RenderNodes");
+}
void SkiaCanvas::callDrawGLFunction(Functor* functor,
- uirenderer::GlFunctorLifecycleListener* listener) { }
+ uirenderer::GlFunctorLifecycleListener* listener) {
+ LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw GL Content");
+}
} // namespace android
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
new file mode 100644
index 000000000000..34c3717557ff
--- /dev/null
+++ b/libs/hwui/SkiaCanvas.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "hwui/Canvas.h"
+#include "CanvasProperty.h"
+#include "DeferredLayerUpdater.h"
+#include "RenderNode.h"
+#include "VectorDrawable.h"
+
+#include <SkCanvas.h>
+#include <SkTLazy.h>
+
+namespace android {
+
+// Holds an SkCanvas reference plus additional native data.
+class SkiaCanvas : public Canvas {
+public:
+ explicit SkiaCanvas(const SkBitmap& bitmap);
+
+ /**
+ * Create a new SkiaCanvas.
+ *
+ * @param canvas SkCanvas to handle calls made to this SkiaCanvas. Must
+ * not be NULL. This constructor does not take ownership, so the caller
+ * must guarantee that it remains valid while the SkiaCanvas is valid.
+ */
+ explicit SkiaCanvas(SkCanvas* canvas);
+
+ virtual ~SkiaCanvas();
+
+ virtual SkCanvas* asSkCanvas() override {
+ return mCanvas;
+ }
+
+ virtual void resetRecording(int width, int height,
+ uirenderer::RenderNode* renderNode) override {
+ LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas");
+ }
+
+ virtual uirenderer::DisplayList* finishRecording() override {
+ LOG_ALWAYS_FATAL("SkiaCanvas does not produce a DisplayList");
+ return nullptr;
+ }
+ virtual void insertReorderBarrier(bool enableReorder) override {
+ LOG_ALWAYS_FATAL("SkiaCanvas does not support reordering barriers");
+ }
+
+ virtual void setBitmap(const SkBitmap& bitmap) override;
+
+ virtual bool isOpaque() override;
+ virtual int width() override;
+ virtual int height() override;
+
+ virtual void setHighContrastText(bool highContrastText) override {
+ mHighContrastText = highContrastText;
+ }
+ virtual bool isHighContrastText() override { return mHighContrastText; }
+
+ virtual int getSaveCount() const override;
+ virtual int save(SaveFlags::Flags flags) override;
+ virtual void restore() override;
+ virtual void restoreToCount(int saveCount) override;
+
+ virtual int saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* paint, SaveFlags::Flags flags) override;
+ virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, SaveFlags::Flags flags) override;
+
+ virtual void getMatrix(SkMatrix* outMatrix) const override;
+ virtual void setMatrix(const SkMatrix& matrix) override;
+ virtual void concat(const SkMatrix& matrix) override;
+ virtual void rotate(float degrees) override;
+ virtual void scale(float sx, float sy) override;
+ virtual void skew(float sx, float sy) override;
+ virtual void translate(float dx, float dy) override;
+
+ virtual bool getClipBounds(SkRect* outRect) const override;
+ virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
+ virtual bool quickRejectPath(const SkPath& path) const override;
+ virtual bool clipRect(float left, float top, float right, float bottom,
+ SkClipOp op) override;
+ virtual bool clipPath(const SkPath* path, SkClipOp op) override;
+
+ virtual SkDrawFilter* getDrawFilter() override;
+ virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
+
+ virtual void drawColor(int color, SkBlendMode mode) override;
+ virtual void drawPaint(const SkPaint& paint) override;
+
+ virtual void drawPoint(float x, float y, const SkPaint& paint) override;
+ virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawLine(float startX, float startY, float stopX, float stopY,
+ const SkPaint& paint) override;
+ virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) override;
+ virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
+ virtual void drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) override;
+ virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
+ virtual void drawOval(float left, float top, float right, float bottom,
+ const SkPaint& paint) override;
+ virtual void drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) override;
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) override;
+ virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+ const float* verts, const float* tex, const int* colors,
+ const uint16_t* indices, int indexCount, const SkPaint& paint) override;
+
+ virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) override;
+ virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) override;
+ virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) override;
+
+ virtual bool drawTextAbsolutePos() const override { return true; }
+ virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
+
+ virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+ uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+ uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+ uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) override;
+ virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x,
+ uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
+ uirenderer::CanvasPropertyPaint* paint) override;
+
+ virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
+ virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
+ virtual void callDrawGLFunction(Functor* functor,
+ uirenderer::GlFunctorLifecycleListener* listener) override;
+
+protected:
+ SkiaCanvas();
+ void reset(SkCanvas* skiaCanvas);
+ void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); }
+
+ virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) override;
+ virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
+ const SkPaint& paint, const SkPath& path, size_t start, size_t end) override;
+
+private:
+ struct SaveRec {
+ int saveCount;
+ SaveFlags::Flags saveFlags;
+ size_t clipIndex;
+ };
+
+ bool mHighContrastText = false;
+
+ const SaveRec* currentSaveRec() const;
+ void recordPartialSave(SaveFlags::Flags flags);
+
+ template<typename T>
+ void recordClip(const T&, SkClipOp);
+ void applyPersistentClips(size_t clipStartIndex);
+
+ void drawPoints(const float* points, int count, const SkPaint& paint,
+ SkCanvas::PointMode mode);
+
+ class Clip;
+
+ std::unique_ptr<SkCanvas> mCanvasOwned; // might own a canvas we allocated
+ SkCanvas* mCanvas; // we do NOT own this canvas, it must survive us
+ // unless it is the same as mCanvasOwned.get()
+ std::unique_ptr<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
+ std::vector<Clip> mClipStack; // tracks persistent clips.
+};
+
+} // namespace android
+
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 5db5efb649e7..f32612d21319 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -20,12 +20,17 @@
#include <log/log.h>
+#include "hwui/Bitmap.h"
+#include <SkLatticeIter.h>
#include <SkPatchUtils.h>
#include <SkPaint.h>
#include <SkPath.h>
#include <SkPixelRef.h>
#include <SkRect.h>
#include <SkRRect.h>
+#include <SkRSXform.h>
+#include <SkSurface.h>
+#include <SkTextBlobRunIterator.h>
namespace android {
namespace uirenderer {
@@ -104,16 +109,12 @@ void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
const SkPaint* paint) {
- SkPixelRef* pxRef = bitmap.pixelRef();
-
+ sk_sp<Bitmap> hwuiBitmap = Bitmap::createFrom(bitmap.info(), *bitmap.pixelRef());
// HWUI doesn't support extractSubset(), so convert any subsetted bitmap into
// a drawBitmapRect(); pass through an un-subsetted bitmap.
- if (pxRef && bitmap.dimensions() != pxRef->info().dimensions()) {
- SkBitmap fullBitmap;
- fullBitmap.setInfo(pxRef->info());
- fullBitmap.setPixelRef(pxRef, 0, 0);
+ if (hwuiBitmap && bitmap.dimensions() != hwuiBitmap->info().dimensions()) {
SkIPoint origin = bitmap.pixelRefOrigin();
- mCanvas->drawBitmap(fullBitmap, origin.fX, origin.fY,
+ mCanvas->drawBitmap(*hwuiBitmap, origin.fX, origin.fY,
origin.fX + bitmap.dimensions().width(),
origin.fY + bitmap.dimensions().height(),
left, top,
@@ -121,15 +122,16 @@ void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScal
top + bitmap.dimensions().height(),
paint);
} else {
- mCanvas->drawBitmap(bitmap, left, top, paint);
+ mCanvas->drawBitmap(*hwuiBitmap, left, top, paint);
}
}
-void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr,
+void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& skBitmap, const SkRect* srcPtr,
const SkRect& dst, const SkPaint* paint, SrcRectConstraint) {
- SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height());
+ SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(skBitmap.width(), skBitmap.height());
// TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
- mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+ Bitmap* bitmap = reinterpret_cast<Bitmap*>(skBitmap.pixelRef());
+ mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
}
@@ -139,9 +141,43 @@ void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& ce
SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported");
}
+void SkiaCanvasProxy::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
+ const SkPaint* paint) {
+ SkBitmap skiaBitmap;
+ if (image->asLegacyBitmap(&skiaBitmap, SkImage::kRO_LegacyBitmapMode)) {
+ onDrawBitmap(skiaBitmap, left, top, paint);
+ }
+}
+
+void SkiaCanvasProxy::onDrawImageRect(const SkImage* image, const SkRect* srcPtr, const SkRect& dst,
+ const SkPaint* paint, SrcRectConstraint constraint) {
+ SkBitmap skiaBitmap;
+ if (image->asLegacyBitmap(&skiaBitmap, SkImage::kRO_LegacyBitmapMode)) {
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(skiaBitmap.info(), *skiaBitmap.pixelRef());
+ SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(image->width(), image->height());
+ mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+ dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
+ }
+}
+
+void SkiaCanvasProxy::onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
+ const SkPaint*) {
+ SkDEBUGFAIL("SkiaCanvasProxy::onDrawImageNine is not yet supported");
+}
+
+void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& lattice,
+ const SkRect& dst, const SkPaint* paint) {
+ SkLatticeIter iter(lattice, dst);
+ SkRect srcR, dstR;
+ while (iter.next(&srcR, &dstR)) {
+ onDrawImageRect(image, &srcR, dstR, paint, SkCanvas::kStrict_SrcRectConstraint);
+ }
+}
+
void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkPoint vertices[],
- const SkPoint texs[], const SkColor colors[], SkXfermode*, const uint16_t indices[],
+ const SkPoint texs[], const SkColor colors[], SkBlendMode, const uint16_t indices[],
int indexCount, const SkPaint& paint) {
+ // TODO: should we pass through blendmode
if (mFilterHwuiCalls) {
return;
}
@@ -154,7 +190,7 @@ void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkP
mCanvas->drawVertices(mode, floatCount, vArray, tArray, cArray, indices, indexCount, paint);
}
-SkSurface* SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
+sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
return NULL;
}
@@ -323,9 +359,9 @@ void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const S
// by Minikin then it had already computed these bounds. Unfortunately,
// there is no way to capture those bounds as part of the Skia drawPosText
// API so we need to do that computation again here.
- SkRect bounds;
+ SkRect bounds = SkRect::MakeEmpty();
for (int i = 0; i < glyphs.count; i++) {
- SkRect glyphBounds;
+ SkRect glyphBounds = SkRect::MakeEmpty();
glyphs.paint.measureText(&glyphs.glyphIDs[i], sizeof(uint16_t), &glyphBounds);
glyphBounds.offset(pos[i].fX, pos[i].fY);
bounds.join(glyphBounds);
@@ -348,18 +384,68 @@ void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const
void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
const SkMatrix* matrix, const SkPaint& origPaint) {
- // convert to glyphIDs if necessary
- GlyphIDConverter glyphs(text, byteLength, origPaint);
- mCanvas->drawGlyphsOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint);
+ SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextOnPath is not supported");
}
+void SkiaCanvasProxy::onDrawTextRSXform(const void* text, size_t byteLength,
+ const SkRSXform xform[], const SkRect* cullRect, const SkPaint& paint) {
+ GlyphIDConverter glyphs(text, byteLength, paint); // Just get count
+ SkMatrix localM, currM, origM;
+ mCanvas->getMatrix(&currM);
+ origM = currM;
+ for (int i = 0; i < glyphs.count; i++) {
+ localM.setRSXform(*xform++);
+ currM.setConcat(origM, localM);
+ mCanvas->setMatrix(currM);
+ this->onDrawText((char*)text + (byteLength / glyphs.count * i),
+ byteLength / glyphs.count, 0, 0, paint);
+ }
+ mCanvas->setMatrix(origM);
+}
+
+
void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint) {
- SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextBlob is not supported");
+ SkPaint runPaint = paint;
+
+ SkTextBlobRunIterator it(blob);
+ for (;!it.done(); it.next()) {
+ size_t textLen = it.glyphCount() * sizeof(uint16_t);
+ const SkPoint& offset = it.offset();
+ // applyFontToPaint() always overwrites the exact same attributes,
+ // so it is safe to not re-seed the paint for this reason.
+ it.applyFontToPaint(&runPaint);
+
+ switch (it.positioning()) {
+ case SkTextBlob::kDefault_Positioning:
+ this->drawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
+ break;
+ case SkTextBlob::kHorizontal_Positioning: {
+ std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
+ for (size_t i = 0; i < it.glyphCount(); i++) {
+ pts[i].set(x + offset.x() + it.pos()[i], y + offset.y());
+ }
+ this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
+ break;
+ }
+ case SkTextBlob::kFull_Positioning: {
+ std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
+ for (size_t i = 0; i < it.glyphCount(); i++) {
+ const size_t xIndex = i*2;
+ const size_t yIndex = xIndex + 1;
+ pts[i].set(x + offset.x() + it.pos()[xIndex], y + offset.y() + it.pos()[yIndex]);
+ }
+ this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
+ break;
+ }
+ default:
+ SkFAIL("unhandled positioning mode");
+ }
+ }
}
void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
- const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
+ const SkPoint texCoords[4], SkBlendMode bmode, const SkPaint& paint) {
if (mFilterHwuiCalls) {
return;
}
@@ -373,28 +459,24 @@ void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors
// If it fails to generate the vertices, then we do not draw.
if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) {
this->drawVertices(SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
- data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount,
+ data.fTexCoords, data.fColors, bmode, data.fIndices, data.fIndexCount,
paint);
}
}
-void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) {
+void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle) {
mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
}
-void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkRegion::Op op, ClipEdgeStyle) {
+void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkClipOp op, ClipEdgeStyle) {
SkPath path;
path.addRRect(roundRect);
mCanvas->clipPath(&path, op);
}
-void SkiaCanvasProxy::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) {
+void SkiaCanvasProxy::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle) {
mCanvas->clipPath(&path, op);
}
-void SkiaCanvasProxy::onClipRegion(const SkRegion& region, SkRegion::Op op) {
- mCanvas->clipRegion(&region, op);
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
index c03c523cd106..b3f6c07dfe64 100644
--- a/libs/hwui/SkiaCanvasProxy.h
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -44,7 +44,7 @@ public:
protected:
- virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
+ virtual sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
virtual void willSave() override;
virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
@@ -66,8 +66,15 @@ protected:
const SkPaint* paint, SrcRectConstraint) override;
virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
const SkRect& dst, const SkPaint*) override;
+ virtual void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*);
+ virtual void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
+ SrcRectConstraint);
+ virtual void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
+ const SkPaint*);
+ virtual void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
+ const SkPaint*);
virtual void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[],
- const SkPoint texs[], const SkColor colors[], SkXfermode*,
+ const SkPoint texs[], const SkColor colors[], SkBlendMode,
const uint16_t indices[], int indexCount,
const SkPaint&) override;
@@ -81,17 +88,18 @@ protected:
SkScalar constY, const SkPaint&) override;
virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
const SkMatrix* matrix, const SkPaint&) override;
+ virtual void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
+ const SkRect* cullRect, const SkPaint& paint);
virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint) override;
virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
- const SkPoint texCoords[4], SkXfermode* xmode,
+ const SkPoint texCoords[4], SkBlendMode,
const SkPaint& paint) override;
- virtual void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
- virtual void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
- virtual void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
- virtual void onClipRegion(const SkRegion&, SkRegion::Op) override;
+ virtual void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
+ virtual void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
+ virtual void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
private:
Canvas* mCanvas;
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 6f4a6839be4e..92399864ff23 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -18,9 +18,9 @@
#include "Caches.h"
#include "Extensions.h"
-#include "Layer.h"
#include "Matrix.h"
#include "Texture.h"
+#include "hwui/Bitmap.h"
#include <SkMatrix.h>
#include <utils/Log.h>
@@ -57,7 +57,7 @@ static inline void bindUniformColor(int slot, FloatColor color) {
}
static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
- caches->textureState().bindTexture(texture->id());
+ caches->textureState().bindTexture(texture->target(), texture->id());
texture->setWrapST(wrapS, wrapT);
}
@@ -80,7 +80,7 @@ static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatr
}
///////////////////////////////////////////////////////////////////////////////
-// gradient shader matrix helpers
+// Gradient shader matrix helpers
///////////////////////////////////////////////////////////////////////////////
static void toLinearUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
@@ -160,7 +160,7 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode
gradInfo.fColorOffsets = &colorOffsets[0];
shader.asAGradient(&gradInfo);
- if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
+ if (CC_UNLIKELY(!description->isSimpleGradient)) {
outData->gradientSampler = (*textureUnit)++;
#ifndef SK_SCALAR_IS_FLOAT
@@ -173,15 +173,16 @@ bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 mode
outData->gradientSampler = 0;
outData->gradientTexture = nullptr;
- outData->startColor.set(gradInfo.fColors[0]);
- outData->endColor.set(gradInfo.fColors[1]);
+ outData->startColor.setUnPreMultipliedSRGB(gradInfo.fColors[0]);
+ outData->endColor.setUnPreMultipliedSRGB(gradInfo.fColors[1]);
}
- outData->ditherSampler = (*textureUnit)++;
return true;
}
-void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) {
+void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data,
+ const GLsizei width, const GLsizei height) {
+
if (CC_UNLIKELY(data.gradientTexture)) {
caches.textureState().activateTexture(data.gradientSampler);
bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST);
@@ -191,10 +192,7 @@ void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& dat
bindUniformColor(caches.program().getUniform("endColor"), data.endColor);
}
- // TODO: remove sampler slot incrementing from dither.setupProgram,
- // since this assignment of slots is done at store, not apply time
- GLuint ditherSampler = data.ditherSampler;
- caches.dither.setupProgram(caches.program(), &ditherSampler);
+ glUniform2f(caches.program().getUniform("screenSize"), 1.0f / width, 1.0f / height);
glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1,
GL_FALSE, &data.screenSpace.data[0]);
}
@@ -208,13 +206,9 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model
return false;
}
- /*
- * Bypass the AssetAtlas, since those textures:
- * 1) require UV mapping, which isn't implemented in matrix computation below
- * 2) can't handle REPEAT simply
- * 3) are safe to upload here (outside of sync stage), since they're static
- */
- outData->bitmapTexture = caches.textureCache.getAndBypassAtlas(&bitmap);
+ // TODO: create hwui-owned BitmapShader.
+ Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef());
+ outData->bitmapTexture = caches.textureCache.get(hwuiBitmap);
if (!outData->bitmapTexture) return false;
outData->bitmapSampler = (*textureUnit)++;
@@ -223,10 +217,14 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model
const float height = outData->bitmapTexture->height();
description->hasBitmap = true;
- if (!caches.extensions().hasNPot()
+ description->isShaderBitmapExternal = hwuiBitmap->isHardware();
+ // gralloc doesn't support non-clamp modes
+ if (hwuiBitmap->isHardware() || (!caches.extensions().hasNPot()
&& (!isPowerOfTwo(width) || !isPowerOfTwo(height))
- && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode)) {
- description->isBitmapNpot = true;
+ && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode))) {
+ // need non-clamp mode, but it's not supported for this draw,
+ // so enable custom shader logic to mimic
+ description->useShaderBasedWrap = true;
description->bitmapWrapS = gTileModes[xy[0]];
description->bitmapWrapT = gTileModes[xy[1]];
@@ -314,49 +312,10 @@ bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& mode
storeCompose(caches, *rec.fShaderB, *rec.fShaderA,
transform, textureUnit, description, outData);
}
- if (!SkXfermode::AsMode(rec.fMode, &description->shadersMode)) {
- // TODO: Support other modes.
- description->shadersMode = SkXfermode::kSrcOver_Mode;
- }
- return true;
-}
-
-bool tryStoreLayer(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
- GLuint* textureUnit, ProgramDescription* description,
- SkiaShaderData::LayerShaderData* outData) {
- Layer* layer;
- if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
- return false;
- }
-
- description->hasBitmap = true;
- outData->layer = layer;
- outData->bitmapSampler = (*textureUnit)++;
-
- const float width = layer->getWidth();
- const float height = layer->getHeight();
-
- computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
- modelViewMatrix);
-
- outData->textureDimension[0] = 1.0f / width;
- outData->textureDimension[1] = 1.0f / height;
+ description->shadersMode = rec.fBlendMode;
return true;
}
-void applyLayer(Caches& caches, const SkiaShaderData::LayerShaderData& data) {
- caches.textureState().activateTexture(data.bitmapSampler);
-
- data.layer->bindTexture();
- data.layer->setWrap(GL_CLAMP_TO_EDGE);
- data.layer->setFilter(GL_LINEAR);
-
- glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
- glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1,
- GL_FALSE, &data.textureTransform.data[0]);
- glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
-}
-
void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
GLuint* textureUnit, ProgramDescription* description,
SkiaShaderData* outData) {
@@ -378,29 +337,20 @@ void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& mo
return;
}
- if (tryStoreLayer(caches, shader, modelViewMatrix,
- textureUnit, description, &outData->layerData)) {
- outData->skiaShaderType = kLayer_SkiaShaderType;
- return;
- }
-
// Unknown/unsupported type, so explicitly ignore shader
outData->skiaShaderType = kNone_SkiaShaderType;
}
-void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) {
+void SkiaShader::apply(Caches& caches, const SkiaShaderData& data,
+ const GLsizei width, const GLsizei height) {
if (!data.skiaShaderType) return;
if (data.skiaShaderType & kGradient_SkiaShaderType) {
- applyGradient(caches, data.gradientData);
+ applyGradient(caches, data.gradientData, width, height);
}
if (data.skiaShaderType & kBitmap_SkiaShaderType) {
applyBitmap(caches, data.bitmapData);
}
-
- if (data.skiaShaderType == kLayer_SkiaShaderType) {
- applyLayer(caches, data.layerData);
- }
}
}; // namespace uirenderer
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 884196d9fc62..ab578d540774 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -22,7 +22,6 @@
#include <GLES2/gl2.h>
#include <SkShader.h>
-#include <SkXfermode.h>
#include <cutils/compiler.h>
namespace android {
@@ -30,7 +29,6 @@ namespace uirenderer {
class Caches;
class Extensions;
-class Layer;
class Texture;
struct ProgramDescription;
@@ -46,7 +44,6 @@ enum SkiaShaderType {
kBitmap_SkiaShaderType = 1,
kGradient_SkiaShaderType = 2,
kCompose_SkiaShaderType = kBitmap_SkiaShaderType | kGradient_SkiaShaderType,
- kLayer_SkiaShaderType = 4,
};
struct SkiaShaderData {
@@ -62,7 +59,6 @@ struct SkiaShaderData {
} bitmapData;
struct GradientShaderData {
Matrix4 screenSpace;
- GLuint ditherSampler;
// simple gradient
FloatColor startColor;
@@ -72,17 +68,7 @@ struct SkiaShaderData {
Texture* gradientTexture;
GLuint gradientSampler;
GLenum wrapST;
-
} gradientData;
- struct LayerShaderData {
- Layer* layer;
- GLuint bitmapSampler;
- GLenum wrapS;
- GLenum wrapT;
-
- Matrix4 textureTransform;
- float textureDimension[2];
- } layerData;
};
class SkiaShader {
@@ -90,7 +76,8 @@ public:
static void store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
GLuint* textureUnit, ProgramDescription* description,
SkiaShaderData* outData);
- static void apply(Caches& caches, const SkiaShaderData& data);
+ static void apply(Caches& caches, const SkiaShaderData& data,
+ const GLsizei width, const GLsizei height);
};
}; // namespace uirenderer
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 7c187fbda457..9d719bd1e997 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -30,14 +30,11 @@ Snapshot::Snapshot()
, previous(nullptr)
, layer(nullptr)
, fbo(0)
- , invisible(false)
- , empty(false)
, alpha(1.0f)
, roundRectClipState(nullptr)
, projectionPathMask(nullptr)
, mClipArea(&mClipAreaRoot) {
transform = &mTransformRoot;
- region = nullptr;
mRelativeLightCenter.x = mRelativeLightCenter.y = mRelativeLightCenter.z = 0;
}
@@ -50,8 +47,6 @@ Snapshot::Snapshot(Snapshot* s, int saveFlags)
, previous(s)
, layer(s->layer)
, fbo(s->fbo)
- , invisible(s->invisible)
- , empty(false)
, alpha(s->alpha)
, roundRectClipState(s->roundRectClipState)
, projectionPathMask(s->projectionPathMask)
@@ -71,32 +66,20 @@ Snapshot::Snapshot(Snapshot* s, int saveFlags)
} else {
mClipArea = s->mClipArea;
}
-
- if (s->flags & Snapshot::kFlagFboTarget) {
- flags |= Snapshot::kFlagFboTarget;
- region = s->region;
- } else {
- region = nullptr;
- }
}
///////////////////////////////////////////////////////////////////////////////
// Clipping
///////////////////////////////////////////////////////////////////////////////
-void Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) {
- flags |= Snapshot::kFlagClipSet;
- mClipArea->clipRegion(region, op);
-}
-
-void Snapshot::clip(const Rect& localClip, SkRegion::Op op) {
+void Snapshot::clip(const Rect& localClip, SkClipOp op) {
flags |= Snapshot::kFlagClipSet;
- mClipArea->clipRectWithTransform(localClip, transform, op);
+ mClipArea->clipRectWithTransform(localClip, transform, static_cast<SkRegion::Op>(op));
}
-void Snapshot::clipPath(const SkPath& path, SkRegion::Op op) {
+void Snapshot::clipPath(const SkPath& path, SkClipOp op) {
flags |= Snapshot::kFlagClipSet;
- mClipArea->clipPathWithTransform(path, transform, op);
+ mClipArea->clipPathWithTransform(path, transform, static_cast<SkRegion::Op>(op));
}
void Snapshot::setClip(float left, float top, float right, float bottom) {
@@ -127,58 +110,6 @@ void Snapshot::resetClip(float left, float top, float right, float bottom) {
}
///////////////////////////////////////////////////////////////////////////////
-// Transforms
-///////////////////////////////////////////////////////////////////////////////
-
-void Snapshot::resetTransform(float x, float y, float z) {
-#if HWUI_NEW_OPS
- LOG_ALWAYS_FATAL("not supported - light center managed differently");
-#else
- // before resetting, map current light pos with inverse of current transform
- Vector3 center = mRelativeLightCenter;
- mat4 inverse;
- inverse.loadInverse(*transform);
- inverse.mapPoint3d(center);
- mRelativeLightCenter = center;
-
- transform = &mTransformRoot;
- transform->loadTranslate(x, y, z);
-#endif
-}
-
-void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const {
-#if HWUI_NEW_OPS
- LOG_ALWAYS_FATAL("not supported - not needed by new ops");
-#else
- // build (reverse ordered) list of the stack of snapshots, terminated with a NULL
- Vector<const Snapshot*> snapshotList;
- snapshotList.push(nullptr);
- const Snapshot* current = this;
- do {
- snapshotList.push(current);
- current = current->previous;
- } while (current);
-
- // traverse the list, adding in each transform that contributes to the total transform
- outTransform->loadIdentity();
- for (size_t i = snapshotList.size() - 1; i > 0; i--) {
- // iterate down the stack
- const Snapshot* current = snapshotList[i];
- const Snapshot* next = snapshotList[i - 1];
- if (current->flags & kFlagIsFboLayer) {
- // if we've hit a layer, translate by the layer's draw offset
- outTransform->translate(current->layer->layer.left, current->layer->layer.top);
- }
- if (!next || (next->flags & kFlagIsFboLayer)) {
- // if this snapshot is last, or if this snapshot is last before an
- // FBO layer (which reset the transform), apply it
- outTransform->multiply(*(current->transform));
- }
- }
-#endif
-}
-
-///////////////////////////////////////////////////////////////////////////////
// Clipping round rect
///////////////////////////////////////////////////////////////////////////////
@@ -227,20 +158,8 @@ void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& boun
roundRectClipState = state;
}
-void Snapshot::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
-#if HWUI_NEW_OPS
- // TODO: remove allocator param for HWUI_NEW_OPS
+void Snapshot::setProjectionPathMask(const SkPath* path) {
projectionPathMask = path;
-#else
- if (path) {
- ProjectionPathMask* mask = new (allocator) ProjectionPathMask;
- mask->projectionMask = path;
- buildScreenSpaceTransform(&(mask->projectionMaskTransform));
- projectionPathMask = mask;
- } else {
- projectionPathMask = nullptr;
- }
-#endif
}
static Snapshot* getClipRoot(Snapshot* target) {
@@ -274,13 +193,9 @@ void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform)
// Queries
///////////////////////////////////////////////////////////////////////////////
-bool Snapshot::isIgnored() const {
- return invisible || empty;
-}
-
void Snapshot::dump() const {
- ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
- this, flags, previous, getViewportHeight(), isIgnored(), !mClipArea->isSimple());
+ ALOGD("Snapshot %p, flags %x, prev %p, height %d, hasComplexClip %d",
+ this, flags, previous, getViewportHeight(), !mClipArea->isSimple());
const Rect& clipRect(mClipArea->getClipRect());
ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d",
clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple());
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index d8f926ef3925..8cd90a68a9bb 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_SNAPSHOT_H
-#define ANDROID_HWUI_SNAPSHOT_H
+#pragma once
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
@@ -24,6 +23,7 @@
#include <utils/RefBase.h>
#include <ui/Region.h>
+#include <SkClipOp.h>
#include <SkRegion.h>
#include "ClipArea.h"
@@ -63,18 +63,6 @@ public:
float radius;
};
-// TODO: remove for HWUI_NEW_OPS
-class ProjectionPathMask {
-public:
- static void* operator new(size_t size) = delete;
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc<ProjectionPathMask>(size);
- }
-
- const SkPath* projectionMask;
- Matrix4 projectionMaskTransform;
-};
-
/**
* A snapshot holds information about the current state of the rendering
* surface. A snapshot is usually created whenever the user calls save()
@@ -113,11 +101,6 @@ public:
* restored when this snapshot is restored.
*/
kFlagIsFboLayer = 0x4,
- /**
- * Indicates that this snapshot or an ancestor snapshot is
- * an FBO layer.
- */
- kFlagFboTarget = 0x8, // TODO: remove for HWUI_NEW_OPS
};
/**
@@ -125,25 +108,19 @@ public:
* the specified operation. The specified rectangle is transformed
* by this snapshot's trasnformation.
*/
- void clip(const Rect& localClip, SkRegion::Op op);
+ void clip(const Rect& localClip, SkClipOp op);
/**
* Modifies the current clip with the new clip rectangle and
* the specified operation. The specified rectangle is considered
* already transformed.
*/
- void clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op);
-
- /**
- * Modifies the current clip with the specified region and operation.
- * The specified region is considered already transformed.
- */
- void clipRegionTransformed(const SkRegion& region, SkRegion::Op op);
+ void clipTransformed(const Rect& r, SkClipOp op = SkClipOp::kIntersect);
/**
* Modifies the current clip with the specified path and operation.
*/
- void clipPath(const SkPath& path, SkRegion::Op op);
+ void clipPath(const SkPath& path, SkClipOp op);
/**
* Sets the current clip.
@@ -179,11 +156,6 @@ public:
*/
void resetClip(float left, float top, float right, float bottom);
- /**
- * Resets the current transform to a pure 3D translation.
- */
- void resetTransform(float x, float y, float z);
-
void initializeViewport(int width, int height) {
mViewportData.initialize(width, height);
mClipAreaRoot.setViewportDimensions(width, height);
@@ -207,13 +179,7 @@ public:
/**
* Sets (and replaces) the current projection mask
*/
- void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
-
- /**
- * Indicates whether this snapshot should be ignored. A snapshot
- * is typically ignored if its layer is invisible or empty.
- */
- bool isIgnored() const;
+ void setProjectionPathMask(const SkPath* path);
/**
* Indicates whether the current transform has perspective components.
@@ -221,13 +187,6 @@ public:
bool hasPerspectiveTransform() const;
/**
- * Fills outTransform with the current, total transform to screen space,
- * across layer boundaries.
- */
- // TODO: remove for HWUI_NEW_OPS
- void buildScreenSpaceTransform(Matrix4* outTransform) const;
-
- /**
* Dirty flags.
*/
int flags;
@@ -251,19 +210,6 @@ public:
GLuint fbo;
/**
- * Indicates that this snapshot is invisible and nothing should be drawn
- * inside it. This flag is set only when the layer clips drawing to its
- * bounds and is passed to subsequent snapshots.
- */
- bool invisible;
-
- /**
- * If set to true, the layer will not be composited. This is similar to
- * invisible but this flag is not passed to subsequent snapshots.
- */
- bool empty;
-
- /**
* Local transformation. Holds the current translation, scale and
* rotation values.
*
@@ -273,14 +219,6 @@ public:
mat4* transform;
/**
- * The ancestor layer's dirty region.
- *
- * This is a reference to a region owned by a layer. This pointer must
- * not be freed.
- */
- Region* region;
-
- /**
* Current alpha value. This value is 1 by default, but may be set by a DisplayList which
* has translucent rendering in a non-overlapping View. This value will be used by
* the renderer to set the alpha in the current color being used for ensuing drawing
@@ -302,11 +240,7 @@ public:
/**
* Current projection masking path - used exclusively to mask projected, tessellated circles.
*/
-#if HWUI_NEW_OPS
const SkPath* projectionPathMask;
-#else
- const ProjectionPathMask* projectionPathMask;
-#endif
void dump() const;
@@ -345,5 +279,3 @@ private:
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_SNAPSHOT_H
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index cc96a137c306..7b0a1bc3e93e 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -302,21 +302,6 @@ bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint,
}
/**
- * Make the polygon turn clockwise.
- *
- * @param polygon the polygon as a Vector2 array.
- * @param len the number of points of the polygon
- */
-void SpotShadow::makeClockwise(Vector2* polygon, int len) {
- if (polygon == nullptr || len == 0) {
- return;
- }
- if (!ShadowTessellator::isClockwise(polygon, len)) {
- reverse(polygon, len);
- }
-}
-
-/**
* Reverse the polygon
*
* @param polygon the polygon as a Vector2 array
diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h
index 62a7e5dc8a3e..6108bb6bd585 100644
--- a/libs/hwui/SpotShadow.h
+++ b/libs/hwui/SpotShadow.h
@@ -54,7 +54,6 @@ private:
static void quicksortX(Vector2* points, int low, int high);
static bool testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len);
- static void makeClockwise(Vector2* polygon, int len);
static void reverse(Vector2* polygon, int len);
static void generateTriangleStrip(bool isCasterOpaque, float shadowStrengthScale,
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index d9e811684610..91e7ac39af90 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -18,7 +18,6 @@
#include <utils/Trace.h>
#include "Caches.h"
-#include "OpenGLRenderer.h"
#include "PathTessellator.h"
#include "ShadowTessellator.h"
#include "TessellationCache.h"
@@ -369,21 +368,6 @@ void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect
mShadowCache.put(key, task.get());
}
-void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip,
- bool opaque, const SkPath* casterPerimeter,
- const Matrix4* transformXY, const Matrix4* transformZ,
- const Vector3& lightCenter, float lightRadius, vertexBuffer_pair_t& outBuffers) {
- ShadowDescription key(casterPerimeter, drawTransform);
- ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key));
- if (!task) {
- precacheShadows(drawTransform, localClip, opaque, casterPerimeter,
- transformXY, transformZ, lightCenter, lightRadius);
- task = static_cast<ShadowTask*>(mShadowCache.get(key));
- }
- LOG_ALWAYS_FATAL_IF(task == nullptr, "shadow not precached");
- outBuffers = task->getResult();
-}
-
sp<TessellationCache::ShadowTask> TessellationCache::getShadowTask(
const Matrix4* drawTransform, const Rect& localClip,
bool opaque, const SkPath* casterPerimeter,
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
index 6141b4ef63d7..ccad1b7bd415 100644
--- a/libs/hwui/TessellationCache.h
+++ b/libs/hwui/TessellationCache.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_TESSELLATION_CACHE_H
-#define ANDROID_HWUI_TESSELLATION_CACHE_H
+#pragma once
#include "Debug.h"
#include "Matrix.h"
@@ -161,17 +160,6 @@ public:
const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint,
float width, float height, float rx, float ry);
- // TODO: delete these when switching to HWUI_NEW_OPS
- void precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
- bool opaque, const SkPath* casterPerimeter,
- const Matrix4* transformXY, const Matrix4* transformZ,
- const Vector3& lightCenter, float lightRadius);
- void getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip,
- bool opaque, const SkPath* casterPerimeter,
- const Matrix4* transformXY, const Matrix4* transformZ,
- const Vector3& lightCenter, float lightRadius,
- vertexBuffer_pair_t& outBuffers);
-
sp<ShadowTask> getShadowTask(const Matrix4* drawTransform, const Rect& localClip,
bool opaque, const SkPath* casterPerimeter,
const Matrix4* transformXY, const Matrix4* transformZ,
@@ -184,6 +172,11 @@ private:
typedef VertexBuffer* (*Tessellator)(const Description&);
+ void precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius);
+
Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint,
float width, float height);
Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint,
@@ -232,5 +225,3 @@ void tessellateShadows(
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_PATH_CACHE_H
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 4f49a3518be0..705395e1e2b7 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -26,71 +26,85 @@
namespace android {
namespace uirenderer {
+// Number of bytes used by a texture in the given format
static int bytesPerPixel(GLint glFormat) {
switch (glFormat) {
// The wrapped-texture case, usually means a SurfaceTexture
case 0:
return 0;
+ case GL_LUMINANCE:
case GL_ALPHA:
return 1;
+ case GL_SRGB8:
case GL_RGB:
return 3;
+ case GL_SRGB8_ALPHA8:
case GL_RGBA:
return 4;
case GL_RGBA16F:
- return 16;
+ return 8;
default:
LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat);
}
}
-void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
- GLenum renderTarget) {
+void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) {
if (force || wrapS != mWrapS || wrapT != mWrapT) {
mWrapS = wrapS;
mWrapT = wrapT;
if (bindTexture) {
- mCaches.textureState().bindTexture(renderTarget, mId);
+ mCaches.textureState().bindTexture(mTarget, mId);
}
- glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
- glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT);
+ glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS);
+ glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT);
}
}
-void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force,
- GLenum renderTarget) {
-
+void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) {
if (force || min != mMinFilter || mag != mMagFilter) {
mMinFilter = min;
mMagFilter = mag;
if (bindTexture) {
- mCaches.textureState().bindTexture(renderTarget, mId);
+ mCaches.textureState().bindTexture(mTarget, mId);
}
if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
- glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min);
- glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag);
+ glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min);
+ glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag);
}
}
void Texture::deleteTexture() {
mCaches.textureState().deleteTexture(mId);
mId = 0;
+ mTarget = GL_NONE;
+ if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
+ EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
+ eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
+ mEglImageHandle = EGL_NO_IMAGE_KHR;
+ }
}
-bool Texture::updateSize(uint32_t width, uint32_t height, GLint format) {
- if (mWidth == width && mHeight == height && mFormat == format) {
+bool Texture::updateSize(uint32_t width, uint32_t height, GLint internalFormat,
+ GLint format, GLenum target) {
+ if (mWidth == width
+ && mHeight == height
+ && mFormat == format
+ && mInternalFormat == internalFormat
+ && mTarget == target) {
return false;
}
mWidth = width;
mHeight = height;
mFormat = format;
- notifySizeChanged(mWidth * mHeight * bytesPerPixel(mFormat));
+ mInternalFormat = internalFormat;
+ mTarget = target;
+ notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat));
return true;
}
@@ -101,10 +115,10 @@ void Texture::resetCachedParams() {
mMagFilter = GL_LINEAR;
}
-void Texture::upload(GLint internalformat, uint32_t width, uint32_t height,
+void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height,
GLenum format, GLenum type, const void* pixels) {
GL_CHECKPOINT(MODERATE);
- bool needsAlloc = updateSize(width, height, internalformat);
+ bool needsAlloc = updateSize(width, height, internalFormat, format, GL_TEXTURE_2D);
if (!mId) {
glGenTextures(1, &mId);
needsAlloc = true;
@@ -112,17 +126,28 @@ void Texture::upload(GLint internalformat, uint32_t width, uint32_t height,
}
mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId);
if (needsAlloc) {
- glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
+ glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0,
format, type, pixels);
} else if (pixels) {
- glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
+ glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0,
format, type, pixels);
}
GL_CHECKPOINT(MODERATE);
}
-static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp,
- GLsizei width, GLsizei height, const GLvoid * data) {
+void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) {
+ EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
+ if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
+ mEglImageHandle = EGL_NO_IMAGE_KHR;
+ }
+ mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ buffer->getNativeBuffer(), 0);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle);
+}
+
+static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type,
+ GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) {
const bool useStride = stride != width
&& Caches::getInstance().extensions().hasUnpackRowLength();
@@ -132,7 +157,7 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str
}
if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+ glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data);
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
}
@@ -156,7 +181,7 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str
}
if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
+ glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp);
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
}
@@ -165,43 +190,72 @@ static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei str
}
}
-static void uploadSkBitmapToTexture(const SkBitmap& bitmap,
- bool resize, GLenum format, GLenum type) {
- uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(),
- bitmap.width(), bitmap.height(), bitmap.getPixels());
-}
-
-static void colorTypeToGlFormatAndType(SkColorType colorType,
- GLint* outFormat, GLint* outType) {
+void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
+ bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) {
switch (colorType) {
case kAlpha_8_SkColorType:
*outFormat = GL_ALPHA;
+ *outInternalFormat = GL_ALPHA;
*outType = GL_UNSIGNED_BYTE;
break;
case kRGB_565_SkColorType:
- *outFormat = GL_RGB;
- *outType = GL_UNSIGNED_SHORT_5_6_5;
+ if (needSRGB) {
+ // We would ideally use a GL_RGB/GL_SRGB8 texture but the
+ // intermediate Skia bitmap needs to be ARGB_8888
+ *outFormat = GL_RGBA;
+ *outInternalFormat = caches.rgbaInternalFormat();
+ *outType = GL_UNSIGNED_BYTE;
+ } else {
+ *outFormat = GL_RGB;
+ *outInternalFormat = GL_RGB;
+ *outType = GL_UNSIGNED_SHORT_5_6_5;
+ }
break;
// ARGB_4444 and Index_8 are both upconverted to RGBA_8888
case kARGB_4444_SkColorType:
case kIndex_8_SkColorType:
case kN32_SkColorType:
*outFormat = GL_RGBA;
+ *outInternalFormat = caches.rgbaInternalFormat(needSRGB);
*outType = GL_UNSIGNED_BYTE;
break;
case kGray_8_SkColorType:
+ // TODO: Handle sRGB
*outFormat = GL_LUMINANCE;
+ *outInternalFormat = GL_LUMINANCE;
*outType = GL_UNSIGNED_BYTE;
break;
+ case kRGBA_F16_SkColorType:
+ // This format is always linear
+ *outFormat = GL_RGBA;
+ *outInternalFormat = GL_RGBA16F;
+ *outType = GL_HALF_FLOAT;
+ break;
default:
LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
break;
}
}
-void Texture::upload(const SkBitmap& bitmap) {
- SkAutoLockPixels alp(bitmap);
+SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB) {
+ SkBitmap rgbaBitmap;
+ rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
+ bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr));
+ rgbaBitmap.eraseColor(0);
+ SkCanvas canvas(rgbaBitmap);
+ canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
+ return rgbaBitmap;
+}
+
+bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB) {
+ bool needSRGB = info.colorSpace() == sRGB;
+ return info.colorType() == kARGB_4444_SkColorType
+ || info.colorType() == kIndex_8_SkColorType
+ || (info.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB);
+}
+
+void Texture::upload(Bitmap& bitmap) {
if (!bitmap.readyToDraw()) {
ALOGE("Cannot generate texture from bitmap");
return;
@@ -224,29 +278,32 @@ void Texture::upload(const SkBitmap& bitmap) {
setDefaultParams = true;
}
- GLint format, type;
- colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type);
-
- if (updateSize(bitmap.width(), bitmap.height(), format)) {
- needsAlloc = true;
- }
-
- blend = !bitmap.isOpaque();
- mCaches.textureState().bindTexture(mId);
+ sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
+ bool needSRGB = bitmap.info().colorSpace() == sRGB.get();
- if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType
- || bitmap.colorType() == kIndex_8_SkColorType)) {
- SkBitmap rgbaBitmap;
- rgbaBitmap.allocPixels(SkImageInfo::MakeN32(mWidth, mHeight,
- bitmap.alphaType()));
- rgbaBitmap.eraseColor(0);
+ GLint internalFormat, format, type;
+ colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type);
- SkCanvas canvas(rgbaBitmap);
- canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
+ GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
+ needsAlloc |= updateSize(bitmap.width(), bitmap.height(), internalFormat, format, target);
- uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, format, type);
+ blend = !bitmap.isOpaque();
+ mCaches.textureState().bindTexture(mTarget, mId);
+
+ // TODO: Handle sRGB gray bitmaps
+ bool hasSRGB = mCaches.extensions().hasSRGB();
+ if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasSRGB, sRGB.get()))) {
+ SkBitmap skBitmap;
+ bitmap.getSkBitmap(&skBitmap);
+ SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
+ uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
+ rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(),
+ rgbaBitmap.height(), rgbaBitmap.getPixels());
+ } else if (bitmap.isHardware()) {
+ uploadHardwareBitmapToTexture(bitmap.graphicBuffer());
} else {
- uploadSkBitmapToTexture(bitmap, needsAlloc, format, type);
+ uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(),
+ bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels());
}
if (canMipMap) {
@@ -262,11 +319,14 @@ void Texture::upload(const SkBitmap& bitmap) {
}
}
-void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint format) {
+void Texture::wrap(GLuint id, uint32_t width, uint32_t height,
+ GLint internalFormat, GLint format, GLenum target) {
mId = id;
mWidth = width;
mHeight = height;
mFormat = format;
+ mInternalFormat = internalFormat;
+ mTarget = target;
// We're wrapping an existing texture, so don't double count this memory
notifySizeChanged(0);
}
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index b72742f45654..b8397ccade9c 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -18,11 +18,17 @@
#define ANDROID_HWUI_TEXTURE_H
#include "GpuMemoryTracker.h"
+#include "hwui/Bitmap.h"
#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
#include <SkBitmap.h>
namespace android {
+
+class GraphicBuffer;
+
namespace uirenderer {
class Caches;
@@ -34,6 +40,11 @@ class Layer;
*/
class Texture : public GpuMemoryTracker {
public:
+ static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB);
+ static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB);
+ static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
+ bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType);
+
explicit Texture(Caches& caches)
: GpuMemoryTracker(GpuObjectType::Texture)
, mCaches(caches)
@@ -41,21 +52,19 @@ public:
virtual ~Texture() { }
- inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false,
- GLenum renderTarget = GL_TEXTURE_2D) {
- setWrapST(wrap, wrap, bindTexture, force, renderTarget);
+ inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) {
+ setWrapST(wrap, wrap, bindTexture, force);
}
virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
- bool force = false, GLenum renderTarget = GL_TEXTURE_2D);
+ bool force = false);
- inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false,
- GLenum renderTarget = GL_TEXTURE_2D) {
- setFilterMinMag(filter, filter, bindTexture, force, renderTarget);
+ inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false) {
+ setFilterMinMag(filter, filter, bindTexture, force);
}
virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
- bool force = false, GLenum renderTarget = GL_TEXTURE_2D);
+ bool force = false);
/**
* Convenience method to call glDeleteTextures() on this texture's id.
@@ -69,29 +78,30 @@ public:
*
* The image data is undefined after calling this.
*/
- void resize(uint32_t width, uint32_t height, GLint format) {
- upload(format, width, height, format, GL_UNSIGNED_BYTE, nullptr);
+ void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) {
+ upload(internalFormat, width, height, format, GL_UNSIGNED_BYTE, nullptr);
}
/**
- * Updates this Texture with the contents of the provided SkBitmap,
+ * Updates this Texture with the contents of the provided Bitmap,
* also setting the appropriate width, height, and format. It is not necessary
* to call resize() prior to this.
*
- * Note this does not set the generation from the SkBitmap.
+ * Note this does not set the generation from the Bitmap.
*/
- void upload(const SkBitmap& source);
+ void upload(Bitmap& source);
/**
* Basically glTexImage2D/glTexSubImage2D.
*/
- void upload(GLint internalformat, uint32_t width, uint32_t height,
+ void upload(GLint internalFormat, uint32_t width, uint32_t height,
GLenum format, GLenum type, const void* pixels);
/**
* Wraps an existing texture.
*/
- void wrap(GLuint id, uint32_t width, uint32_t height, GLint format);
+ void wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat,
+ GLint format, GLenum target);
GLuint id() const {
return mId;
@@ -109,6 +119,14 @@ public:
return mFormat;
}
+ GLint internalFormat() const {
+ return mInternalFormat;
+ }
+
+ GLenum target() const {
+ return mTarget;
+ }
+
/**
* Generation of the backing bitmap,
*/
@@ -140,21 +158,25 @@ public:
* the current frame. This is reset at the start of a new frame.
*/
void* isInUse = nullptr;
-
private:
- // TODO: Temporarily grant private access to Layer, remove once
- // Layer can be de-tangled from being a dual-purpose render target
+ // TODO: Temporarily grant private access to GlLayer, remove once
+ // GlLayer can be de-tangled from being a dual-purpose render target
// and external texture wrapper
- friend class Layer;
+ friend class GlLayer;
// Returns true if the size changed, false if it was the same
- bool updateSize(uint32_t width, uint32_t height, GLint format);
+ bool updateSize(uint32_t width, uint32_t height, GLint internalFormat,
+ GLint format, GLenum target);
+ void uploadHardwareBitmapToTexture(GraphicBuffer* buffer);
void resetCachedParams();
GLuint mId = 0;
uint32_t mWidth = 0;
uint32_t mHeight = 0;
GLint mFormat = 0;
+ GLint mInternalFormat = 0;
+ GLenum mTarget = GL_NONE;
+ EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR;
/* See GLES spec section 3.8.14
* "In the initial state, the value assigned to TEXTURE_MIN_FILTER is
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 523924af5ef1..14ffc85a4f68 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -18,12 +18,12 @@
#include <utils/Mutex.h>
-#include "AssetAtlas.h"
#include "Caches.h"
#include "Texture.h"
#include "TextureCache.h"
#include "Properties.h"
#include "utils/TraceUtils.h"
+#include "hwui/Bitmap.h"
namespace android {
namespace uirenderer {
@@ -36,8 +36,7 @@ TextureCache::TextureCache()
: mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity)
, mSize(0)
, mMaxSize(Properties::textureCacheSize)
- , mFlushRate(Properties::textureCacheFlushRate)
- , mAssetAtlas(nullptr) {
+ , mFlushRate(Properties::textureCacheFlushRate) {
mCache.setOnEntryRemovedListener(this);
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
@@ -84,10 +83,6 @@ void TextureCache::operator()(uint32_t&, Texture*& texture) {
// Caching
///////////////////////////////////////////////////////////////////////////////
-void TextureCache::setAssetAtlas(AssetAtlas* assetAtlas) {
- mAssetAtlas = assetAtlas;
-}
-
void TextureCache::resetMarkInUse(void* ownerToken) {
LruCache<uint32_t, Texture*>::Iterator iter(mCache);
while (iter.next()) {
@@ -97,7 +92,7 @@ void TextureCache::resetMarkInUse(void* ownerToken) {
}
}
-bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) {
+bool TextureCache::canMakeTextureFromBitmap(Bitmap* bitmap) {
if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)",
bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize);
@@ -108,15 +103,8 @@ bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) {
// Returns a prepared Texture* that either is already in the cache or can fit
// in the cache (and is thus added to the cache)
-Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) {
- if (CC_LIKELY(mAssetAtlas != nullptr) && atlasUsageType == AtlasUsageType::Use) {
- AssetAtlas::Entry* entry = mAssetAtlas->getEntry(bitmap->pixelRef());
- if (CC_UNLIKELY(entry)) {
- return entry->texture;
- }
- }
-
- Texture* texture = mCache.get(bitmap->pixelRef()->getStableID());
+Texture* TextureCache::getCachedTexture(Bitmap* bitmap) {
+ Texture* texture = mCache.get(bitmap->getStableID());
if (!texture) {
if (!canMakeTextureFromBitmap(bitmap)) {
@@ -147,7 +135,7 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a
if (mDebugEnabled) {
ALOGD("Texture created, size = %d", size);
}
- mCache.put(bitmap->pixelRef()->getStableID(), texture);
+ mCache.put(bitmap->getStableID(), texture);
}
} else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
// Texture was in the cache but is dirty, re-upload
@@ -159,20 +147,20 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a
return texture;
}
-bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap) {
- Texture* texture = getCachedTexture(bitmap, AtlasUsageType::Use);
+bool TextureCache::prefetchAndMarkInUse(void* ownerToken, Bitmap* bitmap) {
+ Texture* texture = getCachedTexture(bitmap);
if (texture) {
texture->isInUse = ownerToken;
}
return texture;
}
-bool TextureCache::prefetch(const SkBitmap* bitmap) {
- return getCachedTexture(bitmap, AtlasUsageType::Use);
+bool TextureCache::prefetch(Bitmap* bitmap) {
+ return getCachedTexture(bitmap);
}
-Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType) {
- Texture* texture = getCachedTexture(bitmap, atlasUsageType);
+Texture* TextureCache::get(Bitmap* bitmap) {
+ Texture* texture = getCachedTexture(bitmap);
if (!texture) {
if (!canMakeTextureFromBitmap(bitmap)) {
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 0a61b6b1a522..68a548bdb1ff 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -28,6 +28,9 @@
#include <unordered_map>
namespace android {
+
+class Bitmap;
+
namespace uirenderer {
class Texture;
@@ -47,8 +50,6 @@ class Texture;
// Classes
///////////////////////////////////////////////////////////////////////////////
-class AssetAtlas;
-
/**
* A simple LRU texture cache. The cache has a maximum size expressed in bytes.
* Any texture added to the cache causing the cache to grow beyond the maximum
@@ -75,30 +76,20 @@ public:
* acquired for the bitmap, false otherwise. If a Texture was acquired it is
* marked as in use.
*/
- bool prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap);
+ bool prefetchAndMarkInUse(void* ownerToken, Bitmap* bitmap);
/**
* Attempts to precache the SkBitmap. Returns true if a Texture was successfully
* acquired for the bitmap, false otherwise. Does not mark the Texture
* as in use and won't update currently in-use Textures.
*/
- bool prefetch(const SkBitmap* bitmap);
+ bool prefetch(Bitmap* bitmap);
/**
- * Returns the texture associated with the specified bitmap from either within the cache, or
- * the AssetAtlas. If the texture cannot be found in the cache, a new texture is generated.
+ * Returns the texture associated with the specified bitmap from within the cache.
+ * If the texture cannot be found in the cache, a new texture is generated.
*/
- Texture* get(const SkBitmap* bitmap) {
- return get(bitmap, AtlasUsageType::Use);
- }
-
- /**
- * Returns the texture associated with the specified bitmap. If the texture cannot be found in
- * the cache, a new texture is generated, even if it resides in the AssetAtlas.
- */
- Texture* getAndBypassAtlas(const SkBitmap* bitmap) {
- return get(bitmap, AtlasUsageType::Bypass);
- }
+ Texture* get(Bitmap* bitmap);
/**
* Removes the texture associated with the specified pixelRef. This is meant
@@ -130,18 +121,10 @@ public:
*/
void flush();
- void setAssetAtlas(AssetAtlas* assetAtlas);
-
private:
- enum class AtlasUsageType {
- Use,
- Bypass,
- };
+ bool canMakeTextureFromBitmap(Bitmap* bitmap);
- bool canMakeTextureFromBitmap(const SkBitmap* bitmap);
-
- Texture* get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType);
- Texture* getCachedTexture(const SkBitmap* bitmap, AtlasUsageType atlasUsageType);
+ Texture* getCachedTexture(Bitmap* bitmap);
LruCache<uint32_t, Texture*> mCache;
@@ -155,8 +138,6 @@ private:
std::vector<uint32_t> mGarbage;
mutable Mutex mLock;
-
- AssetAtlas* mAssetAtlas;
}; // class TextureCache
}; // namespace uirenderer
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index ac2bdccf5255..749efdd26927 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef TREEINFO_H
-#define TREEINFO_H
+
+#pragma once
#include "utils/Macros.h"
@@ -31,7 +31,6 @@ class CanvasContext;
class DamageAccumulator;
class LayerUpdateQueue;
-class OpenGLRenderer;
class RenderNode;
class RenderState;
@@ -89,13 +88,7 @@ public:
// Must not be null during actual usage
DamageAccumulator* damageAccumulator = nullptr;
-#if HWUI_NEW_OPS
LayerUpdateQueue* layerUpdateQueue = nullptr;
-#else
- // The renderer that will be drawing the next frame. Use this to push any
- // layer updates or similar. May be NULL.
- OpenGLRenderer* renderer = nullptr;
-#endif
ErrorHandler* errorHandler = nullptr;
// Optional, may be nullptr. Used to allow things to observe interesting
@@ -123,10 +116,11 @@ public:
bool canDrawThisFrame = true;
} out;
+ // This flag helps to disable projection for receiver nodes that do not have any backward
+ // projected children.
+ bool hasBackwardProjectedNodes = false;
// TODO: Damage calculations
};
} /* namespace uirenderer */
} /* namespace android */
-
-#endif /* TREEINFO_H */
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 223605fa34ed..208107f65743 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -201,10 +201,7 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca
SkPaint paint;
if (properties.getFillGradient() != nullptr) {
paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha()));
- SkShader* newShader = properties.getFillGradient()->newWithLocalMatrix(matrix);
- // newWithLocalMatrix(...) creates a new SkShader and returns a bare pointer. We need to
- // remove the extra ref so that the ref count is correctly managed.
- paint.setShader(newShader)->unref();
+ paint.setShader(properties.getFillGradient()->makeWithLocalMatrix(matrix));
needsFill = true;
} else if (properties.getFillColor() != SK_ColorTRANSPARENT) {
paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha()));
@@ -223,10 +220,7 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca
bool needsStroke = false;
if (properties.getStrokeGradient() != nullptr) {
paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha()));
- SkShader* newShader = properties.getStrokeGradient()->newWithLocalMatrix(matrix);
- // newWithLocalMatrix(...) creates a new SkShader and returns a bare pointer. We need to
- // remove the extra ref so that the ref count is correctly managed.
- paint.setShader(newShader)->unref();
+ paint.setShader(properties.getStrokeGradient()->makeWithLocalMatrix(matrix));
needsStroke = true;
} else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) {
paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha()));
@@ -314,7 +308,7 @@ void FullPath::FullPathProperties::setPropertyValue(int propertyId, float value)
void ClipPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath,
float strokeScale, const SkMatrix& matrix, bool useStagingData){
- outCanvas->clipPath(renderPath, SkRegion::kIntersect_Op);
+ outCanvas->clipPath(renderPath);
}
Group::Group(const Group& group) : Node(group) {
@@ -508,18 +502,18 @@ int Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter,
}
void Tree::drawStaging(Canvas* outCanvas) {
- bool redrawNeeded = allocateBitmapIfNeeded(&mStagingCache.bitmap,
+ bool redrawNeeded = allocateBitmapIfNeeded(mStagingCache,
mStagingProperties.getScaledWidth(), mStagingProperties.getScaledHeight());
// draw bitmap cache
if (redrawNeeded || mStagingCache.dirty) {
- updateBitmapCache(&mStagingCache.bitmap, true);
+ updateBitmapCache(*mStagingCache.bitmap, true);
mStagingCache.dirty = false;
}
SkPaint tmpPaint;
SkPaint* paint = updatePaint(&tmpPaint, &mStagingProperties);
- outCanvas->drawBitmap(mStagingCache.bitmap, 0, 0,
- mStagingCache.bitmap.width(), mStagingCache.bitmap.height(),
+ outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0,
+ mStagingCache.bitmap->width(), mStagingCache.bitmap->height(),
mStagingProperties.getBounds().left(), mStagingProperties.getBounds().top(),
mStagingProperties.getBounds().right(), mStagingProperties.getBounds().bottom(), paint);
}
@@ -534,49 +528,53 @@ SkPaint* Tree::updatePaint(SkPaint* outPaint, TreeProperties* prop) {
if (prop->getRootAlpha() == 1.0f && prop->getColorFilter() == nullptr) {
return nullptr;
} else {
- outPaint->setColorFilter(prop->getColorFilter());
+ outPaint->setColorFilter(sk_ref_sp(prop->getColorFilter()));
outPaint->setFilterQuality(kLow_SkFilterQuality);
outPaint->setAlpha(prop->getRootAlpha() * 255);
return outPaint;
}
}
-const SkBitmap& Tree::getBitmapUpdateIfDirty() {
- bool redrawNeeded = allocateBitmapIfNeeded(&mCache.bitmap, mProperties.getScaledWidth(),
+Bitmap& Tree::getBitmapUpdateIfDirty() {
+ bool redrawNeeded = allocateBitmapIfNeeded(mCache, mProperties.getScaledWidth(),
mProperties.getScaledHeight());
if (redrawNeeded || mCache.dirty) {
- updateBitmapCache(&mCache.bitmap, false);
+ updateBitmapCache(*mCache.bitmap, false);
mCache.dirty = false;
}
- return mCache.bitmap;
+ return *mCache.bitmap;
}
-void Tree::updateBitmapCache(SkBitmap* outCache, bool useStagingData) {
- outCache->eraseColor(SK_ColorTRANSPARENT);
- SkCanvas outCanvas(*outCache);
+void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
+ SkBitmap outCache;
+ bitmap.getSkBitmap(&outCache);
+ outCache.eraseColor(SK_ColorTRANSPARENT);
+ SkCanvas outCanvas(outCache);
float viewportWidth = useStagingData ?
mStagingProperties.getViewportWidth() : mProperties.getViewportWidth();
float viewportHeight = useStagingData ?
mStagingProperties.getViewportHeight() : mProperties.getViewportHeight();
- float scaleX = outCache->width() / viewportWidth;
- float scaleY = outCache->height() / viewportHeight;
+ float scaleX = outCache.width() / viewportWidth;
+ float scaleY = outCache.height() / viewportHeight;
mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY, useStagingData);
}
-bool Tree::allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height) {
- if (!canReuseBitmap(*outCache, width, height)) {
- SkImageInfo info = SkImageInfo::Make(width, height,
- kN32_SkColorType, kPremul_SkAlphaType);
- outCache->setInfo(info);
- // TODO: Count the bitmap cache against app's java heap
- outCache->allocPixels(info);
+bool Tree::allocateBitmapIfNeeded(Cache& cache, int width, int height) {
+ if (!canReuseBitmap(cache.bitmap.get(), width, height)) {
+#ifndef ANDROID_ENABLE_LINEAR_BLENDING
+ sk_sp<SkColorSpace> colorSpace = nullptr;
+#else
+ sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
+#endif
+ SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace);
+ cache.bitmap = Bitmap::allocateHeapBitmap(info);
return true;
}
return false;
}
-bool Tree::canReuseBitmap(const SkBitmap& bitmap, int width, int height) {
- return width <= bitmap.width() && height <= bitmap.height();
+bool Tree::canReuseBitmap(Bitmap* bitmap, int width, int height) {
+ return bitmap && width <= bitmap->width() && height <= bitmap->height();
}
void Tree::onPropertyChanged(TreeProperties* prop) {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index b867dbc59714..8244a3911183 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -18,6 +18,7 @@
#define ANDROID_HWUI_VPATH_H
#include "hwui/Canvas.h"
+#include "hwui/Bitmap.h"
#include "DisplayList.h"
#include <SkBitmap.h>
@@ -39,6 +40,13 @@
namespace android {
namespace uirenderer {
+// Debug
+#if DEBUG_VECTOR_DRAWABLE
+ #define VECTOR_DRAWABLE_LOGD(...) ALOGD(__VA_ARGS__)
+#else
+ #define VECTOR_DRAWABLE_LOGD(...)
+#endif
+
namespace VectorDrawable {
#define VD_SET_PRIMITIVE_FIELD_WITH_FLAG(field, value, flag) (VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(field, (value)) ? ((flag) = true, true) : false)
#define VD_SET_PROP(field, value) ((value) != (field) ? ((field) = (value), true) : false)
@@ -554,7 +562,7 @@ public:
const SkRect& bounds, bool needsMirroring, bool canReuseCache);
void drawStaging(Canvas* canvas);
- const SkBitmap& getBitmapUpdateIfDirty();
+ Bitmap& getBitmapUpdateIfDirty();
void setAllowCaching(bool allowCaching) {
mAllowCaching = allowCaching;
}
@@ -689,11 +697,15 @@ public:
void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; }
private:
+ struct Cache {
+ sk_sp<Bitmap> bitmap;
+ bool dirty = true;
+ };
SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
- bool allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height);
- bool canReuseBitmap(const SkBitmap&, int width, int height);
- void updateBitmapCache(SkBitmap* outCache, bool useStagingData);
+ bool allocateBitmapIfNeeded(Cache& cache, int width, int height);
+ bool canReuseBitmap(Bitmap*, int width, int height);
+ void updateBitmapCache(Bitmap& outCache, bool useStagingData);
// Cap the bitmap size, such that it won't hurt the performance too much
// and it won't crash due to a very large scale.
// The drawable will look blurry above this size.
@@ -706,10 +718,6 @@ private:
TreeProperties mStagingProperties = TreeProperties(this);
SkPaint mPaint;
- struct Cache {
- SkBitmap bitmap;
- bool dirty = true;
- };
Cache mStagingCache;
Cache mCache;
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index c1bf980658b2..db982ad0c8f4 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -19,6 +19,7 @@
#include "Vector.h"
+#include "FloatColor.h"
#include "utils/Macros.h"
namespace android {
@@ -76,21 +77,19 @@ struct TextureVertex {
REQUIRE_COMPATIBLE_LAYOUT(TextureVertex);
/**
- * Simple structure to describe a vertex with a position, texture UV and ARGB color.
+ * Simple structure to describe a vertex with a position, texture UV and an
+ * sRGB color with alpha. The color is stored pre-multiplied in linear space.
*/
struct ColorTextureVertex {
float x, y;
float u, v;
- float r, g, b, a;
+ float r, g, b, a; // pre-multiplied linear
static inline void set(ColorTextureVertex* vertex, float x, float y,
- float u, float v, int color) {
-
- float a = ((color >> 24) & 0xff) / 255.0f;
- float r = a * ((color >> 16) & 0xff) / 255.0f;
- float g = a * ((color >> 8) & 0xff) / 255.0f;
- float b = a * ((color) & 0xff) / 255.0f;
- *vertex = { x, y, u, v, r, g, b, a };
+ float u, float v, uint32_t color) {
+ FloatColor c;
+ c.set(color);
+ *vertex = { x, y, u, v, c.r, c.g, c.b, c.a };
}
}; // struct ColorTextureVertex
diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp
new file mode 100644
index 000000000000..537b3eaa3ad5
--- /dev/null
+++ b/libs/hwui/VkLayer.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VkLayer.h"
+
+#include "renderstate/RenderState.h"
+
+#include <SkCanvas.h>
+#include <SkSurface.h>
+
+namespace android {
+namespace uirenderer {
+
+void VkLayer::updateTexture() {
+ sk_sp<SkSurface> surface;
+ SkImageInfo info = SkImageInfo::MakeS32(mWidth, mHeight, kPremul_SkAlphaType);
+ surface = SkSurface::MakeRenderTarget(mRenderState.getGrContext(), SkBudgeted::kNo, info);
+ surface->getCanvas()->clear(SK_ColorBLUE);
+ mImage = surface->makeImageSnapshot(SkBudgeted::kNo);
+}
+
+void VkLayer::onVkContextDestroyed() {
+ mImage = nullptr;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h
new file mode 100644
index 000000000000..39522b3c0dda
--- /dev/null
+++ b/libs/hwui/VkLayer.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Layer.h"
+
+#include <SkImage.h>
+
+namespace android {
+namespace uirenderer {
+/**
+ * A layer has dimensions and is backed by a VkImage.
+ */
+class VkLayer : public Layer {
+public:
+ VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight)
+ : Layer(renderState, Api::Vulkan) {}
+
+ virtual ~VkLayer() {}
+
+ uint32_t getWidth() const override {
+ return mWidth;
+ }
+
+ uint32_t getHeight() const override {
+ return mHeight;
+ }
+
+ void setSize(uint32_t width, uint32_t height) override {
+ mWidth = width;
+ mHeight = height;
+ }
+
+ void setBlend(bool blend) override {
+ mBlend = blend;
+ }
+
+ bool isBlend() const override {
+ return mBlend;
+ }
+
+ sk_sp<SkImage> getImage() {
+ return mImage;
+ }
+
+ void updateTexture();
+
+ // If we've destroyed the vulkan context (VkInstance, VkDevice, etc.), we must make sure to
+ // destroy any VkImages that were made with that context.
+ void onVkContextDestroyed();
+
+private:
+ int mWidth;
+ int mHeight;
+ bool mBlend;
+
+ sk_sp<SkImage> mImage;
+
+}; // struct VkLayer
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/debug/DefaultGlesDriver.cpp b/libs/hwui/debug/DefaultGlesDriver.cpp
new file mode 100644
index 000000000000..4515ec1f25a5
--- /dev/null
+++ b/libs/hwui/debug/DefaultGlesDriver.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DefaultGlesDriver.h"
+
+#include "gles_undefine.h"
+
+#include <EGL/egl.h>
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+// Generate the proxy
+#define API_ENTRY(x) DefaultGlesDriver::x##_
+#define CALL_GL_API(x, ...) x(__VA_ARGS__);
+#define CALL_GL_API_RETURN(x, ...) return x(__VA_ARGS__);
+
+#include "gles_stubs.in"
+
+#undef API_ENTRY
+#undef CALL_GL_API
+#undef CALL_GL_API_RETURN
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/DefaultGlesDriver.h b/libs/hwui/debug/DefaultGlesDriver.h
new file mode 100644
index 000000000000..3eab97077004
--- /dev/null
+++ b/libs/hwui/debug/DefaultGlesDriver.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "GlesDriver.h"
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+class DefaultGlesDriver : public GlesDriver {
+public:
+#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override;
+ #include "gles_decls.in"
+#undef GL_ENTRY
+
+};
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/FatalBaseDriver.cpp b/libs/hwui/debug/FatalBaseDriver.cpp
new file mode 100644
index 000000000000..4c38fac33521
--- /dev/null
+++ b/libs/hwui/debug/FatalBaseDriver.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FatalBaseDriver.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+// Generate the proxy
+#define API_ENTRY(x) FatalBaseDriver::x##_
+#define CALL_GL_API(x, ...) LOG_ALWAYS_FATAL("Not Implemented");
+#define CALL_GL_API_RETURN(x, ...) \
+ LOG_ALWAYS_FATAL("Not Implemented"); \
+ return static_cast<decltype(x(__VA_ARGS__))>(0);
+
+#include "gles_stubs.in"
+
+#undef API_ENTRY
+#undef CALL_GL_API
+#undef CALL_GL_API_RETURN
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/FatalBaseDriver.h b/libs/hwui/debug/FatalBaseDriver.h
new file mode 100644
index 000000000000..76c30e90bd39
--- /dev/null
+++ b/libs/hwui/debug/FatalBaseDriver.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "GlesDriver.h"
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+// A base driver that implements all the pure virtuals in the form of
+// LOG_ALWAYS_FATALS. Suitable for selective-override implementations
+// where only a known subset of methods need to be overridden
+class FatalBaseDriver : public GlesDriver {
+public:
+#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override;
+ #include "gles_decls.in"
+#undef GL_ENTRY
+};
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/GlesDriver.cpp b/libs/hwui/debug/GlesDriver.cpp
new file mode 100644
index 000000000000..97e8f3a81b74
--- /dev/null
+++ b/libs/hwui/debug/GlesDriver.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GlesDriver.h"
+#include "DefaultGlesDriver.h"
+#include "GlesErrorCheckWrapper.h"
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+static DefaultGlesDriver sDefaultDriver;
+
+static std::unique_ptr<GlesDriver> sGlesDriver(new GlesErrorCheckWrapper(sDefaultDriver));
+
+GlesDriver* GlesDriver::get() {
+ return sGlesDriver.get();
+}
+
+std::unique_ptr<GlesDriver> GlesDriver::replace(std::unique_ptr<GlesDriver>&& driver) {
+ std::unique_ptr<GlesDriver> ret = std::move(sGlesDriver);
+ sGlesDriver = std::move(driver);
+ return ret;
+}
+
+sk_sp<const GrGLInterface> GlesDriver::getSkiaInterface() {
+ sk_sp<const GrGLInterface> skiaInterface(GrGLCreateNativeInterface());
+ return skiaInterface;
+}
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/GlesDriver.h b/libs/hwui/debug/GlesDriver.h
new file mode 100644
index 000000000000..3c36487f72cf
--- /dev/null
+++ b/libs/hwui/debug/GlesDriver.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef HWUI_GLES_WRAP_ENABLED
+#error Wrapping wasn't enabled, can't use this!
+#endif
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl31.h>
+#include <GLES3/gl32.h>
+
+#include <gl/GrGLInterface.h>
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+// All the gl methods on GlesDriver have a trailing underscore
+// This is to avoid collision with gles_redefine/gles_undefine
+class GlesDriver {
+public:
+ virtual ~GlesDriver() {}
+ virtual sk_sp<const GrGLInterface> getSkiaInterface();
+
+#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) = 0;
+ #include "gles_decls.in"
+#undef GL_ENTRY
+
+ static GlesDriver* get();
+ static std::unique_ptr<GlesDriver> replace(std::unique_ptr<GlesDriver>&& driver);
+};
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.cpp b/libs/hwui/debug/GlesErrorCheckWrapper.cpp
new file mode 100644
index 000000000000..7ededaaa7fc2
--- /dev/null
+++ b/libs/hwui/debug/GlesErrorCheckWrapper.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GlesErrorCheckWrapper.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+void GlesErrorCheckWrapper::assertNoErrors(const char* apicall) {
+ GLenum status = GL_NO_ERROR;
+ GLenum lastError = GL_NO_ERROR;
+ const char* lastErrorName = nullptr;
+ while ((status = mBase.glGetError_()) != GL_NO_ERROR) {
+ lastError = status;
+ switch (status) {
+ case GL_INVALID_ENUM:
+ ALOGE("GL error: GL_INVALID_ENUM");
+ lastErrorName = "GL_INVALID_ENUM";
+ break;
+ case GL_INVALID_VALUE:
+ ALOGE("GL error: GL_INVALID_VALUE");
+ lastErrorName = "GL_INVALID_VALUE";
+ break;
+ case GL_INVALID_OPERATION:
+ ALOGE("GL error: GL_INVALID_OPERATION");
+ lastErrorName = "GL_INVALID_OPERATION";
+ break;
+ case GL_OUT_OF_MEMORY:
+ ALOGE("GL error: Out of memory!");
+ lastErrorName = "GL_OUT_OF_MEMORY";
+ break;
+ default:
+ ALOGE("GL error: 0x%x", status);
+ lastErrorName = "UNKNOWN";
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(lastError != GL_NO_ERROR,
+ "%s error! %s (0x%x)", apicall, lastErrorName, lastError);
+}
+
+#define API_ENTRY(x) GlesErrorCheckWrapper::x##_
+#define CALL_GL_API(x, ...) \
+ mBase.x##_(__VA_ARGS__); assertNoErrors(#x)
+
+#define CALL_GL_API_RETURN(x, ...) \
+ auto ret = mBase.x##_(__VA_ARGS__); \
+ assertNoErrors(#x); \
+ return ret
+
+#include "gles_stubs.in"
+
+#undef API_ENTRY
+#undef CALL_GL_API
+#undef CALL_GL_API_RETURN
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.h b/libs/hwui/debug/GlesErrorCheckWrapper.h
new file mode 100644
index 000000000000..fd45fc0184a1
--- /dev/null
+++ b/libs/hwui/debug/GlesErrorCheckWrapper.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "GlesDriver.h"
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+class GlesErrorCheckWrapper : public GlesDriver {
+public:
+ GlesErrorCheckWrapper(GlesDriver& base) : mBase(base) {}
+
+#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override;
+ #include "gles_decls.in"
+#undef GL_ENTRY
+
+private:
+ void assertNoErrors(const char* apicall);
+
+ GlesDriver& mBase;
+};
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/MockGlesDriver.h b/libs/hwui/debug/MockGlesDriver.h
new file mode 100644
index 000000000000..e0bfc5780b21
--- /dev/null
+++ b/libs/hwui/debug/MockGlesDriver.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FatalBaseDriver.h"
+
+#include <gmock/gmock.h>
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+class MockGlesDriver : public FatalBaseDriver {
+public:
+ MOCK_METHOD2(glBindBuffer_, void(GLenum target, GLuint buffer));
+ MOCK_METHOD4(glBufferData_, void(GLenum target, GLsizeiptr size, const void *data, GLenum usage));
+ MOCK_METHOD2(glGenBuffers_, void(GLsizei n, GLuint *buffers));
+};
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/NullGlesDriver.cpp b/libs/hwui/debug/NullGlesDriver.cpp
new file mode 100644
index 000000000000..8fbe4bfe7033
--- /dev/null
+++ b/libs/hwui/debug/NullGlesDriver.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <debug/NullGlesDriver.h>
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+sk_sp<const GrGLInterface> NullGlesDriver::getSkiaInterface() {
+ sk_sp<const GrGLInterface> skiaInterface(GrGLCreateNullInterface());
+ return skiaInterface;
+}
+
+struct {
+ GLboolean scissorEnabled;
+} gState;
+
+static void nullglGenCommon(GLsizei n, GLuint *buffers) {
+ static GLuint nextId = 0;
+ int i;
+ for(i = 0; i < n; i++) {
+ buffers[i] = ++nextId;
+ }
+}
+
+void NullGlesDriver::glGenBuffers_(GLsizei n, GLuint *buffers) {
+ nullglGenCommon(n, buffers);
+}
+
+void NullGlesDriver::glGenFramebuffers_(GLsizei n, GLuint *framebuffers) {
+ nullglGenCommon(n, framebuffers);
+}
+
+void NullGlesDriver::glGenRenderbuffers_(GLsizei n, GLuint *renderbuffers) {
+ nullglGenCommon(n, renderbuffers);
+}
+
+void NullGlesDriver::glGenTextures_(GLsizei n, GLuint *textures) {
+ nullglGenCommon(n, textures);
+}
+
+GLuint NullGlesDriver::glCreateProgram_(void) {
+ static GLuint nextProgram = 0;
+ return ++nextProgram;
+}
+
+GLuint NullGlesDriver::glCreateShader_(GLenum type) {
+ static GLuint nextShader = 0;
+ return ++nextShader;
+}
+
+void NullGlesDriver::glGetProgramiv_(GLuint program, GLenum pname, GLint *params) {
+ switch (pname) {
+ case GL_DELETE_STATUS:
+ case GL_LINK_STATUS:
+ case GL_VALIDATE_STATUS:
+ *params = GL_TRUE;
+ break;
+ case GL_INFO_LOG_LENGTH:
+ *params = 16;
+ break;
+ }
+}
+
+void NullGlesDriver::glGetProgramInfoLog_(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
+ *length = snprintf(infoLog, bufSize, "success");
+ if (*length >= bufSize) {
+ *length = bufSize - 1;
+ }
+}
+
+void NullGlesDriver::glGetShaderiv_(GLuint shader, GLenum pname, GLint *params) {
+ switch (pname) {
+ case GL_COMPILE_STATUS:
+ case GL_DELETE_STATUS:
+ *params = GL_TRUE;
+ }
+}
+
+void NullGlesDriver::glGetShaderInfoLog_(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
+ *length = snprintf(infoLog, bufSize, "success");
+ if (*length >= bufSize) {
+ *length = bufSize - 1;
+ }
+}
+
+void setBooleanState(GLenum cap, GLboolean value) {
+ switch (cap) {
+ case GL_SCISSOR_TEST:
+ gState.scissorEnabled = value;
+ break;
+ }
+}
+
+void NullGlesDriver::glEnable_(GLenum cap) {
+ setBooleanState(cap, GL_TRUE);
+}
+
+void NullGlesDriver::glDisable_(GLenum cap) {
+ setBooleanState(cap, GL_FALSE);
+}
+
+GLboolean NullGlesDriver::glIsEnabled_(GLenum cap) {
+ switch (cap) {
+ case GL_SCISSOR_TEST:
+ return gState.scissorEnabled;
+ default:
+ return GL_FALSE;
+ }
+}
+
+void NullGlesDriver::glGetIntegerv_(GLenum pname, GLint *data) {
+ switch (pname) {
+ case GL_MAX_TEXTURE_SIZE:
+ *data = 2048;
+ break;
+ case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
+ *data = 4;
+ break;
+ default:
+ *data = 0;
+ }
+}
+
+GLenum NullGlesDriver::glCheckFramebufferStatus_(GLenum target) {
+ switch (target) {
+ case GL_FRAMEBUFFER:
+ return GL_FRAMEBUFFER_COMPLETE;
+ default:
+ return 0; // error case
+ }
+}
+
+static const char* getString(GLenum name) {
+ switch (name) {
+ case GL_VENDOR:
+ return "android";
+ case GL_RENDERER:
+ return "null";
+ case GL_VERSION:
+ return "OpenGL ES 2.0 rev1";
+ case GL_SHADING_LANGUAGE_VERSION:
+ return "OpenGL ES GLSL ES 2.0 rev1";
+ case GL_EXTENSIONS:
+ default:
+ return "";
+ }
+}
+
+const GLubyte* NullGlesDriver::glGetString_(GLenum name) {
+ return (GLubyte*) getString(name);
+}
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/NullGlesDriver.h b/libs/hwui/debug/NullGlesDriver.h
new file mode 100644
index 000000000000..37ca8f34f87b
--- /dev/null
+++ b/libs/hwui/debug/NullGlesDriver.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FatalBaseDriver.h"
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+class NullGlesDriver : public FatalBaseDriver {
+public:
+ virtual sk_sp<const GrGLInterface> getSkiaInterface() override;
+
+ virtual void glGenBuffers_(GLsizei n, GLuint *buffers) override;
+ virtual void glGenFramebuffers_(GLsizei n, GLuint *framebuffers) override;
+ virtual void glGenRenderbuffers_(GLsizei n, GLuint *renderbuffers) override;
+ virtual void glGenTextures_(GLsizei n, GLuint *textures) override;
+ virtual GLuint glCreateProgram_(void) override;
+ virtual GLuint glCreateShader_(GLenum type) override;
+ virtual void glGetProgramiv_(GLuint program, GLenum pname, GLint *params) override;
+ virtual void glGetProgramInfoLog_(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) override;
+ virtual void glGetShaderiv_(GLuint shader, GLenum pname, GLint *params) override;
+ virtual void glGetShaderInfoLog_(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) override;
+ virtual void glEnable_(GLenum cap) override;
+ virtual void glDisable_(GLenum cap) override;
+ virtual GLboolean glIsEnabled_(GLenum cap) override;
+ virtual void glGetIntegerv_(GLenum pname, GLint *data) override;
+ virtual const GLubyte* glGetString_(GLenum name) override;
+ virtual GLenum glCheckFramebufferStatus_(GLenum target) override;
+
+ virtual void glActiveTexture_(GLenum texture) override {}
+ virtual void glAttachShader_(GLuint program, GLuint shader) override {}
+ virtual void glBindAttribLocation_(GLuint program, GLuint index, const GLchar *name) override {}
+ virtual void glBindBuffer_(GLenum target, GLuint buffer) override {}
+ virtual void glBindFramebuffer_(GLenum target, GLuint framebuffer) override {}
+ virtual void glBindRenderbuffer_(GLenum target, GLuint renderbuffer) override {}
+ virtual void glBindTexture_(GLenum target, GLuint texture) override {}
+ virtual void glBlendColor_(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) override {}
+ virtual void glBlendEquation_(GLenum mode) override {}
+ virtual void glBlendEquationSeparate_(GLenum modeRGB, GLenum modeAlpha) override {}
+ virtual void glBlendFunc_(GLenum sfactor, GLenum dfactor) override {}
+ virtual void glBlendFuncSeparate_(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) override {}
+ virtual void glBufferData_(GLenum target, GLsizeiptr size, const void *data, GLenum usage) override {}
+ virtual void glBufferSubData_(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) override {}
+ virtual void glClear_(GLbitfield mask) override {}
+ virtual void glClearColor_(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) override {}
+ virtual void glClearDepthf_(GLfloat d) override {}
+ virtual void glClearStencil_(GLint s) override {}
+ virtual void glColorMask_(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) override {}
+ virtual void glCompileShader_(GLuint shader) override {}
+ virtual void glCompressedTexImage2D_(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) override {}
+ virtual void glCompressedTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) override {}
+ virtual void glCopyTexImage2D_(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) override {}
+ virtual void glCopyTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) override {}
+ virtual void glCullFace_(GLenum mode) override {}
+ virtual void glDeleteBuffers_(GLsizei n, const GLuint *buffers) override {}
+ virtual void glDeleteFramebuffers_(GLsizei n, const GLuint *framebuffers) override {}
+ virtual void glDeleteProgram_(GLuint program) override {}
+ virtual void glDeleteRenderbuffers_(GLsizei n, const GLuint *renderbuffers) override {}
+ virtual void glDeleteShader_(GLuint shader) override {}
+ virtual void glDeleteTextures_(GLsizei n, const GLuint *textures) override {}
+ virtual void glDepthFunc_(GLenum func) override {}
+ virtual void glDepthMask_(GLboolean flag) override {}
+ virtual void glDepthRangef_(GLfloat n, GLfloat f) override {}
+ virtual void glDetachShader_(GLuint program, GLuint shader) override {}
+ virtual void glDisableVertexAttribArray_(GLuint index) override {}
+ virtual void glDrawArrays_(GLenum mode, GLint first, GLsizei count) override {}
+ virtual void glDrawElements_(GLenum mode, GLsizei count, GLenum type, const void *indices) override {}
+ virtual void glEnableVertexAttribArray_(GLuint index) override {}
+ virtual void glFinish_(void) override {}
+ virtual void glFlush_(void) override {}
+ virtual void glFramebufferRenderbuffer_(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) override {}
+ virtual void glFramebufferTexture2D_(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) override {}
+ virtual void glFrontFace_(GLenum mode) override {}
+ virtual void glGenerateMipmap_(GLenum target) override {}
+ virtual GLint glGetAttribLocation_(GLuint program, const GLchar *name) override { return 1; }
+ virtual GLenum glGetError_(void) override { return GL_NO_ERROR; }
+ virtual GLint glGetUniformLocation_(GLuint program, const GLchar *name) override { return 2; }
+ virtual void glHint_(GLenum target, GLenum mode) override {}
+ virtual void glLineWidth_(GLfloat width) override {}
+ virtual void glLinkProgram_(GLuint program) override {}
+ virtual void glPixelStorei_(GLenum pname, GLint param) override {}
+ virtual void glPolygonOffset_(GLfloat factor, GLfloat units) override {}
+ virtual void glReadPixels_(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) override {}
+ virtual void glReleaseShaderCompiler_(void) override {}
+ virtual void glRenderbufferStorage_(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) override {}
+ virtual void glSampleCoverage_(GLfloat value, GLboolean invert) override {}
+ virtual void glScissor_(GLint x, GLint y, GLsizei width, GLsizei height) override {}
+ virtual void glShaderBinary_(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) override {}
+ virtual void glShaderSource_(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) override {}
+ virtual void glStencilFunc_(GLenum func, GLint ref, GLuint mask) override {}
+ virtual void glStencilFuncSeparate_(GLenum face, GLenum func, GLint ref, GLuint mask) override {}
+ virtual void glStencilMask_(GLuint mask) override {}
+ virtual void glStencilMaskSeparate_(GLenum face, GLuint mask) override {}
+ virtual void glStencilOp_(GLenum fail, GLenum zfail, GLenum zpass) override {}
+ virtual void glStencilOpSeparate_(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) override {}
+ virtual void glTexImage2D_(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) override {}
+ virtual void glTexParameterf_(GLenum target, GLenum pname, GLfloat param) override {}
+ virtual void glTexParameterfv_(GLenum target, GLenum pname, const GLfloat *params) override {}
+ virtual void glTexParameteri_(GLenum target, GLenum pname, GLint param) override {}
+ virtual void glTexParameteriv_(GLenum target, GLenum pname, const GLint *params) override {}
+ virtual void glTexSubImage2D_(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) override {}
+ virtual void glUniform1f_(GLint location, GLfloat v0) override {}
+ virtual void glUniform1fv_(GLint location, GLsizei count, const GLfloat *value) override {}
+ virtual void glUniform1i_(GLint location, GLint v0) override {}
+ virtual void glUniform1iv_(GLint location, GLsizei count, const GLint *value) override {}
+ virtual void glUniform2f_(GLint location, GLfloat v0, GLfloat v1) override {}
+ virtual void glUniform2fv_(GLint location, GLsizei count, const GLfloat *value) override {}
+ virtual void glUniform2i_(GLint location, GLint v0, GLint v1) override {}
+ virtual void glUniform2iv_(GLint location, GLsizei count, const GLint *value) override {}
+ virtual void glUniform3f_(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) override {}
+ virtual void glUniform3fv_(GLint location, GLsizei count, const GLfloat *value) override {}
+ virtual void glUniform3i_(GLint location, GLint v0, GLint v1, GLint v2) override {}
+ virtual void glUniform3iv_(GLint location, GLsizei count, const GLint *value) override {}
+ virtual void glUniform4f_(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) override {}
+ virtual void glUniform4fv_(GLint location, GLsizei count, const GLfloat *value) override {}
+ virtual void glUniform4i_(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) override {}
+ virtual void glUniform4iv_(GLint location, GLsizei count, const GLint *value) override {}
+ virtual void glUniformMatrix2fv_(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override {}
+ virtual void glUniformMatrix3fv_(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override {}
+ virtual void glUniformMatrix4fv_(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) override {}
+ virtual void glUseProgram_(GLuint program) override {}
+ virtual void glValidateProgram_(GLuint program) override {}
+ virtual void glVertexAttrib1f_(GLuint index, GLfloat x) override {}
+ virtual void glVertexAttrib1fv_(GLuint index, const GLfloat *v) override {}
+ virtual void glVertexAttrib2f_(GLuint index, GLfloat x, GLfloat y) override {}
+ virtual void glVertexAttrib2fv_(GLuint index, const GLfloat *v) override {}
+ virtual void glVertexAttrib3f_(GLuint index, GLfloat x, GLfloat y, GLfloat z) override {}
+ virtual void glVertexAttrib3fv_(GLuint index, const GLfloat *v) override {}
+ virtual void glVertexAttrib4f_(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) override {}
+ virtual void glVertexAttrib4fv_(GLuint index, const GLfloat *v) override {}
+ virtual void glVertexAttribPointer_(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) override {}
+ virtual void glViewport_(GLint x, GLint y, GLsizei width, GLsizei height) override {}
+
+ // gles2 ext
+ virtual void glInsertEventMarkerEXT_(GLsizei length, const GLchar *marker) override {}
+ virtual void glPushGroupMarkerEXT_(GLsizei length, const GLchar *marker) override {}
+ virtual void glPopGroupMarkerEXT_(void) override {}
+ virtual void glDiscardFramebufferEXT_(GLenum target, GLsizei numAttachments, const GLenum *attachments) override {}
+ virtual void glEGLImageTargetTexture2DOES_(GLenum target, GLeglImageOES image) override {}
+
+ // GLES3
+ virtual void* glMapBufferRange_(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) override {
+ return 0;
+ }
+
+ virtual GLboolean glUnmapBuffer_(GLenum target) override {
+ return GL_FALSE;
+ }
+};
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/ScopedReplaceDriver.h b/libs/hwui/debug/ScopedReplaceDriver.h
new file mode 100644
index 000000000000..342c9d223727
--- /dev/null
+++ b/libs/hwui/debug/ScopedReplaceDriver.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "GlesDriver.h"
+
+namespace android {
+namespace uirenderer {
+namespace debug {
+
+template <typename Driver>
+class ScopedReplaceDriver {
+public:
+ ScopedReplaceDriver() {
+ std::unique_ptr<Driver> glDriver = std::make_unique<Driver>();
+ mCurrentDriver = glDriver.get();
+ mOldDriver = GlesDriver::replace(std::move(glDriver));
+ }
+
+ Driver& get() { return *mCurrentDriver; }
+
+ ~ScopedReplaceDriver() {
+ GlesDriver::replace(std::move(mOldDriver));
+ }
+private:
+ std::unique_ptr<GlesDriver> mOldDriver;
+ Driver* mCurrentDriver;
+};
+
+} // namespace debug
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/debug/gles_decls.in b/libs/hwui/debug/gles_decls.in
new file mode 100644
index 000000000000..16574a7fb074
--- /dev/null
+++ b/libs/hwui/debug/gles_decls.in
@@ -0,0 +1,544 @@
+GL_ENTRY(void, glActiveTexture, GLenum texture)
+GL_ENTRY(void, glAttachShader, GLuint program, GLuint shader)
+GL_ENTRY(void, glBindAttribLocation, GLuint program, GLuint index, const GLchar *name)
+GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer)
+GL_ENTRY(void, glBindFramebuffer, GLenum target, GLuint framebuffer)
+GL_ENTRY(void, glBindRenderbuffer, GLenum target, GLuint renderbuffer)
+GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture)
+GL_ENTRY(void, glBlendColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+GL_ENTRY(void, glBlendEquation, GLenum mode)
+GL_ENTRY(void, glBlendEquationSeparate, GLenum modeRGB, GLenum modeAlpha)
+GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor)
+GL_ENTRY(void, glBlendFuncSeparate, GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)
+GL_ENTRY(void, glBufferData, GLenum target, GLsizeiptr size, const void *data, GLenum usage)
+GL_ENTRY(void, glBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void *data)
+GL_ENTRY(GLenum, glCheckFramebufferStatus, GLenum target)
+GL_ENTRY(void, glClear, GLbitfield mask)
+GL_ENTRY(void, glClearColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+GL_ENTRY(void, glClearDepthf, GLfloat d)
+GL_ENTRY(void, glClearStencil, GLint s)
+GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)
+GL_ENTRY(void, glCompileShader, GLuint shader)
+GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data)
+GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data)
+GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
+GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(GLuint, glCreateProgram, void)
+GL_ENTRY(GLuint, glCreateShader, GLenum type)
+GL_ENTRY(void, glCullFace, GLenum mode)
+GL_ENTRY(void, glDeleteBuffers, GLsizei n, const GLuint *buffers)
+GL_ENTRY(void, glDeleteFramebuffers, GLsizei n, const GLuint *framebuffers)
+GL_ENTRY(void, glDeleteProgram, GLuint program)
+GL_ENTRY(void, glDeleteRenderbuffers, GLsizei n, const GLuint *renderbuffers)
+GL_ENTRY(void, glDeleteShader, GLuint shader)
+GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *textures)
+GL_ENTRY(void, glDepthFunc, GLenum func)
+GL_ENTRY(void, glDepthMask, GLboolean flag)
+GL_ENTRY(void, glDepthRangef, GLfloat n, GLfloat f)
+GL_ENTRY(void, glDetachShader, GLuint program, GLuint shader)
+GL_ENTRY(void, glDisable, GLenum cap)
+GL_ENTRY(void, glDisableVertexAttribArray, GLuint index)
+GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count)
+GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const void *indices)
+GL_ENTRY(void, glEnable, GLenum cap)
+GL_ENTRY(void, glEnableVertexAttribArray, GLuint index)
+GL_ENTRY(void, glFinish, void)
+GL_ENTRY(void, glFlush, void)
+GL_ENTRY(void, glFramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
+GL_ENTRY(void, glFramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
+GL_ENTRY(void, glFrontFace, GLenum mode)
+GL_ENTRY(void, glGenBuffers, GLsizei n, GLuint *buffers)
+GL_ENTRY(void, glGenerateMipmap, GLenum target)
+GL_ENTRY(void, glGenFramebuffers, GLsizei n, GLuint *framebuffers)
+GL_ENTRY(void, glGenRenderbuffers, GLsizei n, GLuint *renderbuffers)
+GL_ENTRY(void, glGenTextures, GLsizei n, GLuint *textures)
+GL_ENTRY(void, glGetActiveAttrib, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
+GL_ENTRY(void, glGetActiveUniform, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
+GL_ENTRY(void, glGetAttachedShaders, GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders)
+GL_ENTRY(GLint, glGetAttribLocation, GLuint program, const GLchar *name)
+GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean *data)
+GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(GLenum, glGetError, void)
+GL_ENTRY(void, glGetFloatv, GLenum pname, GLfloat *data)
+GL_ENTRY(void, glGetFramebufferAttachmentParameteriv, GLenum target, GLenum attachment, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetIntegerv, GLenum pname, GLint *data)
+GL_ENTRY(void, glGetProgramiv, GLuint program, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetProgramInfoLog, GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
+GL_ENTRY(void, glGetRenderbufferParameteriv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetShaderiv, GLuint shader, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetShaderInfoLog, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
+GL_ENTRY(void, glGetShaderPrecisionFormat, GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision)
+GL_ENTRY(void, glGetShaderSource, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source)
+GL_ENTRY(const GLubyte *, glGetString, GLenum name)
+GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetUniformfv, GLuint program, GLint location, GLfloat *params)
+GL_ENTRY(void, glGetUniformiv, GLuint program, GLint location, GLint *params)
+GL_ENTRY(GLint, glGetUniformLocation, GLuint program, const GLchar *name)
+GL_ENTRY(void, glGetVertexAttribfv, GLuint index, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glGetVertexAttribiv, GLuint index, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetVertexAttribPointerv, GLuint index, GLenum pname, void **pointer)
+GL_ENTRY(void, glHint, GLenum target, GLenum mode)
+GL_ENTRY(GLboolean, glIsBuffer, GLuint buffer)
+GL_ENTRY(GLboolean, glIsEnabled, GLenum cap)
+GL_ENTRY(GLboolean, glIsFramebuffer, GLuint framebuffer)
+GL_ENTRY(GLboolean, glIsProgram, GLuint program)
+GL_ENTRY(GLboolean, glIsRenderbuffer, GLuint renderbuffer)
+GL_ENTRY(GLboolean, glIsShader, GLuint shader)
+GL_ENTRY(GLboolean, glIsTexture, GLuint texture)
+GL_ENTRY(void, glLineWidth, GLfloat width)
+GL_ENTRY(void, glLinkProgram, GLuint program)
+GL_ENTRY(void, glPixelStorei, GLenum pname, GLint param)
+GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units)
+GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels)
+GL_ENTRY(void, glReleaseShaderCompiler, void)
+GL_ENTRY(void, glRenderbufferStorage, GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glSampleCoverage, GLfloat value, GLboolean invert)
+GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glShaderBinary, GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length)
+GL_ENTRY(void, glShaderSource, GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length)
+GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask)
+GL_ENTRY(void, glStencilFuncSeparate, GLenum face, GLenum func, GLint ref, GLuint mask)
+GL_ENTRY(void, glStencilMask, GLuint mask)
+GL_ENTRY(void, glStencilMaskSeparate, GLenum face, GLuint mask)
+GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass)
+GL_ENTRY(void, glStencilOpSeparate, GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)
+GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels)
+GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param)
+GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat *params)
+GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param)
+GL_ENTRY(void, glTexParameteriv, GLenum target, GLenum pname, const GLint *params)
+GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
+GL_ENTRY(void, glUniform1f, GLint location, GLfloat v0)
+GL_ENTRY(void, glUniform1fv, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glUniform1i, GLint location, GLint v0)
+GL_ENTRY(void, glUniform1iv, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glUniform2f, GLint location, GLfloat v0, GLfloat v1)
+GL_ENTRY(void, glUniform2fv, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glUniform2i, GLint location, GLint v0, GLint v1)
+GL_ENTRY(void, glUniform2iv, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glUniform3f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2)
+GL_ENTRY(void, glUniform3fv, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glUniform3i, GLint location, GLint v0, GLint v1, GLint v2)
+GL_ENTRY(void, glUniform3iv, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glUniform4f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
+GL_ENTRY(void, glUniform4fv, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glUniform4i, GLint location, GLint v0, GLint v1, GLint v2, GLint v3)
+GL_ENTRY(void, glUniform4iv, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glUniformMatrix2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glUniformMatrix3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glUniformMatrix4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glUseProgram, GLuint program)
+GL_ENTRY(void, glValidateProgram, GLuint program)
+GL_ENTRY(void, glVertexAttrib1f, GLuint index, GLfloat x)
+GL_ENTRY(void, glVertexAttrib1fv, GLuint index, const GLfloat *v)
+GL_ENTRY(void, glVertexAttrib2f, GLuint index, GLfloat x, GLfloat y)
+GL_ENTRY(void, glVertexAttrib2fv, GLuint index, const GLfloat *v)
+GL_ENTRY(void, glVertexAttrib3f, GLuint index, GLfloat x, GLfloat y, GLfloat z)
+GL_ENTRY(void, glVertexAttrib3fv, GLuint index, const GLfloat *v)
+GL_ENTRY(void, glVertexAttrib4f, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+GL_ENTRY(void, glVertexAttrib4fv, GLuint index, const GLfloat *v)
+GL_ENTRY(void, glVertexAttribPointer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glReadBuffer, GLenum src)
+GL_ENTRY(void, glDrawRangeElements, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices)
+GL_ENTRY(void, glTexImage3D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels)
+GL_ENTRY(void, glTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels)
+GL_ENTRY(void, glCopyTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glCompressedTexImage3D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data)
+GL_ENTRY(void, glCompressedTexSubImage3D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data)
+GL_ENTRY(void, glGenQueries, GLsizei n, GLuint *ids)
+GL_ENTRY(void, glDeleteQueries, GLsizei n, const GLuint *ids)
+GL_ENTRY(GLboolean, glIsQuery, GLuint id)
+GL_ENTRY(void, glBeginQuery, GLenum target, GLuint id)
+GL_ENTRY(void, glEndQuery, GLenum target)
+GL_ENTRY(void, glGetQueryiv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetQueryObjectuiv, GLuint id, GLenum pname, GLuint *params)
+GL_ENTRY(GLboolean, glUnmapBuffer, GLenum target)
+GL_ENTRY(void, glGetBufferPointerv, GLenum target, GLenum pname, void **params)
+GL_ENTRY(void, glDrawBuffers, GLsizei n, const GLenum *bufs)
+GL_ENTRY(void, glUniformMatrix2x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glUniformMatrix3x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glUniformMatrix2x4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glUniformMatrix4x2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glUniformMatrix3x4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glUniformMatrix4x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glBlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
+GL_ENTRY(void, glRenderbufferStorageMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glFramebufferTextureLayer, GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)
+GL_ENTRY(void *, glMapBufferRange, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
+GL_ENTRY(void, glFlushMappedBufferRange, GLenum target, GLintptr offset, GLsizeiptr length)
+GL_ENTRY(void, glBindVertexArray, GLuint array)
+GL_ENTRY(void, glDeleteVertexArrays, GLsizei n, const GLuint *arrays)
+GL_ENTRY(void, glGenVertexArrays, GLsizei n, GLuint *arrays)
+GL_ENTRY(GLboolean, glIsVertexArray, GLuint array)
+GL_ENTRY(void, glGetIntegeri_v, GLenum target, GLuint index, GLint *data)
+GL_ENTRY(void, glBeginTransformFeedback, GLenum primitiveMode)
+GL_ENTRY(void, glEndTransformFeedback, void)
+GL_ENTRY(void, glBindBufferRange, GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size)
+GL_ENTRY(void, glBindBufferBase, GLenum target, GLuint index, GLuint buffer)
+GL_ENTRY(void, glTransformFeedbackVaryings, GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode)
+GL_ENTRY(void, glGetTransformFeedbackVarying, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name)
+GL_ENTRY(void, glVertexAttribIPointer, GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer)
+GL_ENTRY(void, glGetVertexAttribIiv, GLuint index, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetVertexAttribIuiv, GLuint index, GLenum pname, GLuint *params)
+GL_ENTRY(void, glVertexAttribI4i, GLuint index, GLint x, GLint y, GLint z, GLint w)
+GL_ENTRY(void, glVertexAttribI4ui, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w)
+GL_ENTRY(void, glVertexAttribI4iv, GLuint index, const GLint *v)
+GL_ENTRY(void, glVertexAttribI4uiv, GLuint index, const GLuint *v)
+GL_ENTRY(void, glGetUniformuiv, GLuint program, GLint location, GLuint *params)
+GL_ENTRY(GLint, glGetFragDataLocation, GLuint program, const GLchar *name)
+GL_ENTRY(void, glUniform1ui, GLint location, GLuint v0)
+GL_ENTRY(void, glUniform2ui, GLint location, GLuint v0, GLuint v1)
+GL_ENTRY(void, glUniform3ui, GLint location, GLuint v0, GLuint v1, GLuint v2)
+GL_ENTRY(void, glUniform4ui, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3)
+GL_ENTRY(void, glUniform1uiv, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glUniform2uiv, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glUniform3uiv, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glUniform4uiv, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glClearBufferiv, GLenum buffer, GLint drawbuffer, const GLint *value)
+GL_ENTRY(void, glClearBufferuiv, GLenum buffer, GLint drawbuffer, const GLuint *value)
+GL_ENTRY(void, glClearBufferfv, GLenum buffer, GLint drawbuffer, const GLfloat *value)
+GL_ENTRY(void, glClearBufferfi, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)
+GL_ENTRY(const GLubyte *, glGetStringi, GLenum name, GLuint index)
+GL_ENTRY(void, glCopyBufferSubData, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size)
+GL_ENTRY(void, glGetUniformIndices, GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices)
+GL_ENTRY(void, glGetActiveUniformsiv, GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params)
+GL_ENTRY(GLuint, glGetUniformBlockIndex, GLuint program, const GLchar *uniformBlockName)
+GL_ENTRY(void, glGetActiveUniformBlockiv, GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetActiveUniformBlockName, GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName)
+GL_ENTRY(void, glUniformBlockBinding, GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
+GL_ENTRY(void, glDrawArraysInstanced, GLenum mode, GLint first, GLsizei count, GLsizei instancecount)
+GL_ENTRY(void, glDrawElementsInstanced, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount)
+GL_ENTRY(GLsync, glFenceSync, GLenum condition, GLbitfield flags)
+GL_ENTRY(GLboolean, glIsSync, GLsync sync)
+GL_ENTRY(void, glDeleteSync, GLsync sync)
+GL_ENTRY(GLenum, glClientWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout)
+GL_ENTRY(void, glWaitSync, GLsync sync, GLbitfield flags, GLuint64 timeout)
+GL_ENTRY(void, glGetInteger64v, GLenum pname, GLint64 *data)
+GL_ENTRY(void, glGetSynciv, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values)
+GL_ENTRY(void, glGetInteger64i_v, GLenum target, GLuint index, GLint64 *data)
+GL_ENTRY(void, glGetBufferParameteri64v, GLenum target, GLenum pname, GLint64 *params)
+GL_ENTRY(void, glGenSamplers, GLsizei count, GLuint *samplers)
+GL_ENTRY(void, glDeleteSamplers, GLsizei count, const GLuint *samplers)
+GL_ENTRY(GLboolean, glIsSampler, GLuint sampler)
+GL_ENTRY(void, glBindSampler, GLuint unit, GLuint sampler)
+GL_ENTRY(void, glSamplerParameteri, GLuint sampler, GLenum pname, GLint param)
+GL_ENTRY(void, glSamplerParameteriv, GLuint sampler, GLenum pname, const GLint *param)
+GL_ENTRY(void, glSamplerParameterf, GLuint sampler, GLenum pname, GLfloat param)
+GL_ENTRY(void, glSamplerParameterfv, GLuint sampler, GLenum pname, const GLfloat *param)
+GL_ENTRY(void, glGetSamplerParameteriv, GLuint sampler, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetSamplerParameterfv, GLuint sampler, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glVertexAttribDivisor, GLuint index, GLuint divisor)
+GL_ENTRY(void, glBindTransformFeedback, GLenum target, GLuint id)
+GL_ENTRY(void, glDeleteTransformFeedbacks, GLsizei n, const GLuint *ids)
+GL_ENTRY(void, glGenTransformFeedbacks, GLsizei n, GLuint *ids)
+GL_ENTRY(GLboolean, glIsTransformFeedback, GLuint id)
+GL_ENTRY(void, glPauseTransformFeedback, void)
+GL_ENTRY(void, glResumeTransformFeedback, void)
+GL_ENTRY(void, glGetProgramBinary, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary)
+GL_ENTRY(void, glProgramBinary, GLuint program, GLenum binaryFormat, const void *binary, GLsizei length)
+GL_ENTRY(void, glProgramParameteri, GLuint program, GLenum pname, GLint value)
+GL_ENTRY(void, glInvalidateFramebuffer, GLenum target, GLsizei numAttachments, const GLenum *attachments)
+GL_ENTRY(void, glInvalidateSubFramebuffer, GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glTexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glTexStorage3D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
+GL_ENTRY(void, glGetInternalformativ, GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params)
+GL_ENTRY(void, glDispatchCompute, GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z)
+GL_ENTRY(void, glDispatchComputeIndirect, GLintptr indirect)
+GL_ENTRY(void, glDrawArraysIndirect, GLenum mode, const void *indirect)
+GL_ENTRY(void, glDrawElementsIndirect, GLenum mode, GLenum type, const void *indirect)
+GL_ENTRY(void, glFramebufferParameteri, GLenum target, GLenum pname, GLint param)
+GL_ENTRY(void, glGetFramebufferParameteriv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetProgramInterfaceiv, GLuint program, GLenum programInterface, GLenum pname, GLint *params)
+GL_ENTRY(GLuint, glGetProgramResourceIndex, GLuint program, GLenum programInterface, const GLchar *name)
+GL_ENTRY(void, glGetProgramResourceName, GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name)
+GL_ENTRY(void, glGetProgramResourceiv, GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params)
+GL_ENTRY(GLint, glGetProgramResourceLocation, GLuint program, GLenum programInterface, const GLchar *name)
+GL_ENTRY(void, glUseProgramStages, GLuint pipeline, GLbitfield stages, GLuint program)
+GL_ENTRY(void, glActiveShaderProgram, GLuint pipeline, GLuint program)
+GL_ENTRY(GLuint, glCreateShaderProgramv, GLenum type, GLsizei count, const GLchar *const*strings)
+GL_ENTRY(void, glBindProgramPipeline, GLuint pipeline)
+GL_ENTRY(void, glDeleteProgramPipelines, GLsizei n, const GLuint *pipelines)
+GL_ENTRY(void, glGenProgramPipelines, GLsizei n, GLuint *pipelines)
+GL_ENTRY(GLboolean, glIsProgramPipeline, GLuint pipeline)
+GL_ENTRY(void, glGetProgramPipelineiv, GLuint pipeline, GLenum pname, GLint *params)
+GL_ENTRY(void, glProgramUniform1i, GLuint program, GLint location, GLint v0)
+GL_ENTRY(void, glProgramUniform2i, GLuint program, GLint location, GLint v0, GLint v1)
+GL_ENTRY(void, glProgramUniform3i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2)
+GL_ENTRY(void, glProgramUniform4i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3)
+GL_ENTRY(void, glProgramUniform1ui, GLuint program, GLint location, GLuint v0)
+GL_ENTRY(void, glProgramUniform2ui, GLuint program, GLint location, GLuint v0, GLuint v1)
+GL_ENTRY(void, glProgramUniform3ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2)
+GL_ENTRY(void, glProgramUniform4ui, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3)
+GL_ENTRY(void, glProgramUniform1f, GLuint program, GLint location, GLfloat v0)
+GL_ENTRY(void, glProgramUniform2f, GLuint program, GLint location, GLfloat v0, GLfloat v1)
+GL_ENTRY(void, glProgramUniform3f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2)
+GL_ENTRY(void, glProgramUniform4f, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
+GL_ENTRY(void, glProgramUniform1iv, GLuint program, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glProgramUniform2iv, GLuint program, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glProgramUniform3iv, GLuint program, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glProgramUniform4iv, GLuint program, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glProgramUniform1uiv, GLuint program, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glProgramUniform2uiv, GLuint program, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glProgramUniform3uiv, GLuint program, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glProgramUniform4uiv, GLuint program, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glProgramUniform1fv, GLuint program, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glProgramUniform2fv, GLuint program, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glProgramUniform3fv, GLuint program, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glProgramUniform4fv, GLuint program, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix2x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix3x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix2x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix4x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix3x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix4x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glValidateProgramPipeline, GLuint pipeline)
+GL_ENTRY(void, glGetProgramPipelineInfoLog, GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
+GL_ENTRY(void, glBindImageTexture, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format)
+GL_ENTRY(void, glGetBooleani_v, GLenum target, GLuint index, GLboolean *data)
+GL_ENTRY(void, glMemoryBarrier, GLbitfield barriers)
+GL_ENTRY(void, glMemoryBarrierByRegion, GLbitfield barriers)
+GL_ENTRY(void, glTexStorage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations)
+GL_ENTRY(void, glGetMultisamplefv, GLenum pname, GLuint index, GLfloat *val)
+GL_ENTRY(void, glSampleMaski, GLuint maskNumber, GLbitfield mask)
+GL_ENTRY(void, glGetTexLevelParameteriv, GLenum target, GLint level, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetTexLevelParameterfv, GLenum target, GLint level, GLenum pname, GLfloat *params)
+GL_ENTRY(void, glBindVertexBuffer, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride)
+GL_ENTRY(void, glVertexAttribFormat, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset)
+GL_ENTRY(void, glVertexAttribIFormat, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset)
+GL_ENTRY(void, glVertexAttribBinding, GLuint attribindex, GLuint bindingindex)
+GL_ENTRY(void, glVertexBindingDivisor, GLuint bindingindex, GLuint divisor)
+GL_ENTRY(void, glBlendBarrier, void)
+GL_ENTRY(void, glCopyImageSubData, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth)
+GL_ENTRY(void, glDebugMessageControl, GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled)
+GL_ENTRY(void, glDebugMessageInsert, GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf)
+GL_ENTRY(void, glDebugMessageCallback, GLDEBUGPROC callback, const void *userParam)
+GL_ENTRY(GLuint, glGetDebugMessageLog, GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog)
+GL_ENTRY(void, glPushDebugGroup, GLenum source, GLuint id, GLsizei length, const GLchar *message)
+GL_ENTRY(void, glPopDebugGroup, void)
+GL_ENTRY(void, glObjectLabel, GLenum identifier, GLuint name, GLsizei length, const GLchar *label)
+GL_ENTRY(void, glGetObjectLabel, GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label)
+GL_ENTRY(void, glObjectPtrLabel, const void *ptr, GLsizei length, const GLchar *label)
+GL_ENTRY(void, glGetObjectPtrLabel, const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label)
+GL_ENTRY(void, glGetPointerv, GLenum pname, void **params)
+GL_ENTRY(void, glEnablei, GLenum target, GLuint index)
+GL_ENTRY(void, glDisablei, GLenum target, GLuint index)
+GL_ENTRY(void, glBlendEquationi, GLuint buf, GLenum mode)
+GL_ENTRY(void, glBlendEquationSeparatei, GLuint buf, GLenum modeRGB, GLenum modeAlpha)
+GL_ENTRY(void, glBlendFunci, GLuint buf, GLenum src, GLenum dst)
+GL_ENTRY(void, glBlendFuncSeparatei, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
+GL_ENTRY(void, glColorMaski, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a)
+GL_ENTRY(GLboolean, glIsEnabledi, GLenum target, GLuint index)
+GL_ENTRY(void, glDrawElementsBaseVertex, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex)
+GL_ENTRY(void, glDrawRangeElementsBaseVertex, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex)
+GL_ENTRY(void, glDrawElementsInstancedBaseVertex, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex)
+GL_ENTRY(void, glFramebufferTexture, GLenum target, GLenum attachment, GLuint texture, GLint level)
+GL_ENTRY(void, glPrimitiveBoundingBox, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW)
+GL_ENTRY(GLenum, glGetGraphicsResetStatus, void)
+GL_ENTRY(void, glReadnPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data)
+GL_ENTRY(void, glGetnUniformfv, GLuint program, GLint location, GLsizei bufSize, GLfloat *params)
+GL_ENTRY(void, glGetnUniformiv, GLuint program, GLint location, GLsizei bufSize, GLint *params)
+GL_ENTRY(void, glGetnUniformuiv, GLuint program, GLint location, GLsizei bufSize, GLuint *params)
+GL_ENTRY(void, glMinSampleShading, GLfloat value)
+GL_ENTRY(void, glPatchParameteri, GLenum pname, GLint value)
+GL_ENTRY(void, glTexParameterIiv, GLenum target, GLenum pname, const GLint *params)
+GL_ENTRY(void, glTexParameterIuiv, GLenum target, GLenum pname, const GLuint *params)
+GL_ENTRY(void, glGetTexParameterIiv, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetTexParameterIuiv, GLenum target, GLenum pname, GLuint *params)
+GL_ENTRY(void, glSamplerParameterIiv, GLuint sampler, GLenum pname, const GLint *param)
+GL_ENTRY(void, glSamplerParameterIuiv, GLuint sampler, GLenum pname, const GLuint *param)
+GL_ENTRY(void, glGetSamplerParameterIiv, GLuint sampler, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetSamplerParameterIuiv, GLuint sampler, GLenum pname, GLuint *params)
+GL_ENTRY(void, glTexBuffer, GLenum target, GLenum internalformat, GLuint buffer)
+GL_ENTRY(void, glTexBufferRange, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size)
+GL_ENTRY(void, glTexStorage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations)
+GL_ENTRY(void, glBlendBarrierKHR, void)
+GL_ENTRY(void, glDebugMessageControlKHR, GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled)
+GL_ENTRY(void, glDebugMessageInsertKHR, GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf)
+GL_ENTRY(void, glDebugMessageCallbackKHR, GLDEBUGPROCKHR callback, const void *userParam)
+GL_ENTRY(GLuint, glGetDebugMessageLogKHR, GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog)
+GL_ENTRY(void, glPushDebugGroupKHR, GLenum source, GLuint id, GLsizei length, const GLchar *message)
+GL_ENTRY(void, glPopDebugGroupKHR, void)
+GL_ENTRY(void, glObjectLabelKHR, GLenum identifier, GLuint name, GLsizei length, const GLchar *label)
+GL_ENTRY(void, glGetObjectLabelKHR, GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label)
+GL_ENTRY(void, glObjectPtrLabelKHR, const void *ptr, GLsizei length, const GLchar *label)
+GL_ENTRY(void, glGetObjectPtrLabelKHR, const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label)
+GL_ENTRY(void, glGetPointervKHR, GLenum pname, void **params)
+GL_ENTRY(GLenum, glGetGraphicsResetStatusKHR, void)
+GL_ENTRY(void, glReadnPixelsKHR, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data)
+GL_ENTRY(void, glGetnUniformfvKHR, GLuint program, GLint location, GLsizei bufSize, GLfloat *params)
+GL_ENTRY(void, glGetnUniformivKHR, GLuint program, GLint location, GLsizei bufSize, GLint *params)
+GL_ENTRY(void, glGetnUniformuivKHR, GLuint program, GLint location, GLsizei bufSize, GLuint *params)
+GL_ENTRY(void, glEGLImageTargetTexture2DOES, GLenum target, GLeglImageOES image)
+GL_ENTRY(void, glEGLImageTargetRenderbufferStorageOES, GLenum target, GLeglImageOES image)
+GL_ENTRY(void, glCopyImageSubDataOES, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth)
+GL_ENTRY(void, glEnableiOES, GLenum target, GLuint index)
+GL_ENTRY(void, glDisableiOES, GLenum target, GLuint index)
+GL_ENTRY(void, glBlendEquationiOES, GLuint buf, GLenum mode)
+GL_ENTRY(void, glBlendEquationSeparateiOES, GLuint buf, GLenum modeRGB, GLenum modeAlpha)
+GL_ENTRY(void, glBlendFunciOES, GLuint buf, GLenum src, GLenum dst)
+GL_ENTRY(void, glBlendFuncSeparateiOES, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
+GL_ENTRY(void, glColorMaskiOES, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a)
+GL_ENTRY(GLboolean, glIsEnablediOES, GLenum target, GLuint index)
+GL_ENTRY(void, glDrawElementsBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex)
+GL_ENTRY(void, glDrawRangeElementsBaseVertexOES, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex)
+GL_ENTRY(void, glDrawElementsInstancedBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex)
+GL_ENTRY(void, glMultiDrawElementsBaseVertexOES, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex)
+GL_ENTRY(void, glFramebufferTextureOES, GLenum target, GLenum attachment, GLuint texture, GLint level)
+GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary)
+GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const void *binary, GLint length)
+GL_ENTRY(void *, glMapBufferOES, GLenum target, GLenum access)
+GL_ENTRY(GLboolean, glUnmapBufferOES, GLenum target)
+GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, void **params)
+GL_ENTRY(void, glPrimitiveBoundingBoxOES, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW)
+GL_ENTRY(void, glMinSampleShadingOES, GLfloat value)
+GL_ENTRY(void, glPatchParameteriOES, GLenum pname, GLint value)
+GL_ENTRY(void, glTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels)
+GL_ENTRY(void, glTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels)
+GL_ENTRY(void, glCopyTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glCompressedTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data)
+GL_ENTRY(void, glCompressedTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data)
+GL_ENTRY(void, glFramebufferTexture3DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)
+GL_ENTRY(void, glTexParameterIivOES, GLenum target, GLenum pname, const GLint *params)
+GL_ENTRY(void, glTexParameterIuivOES, GLenum target, GLenum pname, const GLuint *params)
+GL_ENTRY(void, glGetTexParameterIivOES, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetTexParameterIuivOES, GLenum target, GLenum pname, GLuint *params)
+GL_ENTRY(void, glSamplerParameterIivOES, GLuint sampler, GLenum pname, const GLint *param)
+GL_ENTRY(void, glSamplerParameterIuivOES, GLuint sampler, GLenum pname, const GLuint *param)
+GL_ENTRY(void, glGetSamplerParameterIivOES, GLuint sampler, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetSamplerParameterIuivOES, GLuint sampler, GLenum pname, GLuint *params)
+GL_ENTRY(void, glTexBufferOES, GLenum target, GLenum internalformat, GLuint buffer)
+GL_ENTRY(void, glTexBufferRangeOES, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size)
+GL_ENTRY(void, glTexStorage3DMultisampleOES, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations)
+GL_ENTRY(void, glTextureViewOES, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers)
+GL_ENTRY(void, glBindVertexArrayOES, GLuint array)
+GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays)
+GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays)
+GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array)
+GL_ENTRY(void, glDrawArraysInstancedBaseInstanceEXT, GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance)
+GL_ENTRY(void, glDrawElementsInstancedBaseInstanceEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance)
+GL_ENTRY(void, glDrawElementsInstancedBaseVertexBaseInstanceEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance)
+GL_ENTRY(void, glBindFragDataLocationIndexedEXT, GLuint program, GLuint colorNumber, GLuint index, const GLchar *name)
+GL_ENTRY(void, glBindFragDataLocationEXT, GLuint program, GLuint color, const GLchar *name)
+GL_ENTRY(GLint, glGetProgramResourceLocationIndexEXT, GLuint program, GLenum programInterface, const GLchar *name)
+GL_ENTRY(GLint, glGetFragDataIndexEXT, GLuint program, const GLchar *name)
+GL_ENTRY(void, glBufferStorageEXT, GLenum target, GLsizeiptr size, const void *data, GLbitfield flags)
+GL_ENTRY(void, glCopyImageSubDataEXT, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth)
+GL_ENTRY(void, glLabelObjectEXT, GLenum type, GLuint object, GLsizei length, const GLchar *label)
+GL_ENTRY(void, glGetObjectLabelEXT, GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label)
+GL_ENTRY(void, glInsertEventMarkerEXT, GLsizei length, const GLchar *marker)
+GL_ENTRY(void, glPushGroupMarkerEXT, GLsizei length, const GLchar *marker)
+GL_ENTRY(void, glPopGroupMarkerEXT, void)
+GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments)
+GL_ENTRY(void, glGenQueriesEXT, GLsizei n, GLuint *ids)
+GL_ENTRY(void, glDeleteQueriesEXT, GLsizei n, const GLuint *ids)
+GL_ENTRY(GLboolean, glIsQueryEXT, GLuint id)
+GL_ENTRY(void, glBeginQueryEXT, GLenum target, GLuint id)
+GL_ENTRY(void, glEndQueryEXT, GLenum target)
+GL_ENTRY(void, glQueryCounterEXT, GLuint id, GLenum target)
+GL_ENTRY(void, glGetQueryivEXT, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetQueryObjectivEXT, GLuint id, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetQueryObjectuivEXT, GLuint id, GLenum pname, GLuint *params)
+GL_ENTRY(void, glGetQueryObjecti64vEXT, GLuint id, GLenum pname, GLint64 *params)
+GL_ENTRY(void, glGetQueryObjectui64vEXT, GLuint id, GLenum pname, GLuint64 *params)
+GL_ENTRY(void, glDrawBuffersEXT, GLsizei n, const GLenum *bufs)
+GL_ENTRY(void, glEnableiEXT, GLenum target, GLuint index)
+GL_ENTRY(void, glDisableiEXT, GLenum target, GLuint index)
+GL_ENTRY(void, glBlendEquationiEXT, GLuint buf, GLenum mode)
+GL_ENTRY(void, glBlendEquationSeparateiEXT, GLuint buf, GLenum modeRGB, GLenum modeAlpha)
+GL_ENTRY(void, glBlendFunciEXT, GLuint buf, GLenum src, GLenum dst)
+GL_ENTRY(void, glBlendFuncSeparateiEXT, GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
+GL_ENTRY(void, glColorMaskiEXT, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a)
+GL_ENTRY(GLboolean, glIsEnablediEXT, GLenum target, GLuint index)
+GL_ENTRY(void, glDrawElementsBaseVertexEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex)
+GL_ENTRY(void, glDrawRangeElementsBaseVertexEXT, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex)
+GL_ENTRY(void, glDrawElementsInstancedBaseVertexEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex)
+GL_ENTRY(void, glMultiDrawElementsBaseVertexEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex)
+GL_ENTRY(void, glDrawArraysInstancedEXT, GLenum mode, GLint start, GLsizei count, GLsizei primcount)
+GL_ENTRY(void, glDrawElementsInstancedEXT, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount)
+GL_ENTRY(void, glFramebufferTextureEXT, GLenum target, GLenum attachment, GLuint texture, GLint level)
+GL_ENTRY(void, glVertexAttribDivisorEXT, GLuint index, GLuint divisor)
+GL_ENTRY(void *, glMapBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
+GL_ENTRY(void, glFlushMappedBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length)
+GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount)
+GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount)
+GL_ENTRY(void, glMultiDrawArraysIndirectEXT, GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride)
+GL_ENTRY(void, glMultiDrawElementsIndirectEXT, GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride)
+GL_ENTRY(void, glRenderbufferStorageMultisampleEXT, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glFramebufferTexture2DMultisampleEXT, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples)
+GL_ENTRY(void, glReadBufferIndexedEXT, GLenum src, GLint index)
+GL_ENTRY(void, glDrawBuffersIndexedEXT, GLint n, const GLenum *location, const GLint *indices)
+GL_ENTRY(void, glGetIntegeri_vEXT, GLenum target, GLuint index, GLint *data)
+GL_ENTRY(void, glPrimitiveBoundingBoxEXT, GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW)
+GL_ENTRY(void, glRasterSamplesEXT, GLuint samples, GLboolean fixedsamplelocations)
+GL_ENTRY(GLenum, glGetGraphicsResetStatusEXT, void)
+GL_ENTRY(void, glReadnPixelsEXT, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data)
+GL_ENTRY(void, glGetnUniformfvEXT, GLuint program, GLint location, GLsizei bufSize, GLfloat *params)
+GL_ENTRY(void, glGetnUniformivEXT, GLuint program, GLint location, GLsizei bufSize, GLint *params)
+GL_ENTRY(void, glActiveShaderProgramEXT, GLuint pipeline, GLuint program)
+GL_ENTRY(void, glBindProgramPipelineEXT, GLuint pipeline)
+GL_ENTRY(GLuint, glCreateShaderProgramvEXT, GLenum type, GLsizei count, const GLchar **strings)
+GL_ENTRY(void, glDeleteProgramPipelinesEXT, GLsizei n, const GLuint *pipelines)
+GL_ENTRY(void, glGenProgramPipelinesEXT, GLsizei n, GLuint *pipelines)
+GL_ENTRY(void, glGetProgramPipelineInfoLogEXT, GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
+GL_ENTRY(void, glGetProgramPipelineivEXT, GLuint pipeline, GLenum pname, GLint *params)
+GL_ENTRY(GLboolean, glIsProgramPipelineEXT, GLuint pipeline)
+GL_ENTRY(void, glProgramParameteriEXT, GLuint program, GLenum pname, GLint value)
+GL_ENTRY(void, glProgramUniform1fEXT, GLuint program, GLint location, GLfloat v0)
+GL_ENTRY(void, glProgramUniform1fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glProgramUniform1iEXT, GLuint program, GLint location, GLint v0)
+GL_ENTRY(void, glProgramUniform1ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glProgramUniform2fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1)
+GL_ENTRY(void, glProgramUniform2fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glProgramUniform2iEXT, GLuint program, GLint location, GLint v0, GLint v1)
+GL_ENTRY(void, glProgramUniform2ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glProgramUniform3fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2)
+GL_ENTRY(void, glProgramUniform3fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glProgramUniform3iEXT, GLuint program, GLint location, GLint v0, GLint v1, GLint v2)
+GL_ENTRY(void, glProgramUniform3ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glProgramUniform4fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
+GL_ENTRY(void, glProgramUniform4fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value)
+GL_ENTRY(void, glProgramUniform4iEXT, GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3)
+GL_ENTRY(void, glProgramUniform4ivEXT, GLuint program, GLint location, GLsizei count, const GLint *value)
+GL_ENTRY(void, glProgramUniformMatrix2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glUseProgramStagesEXT, GLuint pipeline, GLbitfield stages, GLuint program)
+GL_ENTRY(void, glValidateProgramPipelineEXT, GLuint pipeline)
+GL_ENTRY(void, glProgramUniform1uiEXT, GLuint program, GLint location, GLuint v0)
+GL_ENTRY(void, glProgramUniform2uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1)
+GL_ENTRY(void, glProgramUniform3uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2)
+GL_ENTRY(void, glProgramUniform4uiEXT, GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3)
+GL_ENTRY(void, glProgramUniform1uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glProgramUniform2uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glProgramUniform3uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glProgramUniform4uivEXT, GLuint program, GLint location, GLsizei count, const GLuint *value)
+GL_ENTRY(void, glProgramUniformMatrix2x3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix3x2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix2x4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix4x2fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix3x4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glProgramUniformMatrix4x3fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+GL_ENTRY(void, glTexPageCommitmentEXT, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit)
+GL_ENTRY(void, glPatchParameteriEXT, GLenum pname, GLint value)
+GL_ENTRY(void, glTexParameterIivEXT, GLenum target, GLenum pname, const GLint *params)
+GL_ENTRY(void, glTexParameterIuivEXT, GLenum target, GLenum pname, const GLuint *params)
+GL_ENTRY(void, glGetTexParameterIivEXT, GLenum target, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetTexParameterIuivEXT, GLenum target, GLenum pname, GLuint *params)
+GL_ENTRY(void, glSamplerParameterIivEXT, GLuint sampler, GLenum pname, const GLint *param)
+GL_ENTRY(void, glSamplerParameterIuivEXT, GLuint sampler, GLenum pname, const GLuint *param)
+GL_ENTRY(void, glGetSamplerParameterIivEXT, GLuint sampler, GLenum pname, GLint *params)
+GL_ENTRY(void, glGetSamplerParameterIuivEXT, GLuint sampler, GLenum pname, GLuint *params)
+GL_ENTRY(void, glTexBufferEXT, GLenum target, GLenum internalformat, GLuint buffer)
+GL_ENTRY(void, glTexBufferRangeEXT, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size)
+GL_ENTRY(void, glTexStorage1DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width)
+GL_ENTRY(void, glTexStorage2DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glTexStorage3DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
+GL_ENTRY(void, glTextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width)
+GL_ENTRY(void, glTextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glTextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
+GL_ENTRY(void, glTextureViewEXT, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) \ No newline at end of file
diff --git a/libs/hwui/debug/gles_redefine.h b/libs/hwui/debug/gles_redefine.h
new file mode 100644
index 000000000000..201f0a945209
--- /dev/null
+++ b/libs/hwui/debug/gles_redefine.h
@@ -0,0 +1,913 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define glActiveShaderProgram wrap_glActiveShaderProgram
+#define glActiveShaderProgramEXT wrap_glActiveShaderProgramEXT
+#define glActiveTexture wrap_glActiveTexture
+#define glAlphaFunc wrap_glAlphaFunc
+#define glAlphaFuncQCOM wrap_glAlphaFuncQCOM
+#define glAlphaFuncx wrap_glAlphaFuncx
+#define glAlphaFuncxOES wrap_glAlphaFuncxOES
+#define glApplyFramebufferAttachmentCMAAINTEL wrap_glApplyFramebufferAttachmentCMAAINTEL
+#define glAttachShader wrap_glAttachShader
+#define glBeginConditionalRenderNV wrap_glBeginConditionalRenderNV
+#define glBeginPerfMonitorAMD wrap_glBeginPerfMonitorAMD
+#define glBeginPerfQueryINTEL wrap_glBeginPerfQueryINTEL
+#define glBeginQuery wrap_glBeginQuery
+#define glBeginQueryEXT wrap_glBeginQueryEXT
+#define glBeginTransformFeedback wrap_glBeginTransformFeedback
+#define glBindAttribLocation wrap_glBindAttribLocation
+#define glBindBuffer wrap_glBindBuffer
+#define glBindBufferBase wrap_glBindBufferBase
+#define glBindBufferRange wrap_glBindBufferRange
+#define glBindFragDataLocationEXT wrap_glBindFragDataLocationEXT
+#define glBindFragDataLocationIndexedEXT wrap_glBindFragDataLocationIndexedEXT
+#define glBindFramebuffer wrap_glBindFramebuffer
+#define glBindFramebufferOES wrap_glBindFramebufferOES
+#define glBindImageTexture wrap_glBindImageTexture
+#define glBindProgramPipeline wrap_glBindProgramPipeline
+#define glBindProgramPipelineEXT wrap_glBindProgramPipelineEXT
+#define glBindRenderbuffer wrap_glBindRenderbuffer
+#define glBindRenderbufferOES wrap_glBindRenderbufferOES
+#define glBindSampler wrap_glBindSampler
+#define glBindTexture wrap_glBindTexture
+#define glBindTransformFeedback wrap_glBindTransformFeedback
+#define glBindVertexArray wrap_glBindVertexArray
+#define glBindVertexArrayOES wrap_glBindVertexArrayOES
+#define glBindVertexBuffer wrap_glBindVertexBuffer
+#define glBlendBarrier wrap_glBlendBarrier
+#define glBlendBarrierKHR wrap_glBlendBarrierKHR
+#define glBlendBarrierNV wrap_glBlendBarrierNV
+#define glBlendColor wrap_glBlendColor
+#define glBlendEquation wrap_glBlendEquation
+#define glBlendEquationOES wrap_glBlendEquationOES
+#define glBlendEquationSeparate wrap_glBlendEquationSeparate
+#define glBlendEquationSeparateOES wrap_glBlendEquationSeparateOES
+#define glBlendEquationSeparatei wrap_glBlendEquationSeparatei
+#define glBlendEquationSeparateiEXT wrap_glBlendEquationSeparateiEXT
+#define glBlendEquationSeparateiOES wrap_glBlendEquationSeparateiOES
+#define glBlendEquationi wrap_glBlendEquationi
+#define glBlendEquationiEXT wrap_glBlendEquationiEXT
+#define glBlendEquationiOES wrap_glBlendEquationiOES
+#define glBlendFunc wrap_glBlendFunc
+#define glBlendFuncSeparate wrap_glBlendFuncSeparate
+#define glBlendFuncSeparateOES wrap_glBlendFuncSeparateOES
+#define glBlendFuncSeparatei wrap_glBlendFuncSeparatei
+#define glBlendFuncSeparateiEXT wrap_glBlendFuncSeparateiEXT
+#define glBlendFuncSeparateiOES wrap_glBlendFuncSeparateiOES
+#define glBlendFunci wrap_glBlendFunci
+#define glBlendFunciEXT wrap_glBlendFunciEXT
+#define glBlendFunciOES wrap_glBlendFunciOES
+#define glBlendParameteriNV wrap_glBlendParameteriNV
+#define glBlitFramebuffer wrap_glBlitFramebuffer
+#define glBlitFramebufferANGLE wrap_glBlitFramebufferANGLE
+#define glBlitFramebufferNV wrap_glBlitFramebufferNV
+#define glBufferData wrap_glBufferData
+#define glBufferStorageEXT wrap_glBufferStorageEXT
+#define glBufferSubData wrap_glBufferSubData
+#define glCheckFramebufferStatus wrap_glCheckFramebufferStatus
+#define glCheckFramebufferStatusOES wrap_glCheckFramebufferStatusOES
+#define glClear wrap_glClear
+#define glClearBufferfi wrap_glClearBufferfi
+#define glClearBufferfv wrap_glClearBufferfv
+#define glClearBufferiv wrap_glClearBufferiv
+#define glClearBufferuiv wrap_glClearBufferuiv
+#define glClearColor wrap_glClearColor
+#define glClearColorx wrap_glClearColorx
+#define glClearColorxOES wrap_glClearColorxOES
+#define glClearDepthf wrap_glClearDepthf
+#define glClearDepthfOES wrap_glClearDepthfOES
+#define glClearDepthx wrap_glClearDepthx
+#define glClearDepthxOES wrap_glClearDepthxOES
+#define glClearStencil wrap_glClearStencil
+#define glClientActiveTexture wrap_glClientActiveTexture
+#define glClientWaitSync wrap_glClientWaitSync
+#define glClientWaitSyncAPPLE wrap_glClientWaitSyncAPPLE
+#define glClipPlanef wrap_glClipPlanef
+#define glClipPlanefIMG wrap_glClipPlanefIMG
+#define glClipPlanefOES wrap_glClipPlanefOES
+#define glClipPlanex wrap_glClipPlanex
+#define glClipPlanexIMG wrap_glClipPlanexIMG
+#define glClipPlanexOES wrap_glClipPlanexOES
+#define glColor4f wrap_glColor4f
+#define glColor4ub wrap_glColor4ub
+#define glColor4x wrap_glColor4x
+#define glColor4xOES wrap_glColor4xOES
+#define glColorMask wrap_glColorMask
+#define glColorMaski wrap_glColorMaski
+#define glColorMaskiEXT wrap_glColorMaskiEXT
+#define glColorMaskiOES wrap_glColorMaskiOES
+#define glColorPointer wrap_glColorPointer
+#define glCompileShader wrap_glCompileShader
+#define glCompressedTexImage2D wrap_glCompressedTexImage2D
+#define glCompressedTexImage3D wrap_glCompressedTexImage3D
+#define glCompressedTexImage3DOES wrap_glCompressedTexImage3DOES
+#define glCompressedTexSubImage2D wrap_glCompressedTexSubImage2D
+#define glCompressedTexSubImage3D wrap_glCompressedTexSubImage3D
+#define glCompressedTexSubImage3DOES wrap_glCompressedTexSubImage3DOES
+#define glCopyBufferSubData wrap_glCopyBufferSubData
+#define glCopyBufferSubDataNV wrap_glCopyBufferSubDataNV
+#define glCopyImageSubData wrap_glCopyImageSubData
+#define glCopyImageSubDataEXT wrap_glCopyImageSubDataEXT
+#define glCopyImageSubDataOES wrap_glCopyImageSubDataOES
+#define glCopyPathNV wrap_glCopyPathNV
+#define glCopyTexImage2D wrap_glCopyTexImage2D
+#define glCopyTexSubImage2D wrap_glCopyTexSubImage2D
+#define glCopyTexSubImage3D wrap_glCopyTexSubImage3D
+#define glCopyTexSubImage3DOES wrap_glCopyTexSubImage3DOES
+#define glCopyTextureLevelsAPPLE wrap_glCopyTextureLevelsAPPLE
+#define glCoverFillPathInstancedNV wrap_glCoverFillPathInstancedNV
+#define glCoverFillPathNV wrap_glCoverFillPathNV
+#define glCoverStrokePathInstancedNV wrap_glCoverStrokePathInstancedNV
+#define glCoverStrokePathNV wrap_glCoverStrokePathNV
+#define glCoverageMaskNV wrap_glCoverageMaskNV
+#define glCoverageModulationNV wrap_glCoverageModulationNV
+#define glCoverageModulationTableNV wrap_glCoverageModulationTableNV
+#define glCoverageOperationNV wrap_glCoverageOperationNV
+#define glCreatePerfQueryINTEL wrap_glCreatePerfQueryINTEL
+#define glCreateProgram wrap_glCreateProgram
+#define glCreateShader wrap_glCreateShader
+#define glCreateShaderProgramv wrap_glCreateShaderProgramv
+#define glCreateShaderProgramvEXT wrap_glCreateShaderProgramvEXT
+#define glCullFace wrap_glCullFace
+#define glCurrentPaletteMatrixOES wrap_glCurrentPaletteMatrixOES
+#define glDebugMessageCallback wrap_glDebugMessageCallback
+#define glDebugMessageCallbackKHR wrap_glDebugMessageCallbackKHR
+#define glDebugMessageControl wrap_glDebugMessageControl
+#define glDebugMessageControlKHR wrap_glDebugMessageControlKHR
+#define glDebugMessageInsert wrap_glDebugMessageInsert
+#define glDebugMessageInsertKHR wrap_glDebugMessageInsertKHR
+#define glDeleteBuffers wrap_glDeleteBuffers
+#define glDeleteFencesNV wrap_glDeleteFencesNV
+#define glDeleteFramebuffers wrap_glDeleteFramebuffers
+#define glDeleteFramebuffersOES wrap_glDeleteFramebuffersOES
+#define glDeletePathsNV wrap_glDeletePathsNV
+#define glDeletePerfMonitorsAMD wrap_glDeletePerfMonitorsAMD
+#define glDeletePerfQueryINTEL wrap_glDeletePerfQueryINTEL
+#define glDeleteProgram wrap_glDeleteProgram
+#define glDeleteProgramPipelines wrap_glDeleteProgramPipelines
+#define glDeleteProgramPipelinesEXT wrap_glDeleteProgramPipelinesEXT
+#define glDeleteQueries wrap_glDeleteQueries
+#define glDeleteQueriesEXT wrap_glDeleteQueriesEXT
+#define glDeleteRenderbuffers wrap_glDeleteRenderbuffers
+#define glDeleteRenderbuffersOES wrap_glDeleteRenderbuffersOES
+#define glDeleteSamplers wrap_glDeleteSamplers
+#define glDeleteShader wrap_glDeleteShader
+#define glDeleteSync wrap_glDeleteSync
+#define glDeleteSyncAPPLE wrap_glDeleteSyncAPPLE
+#define glDeleteTextures wrap_glDeleteTextures
+#define glDeleteTransformFeedbacks wrap_glDeleteTransformFeedbacks
+#define glDeleteVertexArrays wrap_glDeleteVertexArrays
+#define glDeleteVertexArraysOES wrap_glDeleteVertexArraysOES
+#define glDepthFunc wrap_glDepthFunc
+#define glDepthMask wrap_glDepthMask
+#define glDepthRangeArrayfvNV wrap_glDepthRangeArrayfvNV
+#define glDepthRangeIndexedfNV wrap_glDepthRangeIndexedfNV
+#define glDepthRangef wrap_glDepthRangef
+#define glDepthRangefOES wrap_glDepthRangefOES
+#define glDepthRangex wrap_glDepthRangex
+#define glDepthRangexOES wrap_glDepthRangexOES
+#define glDetachShader wrap_glDetachShader
+#define glDisable wrap_glDisable
+#define glDisableClientState wrap_glDisableClientState
+#define glDisableDriverControlQCOM wrap_glDisableDriverControlQCOM
+#define glDisableVertexAttribArray wrap_glDisableVertexAttribArray
+#define glDisablei wrap_glDisablei
+#define glDisableiEXT wrap_glDisableiEXT
+#define glDisableiNV wrap_glDisableiNV
+#define glDisableiOES wrap_glDisableiOES
+#define glDiscardFramebufferEXT wrap_glDiscardFramebufferEXT
+#define glDispatchCompute wrap_glDispatchCompute
+#define glDispatchComputeIndirect wrap_glDispatchComputeIndirect
+#define glDrawArrays wrap_glDrawArrays
+#define glDrawArraysIndirect wrap_glDrawArraysIndirect
+#define glDrawArraysInstanced wrap_glDrawArraysInstanced
+#define glDrawArraysInstancedANGLE wrap_glDrawArraysInstancedANGLE
+#define glDrawArraysInstancedBaseInstanceEXT wrap_glDrawArraysInstancedBaseInstanceEXT
+#define glDrawArraysInstancedEXT wrap_glDrawArraysInstancedEXT
+#define glDrawArraysInstancedNV wrap_glDrawArraysInstancedNV
+#define glDrawBuffers wrap_glDrawBuffers
+#define glDrawBuffersEXT wrap_glDrawBuffersEXT
+#define glDrawBuffersIndexedEXT wrap_glDrawBuffersIndexedEXT
+#define glDrawBuffersNV wrap_glDrawBuffersNV
+#define glDrawElements wrap_glDrawElements
+#define glDrawElementsBaseVertex wrap_glDrawElementsBaseVertex
+#define glDrawElementsBaseVertexEXT wrap_glDrawElementsBaseVertexEXT
+#define glDrawElementsBaseVertexOES wrap_glDrawElementsBaseVertexOES
+#define glDrawElementsIndirect wrap_glDrawElementsIndirect
+#define glDrawElementsInstanced wrap_glDrawElementsInstanced
+#define glDrawElementsInstancedANGLE wrap_glDrawElementsInstancedANGLE
+#define glDrawElementsInstancedBaseInstanceEXT wrap_glDrawElementsInstancedBaseInstanceEXT
+#define glDrawElementsInstancedBaseVertex wrap_glDrawElementsInstancedBaseVertex
+#define glDrawElementsInstancedBaseVertexBaseInstanceEXT wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT
+#define glDrawElementsInstancedBaseVertexEXT wrap_glDrawElementsInstancedBaseVertexEXT
+#define glDrawElementsInstancedBaseVertexOES wrap_glDrawElementsInstancedBaseVertexOES
+#define glDrawElementsInstancedEXT wrap_glDrawElementsInstancedEXT
+#define glDrawElementsInstancedNV wrap_glDrawElementsInstancedNV
+#define glDrawRangeElements wrap_glDrawRangeElements
+#define glDrawRangeElementsBaseVertex wrap_glDrawRangeElementsBaseVertex
+#define glDrawRangeElementsBaseVertexEXT wrap_glDrawRangeElementsBaseVertexEXT
+#define glDrawRangeElementsBaseVertexOES wrap_glDrawRangeElementsBaseVertexOES
+#define glDrawTexfOES wrap_glDrawTexfOES
+#define glDrawTexfvOES wrap_glDrawTexfvOES
+#define glDrawTexiOES wrap_glDrawTexiOES
+#define glDrawTexivOES wrap_glDrawTexivOES
+#define glDrawTexsOES wrap_glDrawTexsOES
+#define glDrawTexsvOES wrap_glDrawTexsvOES
+#define glDrawTexxOES wrap_glDrawTexxOES
+#define glDrawTexxvOES wrap_glDrawTexxvOES
+#define glEGLImageTargetRenderbufferStorageOES wrap_glEGLImageTargetRenderbufferStorageOES
+#define glEGLImageTargetTexture2DOES wrap_glEGLImageTargetTexture2DOES
+#define glEnable wrap_glEnable
+#define glEnableClientState wrap_glEnableClientState
+#define glEnableDriverControlQCOM wrap_glEnableDriverControlQCOM
+#define glEnableVertexAttribArray wrap_glEnableVertexAttribArray
+#define glEnablei wrap_glEnablei
+#define glEnableiEXT wrap_glEnableiEXT
+#define glEnableiNV wrap_glEnableiNV
+#define glEnableiOES wrap_glEnableiOES
+#define glEndConditionalRenderNV wrap_glEndConditionalRenderNV
+#define glEndPerfMonitorAMD wrap_glEndPerfMonitorAMD
+#define glEndPerfQueryINTEL wrap_glEndPerfQueryINTEL
+#define glEndQuery wrap_glEndQuery
+#define glEndQueryEXT wrap_glEndQueryEXT
+#define glEndTilingQCOM wrap_glEndTilingQCOM
+#define glEndTransformFeedback wrap_glEndTransformFeedback
+#define glExtGetBufferPointervQCOM wrap_glExtGetBufferPointervQCOM
+#define glExtGetBuffersQCOM wrap_glExtGetBuffersQCOM
+#define glExtGetFramebuffersQCOM wrap_glExtGetFramebuffersQCOM
+#define glExtGetProgramBinarySourceQCOM wrap_glExtGetProgramBinarySourceQCOM
+#define glExtGetProgramsQCOM wrap_glExtGetProgramsQCOM
+#define glExtGetRenderbuffersQCOM wrap_glExtGetRenderbuffersQCOM
+#define glExtGetShadersQCOM wrap_glExtGetShadersQCOM
+#define glExtGetTexLevelParameterivQCOM wrap_glExtGetTexLevelParameterivQCOM
+#define glExtGetTexSubImageQCOM wrap_glExtGetTexSubImageQCOM
+#define glExtGetTexturesQCOM wrap_glExtGetTexturesQCOM
+#define glExtIsProgramBinaryQCOM wrap_glExtIsProgramBinaryQCOM
+#define glExtTexObjectStateOverrideiQCOM wrap_glExtTexObjectStateOverrideiQCOM
+#define glFenceSync wrap_glFenceSync
+#define glFenceSyncAPPLE wrap_glFenceSyncAPPLE
+#define glFinish wrap_glFinish
+#define glFinishFenceNV wrap_glFinishFenceNV
+#define glFlush wrap_glFlush
+#define glFlushMappedBufferRange wrap_glFlushMappedBufferRange
+#define glFlushMappedBufferRangeEXT wrap_glFlushMappedBufferRangeEXT
+#define glFogf wrap_glFogf
+#define glFogfv wrap_glFogfv
+#define glFogx wrap_glFogx
+#define glFogxOES wrap_glFogxOES
+#define glFogxv wrap_glFogxv
+#define glFogxvOES wrap_glFogxvOES
+#define glFragmentCoverageColorNV wrap_glFragmentCoverageColorNV
+#define glFramebufferParameteri wrap_glFramebufferParameteri
+#define glFramebufferRenderbuffer wrap_glFramebufferRenderbuffer
+#define glFramebufferRenderbufferOES wrap_glFramebufferRenderbufferOES
+#define glFramebufferSampleLocationsfvNV wrap_glFramebufferSampleLocationsfvNV
+#define glFramebufferTexture wrap_glFramebufferTexture
+#define glFramebufferTexture2D wrap_glFramebufferTexture2D
+#define glFramebufferTexture2DMultisampleEXT wrap_glFramebufferTexture2DMultisampleEXT
+#define glFramebufferTexture2DMultisampleIMG wrap_glFramebufferTexture2DMultisampleIMG
+#define glFramebufferTexture2DOES wrap_glFramebufferTexture2DOES
+#define glFramebufferTexture3DOES wrap_glFramebufferTexture3DOES
+#define glFramebufferTextureEXT wrap_glFramebufferTextureEXT
+#define glFramebufferTextureLayer wrap_glFramebufferTextureLayer
+#define glFramebufferTextureMultisampleMultiviewOVR wrap_glFramebufferTextureMultisampleMultiviewOVR
+#define glFramebufferTextureMultiviewOVR wrap_glFramebufferTextureMultiviewOVR
+#define glFramebufferTextureOES wrap_glFramebufferTextureOES
+#define glFrontFace wrap_glFrontFace
+#define glFrustumf wrap_glFrustumf
+#define glFrustumfOES wrap_glFrustumfOES
+#define glFrustumx wrap_glFrustumx
+#define glFrustumxOES wrap_glFrustumxOES
+#define glGenBuffers wrap_glGenBuffers
+#define glGenFencesNV wrap_glGenFencesNV
+#define glGenFramebuffers wrap_glGenFramebuffers
+#define glGenFramebuffersOES wrap_glGenFramebuffersOES
+#define glGenPathsNV wrap_glGenPathsNV
+#define glGenPerfMonitorsAMD wrap_glGenPerfMonitorsAMD
+#define glGenProgramPipelines wrap_glGenProgramPipelines
+#define glGenProgramPipelinesEXT wrap_glGenProgramPipelinesEXT
+#define glGenQueries wrap_glGenQueries
+#define glGenQueriesEXT wrap_glGenQueriesEXT
+#define glGenRenderbuffers wrap_glGenRenderbuffers
+#define glGenRenderbuffersOES wrap_glGenRenderbuffersOES
+#define glGenSamplers wrap_glGenSamplers
+#define glGenTextures wrap_glGenTextures
+#define glGenTransformFeedbacks wrap_glGenTransformFeedbacks
+#define glGenVertexArrays wrap_glGenVertexArrays
+#define glGenVertexArraysOES wrap_glGenVertexArraysOES
+#define glGenerateMipmap wrap_glGenerateMipmap
+#define glGenerateMipmapOES wrap_glGenerateMipmapOES
+#define glGetActiveAttrib wrap_glGetActiveAttrib
+#define glGetActiveUniform wrap_glGetActiveUniform
+#define glGetActiveUniformBlockName wrap_glGetActiveUniformBlockName
+#define glGetActiveUniformBlockiv wrap_glGetActiveUniformBlockiv
+#define glGetActiveUniformsiv wrap_glGetActiveUniformsiv
+#define glGetAttachedShaders wrap_glGetAttachedShaders
+#define glGetAttribLocation wrap_glGetAttribLocation
+#define glGetBooleani_v wrap_glGetBooleani_v
+#define glGetBooleanv wrap_glGetBooleanv
+#define glGetBufferParameteri64v wrap_glGetBufferParameteri64v
+#define glGetBufferParameteriv wrap_glGetBufferParameteriv
+#define glGetBufferPointerv wrap_glGetBufferPointerv
+#define glGetBufferPointervOES wrap_glGetBufferPointervOES
+#define glGetClipPlanef wrap_glGetClipPlanef
+#define glGetClipPlanefOES wrap_glGetClipPlanefOES
+#define glGetClipPlanex wrap_glGetClipPlanex
+#define glGetClipPlanexOES wrap_glGetClipPlanexOES
+#define glGetCoverageModulationTableNV wrap_glGetCoverageModulationTableNV
+#define glGetDebugMessageLog wrap_glGetDebugMessageLog
+#define glGetDebugMessageLogKHR wrap_glGetDebugMessageLogKHR
+#define glGetDriverControlStringQCOM wrap_glGetDriverControlStringQCOM
+#define glGetDriverControlsQCOM wrap_glGetDriverControlsQCOM
+#define glGetError wrap_glGetError
+#define glGetFenceivNV wrap_glGetFenceivNV
+#define glGetFirstPerfQueryIdINTEL wrap_glGetFirstPerfQueryIdINTEL
+#define glGetFixedv wrap_glGetFixedv
+#define glGetFixedvOES wrap_glGetFixedvOES
+#define glGetFloati_vNV wrap_glGetFloati_vNV
+#define glGetFloatv wrap_glGetFloatv
+#define glGetFragDataIndexEXT wrap_glGetFragDataIndexEXT
+#define glGetFragDataLocation wrap_glGetFragDataLocation
+#define glGetFramebufferAttachmentParameteriv wrap_glGetFramebufferAttachmentParameteriv
+#define glGetFramebufferAttachmentParameterivOES wrap_glGetFramebufferAttachmentParameterivOES
+#define glGetFramebufferParameteriv wrap_glGetFramebufferParameteriv
+#define glGetGraphicsResetStatus wrap_glGetGraphicsResetStatus
+#define glGetGraphicsResetStatusEXT wrap_glGetGraphicsResetStatusEXT
+#define glGetGraphicsResetStatusKHR wrap_glGetGraphicsResetStatusKHR
+#define glGetImageHandleNV wrap_glGetImageHandleNV
+#define glGetInteger64i_v wrap_glGetInteger64i_v
+#define glGetInteger64v wrap_glGetInteger64v
+#define glGetInteger64vAPPLE wrap_glGetInteger64vAPPLE
+#define glGetIntegeri_v wrap_glGetIntegeri_v
+#define glGetIntegeri_vEXT wrap_glGetIntegeri_vEXT
+#define glGetIntegerv wrap_glGetIntegerv
+#define glGetInternalformatSampleivNV wrap_glGetInternalformatSampleivNV
+#define glGetInternalformativ wrap_glGetInternalformativ
+#define glGetLightfv wrap_glGetLightfv
+#define glGetLightxv wrap_glGetLightxv
+#define glGetLightxvOES wrap_glGetLightxvOES
+#define glGetMaterialfv wrap_glGetMaterialfv
+#define glGetMaterialxv wrap_glGetMaterialxv
+#define glGetMaterialxvOES wrap_glGetMaterialxvOES
+#define glGetMultisamplefv wrap_glGetMultisamplefv
+#define glGetNextPerfQueryIdINTEL wrap_glGetNextPerfQueryIdINTEL
+#define glGetObjectLabel wrap_glGetObjectLabel
+#define glGetObjectLabelEXT wrap_glGetObjectLabelEXT
+#define glGetObjectLabelKHR wrap_glGetObjectLabelKHR
+#define glGetObjectPtrLabel wrap_glGetObjectPtrLabel
+#define glGetObjectPtrLabelKHR wrap_glGetObjectPtrLabelKHR
+#define glGetPathCommandsNV wrap_glGetPathCommandsNV
+#define glGetPathCoordsNV wrap_glGetPathCoordsNV
+#define glGetPathDashArrayNV wrap_glGetPathDashArrayNV
+#define glGetPathLengthNV wrap_glGetPathLengthNV
+#define glGetPathMetricRangeNV wrap_glGetPathMetricRangeNV
+#define glGetPathMetricsNV wrap_glGetPathMetricsNV
+#define glGetPathParameterfvNV wrap_glGetPathParameterfvNV
+#define glGetPathParameterivNV wrap_glGetPathParameterivNV
+#define glGetPathSpacingNV wrap_glGetPathSpacingNV
+#define glGetPerfCounterInfoINTEL wrap_glGetPerfCounterInfoINTEL
+#define glGetPerfMonitorCounterDataAMD wrap_glGetPerfMonitorCounterDataAMD
+#define glGetPerfMonitorCounterInfoAMD wrap_glGetPerfMonitorCounterInfoAMD
+#define glGetPerfMonitorCounterStringAMD wrap_glGetPerfMonitorCounterStringAMD
+#define glGetPerfMonitorCountersAMD wrap_glGetPerfMonitorCountersAMD
+#define glGetPerfMonitorGroupStringAMD wrap_glGetPerfMonitorGroupStringAMD
+#define glGetPerfMonitorGroupsAMD wrap_glGetPerfMonitorGroupsAMD
+#define glGetPerfQueryDataINTEL wrap_glGetPerfQueryDataINTEL
+#define glGetPerfQueryIdByNameINTEL wrap_glGetPerfQueryIdByNameINTEL
+#define glGetPerfQueryInfoINTEL wrap_glGetPerfQueryInfoINTEL
+#define glGetPointerv wrap_glGetPointerv
+#define glGetPointervKHR wrap_glGetPointervKHR
+#define glGetProgramBinary wrap_glGetProgramBinary
+#define glGetProgramBinaryOES wrap_glGetProgramBinaryOES
+#define glGetProgramInfoLog wrap_glGetProgramInfoLog
+#define glGetProgramInterfaceiv wrap_glGetProgramInterfaceiv
+#define glGetProgramPipelineInfoLog wrap_glGetProgramPipelineInfoLog
+#define glGetProgramPipelineInfoLogEXT wrap_glGetProgramPipelineInfoLogEXT
+#define glGetProgramPipelineiv wrap_glGetProgramPipelineiv
+#define glGetProgramPipelineivEXT wrap_glGetProgramPipelineivEXT
+#define glGetProgramResourceIndex wrap_glGetProgramResourceIndex
+#define glGetProgramResourceLocation wrap_glGetProgramResourceLocation
+#define glGetProgramResourceLocationIndexEXT wrap_glGetProgramResourceLocationIndexEXT
+#define glGetProgramResourceName wrap_glGetProgramResourceName
+#define glGetProgramResourcefvNV wrap_glGetProgramResourcefvNV
+#define glGetProgramResourceiv wrap_glGetProgramResourceiv
+#define glGetProgramiv wrap_glGetProgramiv
+#define glGetQueryObjecti64vEXT wrap_glGetQueryObjecti64vEXT
+#define glGetQueryObjectivEXT wrap_glGetQueryObjectivEXT
+#define glGetQueryObjectui64vEXT wrap_glGetQueryObjectui64vEXT
+#define glGetQueryObjectuiv wrap_glGetQueryObjectuiv
+#define glGetQueryObjectuivEXT wrap_glGetQueryObjectuivEXT
+#define glGetQueryiv wrap_glGetQueryiv
+#define glGetQueryivEXT wrap_glGetQueryivEXT
+#define glGetRenderbufferParameteriv wrap_glGetRenderbufferParameteriv
+#define glGetRenderbufferParameterivOES wrap_glGetRenderbufferParameterivOES
+#define glGetSamplerParameterIiv wrap_glGetSamplerParameterIiv
+#define glGetSamplerParameterIivEXT wrap_glGetSamplerParameterIivEXT
+#define glGetSamplerParameterIivOES wrap_glGetSamplerParameterIivOES
+#define glGetSamplerParameterIuiv wrap_glGetSamplerParameterIuiv
+#define glGetSamplerParameterIuivEXT wrap_glGetSamplerParameterIuivEXT
+#define glGetSamplerParameterIuivOES wrap_glGetSamplerParameterIuivOES
+#define glGetSamplerParameterfv wrap_glGetSamplerParameterfv
+#define glGetSamplerParameteriv wrap_glGetSamplerParameteriv
+#define glGetShaderInfoLog wrap_glGetShaderInfoLog
+#define glGetShaderPrecisionFormat wrap_glGetShaderPrecisionFormat
+#define glGetShaderSource wrap_glGetShaderSource
+#define glGetShaderiv wrap_glGetShaderiv
+#define glGetString wrap_glGetString
+#define glGetStringi wrap_glGetStringi
+#define glGetSynciv wrap_glGetSynciv
+#define glGetSyncivAPPLE wrap_glGetSyncivAPPLE
+#define glGetTexEnvfv wrap_glGetTexEnvfv
+#define glGetTexEnviv wrap_glGetTexEnviv
+#define glGetTexEnvxv wrap_glGetTexEnvxv
+#define glGetTexEnvxvOES wrap_glGetTexEnvxvOES
+#define glGetTexGenfvOES wrap_glGetTexGenfvOES
+#define glGetTexGenivOES wrap_glGetTexGenivOES
+#define glGetTexGenxvOES wrap_glGetTexGenxvOES
+#define glGetTexLevelParameterfv wrap_glGetTexLevelParameterfv
+#define glGetTexLevelParameteriv wrap_glGetTexLevelParameteriv
+#define glGetTexParameterIiv wrap_glGetTexParameterIiv
+#define glGetTexParameterIivEXT wrap_glGetTexParameterIivEXT
+#define glGetTexParameterIivOES wrap_glGetTexParameterIivOES
+#define glGetTexParameterIuiv wrap_glGetTexParameterIuiv
+#define glGetTexParameterIuivEXT wrap_glGetTexParameterIuivEXT
+#define glGetTexParameterIuivOES wrap_glGetTexParameterIuivOES
+#define glGetTexParameterfv wrap_glGetTexParameterfv
+#define glGetTexParameteriv wrap_glGetTexParameteriv
+#define glGetTexParameterxv wrap_glGetTexParameterxv
+#define glGetTexParameterxvOES wrap_glGetTexParameterxvOES
+#define glGetTextureHandleNV wrap_glGetTextureHandleNV
+#define glGetTextureSamplerHandleNV wrap_glGetTextureSamplerHandleNV
+#define glGetTransformFeedbackVarying wrap_glGetTransformFeedbackVarying
+#define glGetTranslatedShaderSourceANGLE wrap_glGetTranslatedShaderSourceANGLE
+#define glGetUniformBlockIndex wrap_glGetUniformBlockIndex
+#define glGetUniformIndices wrap_glGetUniformIndices
+#define glGetUniformLocation wrap_glGetUniformLocation
+#define glGetUniformfv wrap_glGetUniformfv
+#define glGetUniformiv wrap_glGetUniformiv
+#define glGetUniformuiv wrap_glGetUniformuiv
+#define glGetVertexAttribIiv wrap_glGetVertexAttribIiv
+#define glGetVertexAttribIuiv wrap_glGetVertexAttribIuiv
+#define glGetVertexAttribPointerv wrap_glGetVertexAttribPointerv
+#define glGetVertexAttribfv wrap_glGetVertexAttribfv
+#define glGetVertexAttribiv wrap_glGetVertexAttribiv
+#define glGetnUniformfv wrap_glGetnUniformfv
+#define glGetnUniformfvEXT wrap_glGetnUniformfvEXT
+#define glGetnUniformfvKHR wrap_glGetnUniformfvKHR
+#define glGetnUniformiv wrap_glGetnUniformiv
+#define glGetnUniformivEXT wrap_glGetnUniformivEXT
+#define glGetnUniformivKHR wrap_glGetnUniformivKHR
+#define glGetnUniformuiv wrap_glGetnUniformuiv
+#define glGetnUniformuivKHR wrap_glGetnUniformuivKHR
+#define glHint wrap_glHint
+#define glInsertEventMarkerEXT wrap_glInsertEventMarkerEXT
+#define glInterpolatePathsNV wrap_glInterpolatePathsNV
+#define glInvalidateFramebuffer wrap_glInvalidateFramebuffer
+#define glInvalidateSubFramebuffer wrap_glInvalidateSubFramebuffer
+#define glIsBuffer wrap_glIsBuffer
+#define glIsEnabled wrap_glIsEnabled
+#define glIsEnabledi wrap_glIsEnabledi
+#define glIsEnablediEXT wrap_glIsEnablediEXT
+#define glIsEnablediNV wrap_glIsEnablediNV
+#define glIsEnablediOES wrap_glIsEnablediOES
+#define glIsFenceNV wrap_glIsFenceNV
+#define glIsFramebuffer wrap_glIsFramebuffer
+#define glIsFramebufferOES wrap_glIsFramebufferOES
+#define glIsImageHandleResidentNV wrap_glIsImageHandleResidentNV
+#define glIsPathNV wrap_glIsPathNV
+#define glIsPointInFillPathNV wrap_glIsPointInFillPathNV
+#define glIsPointInStrokePathNV wrap_glIsPointInStrokePathNV
+#define glIsProgram wrap_glIsProgram
+#define glIsProgramPipeline wrap_glIsProgramPipeline
+#define glIsProgramPipelineEXT wrap_glIsProgramPipelineEXT
+#define glIsQuery wrap_glIsQuery
+#define glIsQueryEXT wrap_glIsQueryEXT
+#define glIsRenderbuffer wrap_glIsRenderbuffer
+#define glIsRenderbufferOES wrap_glIsRenderbufferOES
+#define glIsSampler wrap_glIsSampler
+#define glIsShader wrap_glIsShader
+#define glIsSync wrap_glIsSync
+#define glIsSyncAPPLE wrap_glIsSyncAPPLE
+#define glIsTexture wrap_glIsTexture
+#define glIsTextureHandleResidentNV wrap_glIsTextureHandleResidentNV
+#define glIsTransformFeedback wrap_glIsTransformFeedback
+#define glIsVertexArray wrap_glIsVertexArray
+#define glIsVertexArrayOES wrap_glIsVertexArrayOES
+#define glLabelObjectEXT wrap_glLabelObjectEXT
+#define glLightModelf wrap_glLightModelf
+#define glLightModelfv wrap_glLightModelfv
+#define glLightModelx wrap_glLightModelx
+#define glLightModelxOES wrap_glLightModelxOES
+#define glLightModelxv wrap_glLightModelxv
+#define glLightModelxvOES wrap_glLightModelxvOES
+#define glLightf wrap_glLightf
+#define glLightfv wrap_glLightfv
+#define glLightx wrap_glLightx
+#define glLightxOES wrap_glLightxOES
+#define glLightxv wrap_glLightxv
+#define glLightxvOES wrap_glLightxvOES
+#define glLineWidth wrap_glLineWidth
+#define glLineWidthx wrap_glLineWidthx
+#define glLineWidthxOES wrap_glLineWidthxOES
+#define glLinkProgram wrap_glLinkProgram
+#define glLoadIdentity wrap_glLoadIdentity
+#define glLoadMatrixf wrap_glLoadMatrixf
+#define glLoadMatrixx wrap_glLoadMatrixx
+#define glLoadMatrixxOES wrap_glLoadMatrixxOES
+#define glLoadPaletteFromModelViewMatrixOES wrap_glLoadPaletteFromModelViewMatrixOES
+#define glLogicOp wrap_glLogicOp
+#define glMakeImageHandleNonResidentNV wrap_glMakeImageHandleNonResidentNV
+#define glMakeImageHandleResidentNV wrap_glMakeImageHandleResidentNV
+#define glMakeTextureHandleNonResidentNV wrap_glMakeTextureHandleNonResidentNV
+#define glMakeTextureHandleResidentNV wrap_glMakeTextureHandleResidentNV
+#define glMapBufferOES wrap_glMapBufferOES
+#define glMapBufferRange wrap_glMapBufferRange
+#define glMapBufferRangeEXT wrap_glMapBufferRangeEXT
+#define glMaterialf wrap_glMaterialf
+#define glMaterialfv wrap_glMaterialfv
+#define glMaterialx wrap_glMaterialx
+#define glMaterialxOES wrap_glMaterialxOES
+#define glMaterialxv wrap_glMaterialxv
+#define glMaterialxvOES wrap_glMaterialxvOES
+#define glMatrixIndexPointerOES wrap_glMatrixIndexPointerOES
+#define glMatrixLoad3x2fNV wrap_glMatrixLoad3x2fNV
+#define glMatrixLoad3x3fNV wrap_glMatrixLoad3x3fNV
+#define glMatrixLoadTranspose3x3fNV wrap_glMatrixLoadTranspose3x3fNV
+#define glMatrixMode wrap_glMatrixMode
+#define glMatrixMult3x2fNV wrap_glMatrixMult3x2fNV
+#define glMatrixMult3x3fNV wrap_glMatrixMult3x3fNV
+#define glMatrixMultTranspose3x3fNV wrap_glMatrixMultTranspose3x3fNV
+#define glMemoryBarrier wrap_glMemoryBarrier
+#define glMemoryBarrierByRegion wrap_glMemoryBarrierByRegion
+#define glMinSampleShading wrap_glMinSampleShading
+#define glMinSampleShadingOES wrap_glMinSampleShadingOES
+#define glMultMatrixf wrap_glMultMatrixf
+#define glMultMatrixx wrap_glMultMatrixx
+#define glMultMatrixxOES wrap_glMultMatrixxOES
+#define glMultiDrawArraysEXT wrap_glMultiDrawArraysEXT
+#define glMultiDrawArraysIndirectEXT wrap_glMultiDrawArraysIndirectEXT
+#define glMultiDrawElementsBaseVertexEXT wrap_glMultiDrawElementsBaseVertexEXT
+#define glMultiDrawElementsBaseVertexOES wrap_glMultiDrawElementsBaseVertexOES
+#define glMultiDrawElementsEXT wrap_glMultiDrawElementsEXT
+#define glMultiDrawElementsIndirectEXT wrap_glMultiDrawElementsIndirectEXT
+#define glMultiTexCoord4f wrap_glMultiTexCoord4f
+#define glMultiTexCoord4x wrap_glMultiTexCoord4x
+#define glMultiTexCoord4xOES wrap_glMultiTexCoord4xOES
+#define glNamedFramebufferSampleLocationsfvNV wrap_glNamedFramebufferSampleLocationsfvNV
+#define glNormal3f wrap_glNormal3f
+#define glNormal3x wrap_glNormal3x
+#define glNormal3xOES wrap_glNormal3xOES
+#define glNormalPointer wrap_glNormalPointer
+#define glObjectLabel wrap_glObjectLabel
+#define glObjectLabelKHR wrap_glObjectLabelKHR
+#define glObjectPtrLabel wrap_glObjectPtrLabel
+#define glObjectPtrLabelKHR wrap_glObjectPtrLabelKHR
+#define glOrthof wrap_glOrthof
+#define glOrthofOES wrap_glOrthofOES
+#define glOrthox wrap_glOrthox
+#define glOrthoxOES wrap_glOrthoxOES
+#define glPatchParameteri wrap_glPatchParameteri
+#define glPatchParameteriEXT wrap_glPatchParameteriEXT
+#define glPatchParameteriOES wrap_glPatchParameteriOES
+#define glPathCommandsNV wrap_glPathCommandsNV
+#define glPathCoordsNV wrap_glPathCoordsNV
+#define glPathCoverDepthFuncNV wrap_glPathCoverDepthFuncNV
+#define glPathDashArrayNV wrap_glPathDashArrayNV
+#define glPathGlyphIndexArrayNV wrap_glPathGlyphIndexArrayNV
+#define glPathGlyphIndexRangeNV wrap_glPathGlyphIndexRangeNV
+#define glPathGlyphRangeNV wrap_glPathGlyphRangeNV
+#define glPathGlyphsNV wrap_glPathGlyphsNV
+#define glPathMemoryGlyphIndexArrayNV wrap_glPathMemoryGlyphIndexArrayNV
+#define glPathParameterfNV wrap_glPathParameterfNV
+#define glPathParameterfvNV wrap_glPathParameterfvNV
+#define glPathParameteriNV wrap_glPathParameteriNV
+#define glPathParameterivNV wrap_glPathParameterivNV
+#define glPathStencilDepthOffsetNV wrap_glPathStencilDepthOffsetNV
+#define glPathStencilFuncNV wrap_glPathStencilFuncNV
+#define glPathStringNV wrap_glPathStringNV
+#define glPathSubCommandsNV wrap_glPathSubCommandsNV
+#define glPathSubCoordsNV wrap_glPathSubCoordsNV
+#define glPauseTransformFeedback wrap_glPauseTransformFeedback
+#define glPixelStorei wrap_glPixelStorei
+#define glPointAlongPathNV wrap_glPointAlongPathNV
+#define glPointParameterf wrap_glPointParameterf
+#define glPointParameterfv wrap_glPointParameterfv
+#define glPointParameterx wrap_glPointParameterx
+#define glPointParameterxOES wrap_glPointParameterxOES
+#define glPointParameterxv wrap_glPointParameterxv
+#define glPointParameterxvOES wrap_glPointParameterxvOES
+#define glPointSize wrap_glPointSize
+#define glPointSizePointerOES wrap_glPointSizePointerOES
+#define glPointSizex wrap_glPointSizex
+#define glPointSizexOES wrap_glPointSizexOES
+#define glPolygonModeNV wrap_glPolygonModeNV
+#define glPolygonOffset wrap_glPolygonOffset
+#define glPolygonOffsetx wrap_glPolygonOffsetx
+#define glPolygonOffsetxOES wrap_glPolygonOffsetxOES
+#define glPopDebugGroup wrap_glPopDebugGroup
+#define glPopDebugGroupKHR wrap_glPopDebugGroupKHR
+#define glPopGroupMarkerEXT wrap_glPopGroupMarkerEXT
+#define glPopMatrix wrap_glPopMatrix
+#define glPrimitiveBoundingBox wrap_glPrimitiveBoundingBox
+#define glPrimitiveBoundingBoxEXT wrap_glPrimitiveBoundingBoxEXT
+#define glPrimitiveBoundingBoxOES wrap_glPrimitiveBoundingBoxOES
+#define glProgramBinary wrap_glProgramBinary
+#define glProgramBinaryOES wrap_glProgramBinaryOES
+#define glProgramParameteri wrap_glProgramParameteri
+#define glProgramParameteriEXT wrap_glProgramParameteriEXT
+#define glProgramPathFragmentInputGenNV wrap_glProgramPathFragmentInputGenNV
+#define glProgramUniform1f wrap_glProgramUniform1f
+#define glProgramUniform1fEXT wrap_glProgramUniform1fEXT
+#define glProgramUniform1fv wrap_glProgramUniform1fv
+#define glProgramUniform1fvEXT wrap_glProgramUniform1fvEXT
+#define glProgramUniform1i wrap_glProgramUniform1i
+#define glProgramUniform1iEXT wrap_glProgramUniform1iEXT
+#define glProgramUniform1iv wrap_glProgramUniform1iv
+#define glProgramUniform1ivEXT wrap_glProgramUniform1ivEXT
+#define glProgramUniform1ui wrap_glProgramUniform1ui
+#define glProgramUniform1uiEXT wrap_glProgramUniform1uiEXT
+#define glProgramUniform1uiv wrap_glProgramUniform1uiv
+#define glProgramUniform1uivEXT wrap_glProgramUniform1uivEXT
+#define glProgramUniform2f wrap_glProgramUniform2f
+#define glProgramUniform2fEXT wrap_glProgramUniform2fEXT
+#define glProgramUniform2fv wrap_glProgramUniform2fv
+#define glProgramUniform2fvEXT wrap_glProgramUniform2fvEXT
+#define glProgramUniform2i wrap_glProgramUniform2i
+#define glProgramUniform2iEXT wrap_glProgramUniform2iEXT
+#define glProgramUniform2iv wrap_glProgramUniform2iv
+#define glProgramUniform2ivEXT wrap_glProgramUniform2ivEXT
+#define glProgramUniform2ui wrap_glProgramUniform2ui
+#define glProgramUniform2uiEXT wrap_glProgramUniform2uiEXT
+#define glProgramUniform2uiv wrap_glProgramUniform2uiv
+#define glProgramUniform2uivEXT wrap_glProgramUniform2uivEXT
+#define glProgramUniform3f wrap_glProgramUniform3f
+#define glProgramUniform3fEXT wrap_glProgramUniform3fEXT
+#define glProgramUniform3fv wrap_glProgramUniform3fv
+#define glProgramUniform3fvEXT wrap_glProgramUniform3fvEXT
+#define glProgramUniform3i wrap_glProgramUniform3i
+#define glProgramUniform3iEXT wrap_glProgramUniform3iEXT
+#define glProgramUniform3iv wrap_glProgramUniform3iv
+#define glProgramUniform3ivEXT wrap_glProgramUniform3ivEXT
+#define glProgramUniform3ui wrap_glProgramUniform3ui
+#define glProgramUniform3uiEXT wrap_glProgramUniform3uiEXT
+#define glProgramUniform3uiv wrap_glProgramUniform3uiv
+#define glProgramUniform3uivEXT wrap_glProgramUniform3uivEXT
+#define glProgramUniform4f wrap_glProgramUniform4f
+#define glProgramUniform4fEXT wrap_glProgramUniform4fEXT
+#define glProgramUniform4fv wrap_glProgramUniform4fv
+#define glProgramUniform4fvEXT wrap_glProgramUniform4fvEXT
+#define glProgramUniform4i wrap_glProgramUniform4i
+#define glProgramUniform4iEXT wrap_glProgramUniform4iEXT
+#define glProgramUniform4iv wrap_glProgramUniform4iv
+#define glProgramUniform4ivEXT wrap_glProgramUniform4ivEXT
+#define glProgramUniform4ui wrap_glProgramUniform4ui
+#define glProgramUniform4uiEXT wrap_glProgramUniform4uiEXT
+#define glProgramUniform4uiv wrap_glProgramUniform4uiv
+#define glProgramUniform4uivEXT wrap_glProgramUniform4uivEXT
+#define glProgramUniformHandleui64NV wrap_glProgramUniformHandleui64NV
+#define glProgramUniformHandleui64vNV wrap_glProgramUniformHandleui64vNV
+#define glProgramUniformMatrix2fv wrap_glProgramUniformMatrix2fv
+#define glProgramUniformMatrix2fvEXT wrap_glProgramUniformMatrix2fvEXT
+#define glProgramUniformMatrix2x3fv wrap_glProgramUniformMatrix2x3fv
+#define glProgramUniformMatrix2x3fvEXT wrap_glProgramUniformMatrix2x3fvEXT
+#define glProgramUniformMatrix2x4fv wrap_glProgramUniformMatrix2x4fv
+#define glProgramUniformMatrix2x4fvEXT wrap_glProgramUniformMatrix2x4fvEXT
+#define glProgramUniformMatrix3fv wrap_glProgramUniformMatrix3fv
+#define glProgramUniformMatrix3fvEXT wrap_glProgramUniformMatrix3fvEXT
+#define glProgramUniformMatrix3x2fv wrap_glProgramUniformMatrix3x2fv
+#define glProgramUniformMatrix3x2fvEXT wrap_glProgramUniformMatrix3x2fvEXT
+#define glProgramUniformMatrix3x4fv wrap_glProgramUniformMatrix3x4fv
+#define glProgramUniformMatrix3x4fvEXT wrap_glProgramUniformMatrix3x4fvEXT
+#define glProgramUniformMatrix4fv wrap_glProgramUniformMatrix4fv
+#define glProgramUniformMatrix4fvEXT wrap_glProgramUniformMatrix4fvEXT
+#define glProgramUniformMatrix4x2fv wrap_glProgramUniformMatrix4x2fv
+#define glProgramUniformMatrix4x2fvEXT wrap_glProgramUniformMatrix4x2fvEXT
+#define glProgramUniformMatrix4x3fv wrap_glProgramUniformMatrix4x3fv
+#define glProgramUniformMatrix4x3fvEXT wrap_glProgramUniformMatrix4x3fvEXT
+#define glPushDebugGroup wrap_glPushDebugGroup
+#define glPushDebugGroupKHR wrap_glPushDebugGroupKHR
+#define glPushGroupMarkerEXT wrap_glPushGroupMarkerEXT
+#define glPushMatrix wrap_glPushMatrix
+#define glQueryCounterEXT wrap_glQueryCounterEXT
+#define glQueryMatrixxOES wrap_glQueryMatrixxOES
+#define glRasterSamplesEXT wrap_glRasterSamplesEXT
+#define glReadBuffer wrap_glReadBuffer
+#define glReadBufferIndexedEXT wrap_glReadBufferIndexedEXT
+#define glReadBufferNV wrap_glReadBufferNV
+#define glReadPixels wrap_glReadPixels
+#define glReadnPixels wrap_glReadnPixels
+#define glReadnPixelsEXT wrap_glReadnPixelsEXT
+#define glReadnPixelsKHR wrap_glReadnPixelsKHR
+#define glReleaseShaderCompiler wrap_glReleaseShaderCompiler
+#define glRenderbufferStorage wrap_glRenderbufferStorage
+#define glRenderbufferStorageMultisample wrap_glRenderbufferStorageMultisample
+#define glRenderbufferStorageMultisampleANGLE wrap_glRenderbufferStorageMultisampleANGLE
+#define glRenderbufferStorageMultisampleAPPLE wrap_glRenderbufferStorageMultisampleAPPLE
+#define glRenderbufferStorageMultisampleEXT wrap_glRenderbufferStorageMultisampleEXT
+#define glRenderbufferStorageMultisampleIMG wrap_glRenderbufferStorageMultisampleIMG
+#define glRenderbufferStorageMultisampleNV wrap_glRenderbufferStorageMultisampleNV
+#define glRenderbufferStorageOES wrap_glRenderbufferStorageOES
+#define glResolveDepthValuesNV wrap_glResolveDepthValuesNV
+#define glResolveMultisampleFramebufferAPPLE wrap_glResolveMultisampleFramebufferAPPLE
+#define glResumeTransformFeedback wrap_glResumeTransformFeedback
+#define glRotatef wrap_glRotatef
+#define glRotatex wrap_glRotatex
+#define glRotatexOES wrap_glRotatexOES
+#define glSampleCoverage wrap_glSampleCoverage
+#define glSampleCoveragex wrap_glSampleCoveragex
+#define glSampleCoveragexOES wrap_glSampleCoveragexOES
+#define glSampleMaski wrap_glSampleMaski
+#define glSamplerParameterIiv wrap_glSamplerParameterIiv
+#define glSamplerParameterIivEXT wrap_glSamplerParameterIivEXT
+#define glSamplerParameterIivOES wrap_glSamplerParameterIivOES
+#define glSamplerParameterIuiv wrap_glSamplerParameterIuiv
+#define glSamplerParameterIuivEXT wrap_glSamplerParameterIuivEXT
+#define glSamplerParameterIuivOES wrap_glSamplerParameterIuivOES
+#define glSamplerParameterf wrap_glSamplerParameterf
+#define glSamplerParameterfv wrap_glSamplerParameterfv
+#define glSamplerParameteri wrap_glSamplerParameteri
+#define glSamplerParameteriv wrap_glSamplerParameteriv
+#define glScalef wrap_glScalef
+#define glScalex wrap_glScalex
+#define glScalexOES wrap_glScalexOES
+#define glScissor wrap_glScissor
+#define glScissorArrayvNV wrap_glScissorArrayvNV
+#define glScissorIndexedNV wrap_glScissorIndexedNV
+#define glScissorIndexedvNV wrap_glScissorIndexedvNV
+#define glSelectPerfMonitorCountersAMD wrap_glSelectPerfMonitorCountersAMD
+#define glSetFenceNV wrap_glSetFenceNV
+#define glShadeModel wrap_glShadeModel
+#define glShaderBinary wrap_glShaderBinary
+#define glShaderSource wrap_glShaderSource
+#define glStartTilingQCOM wrap_glStartTilingQCOM
+#define glStencilFillPathInstancedNV wrap_glStencilFillPathInstancedNV
+#define glStencilFillPathNV wrap_glStencilFillPathNV
+#define glStencilFunc wrap_glStencilFunc
+#define glStencilFuncSeparate wrap_glStencilFuncSeparate
+#define glStencilMask wrap_glStencilMask
+#define glStencilMaskSeparate wrap_glStencilMaskSeparate
+#define glStencilOp wrap_glStencilOp
+#define glStencilOpSeparate wrap_glStencilOpSeparate
+#define glStencilStrokePathInstancedNV wrap_glStencilStrokePathInstancedNV
+#define glStencilStrokePathNV wrap_glStencilStrokePathNV
+#define glStencilThenCoverFillPathInstancedNV wrap_glStencilThenCoverFillPathInstancedNV
+#define glStencilThenCoverFillPathNV wrap_glStencilThenCoverFillPathNV
+#define glStencilThenCoverStrokePathInstancedNV wrap_glStencilThenCoverStrokePathInstancedNV
+#define glStencilThenCoverStrokePathNV wrap_glStencilThenCoverStrokePathNV
+#define glSubpixelPrecisionBiasNV wrap_glSubpixelPrecisionBiasNV
+#define glTestFenceNV wrap_glTestFenceNV
+#define glTexBuffer wrap_glTexBuffer
+#define glTexBufferEXT wrap_glTexBufferEXT
+#define glTexBufferOES wrap_glTexBufferOES
+#define glTexBufferRange wrap_glTexBufferRange
+#define glTexBufferRangeEXT wrap_glTexBufferRangeEXT
+#define glTexBufferRangeOES wrap_glTexBufferRangeOES
+#define glTexCoordPointer wrap_glTexCoordPointer
+#define glTexEnvf wrap_glTexEnvf
+#define glTexEnvfv wrap_glTexEnvfv
+#define glTexEnvi wrap_glTexEnvi
+#define glTexEnviv wrap_glTexEnviv
+#define glTexEnvx wrap_glTexEnvx
+#define glTexEnvxOES wrap_glTexEnvxOES
+#define glTexEnvxv wrap_glTexEnvxv
+#define glTexEnvxvOES wrap_glTexEnvxvOES
+#define glTexGenfOES wrap_glTexGenfOES
+#define glTexGenfvOES wrap_glTexGenfvOES
+#define glTexGeniOES wrap_glTexGeniOES
+#define glTexGenivOES wrap_glTexGenivOES
+#define glTexGenxOES wrap_glTexGenxOES
+#define glTexGenxvOES wrap_glTexGenxvOES
+#define glTexImage2D wrap_glTexImage2D
+#define glTexImage3D wrap_glTexImage3D
+#define glTexImage3DOES wrap_glTexImage3DOES
+#define glTexPageCommitmentEXT wrap_glTexPageCommitmentEXT
+#define glTexParameterIiv wrap_glTexParameterIiv
+#define glTexParameterIivEXT wrap_glTexParameterIivEXT
+#define glTexParameterIivOES wrap_glTexParameterIivOES
+#define glTexParameterIuiv wrap_glTexParameterIuiv
+#define glTexParameterIuivEXT wrap_glTexParameterIuivEXT
+#define glTexParameterIuivOES wrap_glTexParameterIuivOES
+#define glTexParameterf wrap_glTexParameterf
+#define glTexParameterfv wrap_glTexParameterfv
+#define glTexParameteri wrap_glTexParameteri
+#define glTexParameteriv wrap_glTexParameteriv
+#define glTexParameterx wrap_glTexParameterx
+#define glTexParameterxOES wrap_glTexParameterxOES
+#define glTexParameterxv wrap_glTexParameterxv
+#define glTexParameterxvOES wrap_glTexParameterxvOES
+#define glTexStorage1DEXT wrap_glTexStorage1DEXT
+#define glTexStorage2D wrap_glTexStorage2D
+#define glTexStorage2DEXT wrap_glTexStorage2DEXT
+#define glTexStorage2DMultisample wrap_glTexStorage2DMultisample
+#define glTexStorage3D wrap_glTexStorage3D
+#define glTexStorage3DEXT wrap_glTexStorage3DEXT
+#define glTexStorage3DMultisample wrap_glTexStorage3DMultisample
+#define glTexStorage3DMultisampleOES wrap_glTexStorage3DMultisampleOES
+#define glTexSubImage2D wrap_glTexSubImage2D
+#define glTexSubImage3D wrap_glTexSubImage3D
+#define glTexSubImage3DOES wrap_glTexSubImage3DOES
+#define glTextureStorage1DEXT wrap_glTextureStorage1DEXT
+#define glTextureStorage2DEXT wrap_glTextureStorage2DEXT
+#define glTextureStorage3DEXT wrap_glTextureStorage3DEXT
+#define glTextureViewEXT wrap_glTextureViewEXT
+#define glTextureViewOES wrap_glTextureViewOES
+#define glTransformFeedbackVaryings wrap_glTransformFeedbackVaryings
+#define glTransformPathNV wrap_glTransformPathNV
+#define glTranslatef wrap_glTranslatef
+#define glTranslatex wrap_glTranslatex
+#define glTranslatexOES wrap_glTranslatexOES
+#define glUniform1f wrap_glUniform1f
+#define glUniform1fv wrap_glUniform1fv
+#define glUniform1i wrap_glUniform1i
+#define glUniform1iv wrap_glUniform1iv
+#define glUniform1ui wrap_glUniform1ui
+#define glUniform1uiv wrap_glUniform1uiv
+#define glUniform2f wrap_glUniform2f
+#define glUniform2fv wrap_glUniform2fv
+#define glUniform2i wrap_glUniform2i
+#define glUniform2iv wrap_glUniform2iv
+#define glUniform2ui wrap_glUniform2ui
+#define glUniform2uiv wrap_glUniform2uiv
+#define glUniform3f wrap_glUniform3f
+#define glUniform3fv wrap_glUniform3fv
+#define glUniform3i wrap_glUniform3i
+#define glUniform3iv wrap_glUniform3iv
+#define glUniform3ui wrap_glUniform3ui
+#define glUniform3uiv wrap_glUniform3uiv
+#define glUniform4f wrap_glUniform4f
+#define glUniform4fv wrap_glUniform4fv
+#define glUniform4i wrap_glUniform4i
+#define glUniform4iv wrap_glUniform4iv
+#define glUniform4ui wrap_glUniform4ui
+#define glUniform4uiv wrap_glUniform4uiv
+#define glUniformBlockBinding wrap_glUniformBlockBinding
+#define glUniformHandleui64NV wrap_glUniformHandleui64NV
+#define glUniformHandleui64vNV wrap_glUniformHandleui64vNV
+#define glUniformMatrix2fv wrap_glUniformMatrix2fv
+#define glUniformMatrix2x3fv wrap_glUniformMatrix2x3fv
+#define glUniformMatrix2x3fvNV wrap_glUniformMatrix2x3fvNV
+#define glUniformMatrix2x4fv wrap_glUniformMatrix2x4fv
+#define glUniformMatrix2x4fvNV wrap_glUniformMatrix2x4fvNV
+#define glUniformMatrix3fv wrap_glUniformMatrix3fv
+#define glUniformMatrix3x2fv wrap_glUniformMatrix3x2fv
+#define glUniformMatrix3x2fvNV wrap_glUniformMatrix3x2fvNV
+#define glUniformMatrix3x4fv wrap_glUniformMatrix3x4fv
+#define glUniformMatrix3x4fvNV wrap_glUniformMatrix3x4fvNV
+#define glUniformMatrix4fv wrap_glUniformMatrix4fv
+#define glUniformMatrix4x2fv wrap_glUniformMatrix4x2fv
+#define glUniformMatrix4x2fvNV wrap_glUniformMatrix4x2fvNV
+#define glUniformMatrix4x3fv wrap_glUniformMatrix4x3fv
+#define glUniformMatrix4x3fvNV wrap_glUniformMatrix4x3fvNV
+#define glUnmapBuffer wrap_glUnmapBuffer
+#define glUnmapBufferOES wrap_glUnmapBufferOES
+#define glUseProgram wrap_glUseProgram
+#define glUseProgramStages wrap_glUseProgramStages
+#define glUseProgramStagesEXT wrap_glUseProgramStagesEXT
+#define glValidateProgram wrap_glValidateProgram
+#define glValidateProgramPipeline wrap_glValidateProgramPipeline
+#define glValidateProgramPipelineEXT wrap_glValidateProgramPipelineEXT
+#define glVertexAttrib1f wrap_glVertexAttrib1f
+#define glVertexAttrib1fv wrap_glVertexAttrib1fv
+#define glVertexAttrib2f wrap_glVertexAttrib2f
+#define glVertexAttrib2fv wrap_glVertexAttrib2fv
+#define glVertexAttrib3f wrap_glVertexAttrib3f
+#define glVertexAttrib3fv wrap_glVertexAttrib3fv
+#define glVertexAttrib4f wrap_glVertexAttrib4f
+#define glVertexAttrib4fv wrap_glVertexAttrib4fv
+#define glVertexAttribBinding wrap_glVertexAttribBinding
+#define glVertexAttribDivisor wrap_glVertexAttribDivisor
+#define glVertexAttribDivisorANGLE wrap_glVertexAttribDivisorANGLE
+#define glVertexAttribDivisorEXT wrap_glVertexAttribDivisorEXT
+#define glVertexAttribDivisorNV wrap_glVertexAttribDivisorNV
+#define glVertexAttribFormat wrap_glVertexAttribFormat
+#define glVertexAttribI4i wrap_glVertexAttribI4i
+#define glVertexAttribI4iv wrap_glVertexAttribI4iv
+#define glVertexAttribI4ui wrap_glVertexAttribI4ui
+#define glVertexAttribI4uiv wrap_glVertexAttribI4uiv
+#define glVertexAttribIFormat wrap_glVertexAttribIFormat
+#define glVertexAttribIPointer wrap_glVertexAttribIPointer
+#define glVertexAttribPointer wrap_glVertexAttribPointer
+#define glVertexBindingDivisor wrap_glVertexBindingDivisor
+#define glVertexPointer wrap_glVertexPointer
+#define glViewport wrap_glViewport
+#define glViewportArrayvNV wrap_glViewportArrayvNV
+#define glViewportIndexedfNV wrap_glViewportIndexedfNV
+#define glViewportIndexedfvNV wrap_glViewportIndexedfvNV
+#define glWaitSync wrap_glWaitSync
+#define glWaitSyncAPPLE wrap_glWaitSyncAPPLE
+#define glWeightPathsNV wrap_glWeightPathsNV
+#define glWeightPointerOES wrap_glWeightPointerOES \ No newline at end of file
diff --git a/libs/hwui/debug/gles_stubs.in b/libs/hwui/debug/gles_stubs.in
new file mode 100644
index 000000000000..4064a391a71d
--- /dev/null
+++ b/libs/hwui/debug/gles_stubs.in
@@ -0,0 +1,1632 @@
+void API_ENTRY(glActiveTexture)(GLenum texture) {
+ CALL_GL_API(glActiveTexture, texture);
+}
+void API_ENTRY(glAttachShader)(GLuint program, GLuint shader) {
+ CALL_GL_API(glAttachShader, program, shader);
+}
+void API_ENTRY(glBindAttribLocation)(GLuint program, GLuint index, const GLchar *name) {
+ CALL_GL_API(glBindAttribLocation, program, index, name);
+}
+void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) {
+ CALL_GL_API(glBindBuffer, target, buffer);
+}
+void API_ENTRY(glBindFramebuffer)(GLenum target, GLuint framebuffer) {
+ CALL_GL_API(glBindFramebuffer, target, framebuffer);
+}
+void API_ENTRY(glBindRenderbuffer)(GLenum target, GLuint renderbuffer) {
+ CALL_GL_API(glBindRenderbuffer, target, renderbuffer);
+}
+void API_ENTRY(glBindTexture)(GLenum target, GLuint texture) {
+ CALL_GL_API(glBindTexture, target, texture);
+}
+void API_ENTRY(glBlendColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
+ CALL_GL_API(glBlendColor, red, green, blue, alpha);
+}
+void API_ENTRY(glBlendEquation)(GLenum mode) {
+ CALL_GL_API(glBlendEquation, mode);
+}
+void API_ENTRY(glBlendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha) {
+ CALL_GL_API(glBlendEquationSeparate, modeRGB, modeAlpha);
+}
+void API_ENTRY(glBlendFunc)(GLenum sfactor, GLenum dfactor) {
+ CALL_GL_API(glBlendFunc, sfactor, dfactor);
+}
+void API_ENTRY(glBlendFuncSeparate)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) {
+ CALL_GL_API(glBlendFuncSeparate, sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
+}
+void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const void *data, GLenum usage) {
+ CALL_GL_API(glBufferData, target, size, data, usage);
+}
+void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) {
+ CALL_GL_API(glBufferSubData, target, offset, size, data);
+}
+GLenum API_ENTRY(glCheckFramebufferStatus)(GLenum target) {
+ CALL_GL_API_RETURN(glCheckFramebufferStatus, target);
+}
+void API_ENTRY(glClear)(GLbitfield mask) {
+ CALL_GL_API(glClear, mask);
+}
+void API_ENTRY(glClearColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
+ CALL_GL_API(glClearColor, red, green, blue, alpha);
+}
+void API_ENTRY(glClearDepthf)(GLfloat d) {
+ CALL_GL_API(glClearDepthf, d);
+}
+void API_ENTRY(glClearStencil)(GLint s) {
+ CALL_GL_API(glClearStencil, s);
+}
+void API_ENTRY(glColorMask)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {
+ CALL_GL_API(glColorMask, red, green, blue, alpha);
+}
+void API_ENTRY(glCompileShader)(GLuint shader) {
+ CALL_GL_API(glCompileShader, shader);
+}
+void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) {
+ CALL_GL_API(glCompressedTexImage2D, target, level, internalformat, width, height, border, imageSize, data);
+}
+void API_ENTRY(glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) {
+ CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset, width, height, format, imageSize, data);
+}
+void API_ENTRY(glCopyTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {
+ CALL_GL_API(glCopyTexImage2D, target, level, internalformat, x, y, width, height, border);
+}
+void API_ENTRY(glCopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
+ CALL_GL_API(glCopyTexSubImage2D, target, level, xoffset, yoffset, x, y, width, height);
+}
+GLuint API_ENTRY(glCreateProgram)(void) {
+ CALL_GL_API_RETURN(glCreateProgram);
+}
+GLuint API_ENTRY(glCreateShader)(GLenum type) {
+ CALL_GL_API_RETURN(glCreateShader, type);
+}
+void API_ENTRY(glCullFace)(GLenum mode) {
+ CALL_GL_API(glCullFace, mode);
+}
+void API_ENTRY(glDeleteBuffers)(GLsizei n, const GLuint *buffers) {
+ CALL_GL_API(glDeleteBuffers, n, buffers);
+}
+void API_ENTRY(glDeleteFramebuffers)(GLsizei n, const GLuint *framebuffers) {
+ CALL_GL_API(glDeleteFramebuffers, n, framebuffers);
+}
+void API_ENTRY(glDeleteProgram)(GLuint program) {
+ CALL_GL_API(glDeleteProgram, program);
+}
+void API_ENTRY(glDeleteRenderbuffers)(GLsizei n, const GLuint *renderbuffers) {
+ CALL_GL_API(glDeleteRenderbuffers, n, renderbuffers);
+}
+void API_ENTRY(glDeleteShader)(GLuint shader) {
+ CALL_GL_API(glDeleteShader, shader);
+}
+void API_ENTRY(glDeleteTextures)(GLsizei n, const GLuint *textures) {
+ CALL_GL_API(glDeleteTextures, n, textures);
+}
+void API_ENTRY(glDepthFunc)(GLenum func) {
+ CALL_GL_API(glDepthFunc, func);
+}
+void API_ENTRY(glDepthMask)(GLboolean flag) {
+ CALL_GL_API(glDepthMask, flag);
+}
+void API_ENTRY(glDepthRangef)(GLfloat n, GLfloat f) {
+ CALL_GL_API(glDepthRangef, n, f);
+}
+void API_ENTRY(glDetachShader)(GLuint program, GLuint shader) {
+ CALL_GL_API(glDetachShader, program, shader);
+}
+void API_ENTRY(glDisable)(GLenum cap) {
+ CALL_GL_API(glDisable, cap);
+}
+void API_ENTRY(glDisableVertexAttribArray)(GLuint index) {
+ CALL_GL_API(glDisableVertexAttribArray, index);
+}
+void API_ENTRY(glDrawArrays)(GLenum mode, GLint first, GLsizei count) {
+ CALL_GL_API(glDrawArrays, mode, first, count);
+}
+void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void *indices) {
+ CALL_GL_API(glDrawElements, mode, count, type, indices);
+}
+void API_ENTRY(glEnable)(GLenum cap) {
+ CALL_GL_API(glEnable, cap);
+}
+void API_ENTRY(glEnableVertexAttribArray)(GLuint index) {
+ CALL_GL_API(glEnableVertexAttribArray, index);
+}
+void API_ENTRY(glFinish)(void) {
+ CALL_GL_API(glFinish);
+}
+void API_ENTRY(glFlush)(void) {
+ CALL_GL_API(glFlush);
+}
+void API_ENTRY(glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {
+ CALL_GL_API(glFramebufferRenderbuffer, target, attachment, renderbuffertarget, renderbuffer);
+}
+void API_ENTRY(glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {
+ CALL_GL_API(glFramebufferTexture2D, target, attachment, textarget, texture, level);
+}
+void API_ENTRY(glFrontFace)(GLenum mode) {
+ CALL_GL_API(glFrontFace, mode);
+}
+void API_ENTRY(glGenBuffers)(GLsizei n, GLuint *buffers) {
+ CALL_GL_API(glGenBuffers, n, buffers);
+}
+void API_ENTRY(glGenerateMipmap)(GLenum target) {
+ CALL_GL_API(glGenerateMipmap, target);
+}
+void API_ENTRY(glGenFramebuffers)(GLsizei n, GLuint *framebuffers) {
+ CALL_GL_API(glGenFramebuffers, n, framebuffers);
+}
+void API_ENTRY(glGenRenderbuffers)(GLsizei n, GLuint *renderbuffers) {
+ CALL_GL_API(glGenRenderbuffers, n, renderbuffers);
+}
+void API_ENTRY(glGenTextures)(GLsizei n, GLuint *textures) {
+ CALL_GL_API(glGenTextures, n, textures);
+}
+void API_ENTRY(glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) {
+ CALL_GL_API(glGetActiveAttrib, program, index, bufSize, length, size, type, name);
+}
+void API_ENTRY(glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) {
+ CALL_GL_API(glGetActiveUniform, program, index, bufSize, length, size, type, name);
+}
+void API_ENTRY(glGetAttachedShaders)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders) {
+ CALL_GL_API(glGetAttachedShaders, program, maxCount, count, shaders);
+}
+GLint API_ENTRY(glGetAttribLocation)(GLuint program, const GLchar *name) {
+ CALL_GL_API_RETURN(glGetAttribLocation, program, name);
+}
+void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *data) {
+ CALL_GL_API(glGetBooleanv, pname, data);
+}
+void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetBufferParameteriv, target, pname, params);
+}
+GLenum API_ENTRY(glGetError)(void) {
+ CALL_GL_API_RETURN(glGetError);
+}
+void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *data) {
+ CALL_GL_API(glGetFloatv, pname, data);
+}
+void API_ENTRY(glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetFramebufferAttachmentParameteriv, target, attachment, pname, params);
+}
+void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *data) {
+ CALL_GL_API(glGetIntegerv, pname, data);
+}
+void API_ENTRY(glGetProgramiv)(GLuint program, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetProgramiv, program, pname, params);
+}
+void API_ENTRY(glGetProgramInfoLog)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
+ CALL_GL_API(glGetProgramInfoLog, program, bufSize, length, infoLog);
+}
+void API_ENTRY(glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetRenderbufferParameteriv, target, pname, params);
+}
+void API_ENTRY(glGetShaderiv)(GLuint shader, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetShaderiv, shader, pname, params);
+}
+void API_ENTRY(glGetShaderInfoLog)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
+ CALL_GL_API(glGetShaderInfoLog, shader, bufSize, length, infoLog);
+}
+void API_ENTRY(glGetShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision) {
+ CALL_GL_API(glGetShaderPrecisionFormat, shadertype, precisiontype, range, precision);
+}
+void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source) {
+ CALL_GL_API(glGetShaderSource, shader, bufSize, length, source);
+}
+const GLubyte * API_ENTRY(glGetString)(GLenum name) {
+ CALL_GL_API_RETURN(glGetString, name);
+}
+void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat *params) {
+ CALL_GL_API(glGetTexParameterfv, target, pname, params);
+}
+void API_ENTRY(glGetTexParameteriv)(GLenum target, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetTexParameteriv, target, pname, params);
+}
+void API_ENTRY(glGetUniformfv)(GLuint program, GLint location, GLfloat *params) {
+ CALL_GL_API(glGetUniformfv, program, location, params);
+}
+void API_ENTRY(glGetUniformiv)(GLuint program, GLint location, GLint *params) {
+ CALL_GL_API(glGetUniformiv, program, location, params);
+}
+GLint API_ENTRY(glGetUniformLocation)(GLuint program, const GLchar *name) {
+ CALL_GL_API_RETURN(glGetUniformLocation, program, name);
+}
+void API_ENTRY(glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat *params) {
+ CALL_GL_API(glGetVertexAttribfv, index, pname, params);
+}
+void API_ENTRY(glGetVertexAttribiv)(GLuint index, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetVertexAttribiv, index, pname, params);
+}
+void API_ENTRY(glGetVertexAttribPointerv)(GLuint index, GLenum pname, void **pointer) {
+ CALL_GL_API(glGetVertexAttribPointerv, index, pname, pointer);
+}
+void API_ENTRY(glHint)(GLenum target, GLenum mode) {
+ CALL_GL_API(glHint, target, mode);
+}
+GLboolean API_ENTRY(glIsBuffer)(GLuint buffer) {
+ CALL_GL_API_RETURN(glIsBuffer, buffer);
+}
+GLboolean API_ENTRY(glIsEnabled)(GLenum cap) {
+ CALL_GL_API_RETURN(glIsEnabled, cap);
+}
+GLboolean API_ENTRY(glIsFramebuffer)(GLuint framebuffer) {
+ CALL_GL_API_RETURN(glIsFramebuffer, framebuffer);
+}
+GLboolean API_ENTRY(glIsProgram)(GLuint program) {
+ CALL_GL_API_RETURN(glIsProgram, program);
+}
+GLboolean API_ENTRY(glIsRenderbuffer)(GLuint renderbuffer) {
+ CALL_GL_API_RETURN(glIsRenderbuffer, renderbuffer);
+}
+GLboolean API_ENTRY(glIsShader)(GLuint shader) {
+ CALL_GL_API_RETURN(glIsShader, shader);
+}
+GLboolean API_ENTRY(glIsTexture)(GLuint texture) {
+ CALL_GL_API_RETURN(glIsTexture, texture);
+}
+void API_ENTRY(glLineWidth)(GLfloat width) {
+ CALL_GL_API(glLineWidth, width);
+}
+void API_ENTRY(glLinkProgram)(GLuint program) {
+ CALL_GL_API(glLinkProgram, program);
+}
+void API_ENTRY(glPixelStorei)(GLenum pname, GLint param) {
+ CALL_GL_API(glPixelStorei, pname, param);
+}
+void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) {
+ CALL_GL_API(glPolygonOffset, factor, units);
+}
+void API_ENTRY(glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) {
+ CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels);
+}
+void API_ENTRY(glReleaseShaderCompiler)(void) {
+ CALL_GL_API(glReleaseShaderCompiler);
+}
+void API_ENTRY(glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
+ CALL_GL_API(glRenderbufferStorage, target, internalformat, width, height);
+}
+void API_ENTRY(glSampleCoverage)(GLfloat value, GLboolean invert) {
+ CALL_GL_API(glSampleCoverage, value, invert);
+}
+void API_ENTRY(glScissor)(GLint x, GLint y, GLsizei width, GLsizei height) {
+ CALL_GL_API(glScissor, x, y, width, height);
+}
+void API_ENTRY(glShaderBinary)(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) {
+ CALL_GL_API(glShaderBinary, count, shaders, binaryformat, binary, length);
+}
+void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) {
+ CALL_GL_API(glShaderSource, shader, count, string, length);
+}
+void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) {
+ CALL_GL_API(glStencilFunc, func, ref, mask);
+}
+void API_ENTRY(glStencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask) {
+ CALL_GL_API(glStencilFuncSeparate, face, func, ref, mask);
+}
+void API_ENTRY(glStencilMask)(GLuint mask) {
+ CALL_GL_API(glStencilMask, mask);
+}
+void API_ENTRY(glStencilMaskSeparate)(GLenum face, GLuint mask) {
+ CALL_GL_API(glStencilMaskSeparate, face, mask);
+}
+void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) {
+ CALL_GL_API(glStencilOp, fail, zfail, zpass);
+}
+void API_ENTRY(glStencilOpSeparate)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {
+ CALL_GL_API(glStencilOpSeparate, face, sfail, dpfail, dppass);
+}
+void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {
+ CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, border, format, type, pixels);
+}
+void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) {
+ CALL_GL_API(glTexParameterf, target, pname, param);
+}
+void API_ENTRY(glTexParameterfv)(GLenum target, GLenum pname, const GLfloat *params) {
+ CALL_GL_API(glTexParameterfv, target, pname, params);
+}
+void API_ENTRY(glTexParameteri)(GLenum target, GLenum pname, GLint param) {
+ CALL_GL_API(glTexParameteri, target, pname, param);
+}
+void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint *params) {
+ CALL_GL_API(glTexParameteriv, target, pname, params);
+}
+void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {
+ CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset, width, height, format, type, pixels);
+}
+void API_ENTRY(glUniform1f)(GLint location, GLfloat v0) {
+ CALL_GL_API(glUniform1f, location, v0);
+}
+void API_ENTRY(glUniform1fv)(GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glUniform1fv, location, count, value);
+}
+void API_ENTRY(glUniform1i)(GLint location, GLint v0) {
+ CALL_GL_API(glUniform1i, location, v0);
+}
+void API_ENTRY(glUniform1iv)(GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glUniform1iv, location, count, value);
+}
+void API_ENTRY(glUniform2f)(GLint location, GLfloat v0, GLfloat v1) {
+ CALL_GL_API(glUniform2f, location, v0, v1);
+}
+void API_ENTRY(glUniform2fv)(GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glUniform2fv, location, count, value);
+}
+void API_ENTRY(glUniform2i)(GLint location, GLint v0, GLint v1) {
+ CALL_GL_API(glUniform2i, location, v0, v1);
+}
+void API_ENTRY(glUniform2iv)(GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glUniform2iv, location, count, value);
+}
+void API_ENTRY(glUniform3f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {
+ CALL_GL_API(glUniform3f, location, v0, v1, v2);
+}
+void API_ENTRY(glUniform3fv)(GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glUniform3fv, location, count, value);
+}
+void API_ENTRY(glUniform3i)(GLint location, GLint v0, GLint v1, GLint v2) {
+ CALL_GL_API(glUniform3i, location, v0, v1, v2);
+}
+void API_ENTRY(glUniform3iv)(GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glUniform3iv, location, count, value);
+}
+void API_ENTRY(glUniform4f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {
+ CALL_GL_API(glUniform4f, location, v0, v1, v2, v3);
+}
+void API_ENTRY(glUniform4fv)(GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glUniform4fv, location, count, value);
+}
+void API_ENTRY(glUniform4i)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) {
+ CALL_GL_API(glUniform4i, location, v0, v1, v2, v3);
+}
+void API_ENTRY(glUniform4iv)(GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glUniform4iv, location, count, value);
+}
+void API_ENTRY(glUniformMatrix2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glUniformMatrix2fv, location, count, transpose, value);
+}
+void API_ENTRY(glUniformMatrix3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glUniformMatrix3fv, location, count, transpose, value);
+}
+void API_ENTRY(glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glUniformMatrix4fv, location, count, transpose, value);
+}
+void API_ENTRY(glUseProgram)(GLuint program) {
+ CALL_GL_API(glUseProgram, program);
+}
+void API_ENTRY(glValidateProgram)(GLuint program) {
+ CALL_GL_API(glValidateProgram, program);
+}
+void API_ENTRY(glVertexAttrib1f)(GLuint index, GLfloat x) {
+ CALL_GL_API(glVertexAttrib1f, index, x);
+}
+void API_ENTRY(glVertexAttrib1fv)(GLuint index, const GLfloat *v) {
+ CALL_GL_API(glVertexAttrib1fv, index, v);
+}
+void API_ENTRY(glVertexAttrib2f)(GLuint index, GLfloat x, GLfloat y) {
+ CALL_GL_API(glVertexAttrib2f, index, x, y);
+}
+void API_ENTRY(glVertexAttrib2fv)(GLuint index, const GLfloat *v) {
+ CALL_GL_API(glVertexAttrib2fv, index, v);
+}
+void API_ENTRY(glVertexAttrib3f)(GLuint index, GLfloat x, GLfloat y, GLfloat z) {
+ CALL_GL_API(glVertexAttrib3f, index, x, y, z);
+}
+void API_ENTRY(glVertexAttrib3fv)(GLuint index, const GLfloat *v) {
+ CALL_GL_API(glVertexAttrib3fv, index, v);
+}
+void API_ENTRY(glVertexAttrib4f)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) {
+ CALL_GL_API(glVertexAttrib4f, index, x, y, z, w);
+}
+void API_ENTRY(glVertexAttrib4fv)(GLuint index, const GLfloat *v) {
+ CALL_GL_API(glVertexAttrib4fv, index, v);
+}
+void API_ENTRY(glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) {
+ CALL_GL_API(glVertexAttribPointer, index, size, type, normalized, stride, pointer);
+}
+void API_ENTRY(glViewport)(GLint x, GLint y, GLsizei width, GLsizei height) {
+ CALL_GL_API(glViewport, x, y, width, height);
+}
+void API_ENTRY(glReadBuffer)(GLenum src) {
+ CALL_GL_API(glReadBuffer, src);
+}
+void API_ENTRY(glDrawRangeElements)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices) {
+ CALL_GL_API(glDrawRangeElements, mode, start, end, count, type, indices);
+}
+void API_ENTRY(glTexImage3D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) {
+ CALL_GL_API(glTexImage3D, target, level, internalformat, width, height, depth, border, format, type, pixels);
+}
+void API_ENTRY(glTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) {
+ CALL_GL_API(glTexSubImage3D, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
+}
+void API_ENTRY(glCopyTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
+ CALL_GL_API(glCopyTexSubImage3D, target, level, xoffset, yoffset, zoffset, x, y, width, height);
+}
+void API_ENTRY(glCompressedTexImage3D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) {
+ CALL_GL_API(glCompressedTexImage3D, target, level, internalformat, width, height, depth, border, imageSize, data);
+}
+void API_ENTRY(glCompressedTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) {
+ CALL_GL_API(glCompressedTexSubImage3D, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data);
+}
+void API_ENTRY(glGenQueries)(GLsizei n, GLuint *ids) {
+ CALL_GL_API(glGenQueries, n, ids);
+}
+void API_ENTRY(glDeleteQueries)(GLsizei n, const GLuint *ids) {
+ CALL_GL_API(glDeleteQueries, n, ids);
+}
+GLboolean API_ENTRY(glIsQuery)(GLuint id) {
+ CALL_GL_API_RETURN(glIsQuery, id);
+}
+void API_ENTRY(glBeginQuery)(GLenum target, GLuint id) {
+ CALL_GL_API(glBeginQuery, target, id);
+}
+void API_ENTRY(glEndQuery)(GLenum target) {
+ CALL_GL_API(glEndQuery, target);
+}
+void API_ENTRY(glGetQueryiv)(GLenum target, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetQueryiv, target, pname, params);
+}
+void API_ENTRY(glGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params) {
+ CALL_GL_API(glGetQueryObjectuiv, id, pname, params);
+}
+GLboolean API_ENTRY(glUnmapBuffer)(GLenum target) {
+ CALL_GL_API_RETURN(glUnmapBuffer, target);
+}
+void API_ENTRY(glGetBufferPointerv)(GLenum target, GLenum pname, void **params) {
+ CALL_GL_API(glGetBufferPointerv, target, pname, params);
+}
+void API_ENTRY(glDrawBuffers)(GLsizei n, const GLenum *bufs) {
+ CALL_GL_API(glDrawBuffers, n, bufs);
+}
+void API_ENTRY(glUniformMatrix2x3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glUniformMatrix2x3fv, location, count, transpose, value);
+}
+void API_ENTRY(glUniformMatrix3x2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glUniformMatrix3x2fv, location, count, transpose, value);
+}
+void API_ENTRY(glUniformMatrix2x4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glUniformMatrix2x4fv, location, count, transpose, value);
+}
+void API_ENTRY(glUniformMatrix4x2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glUniformMatrix4x2fv, location, count, transpose, value);
+}
+void API_ENTRY(glUniformMatrix3x4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glUniformMatrix3x4fv, location, count, transpose, value);
+}
+void API_ENTRY(glUniformMatrix4x3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glUniformMatrix4x3fv, location, count, transpose, value);
+}
+void API_ENTRY(glBlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) {
+ CALL_GL_API(glBlitFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+}
+void API_ENTRY(glRenderbufferStorageMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) {
+ CALL_GL_API(glRenderbufferStorageMultisample, target, samples, internalformat, width, height);
+}
+void API_ENTRY(glFramebufferTextureLayer)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) {
+ CALL_GL_API(glFramebufferTextureLayer, target, attachment, texture, level, layer);
+}
+void * API_ENTRY(glMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) {
+ CALL_GL_API_RETURN(glMapBufferRange, target, offset, length, access);
+}
+void API_ENTRY(glFlushMappedBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length) {
+ CALL_GL_API(glFlushMappedBufferRange, target, offset, length);
+}
+void API_ENTRY(glBindVertexArray)(GLuint array) {
+ CALL_GL_API(glBindVertexArray, array);
+}
+void API_ENTRY(glDeleteVertexArrays)(GLsizei n, const GLuint *arrays) {
+ CALL_GL_API(glDeleteVertexArrays, n, arrays);
+}
+void API_ENTRY(glGenVertexArrays)(GLsizei n, GLuint *arrays) {
+ CALL_GL_API(glGenVertexArrays, n, arrays);
+}
+GLboolean API_ENTRY(glIsVertexArray)(GLuint array) {
+ CALL_GL_API_RETURN(glIsVertexArray, array);
+}
+void API_ENTRY(glGetIntegeri_v)(GLenum target, GLuint index, GLint *data) {
+ CALL_GL_API(glGetIntegeri_v, target, index, data);
+}
+void API_ENTRY(glBeginTransformFeedback)(GLenum primitiveMode) {
+ CALL_GL_API(glBeginTransformFeedback, primitiveMode);
+}
+void API_ENTRY(glEndTransformFeedback)(void) {
+ CALL_GL_API(glEndTransformFeedback);
+}
+void API_ENTRY(glBindBufferRange)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) {
+ CALL_GL_API(glBindBufferRange, target, index, buffer, offset, size);
+}
+void API_ENTRY(glBindBufferBase)(GLenum target, GLuint index, GLuint buffer) {
+ CALL_GL_API(glBindBufferBase, target, index, buffer);
+}
+void API_ENTRY(glTransformFeedbackVaryings)(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode) {
+ CALL_GL_API(glTransformFeedbackVaryings, program, count, varyings, bufferMode);
+}
+void API_ENTRY(glGetTransformFeedbackVarying)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) {
+ CALL_GL_API(glGetTransformFeedbackVarying, program, index, bufSize, length, size, type, name);
+}
+void API_ENTRY(glVertexAttribIPointer)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer) {
+ CALL_GL_API(glVertexAttribIPointer, index, size, type, stride, pointer);
+}
+void API_ENTRY(glGetVertexAttribIiv)(GLuint index, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetVertexAttribIiv, index, pname, params);
+}
+void API_ENTRY(glGetVertexAttribIuiv)(GLuint index, GLenum pname, GLuint *params) {
+ CALL_GL_API(glGetVertexAttribIuiv, index, pname, params);
+}
+void API_ENTRY(glVertexAttribI4i)(GLuint index, GLint x, GLint y, GLint z, GLint w) {
+ CALL_GL_API(glVertexAttribI4i, index, x, y, z, w);
+}
+void API_ENTRY(glVertexAttribI4ui)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) {
+ CALL_GL_API(glVertexAttribI4ui, index, x, y, z, w);
+}
+void API_ENTRY(glVertexAttribI4iv)(GLuint index, const GLint *v) {
+ CALL_GL_API(glVertexAttribI4iv, index, v);
+}
+void API_ENTRY(glVertexAttribI4uiv)(GLuint index, const GLuint *v) {
+ CALL_GL_API(glVertexAttribI4uiv, index, v);
+}
+void API_ENTRY(glGetUniformuiv)(GLuint program, GLint location, GLuint *params) {
+ CALL_GL_API(glGetUniformuiv, program, location, params);
+}
+GLint API_ENTRY(glGetFragDataLocation)(GLuint program, const GLchar *name) {
+ CALL_GL_API_RETURN(glGetFragDataLocation, program, name);
+}
+void API_ENTRY(glUniform1ui)(GLint location, GLuint v0) {
+ CALL_GL_API(glUniform1ui, location, v0);
+}
+void API_ENTRY(glUniform2ui)(GLint location, GLuint v0, GLuint v1) {
+ CALL_GL_API(glUniform2ui, location, v0, v1);
+}
+void API_ENTRY(glUniform3ui)(GLint location, GLuint v0, GLuint v1, GLuint v2) {
+ CALL_GL_API(glUniform3ui, location, v0, v1, v2);
+}
+void API_ENTRY(glUniform4ui)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) {
+ CALL_GL_API(glUniform4ui, location, v0, v1, v2, v3);
+}
+void API_ENTRY(glUniform1uiv)(GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glUniform1uiv, location, count, value);
+}
+void API_ENTRY(glUniform2uiv)(GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glUniform2uiv, location, count, value);
+}
+void API_ENTRY(glUniform3uiv)(GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glUniform3uiv, location, count, value);
+}
+void API_ENTRY(glUniform4uiv)(GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glUniform4uiv, location, count, value);
+}
+void API_ENTRY(glClearBufferiv)(GLenum buffer, GLint drawbuffer, const GLint *value) {
+ CALL_GL_API(glClearBufferiv, buffer, drawbuffer, value);
+}
+void API_ENTRY(glClearBufferuiv)(GLenum buffer, GLint drawbuffer, const GLuint *value) {
+ CALL_GL_API(glClearBufferuiv, buffer, drawbuffer, value);
+}
+void API_ENTRY(glClearBufferfv)(GLenum buffer, GLint drawbuffer, const GLfloat *value) {
+ CALL_GL_API(glClearBufferfv, buffer, drawbuffer, value);
+}
+void API_ENTRY(glClearBufferfi)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
+ CALL_GL_API(glClearBufferfi, buffer, drawbuffer, depth, stencil);
+}
+const GLubyte * API_ENTRY(glGetStringi)(GLenum name, GLuint index) {
+ CALL_GL_API_RETURN(glGetStringi, name, index);
+}
+void API_ENTRY(glCopyBufferSubData)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) {
+ CALL_GL_API(glCopyBufferSubData, readTarget, writeTarget, readOffset, writeOffset, size);
+}
+void API_ENTRY(glGetUniformIndices)(GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices) {
+ CALL_GL_API(glGetUniformIndices, program, uniformCount, uniformNames, uniformIndices);
+}
+void API_ENTRY(glGetActiveUniformsiv)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetActiveUniformsiv, program, uniformCount, uniformIndices, pname, params);
+}
+GLuint API_ENTRY(glGetUniformBlockIndex)(GLuint program, const GLchar *uniformBlockName) {
+ CALL_GL_API_RETURN(glGetUniformBlockIndex, program, uniformBlockName);
+}
+void API_ENTRY(glGetActiveUniformBlockiv)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetActiveUniformBlockiv, program, uniformBlockIndex, pname, params);
+}
+void API_ENTRY(glGetActiveUniformBlockName)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) {
+ CALL_GL_API(glGetActiveUniformBlockName, program, uniformBlockIndex, bufSize, length, uniformBlockName);
+}
+void API_ENTRY(glUniformBlockBinding)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) {
+ CALL_GL_API(glUniformBlockBinding, program, uniformBlockIndex, uniformBlockBinding);
+}
+void API_ENTRY(glDrawArraysInstanced)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) {
+ CALL_GL_API(glDrawArraysInstanced, mode, first, count, instancecount);
+}
+void API_ENTRY(glDrawElementsInstanced)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount) {
+ CALL_GL_API(glDrawElementsInstanced, mode, count, type, indices, instancecount);
+}
+GLsync API_ENTRY(glFenceSync)(GLenum condition, GLbitfield flags) {
+ CALL_GL_API_RETURN(glFenceSync, condition, flags);
+}
+GLboolean API_ENTRY(glIsSync)(GLsync sync) {
+ CALL_GL_API_RETURN(glIsSync, sync);
+}
+void API_ENTRY(glDeleteSync)(GLsync sync) {
+ CALL_GL_API(glDeleteSync, sync);
+}
+GLenum API_ENTRY(glClientWaitSync)(GLsync sync, GLbitfield flags, GLuint64 timeout) {
+ CALL_GL_API_RETURN(glClientWaitSync, sync, flags, timeout);
+}
+void API_ENTRY(glWaitSync)(GLsync sync, GLbitfield flags, GLuint64 timeout) {
+ CALL_GL_API(glWaitSync, sync, flags, timeout);
+}
+void API_ENTRY(glGetInteger64v)(GLenum pname, GLint64 *data) {
+ CALL_GL_API(glGetInteger64v, pname, data);
+}
+void API_ENTRY(glGetSynciv)(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) {
+ CALL_GL_API(glGetSynciv, sync, pname, bufSize, length, values);
+}
+void API_ENTRY(glGetInteger64i_v)(GLenum target, GLuint index, GLint64 *data) {
+ CALL_GL_API(glGetInteger64i_v, target, index, data);
+}
+void API_ENTRY(glGetBufferParameteri64v)(GLenum target, GLenum pname, GLint64 *params) {
+ CALL_GL_API(glGetBufferParameteri64v, target, pname, params);
+}
+void API_ENTRY(glGenSamplers)(GLsizei count, GLuint *samplers) {
+ CALL_GL_API(glGenSamplers, count, samplers);
+}
+void API_ENTRY(glDeleteSamplers)(GLsizei count, const GLuint *samplers) {
+ CALL_GL_API(glDeleteSamplers, count, samplers);
+}
+GLboolean API_ENTRY(glIsSampler)(GLuint sampler) {
+ CALL_GL_API_RETURN(glIsSampler, sampler);
+}
+void API_ENTRY(glBindSampler)(GLuint unit, GLuint sampler) {
+ CALL_GL_API(glBindSampler, unit, sampler);
+}
+void API_ENTRY(glSamplerParameteri)(GLuint sampler, GLenum pname, GLint param) {
+ CALL_GL_API(glSamplerParameteri, sampler, pname, param);
+}
+void API_ENTRY(glSamplerParameteriv)(GLuint sampler, GLenum pname, const GLint *param) {
+ CALL_GL_API(glSamplerParameteriv, sampler, pname, param);
+}
+void API_ENTRY(glSamplerParameterf)(GLuint sampler, GLenum pname, GLfloat param) {
+ CALL_GL_API(glSamplerParameterf, sampler, pname, param);
+}
+void API_ENTRY(glSamplerParameterfv)(GLuint sampler, GLenum pname, const GLfloat *param) {
+ CALL_GL_API(glSamplerParameterfv, sampler, pname, param);
+}
+void API_ENTRY(glGetSamplerParameteriv)(GLuint sampler, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetSamplerParameteriv, sampler, pname, params);
+}
+void API_ENTRY(glGetSamplerParameterfv)(GLuint sampler, GLenum pname, GLfloat *params) {
+ CALL_GL_API(glGetSamplerParameterfv, sampler, pname, params);
+}
+void API_ENTRY(glVertexAttribDivisor)(GLuint index, GLuint divisor) {
+ CALL_GL_API(glVertexAttribDivisor, index, divisor);
+}
+void API_ENTRY(glBindTransformFeedback)(GLenum target, GLuint id) {
+ CALL_GL_API(glBindTransformFeedback, target, id);
+}
+void API_ENTRY(glDeleteTransformFeedbacks)(GLsizei n, const GLuint *ids) {
+ CALL_GL_API(glDeleteTransformFeedbacks, n, ids);
+}
+void API_ENTRY(glGenTransformFeedbacks)(GLsizei n, GLuint *ids) {
+ CALL_GL_API(glGenTransformFeedbacks, n, ids);
+}
+GLboolean API_ENTRY(glIsTransformFeedback)(GLuint id) {
+ CALL_GL_API_RETURN(glIsTransformFeedback, id);
+}
+void API_ENTRY(glPauseTransformFeedback)(void) {
+ CALL_GL_API(glPauseTransformFeedback);
+}
+void API_ENTRY(glResumeTransformFeedback)(void) {
+ CALL_GL_API(glResumeTransformFeedback);
+}
+void API_ENTRY(glGetProgramBinary)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) {
+ CALL_GL_API(glGetProgramBinary, program, bufSize, length, binaryFormat, binary);
+}
+void API_ENTRY(glProgramBinary)(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length) {
+ CALL_GL_API(glProgramBinary, program, binaryFormat, binary, length);
+}
+void API_ENTRY(glProgramParameteri)(GLuint program, GLenum pname, GLint value) {
+ CALL_GL_API(glProgramParameteri, program, pname, value);
+}
+void API_ENTRY(glInvalidateFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments) {
+ CALL_GL_API(glInvalidateFramebuffer, target, numAttachments, attachments);
+}
+void API_ENTRY(glInvalidateSubFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height) {
+ CALL_GL_API(glInvalidateSubFramebuffer, target, numAttachments, attachments, x, y, width, height);
+}
+void API_ENTRY(glTexStorage2D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) {
+ CALL_GL_API(glTexStorage2D, target, levels, internalformat, width, height);
+}
+void API_ENTRY(glTexStorage3D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) {
+ CALL_GL_API(glTexStorage3D, target, levels, internalformat, width, height, depth);
+}
+void API_ENTRY(glGetInternalformativ)(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params) {
+ CALL_GL_API(glGetInternalformativ, target, internalformat, pname, bufSize, params);
+}
+void API_ENTRY(glDispatchCompute)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z) {
+ CALL_GL_API(glDispatchCompute, num_groups_x, num_groups_y, num_groups_z);
+}
+void API_ENTRY(glDispatchComputeIndirect)(GLintptr indirect) {
+ CALL_GL_API(glDispatchComputeIndirect, indirect);
+}
+void API_ENTRY(glDrawArraysIndirect)(GLenum mode, const void *indirect) {
+ CALL_GL_API(glDrawArraysIndirect, mode, indirect);
+}
+void API_ENTRY(glDrawElementsIndirect)(GLenum mode, GLenum type, const void *indirect) {
+ CALL_GL_API(glDrawElementsIndirect, mode, type, indirect);
+}
+void API_ENTRY(glFramebufferParameteri)(GLenum target, GLenum pname, GLint param) {
+ CALL_GL_API(glFramebufferParameteri, target, pname, param);
+}
+void API_ENTRY(glGetFramebufferParameteriv)(GLenum target, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetFramebufferParameteriv, target, pname, params);
+}
+void API_ENTRY(glGetProgramInterfaceiv)(GLuint program, GLenum programInterface, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetProgramInterfaceiv, program, programInterface, pname, params);
+}
+GLuint API_ENTRY(glGetProgramResourceIndex)(GLuint program, GLenum programInterface, const GLchar *name) {
+ CALL_GL_API_RETURN(glGetProgramResourceIndex, program, programInterface, name);
+}
+void API_ENTRY(glGetProgramResourceName)(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) {
+ CALL_GL_API(glGetProgramResourceName, program, programInterface, index, bufSize, length, name);
+}
+void API_ENTRY(glGetProgramResourceiv)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params) {
+ CALL_GL_API(glGetProgramResourceiv, program, programInterface, index, propCount, props, bufSize, length, params);
+}
+GLint API_ENTRY(glGetProgramResourceLocation)(GLuint program, GLenum programInterface, const GLchar *name) {
+ CALL_GL_API_RETURN(glGetProgramResourceLocation, program, programInterface, name);
+}
+void API_ENTRY(glUseProgramStages)(GLuint pipeline, GLbitfield stages, GLuint program) {
+ CALL_GL_API(glUseProgramStages, pipeline, stages, program);
+}
+void API_ENTRY(glActiveShaderProgram)(GLuint pipeline, GLuint program) {
+ CALL_GL_API(glActiveShaderProgram, pipeline, program);
+}
+GLuint API_ENTRY(glCreateShaderProgramv)(GLenum type, GLsizei count, const GLchar *const*strings) {
+ CALL_GL_API_RETURN(glCreateShaderProgramv, type, count, strings);
+}
+void API_ENTRY(glBindProgramPipeline)(GLuint pipeline) {
+ CALL_GL_API(glBindProgramPipeline, pipeline);
+}
+void API_ENTRY(glDeleteProgramPipelines)(GLsizei n, const GLuint *pipelines) {
+ CALL_GL_API(glDeleteProgramPipelines, n, pipelines);
+}
+void API_ENTRY(glGenProgramPipelines)(GLsizei n, GLuint *pipelines) {
+ CALL_GL_API(glGenProgramPipelines, n, pipelines);
+}
+GLboolean API_ENTRY(glIsProgramPipeline)(GLuint pipeline) {
+ CALL_GL_API_RETURN(glIsProgramPipeline, pipeline);
+}
+void API_ENTRY(glGetProgramPipelineiv)(GLuint pipeline, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetProgramPipelineiv, pipeline, pname, params);
+}
+void API_ENTRY(glProgramUniform1i)(GLuint program, GLint location, GLint v0) {
+ CALL_GL_API(glProgramUniform1i, program, location, v0);
+}
+void API_ENTRY(glProgramUniform2i)(GLuint program, GLint location, GLint v0, GLint v1) {
+ CALL_GL_API(glProgramUniform2i, program, location, v0, v1);
+}
+void API_ENTRY(glProgramUniform3i)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2) {
+ CALL_GL_API(glProgramUniform3i, program, location, v0, v1, v2);
+}
+void API_ENTRY(glProgramUniform4i)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) {
+ CALL_GL_API(glProgramUniform4i, program, location, v0, v1, v2, v3);
+}
+void API_ENTRY(glProgramUniform1ui)(GLuint program, GLint location, GLuint v0) {
+ CALL_GL_API(glProgramUniform1ui, program, location, v0);
+}
+void API_ENTRY(glProgramUniform2ui)(GLuint program, GLint location, GLuint v0, GLuint v1) {
+ CALL_GL_API(glProgramUniform2ui, program, location, v0, v1);
+}
+void API_ENTRY(glProgramUniform3ui)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) {
+ CALL_GL_API(glProgramUniform3ui, program, location, v0, v1, v2);
+}
+void API_ENTRY(glProgramUniform4ui)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) {
+ CALL_GL_API(glProgramUniform4ui, program, location, v0, v1, v2, v3);
+}
+void API_ENTRY(glProgramUniform1f)(GLuint program, GLint location, GLfloat v0) {
+ CALL_GL_API(glProgramUniform1f, program, location, v0);
+}
+void API_ENTRY(glProgramUniform2f)(GLuint program, GLint location, GLfloat v0, GLfloat v1) {
+ CALL_GL_API(glProgramUniform2f, program, location, v0, v1);
+}
+void API_ENTRY(glProgramUniform3f)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {
+ CALL_GL_API(glProgramUniform3f, program, location, v0, v1, v2);
+}
+void API_ENTRY(glProgramUniform4f)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {
+ CALL_GL_API(glProgramUniform4f, program, location, v0, v1, v2, v3);
+}
+void API_ENTRY(glProgramUniform1iv)(GLuint program, GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glProgramUniform1iv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform2iv)(GLuint program, GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glProgramUniform2iv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform3iv)(GLuint program, GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glProgramUniform3iv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform4iv)(GLuint program, GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glProgramUniform4iv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform1uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glProgramUniform1uiv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform2uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glProgramUniform2uiv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform3uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glProgramUniform3uiv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform4uiv)(GLuint program, GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glProgramUniform4uiv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform1fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glProgramUniform1fv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform2fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glProgramUniform2fv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform3fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glProgramUniform3fv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform4fv)(GLuint program, GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glProgramUniform4fv, program, location, count, value);
+}
+void API_ENTRY(glProgramUniformMatrix2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix2fv, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix3fv, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix4fv, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix2x3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix2x3fv, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix3x2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix3x2fv, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix2x4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix2x4fv, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix4x2fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix4x2fv, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix3x4fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix3x4fv, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix4x3fv)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix4x3fv, program, location, count, transpose, value);
+}
+void API_ENTRY(glValidateProgramPipeline)(GLuint pipeline) {
+ CALL_GL_API(glValidateProgramPipeline, pipeline);
+}
+void API_ENTRY(glGetProgramPipelineInfoLog)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
+ CALL_GL_API(glGetProgramPipelineInfoLog, pipeline, bufSize, length, infoLog);
+}
+void API_ENTRY(glBindImageTexture)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) {
+ CALL_GL_API(glBindImageTexture, unit, texture, level, layered, layer, access, format);
+}
+void API_ENTRY(glGetBooleani_v)(GLenum target, GLuint index, GLboolean *data) {
+ CALL_GL_API(glGetBooleani_v, target, index, data);
+}
+void API_ENTRY(glMemoryBarrier)(GLbitfield barriers) {
+ CALL_GL_API(glMemoryBarrier, barriers);
+}
+void API_ENTRY(glMemoryBarrierByRegion)(GLbitfield barriers) {
+ CALL_GL_API(glMemoryBarrierByRegion, barriers);
+}
+void API_ENTRY(glTexStorage2DMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) {
+ CALL_GL_API(glTexStorage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations);
+}
+void API_ENTRY(glGetMultisamplefv)(GLenum pname, GLuint index, GLfloat *val) {
+ CALL_GL_API(glGetMultisamplefv, pname, index, val);
+}
+void API_ENTRY(glSampleMaski)(GLuint maskNumber, GLbitfield mask) {
+ CALL_GL_API(glSampleMaski, maskNumber, mask);
+}
+void API_ENTRY(glGetTexLevelParameteriv)(GLenum target, GLint level, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetTexLevelParameteriv, target, level, pname, params);
+}
+void API_ENTRY(glGetTexLevelParameterfv)(GLenum target, GLint level, GLenum pname, GLfloat *params) {
+ CALL_GL_API(glGetTexLevelParameterfv, target, level, pname, params);
+}
+void API_ENTRY(glBindVertexBuffer)(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride) {
+ CALL_GL_API(glBindVertexBuffer, bindingindex, buffer, offset, stride);
+}
+void API_ENTRY(glVertexAttribFormat)(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) {
+ CALL_GL_API(glVertexAttribFormat, attribindex, size, type, normalized, relativeoffset);
+}
+void API_ENTRY(glVertexAttribIFormat)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) {
+ CALL_GL_API(glVertexAttribIFormat, attribindex, size, type, relativeoffset);
+}
+void API_ENTRY(glVertexAttribBinding)(GLuint attribindex, GLuint bindingindex) {
+ CALL_GL_API(glVertexAttribBinding, attribindex, bindingindex);
+}
+void API_ENTRY(glVertexBindingDivisor)(GLuint bindingindex, GLuint divisor) {
+ CALL_GL_API(glVertexBindingDivisor, bindingindex, divisor);
+}
+void API_ENTRY(glBlendBarrier)(void) {
+ CALL_GL_API(glBlendBarrier);
+}
+void API_ENTRY(glCopyImageSubData)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) {
+ CALL_GL_API(glCopyImageSubData, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth);
+}
+void API_ENTRY(glDebugMessageControl)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) {
+ CALL_GL_API(glDebugMessageControl, source, type, severity, count, ids, enabled);
+}
+void API_ENTRY(glDebugMessageInsert)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) {
+ CALL_GL_API(glDebugMessageInsert, source, type, id, severity, length, buf);
+}
+void API_ENTRY(glDebugMessageCallback)(GLDEBUGPROC callback, const void *userParam) {
+ CALL_GL_API(glDebugMessageCallback, callback, userParam);
+}
+GLuint API_ENTRY(glGetDebugMessageLog)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) {
+ CALL_GL_API_RETURN(glGetDebugMessageLog, count, bufSize, sources, types, ids, severities, lengths, messageLog);
+}
+void API_ENTRY(glPushDebugGroup)(GLenum source, GLuint id, GLsizei length, const GLchar *message) {
+ CALL_GL_API(glPushDebugGroup, source, id, length, message);
+}
+void API_ENTRY(glPopDebugGroup)(void) {
+ CALL_GL_API(glPopDebugGroup);
+}
+void API_ENTRY(glObjectLabel)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label) {
+ CALL_GL_API(glObjectLabel, identifier, name, length, label);
+}
+void API_ENTRY(glGetObjectLabel)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) {
+ CALL_GL_API(glGetObjectLabel, identifier, name, bufSize, length, label);
+}
+void API_ENTRY(glObjectPtrLabel)(const void *ptr, GLsizei length, const GLchar *label) {
+ CALL_GL_API(glObjectPtrLabel, ptr, length, label);
+}
+void API_ENTRY(glGetObjectPtrLabel)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) {
+ CALL_GL_API(glGetObjectPtrLabel, ptr, bufSize, length, label);
+}
+void API_ENTRY(glGetPointerv)(GLenum pname, void **params) {
+ CALL_GL_API(glGetPointerv, pname, params);
+}
+void API_ENTRY(glEnablei)(GLenum target, GLuint index) {
+ CALL_GL_API(glEnablei, target, index);
+}
+void API_ENTRY(glDisablei)(GLenum target, GLuint index) {
+ CALL_GL_API(glDisablei, target, index);
+}
+void API_ENTRY(glBlendEquationi)(GLuint buf, GLenum mode) {
+ CALL_GL_API(glBlendEquationi, buf, mode);
+}
+void API_ENTRY(glBlendEquationSeparatei)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) {
+ CALL_GL_API(glBlendEquationSeparatei, buf, modeRGB, modeAlpha);
+}
+void API_ENTRY(glBlendFunci)(GLuint buf, GLenum src, GLenum dst) {
+ CALL_GL_API(glBlendFunci, buf, src, dst);
+}
+void API_ENTRY(glBlendFuncSeparatei)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
+ CALL_GL_API(glBlendFuncSeparatei, buf, srcRGB, dstRGB, srcAlpha, dstAlpha);
+}
+void API_ENTRY(glColorMaski)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) {
+ CALL_GL_API(glColorMaski, index, r, g, b, a);
+}
+GLboolean API_ENTRY(glIsEnabledi)(GLenum target, GLuint index) {
+ CALL_GL_API_RETURN(glIsEnabledi, target, index);
+}
+void API_ENTRY(glDrawElementsBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) {
+ CALL_GL_API(glDrawElementsBaseVertex, mode, count, type, indices, basevertex);
+}
+void API_ENTRY(glDrawRangeElementsBaseVertex)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) {
+ CALL_GL_API(glDrawRangeElementsBaseVertex, mode, start, end, count, type, indices, basevertex);
+}
+void API_ENTRY(glDrawElementsInstancedBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) {
+ CALL_GL_API(glDrawElementsInstancedBaseVertex, mode, count, type, indices, instancecount, basevertex);
+}
+void API_ENTRY(glFramebufferTexture)(GLenum target, GLenum attachment, GLuint texture, GLint level) {
+ CALL_GL_API(glFramebufferTexture, target, attachment, texture, level);
+}
+void API_ENTRY(glPrimitiveBoundingBox)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) {
+ CALL_GL_API(glPrimitiveBoundingBox, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW);
+}
+GLenum API_ENTRY(glGetGraphicsResetStatus)(void) {
+ CALL_GL_API_RETURN(glGetGraphicsResetStatus);
+}
+void API_ENTRY(glReadnPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) {
+ CALL_GL_API(glReadnPixels, x, y, width, height, format, type, bufSize, data);
+}
+void API_ENTRY(glGetnUniformfv)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) {
+ CALL_GL_API(glGetnUniformfv, program, location, bufSize, params);
+}
+void API_ENTRY(glGetnUniformiv)(GLuint program, GLint location, GLsizei bufSize, GLint *params) {
+ CALL_GL_API(glGetnUniformiv, program, location, bufSize, params);
+}
+void API_ENTRY(glGetnUniformuiv)(GLuint program, GLint location, GLsizei bufSize, GLuint *params) {
+ CALL_GL_API(glGetnUniformuiv, program, location, bufSize, params);
+}
+void API_ENTRY(glMinSampleShading)(GLfloat value) {
+ CALL_GL_API(glMinSampleShading, value);
+}
+void API_ENTRY(glPatchParameteri)(GLenum pname, GLint value) {
+ CALL_GL_API(glPatchParameteri, pname, value);
+}
+void API_ENTRY(glTexParameterIiv)(GLenum target, GLenum pname, const GLint *params) {
+ CALL_GL_API(glTexParameterIiv, target, pname, params);
+}
+void API_ENTRY(glTexParameterIuiv)(GLenum target, GLenum pname, const GLuint *params) {
+ CALL_GL_API(glTexParameterIuiv, target, pname, params);
+}
+void API_ENTRY(glGetTexParameterIiv)(GLenum target, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetTexParameterIiv, target, pname, params);
+}
+void API_ENTRY(glGetTexParameterIuiv)(GLenum target, GLenum pname, GLuint *params) {
+ CALL_GL_API(glGetTexParameterIuiv, target, pname, params);
+}
+void API_ENTRY(glSamplerParameterIiv)(GLuint sampler, GLenum pname, const GLint *param) {
+ CALL_GL_API(glSamplerParameterIiv, sampler, pname, param);
+}
+void API_ENTRY(glSamplerParameterIuiv)(GLuint sampler, GLenum pname, const GLuint *param) {
+ CALL_GL_API(glSamplerParameterIuiv, sampler, pname, param);
+}
+void API_ENTRY(glGetSamplerParameterIiv)(GLuint sampler, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetSamplerParameterIiv, sampler, pname, params);
+}
+void API_ENTRY(glGetSamplerParameterIuiv)(GLuint sampler, GLenum pname, GLuint *params) {
+ CALL_GL_API(glGetSamplerParameterIuiv, sampler, pname, params);
+}
+void API_ENTRY(glTexBuffer)(GLenum target, GLenum internalformat, GLuint buffer) {
+ CALL_GL_API(glTexBuffer, target, internalformat, buffer);
+}
+void API_ENTRY(glTexBufferRange)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) {
+ CALL_GL_API(glTexBufferRange, target, internalformat, buffer, offset, size);
+}
+void API_ENTRY(glTexStorage3DMultisample)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) {
+ CALL_GL_API(glTexStorage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations);
+}
+void API_ENTRY(glBlendBarrierKHR)(void) {
+ CALL_GL_API(glBlendBarrierKHR);
+}
+void API_ENTRY(glDebugMessageControlKHR)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled) {
+ CALL_GL_API(glDebugMessageControlKHR, source, type, severity, count, ids, enabled);
+}
+void API_ENTRY(glDebugMessageInsertKHR)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf) {
+ CALL_GL_API(glDebugMessageInsertKHR, source, type, id, severity, length, buf);
+}
+void API_ENTRY(glDebugMessageCallbackKHR)(GLDEBUGPROCKHR callback, const void *userParam) {
+ CALL_GL_API(glDebugMessageCallbackKHR, callback, userParam);
+}
+GLuint API_ENTRY(glGetDebugMessageLogKHR)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog) {
+ CALL_GL_API_RETURN(glGetDebugMessageLogKHR, count, bufSize, sources, types, ids, severities, lengths, messageLog);
+}
+void API_ENTRY(glPushDebugGroupKHR)(GLenum source, GLuint id, GLsizei length, const GLchar *message) {
+ CALL_GL_API(glPushDebugGroupKHR, source, id, length, message);
+}
+void API_ENTRY(glPopDebugGroupKHR)(void) {
+ CALL_GL_API(glPopDebugGroupKHR);
+}
+void API_ENTRY(glObjectLabelKHR)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label) {
+ CALL_GL_API(glObjectLabelKHR, identifier, name, length, label);
+}
+void API_ENTRY(glGetObjectLabelKHR)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label) {
+ CALL_GL_API(glGetObjectLabelKHR, identifier, name, bufSize, length, label);
+}
+void API_ENTRY(glObjectPtrLabelKHR)(const void *ptr, GLsizei length, const GLchar *label) {
+ CALL_GL_API(glObjectPtrLabelKHR, ptr, length, label);
+}
+void API_ENTRY(glGetObjectPtrLabelKHR)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label) {
+ CALL_GL_API(glGetObjectPtrLabelKHR, ptr, bufSize, length, label);
+}
+void API_ENTRY(glGetPointervKHR)(GLenum pname, void **params) {
+ CALL_GL_API(glGetPointervKHR, pname, params);
+}
+GLenum API_ENTRY(glGetGraphicsResetStatusKHR)(void) {
+ CALL_GL_API_RETURN(glGetGraphicsResetStatusKHR);
+}
+void API_ENTRY(glReadnPixelsKHR)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) {
+ CALL_GL_API(glReadnPixelsKHR, x, y, width, height, format, type, bufSize, data);
+}
+void API_ENTRY(glGetnUniformfvKHR)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) {
+ CALL_GL_API(glGetnUniformfvKHR, program, location, bufSize, params);
+}
+void API_ENTRY(glGetnUniformivKHR)(GLuint program, GLint location, GLsizei bufSize, GLint *params) {
+ CALL_GL_API(glGetnUniformivKHR, program, location, bufSize, params);
+}
+void API_ENTRY(glGetnUniformuivKHR)(GLuint program, GLint location, GLsizei bufSize, GLuint *params) {
+ CALL_GL_API(glGetnUniformuivKHR, program, location, bufSize, params);
+}
+void API_ENTRY(glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) {
+ CALL_GL_API(glEGLImageTargetTexture2DOES, target, image);
+}
+void API_ENTRY(glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {
+ CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image);
+}
+void API_ENTRY(glCopyImageSubDataOES)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) {
+ CALL_GL_API(glCopyImageSubDataOES, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth);
+}
+void API_ENTRY(glEnableiOES)(GLenum target, GLuint index) {
+ CALL_GL_API(glEnableiOES, target, index);
+}
+void API_ENTRY(glDisableiOES)(GLenum target, GLuint index) {
+ CALL_GL_API(glDisableiOES, target, index);
+}
+void API_ENTRY(glBlendEquationiOES)(GLuint buf, GLenum mode) {
+ CALL_GL_API(glBlendEquationiOES, buf, mode);
+}
+void API_ENTRY(glBlendEquationSeparateiOES)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) {
+ CALL_GL_API(glBlendEquationSeparateiOES, buf, modeRGB, modeAlpha);
+}
+void API_ENTRY(glBlendFunciOES)(GLuint buf, GLenum src, GLenum dst) {
+ CALL_GL_API(glBlendFunciOES, buf, src, dst);
+}
+void API_ENTRY(glBlendFuncSeparateiOES)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
+ CALL_GL_API(glBlendFuncSeparateiOES, buf, srcRGB, dstRGB, srcAlpha, dstAlpha);
+}
+void API_ENTRY(glColorMaskiOES)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) {
+ CALL_GL_API(glColorMaskiOES, index, r, g, b, a);
+}
+GLboolean API_ENTRY(glIsEnablediOES)(GLenum target, GLuint index) {
+ CALL_GL_API_RETURN(glIsEnablediOES, target, index);
+}
+void API_ENTRY(glDrawElementsBaseVertexOES)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) {
+ CALL_GL_API(glDrawElementsBaseVertexOES, mode, count, type, indices, basevertex);
+}
+void API_ENTRY(glDrawRangeElementsBaseVertexOES)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) {
+ CALL_GL_API(glDrawRangeElementsBaseVertexOES, mode, start, end, count, type, indices, basevertex);
+}
+void API_ENTRY(glDrawElementsInstancedBaseVertexOES)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) {
+ CALL_GL_API(glDrawElementsInstancedBaseVertexOES, mode, count, type, indices, instancecount, basevertex);
+}
+void API_ENTRY(glMultiDrawElementsBaseVertexOES)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) {
+ CALL_GL_API(glMultiDrawElementsBaseVertexOES, mode, count, type, indices, primcount, basevertex);
+}
+void API_ENTRY(glFramebufferTextureOES)(GLenum target, GLenum attachment, GLuint texture, GLint level) {
+ CALL_GL_API(glFramebufferTextureOES, target, attachment, texture, level);
+}
+void API_ENTRY(glGetProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) {
+ CALL_GL_API(glGetProgramBinaryOES, program, bufSize, length, binaryFormat, binary);
+}
+void API_ENTRY(glProgramBinaryOES)(GLuint program, GLenum binaryFormat, const void *binary, GLint length) {
+ CALL_GL_API(glProgramBinaryOES, program, binaryFormat, binary, length);
+}
+void * API_ENTRY(glMapBufferOES)(GLenum target, GLenum access) {
+ CALL_GL_API_RETURN(glMapBufferOES, target, access);
+}
+GLboolean API_ENTRY(glUnmapBufferOES)(GLenum target) {
+ CALL_GL_API_RETURN(glUnmapBufferOES, target);
+}
+void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, void **params) {
+ CALL_GL_API(glGetBufferPointervOES, target, pname, params);
+}
+void API_ENTRY(glPrimitiveBoundingBoxOES)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) {
+ CALL_GL_API(glPrimitiveBoundingBoxOES, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW);
+}
+void API_ENTRY(glMinSampleShadingOES)(GLfloat value) {
+ CALL_GL_API(glMinSampleShadingOES, value);
+}
+void API_ENTRY(glPatchParameteriOES)(GLenum pname, GLint value) {
+ CALL_GL_API(glPatchParameteriOES, pname, value);
+}
+void API_ENTRY(glTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) {
+ CALL_GL_API(glTexImage3DOES, target, level, internalformat, width, height, depth, border, format, type, pixels);
+}
+void API_ENTRY(glTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) {
+ CALL_GL_API(glTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
+}
+void API_ENTRY(glCopyTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
+ CALL_GL_API(glCopyTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, x, y, width, height);
+}
+void API_ENTRY(glCompressedTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data) {
+ CALL_GL_API(glCompressedTexImage3DOES, target, level, internalformat, width, height, depth, border, imageSize, data);
+}
+void API_ENTRY(glCompressedTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data) {
+ CALL_GL_API(glCompressedTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data);
+}
+void API_ENTRY(glFramebufferTexture3DOES)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) {
+ CALL_GL_API(glFramebufferTexture3DOES, target, attachment, textarget, texture, level, zoffset);
+}
+void API_ENTRY(glTexParameterIivOES)(GLenum target, GLenum pname, const GLint *params) {
+ CALL_GL_API(glTexParameterIivOES, target, pname, params);
+}
+void API_ENTRY(glTexParameterIuivOES)(GLenum target, GLenum pname, const GLuint *params) {
+ CALL_GL_API(glTexParameterIuivOES, target, pname, params);
+}
+void API_ENTRY(glGetTexParameterIivOES)(GLenum target, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetTexParameterIivOES, target, pname, params);
+}
+void API_ENTRY(glGetTexParameterIuivOES)(GLenum target, GLenum pname, GLuint *params) {
+ CALL_GL_API(glGetTexParameterIuivOES, target, pname, params);
+}
+void API_ENTRY(glSamplerParameterIivOES)(GLuint sampler, GLenum pname, const GLint *param) {
+ CALL_GL_API(glSamplerParameterIivOES, sampler, pname, param);
+}
+void API_ENTRY(glSamplerParameterIuivOES)(GLuint sampler, GLenum pname, const GLuint *param) {
+ CALL_GL_API(glSamplerParameterIuivOES, sampler, pname, param);
+}
+void API_ENTRY(glGetSamplerParameterIivOES)(GLuint sampler, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetSamplerParameterIivOES, sampler, pname, params);
+}
+void API_ENTRY(glGetSamplerParameterIuivOES)(GLuint sampler, GLenum pname, GLuint *params) {
+ CALL_GL_API(glGetSamplerParameterIuivOES, sampler, pname, params);
+}
+void API_ENTRY(glTexBufferOES)(GLenum target, GLenum internalformat, GLuint buffer) {
+ CALL_GL_API(glTexBufferOES, target, internalformat, buffer);
+}
+void API_ENTRY(glTexBufferRangeOES)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) {
+ CALL_GL_API(glTexBufferRangeOES, target, internalformat, buffer, offset, size);
+}
+void API_ENTRY(glTexStorage3DMultisampleOES)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) {
+ CALL_GL_API(glTexStorage3DMultisampleOES, target, samples, internalformat, width, height, depth, fixedsamplelocations);
+}
+void API_ENTRY(glTextureViewOES)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) {
+ CALL_GL_API(glTextureViewOES, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers);
+}
+void API_ENTRY(glBindVertexArrayOES)(GLuint array) {
+ CALL_GL_API(glBindVertexArrayOES, array);
+}
+void API_ENTRY(glDeleteVertexArraysOES)(GLsizei n, const GLuint *arrays) {
+ CALL_GL_API(glDeleteVertexArraysOES, n, arrays);
+}
+void API_ENTRY(glGenVertexArraysOES)(GLsizei n, GLuint *arrays) {
+ CALL_GL_API(glGenVertexArraysOES, n, arrays);
+}
+GLboolean API_ENTRY(glIsVertexArrayOES)(GLuint array) {
+ CALL_GL_API_RETURN(glIsVertexArrayOES, array);
+}
+void API_ENTRY(glDrawArraysInstancedBaseInstanceEXT)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance) {
+ CALL_GL_API(glDrawArraysInstancedBaseInstanceEXT, mode, first, count, instancecount, baseinstance);
+}
+void API_ENTRY(glDrawElementsInstancedBaseInstanceEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance) {
+ CALL_GL_API(glDrawElementsInstancedBaseInstanceEXT, mode, count, type, indices, instancecount, baseinstance);
+}
+void API_ENTRY(glDrawElementsInstancedBaseVertexBaseInstanceEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance) {
+ CALL_GL_API(glDrawElementsInstancedBaseVertexBaseInstanceEXT, mode, count, type, indices, instancecount, basevertex, baseinstance);
+}
+void API_ENTRY(glBindFragDataLocationIndexedEXT)(GLuint program, GLuint colorNumber, GLuint index, const GLchar *name) {
+ CALL_GL_API(glBindFragDataLocationIndexedEXT, program, colorNumber, index, name);
+}
+void API_ENTRY(glBindFragDataLocationEXT)(GLuint program, GLuint color, const GLchar *name) {
+ CALL_GL_API(glBindFragDataLocationEXT, program, color, name);
+}
+GLint API_ENTRY(glGetProgramResourceLocationIndexEXT)(GLuint program, GLenum programInterface, const GLchar *name) {
+ CALL_GL_API_RETURN(glGetProgramResourceLocationIndexEXT, program, programInterface, name);
+}
+GLint API_ENTRY(glGetFragDataIndexEXT)(GLuint program, const GLchar *name) {
+ CALL_GL_API_RETURN(glGetFragDataIndexEXT, program, name);
+}
+void API_ENTRY(glBufferStorageEXT)(GLenum target, GLsizeiptr size, const void *data, GLbitfield flags) {
+ CALL_GL_API(glBufferStorageEXT, target, size, data, flags);
+}
+void API_ENTRY(glCopyImageSubDataEXT)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) {
+ CALL_GL_API(glCopyImageSubDataEXT, srcName, srcTarget, srcLevel, srcX, srcY, srcZ, dstName, dstTarget, dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth);
+}
+void API_ENTRY(glLabelObjectEXT)(GLenum type, GLuint object, GLsizei length, const GLchar *label) {
+ CALL_GL_API(glLabelObjectEXT, type, object, length, label);
+}
+void API_ENTRY(glGetObjectLabelEXT)(GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label) {
+ CALL_GL_API(glGetObjectLabelEXT, type, object, bufSize, length, label);
+}
+void API_ENTRY(glInsertEventMarkerEXT)(GLsizei length, const GLchar *marker) {
+ CALL_GL_API(glInsertEventMarkerEXT, length, marker);
+}
+void API_ENTRY(glPushGroupMarkerEXT)(GLsizei length, const GLchar *marker) {
+ CALL_GL_API(glPushGroupMarkerEXT, length, marker);
+}
+void API_ENTRY(glPopGroupMarkerEXT)(void) {
+ CALL_GL_API(glPopGroupMarkerEXT);
+}
+void API_ENTRY(glDiscardFramebufferEXT)(GLenum target, GLsizei numAttachments, const GLenum *attachments) {
+ CALL_GL_API(glDiscardFramebufferEXT, target, numAttachments, attachments);
+}
+void API_ENTRY(glGenQueriesEXT)(GLsizei n, GLuint *ids) {
+ CALL_GL_API(glGenQueriesEXT, n, ids);
+}
+void API_ENTRY(glDeleteQueriesEXT)(GLsizei n, const GLuint *ids) {
+ CALL_GL_API(glDeleteQueriesEXT, n, ids);
+}
+GLboolean API_ENTRY(glIsQueryEXT)(GLuint id) {
+ CALL_GL_API_RETURN(glIsQueryEXT, id);
+}
+void API_ENTRY(glBeginQueryEXT)(GLenum target, GLuint id) {
+ CALL_GL_API(glBeginQueryEXT, target, id);
+}
+void API_ENTRY(glEndQueryEXT)(GLenum target) {
+ CALL_GL_API(glEndQueryEXT, target);
+}
+void API_ENTRY(glQueryCounterEXT)(GLuint id, GLenum target) {
+ CALL_GL_API(glQueryCounterEXT, id, target);
+}
+void API_ENTRY(glGetQueryivEXT)(GLenum target, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetQueryivEXT, target, pname, params);
+}
+void API_ENTRY(glGetQueryObjectivEXT)(GLuint id, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetQueryObjectivEXT, id, pname, params);
+}
+void API_ENTRY(glGetQueryObjectuivEXT)(GLuint id, GLenum pname, GLuint *params) {
+ CALL_GL_API(glGetQueryObjectuivEXT, id, pname, params);
+}
+void API_ENTRY(glGetQueryObjecti64vEXT)(GLuint id, GLenum pname, GLint64 *params) {
+ CALL_GL_API(glGetQueryObjecti64vEXT, id, pname, params);
+}
+void API_ENTRY(glGetQueryObjectui64vEXT)(GLuint id, GLenum pname, GLuint64 *params) {
+ CALL_GL_API(glGetQueryObjectui64vEXT, id, pname, params);
+}
+void API_ENTRY(glDrawBuffersEXT)(GLsizei n, const GLenum *bufs) {
+ CALL_GL_API(glDrawBuffersEXT, n, bufs);
+}
+void API_ENTRY(glEnableiEXT)(GLenum target, GLuint index) {
+ CALL_GL_API(glEnableiEXT, target, index);
+}
+void API_ENTRY(glDisableiEXT)(GLenum target, GLuint index) {
+ CALL_GL_API(glDisableiEXT, target, index);
+}
+void API_ENTRY(glBlendEquationiEXT)(GLuint buf, GLenum mode) {
+ CALL_GL_API(glBlendEquationiEXT, buf, mode);
+}
+void API_ENTRY(glBlendEquationSeparateiEXT)(GLuint buf, GLenum modeRGB, GLenum modeAlpha) {
+ CALL_GL_API(glBlendEquationSeparateiEXT, buf, modeRGB, modeAlpha);
+}
+void API_ENTRY(glBlendFunciEXT)(GLuint buf, GLenum src, GLenum dst) {
+ CALL_GL_API(glBlendFunciEXT, buf, src, dst);
+}
+void API_ENTRY(glBlendFuncSeparateiEXT)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
+ CALL_GL_API(glBlendFuncSeparateiEXT, buf, srcRGB, dstRGB, srcAlpha, dstAlpha);
+}
+void API_ENTRY(glColorMaskiEXT)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) {
+ CALL_GL_API(glColorMaskiEXT, index, r, g, b, a);
+}
+GLboolean API_ENTRY(glIsEnablediEXT)(GLenum target, GLuint index) {
+ CALL_GL_API_RETURN(glIsEnablediEXT, target, index);
+}
+void API_ENTRY(glDrawElementsBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex) {
+ CALL_GL_API(glDrawElementsBaseVertexEXT, mode, count, type, indices, basevertex);
+}
+void API_ENTRY(glDrawRangeElementsBaseVertexEXT)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex) {
+ CALL_GL_API(glDrawRangeElementsBaseVertexEXT, mode, start, end, count, type, indices, basevertex);
+}
+void API_ENTRY(glDrawElementsInstancedBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) {
+ CALL_GL_API(glDrawElementsInstancedBaseVertexEXT, mode, count, type, indices, instancecount, basevertex);
+}
+void API_ENTRY(glMultiDrawElementsBaseVertexEXT)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) {
+ CALL_GL_API(glMultiDrawElementsBaseVertexEXT, mode, count, type, indices, primcount, basevertex);
+}
+void API_ENTRY(glDrawArraysInstancedEXT)(GLenum mode, GLint start, GLsizei count, GLsizei primcount) {
+ CALL_GL_API(glDrawArraysInstancedEXT, mode, start, count, primcount);
+}
+void API_ENTRY(glDrawElementsInstancedEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount) {
+ CALL_GL_API(glDrawElementsInstancedEXT, mode, count, type, indices, primcount);
+}
+void API_ENTRY(glFramebufferTextureEXT)(GLenum target, GLenum attachment, GLuint texture, GLint level) {
+ CALL_GL_API(glFramebufferTextureEXT, target, attachment, texture, level);
+}
+void API_ENTRY(glVertexAttribDivisorEXT)(GLuint index, GLuint divisor) {
+ CALL_GL_API(glVertexAttribDivisorEXT, index, divisor);
+}
+void * API_ENTRY(glMapBufferRangeEXT)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) {
+ CALL_GL_API_RETURN(glMapBufferRangeEXT, target, offset, length, access);
+}
+void API_ENTRY(glFlushMappedBufferRangeEXT)(GLenum target, GLintptr offset, GLsizeiptr length) {
+ CALL_GL_API(glFlushMappedBufferRangeEXT, target, offset, length);
+}
+void API_ENTRY(glMultiDrawArraysEXT)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) {
+ CALL_GL_API(glMultiDrawArraysEXT, mode, first, count, primcount);
+}
+void API_ENTRY(glMultiDrawElementsEXT)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount) {
+ CALL_GL_API(glMultiDrawElementsEXT, mode, count, type, indices, primcount);
+}
+void API_ENTRY(glMultiDrawArraysIndirectEXT)(GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride) {
+ CALL_GL_API(glMultiDrawArraysIndirectEXT, mode, indirect, drawcount, stride);
+}
+void API_ENTRY(glMultiDrawElementsIndirectEXT)(GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride) {
+ CALL_GL_API(glMultiDrawElementsIndirectEXT, mode, type, indirect, drawcount, stride);
+}
+void API_ENTRY(glRenderbufferStorageMultisampleEXT)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) {
+ CALL_GL_API(glRenderbufferStorageMultisampleEXT, target, samples, internalformat, width, height);
+}
+void API_ENTRY(glFramebufferTexture2DMultisampleEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) {
+ CALL_GL_API(glFramebufferTexture2DMultisampleEXT, target, attachment, textarget, texture, level, samples);
+}
+void API_ENTRY(glReadBufferIndexedEXT)(GLenum src, GLint index) {
+ CALL_GL_API(glReadBufferIndexedEXT, src, index);
+}
+void API_ENTRY(glDrawBuffersIndexedEXT)(GLint n, const GLenum *location, const GLint *indices) {
+ CALL_GL_API(glDrawBuffersIndexedEXT, n, location, indices);
+}
+void API_ENTRY(glGetIntegeri_vEXT)(GLenum target, GLuint index, GLint *data) {
+ CALL_GL_API(glGetIntegeri_vEXT, target, index, data);
+}
+void API_ENTRY(glPrimitiveBoundingBoxEXT)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW) {
+ CALL_GL_API(glPrimitiveBoundingBoxEXT, minX, minY, minZ, minW, maxX, maxY, maxZ, maxW);
+}
+void API_ENTRY(glRasterSamplesEXT)(GLuint samples, GLboolean fixedsamplelocations) {
+ CALL_GL_API(glRasterSamplesEXT, samples, fixedsamplelocations);
+}
+GLenum API_ENTRY(glGetGraphicsResetStatusEXT)(void) {
+ CALL_GL_API_RETURN(glGetGraphicsResetStatusEXT);
+}
+void API_ENTRY(glReadnPixelsEXT)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) {
+ CALL_GL_API(glReadnPixelsEXT, x, y, width, height, format, type, bufSize, data);
+}
+void API_ENTRY(glGetnUniformfvEXT)(GLuint program, GLint location, GLsizei bufSize, GLfloat *params) {
+ CALL_GL_API(glGetnUniformfvEXT, program, location, bufSize, params);
+}
+void API_ENTRY(glGetnUniformivEXT)(GLuint program, GLint location, GLsizei bufSize, GLint *params) {
+ CALL_GL_API(glGetnUniformivEXT, program, location, bufSize, params);
+}
+void API_ENTRY(glActiveShaderProgramEXT)(GLuint pipeline, GLuint program) {
+ CALL_GL_API(glActiveShaderProgramEXT, pipeline, program);
+}
+void API_ENTRY(glBindProgramPipelineEXT)(GLuint pipeline) {
+ CALL_GL_API(glBindProgramPipelineEXT, pipeline);
+}
+GLuint API_ENTRY(glCreateShaderProgramvEXT)(GLenum type, GLsizei count, const GLchar **strings) {
+ CALL_GL_API_RETURN(glCreateShaderProgramvEXT, type, count, strings);
+}
+void API_ENTRY(glDeleteProgramPipelinesEXT)(GLsizei n, const GLuint *pipelines) {
+ CALL_GL_API(glDeleteProgramPipelinesEXT, n, pipelines);
+}
+void API_ENTRY(glGenProgramPipelinesEXT)(GLsizei n, GLuint *pipelines) {
+ CALL_GL_API(glGenProgramPipelinesEXT, n, pipelines);
+}
+void API_ENTRY(glGetProgramPipelineInfoLogEXT)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
+ CALL_GL_API(glGetProgramPipelineInfoLogEXT, pipeline, bufSize, length, infoLog);
+}
+void API_ENTRY(glGetProgramPipelineivEXT)(GLuint pipeline, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetProgramPipelineivEXT, pipeline, pname, params);
+}
+GLboolean API_ENTRY(glIsProgramPipelineEXT)(GLuint pipeline) {
+ CALL_GL_API_RETURN(glIsProgramPipelineEXT, pipeline);
+}
+void API_ENTRY(glProgramParameteriEXT)(GLuint program, GLenum pname, GLint value) {
+ CALL_GL_API(glProgramParameteriEXT, program, pname, value);
+}
+void API_ENTRY(glProgramUniform1fEXT)(GLuint program, GLint location, GLfloat v0) {
+ CALL_GL_API(glProgramUniform1fEXT, program, location, v0);
+}
+void API_ENTRY(glProgramUniform1fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glProgramUniform1fvEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform1iEXT)(GLuint program, GLint location, GLint v0) {
+ CALL_GL_API(glProgramUniform1iEXT, program, location, v0);
+}
+void API_ENTRY(glProgramUniform1ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glProgramUniform1ivEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform2fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1) {
+ CALL_GL_API(glProgramUniform2fEXT, program, location, v0, v1);
+}
+void API_ENTRY(glProgramUniform2fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glProgramUniform2fvEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform2iEXT)(GLuint program, GLint location, GLint v0, GLint v1) {
+ CALL_GL_API(glProgramUniform2iEXT, program, location, v0, v1);
+}
+void API_ENTRY(glProgramUniform2ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glProgramUniform2ivEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform3fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {
+ CALL_GL_API(glProgramUniform3fEXT, program, location, v0, v1, v2);
+}
+void API_ENTRY(glProgramUniform3fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glProgramUniform3fvEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform3iEXT)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2) {
+ CALL_GL_API(glProgramUniform3iEXT, program, location, v0, v1, v2);
+}
+void API_ENTRY(glProgramUniform3ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glProgramUniform3ivEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform4fEXT)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {
+ CALL_GL_API(glProgramUniform4fEXT, program, location, v0, v1, v2, v3);
+}
+void API_ENTRY(glProgramUniform4fvEXT)(GLuint program, GLint location, GLsizei count, const GLfloat *value) {
+ CALL_GL_API(glProgramUniform4fvEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform4iEXT)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3) {
+ CALL_GL_API(glProgramUniform4iEXT, program, location, v0, v1, v2, v3);
+}
+void API_ENTRY(glProgramUniform4ivEXT)(GLuint program, GLint location, GLsizei count, const GLint *value) {
+ CALL_GL_API(glProgramUniform4ivEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniformMatrix2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix2fvEXT, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix3fvEXT, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix4fvEXT, program, location, count, transpose, value);
+}
+void API_ENTRY(glUseProgramStagesEXT)(GLuint pipeline, GLbitfield stages, GLuint program) {
+ CALL_GL_API(glUseProgramStagesEXT, pipeline, stages, program);
+}
+void API_ENTRY(glValidateProgramPipelineEXT)(GLuint pipeline) {
+ CALL_GL_API(glValidateProgramPipelineEXT, pipeline);
+}
+void API_ENTRY(glProgramUniform1uiEXT)(GLuint program, GLint location, GLuint v0) {
+ CALL_GL_API(glProgramUniform1uiEXT, program, location, v0);
+}
+void API_ENTRY(glProgramUniform2uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1) {
+ CALL_GL_API(glProgramUniform2uiEXT, program, location, v0, v1);
+}
+void API_ENTRY(glProgramUniform3uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2) {
+ CALL_GL_API(glProgramUniform3uiEXT, program, location, v0, v1, v2);
+}
+void API_ENTRY(glProgramUniform4uiEXT)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) {
+ CALL_GL_API(glProgramUniform4uiEXT, program, location, v0, v1, v2, v3);
+}
+void API_ENTRY(glProgramUniform1uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glProgramUniform1uivEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform2uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glProgramUniform2uivEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform3uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glProgramUniform3uivEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniform4uivEXT)(GLuint program, GLint location, GLsizei count, const GLuint *value) {
+ CALL_GL_API(glProgramUniform4uivEXT, program, location, count, value);
+}
+void API_ENTRY(glProgramUniformMatrix2x3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix2x3fvEXT, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix3x2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix3x2fvEXT, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix2x4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix2x4fvEXT, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix4x2fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix4x2fvEXT, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix3x4fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix3x4fvEXT, program, location, count, transpose, value);
+}
+void API_ENTRY(glProgramUniformMatrix4x3fvEXT)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {
+ CALL_GL_API(glProgramUniformMatrix4x3fvEXT, program, location, count, transpose, value);
+}
+void API_ENTRY(glTexPageCommitmentEXT)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit) {
+ CALL_GL_API(glTexPageCommitmentEXT, target, level, xoffset, yoffset, zoffset, width, height, depth, commit);
+}
+void API_ENTRY(glPatchParameteriEXT)(GLenum pname, GLint value) {
+ CALL_GL_API(glPatchParameteriEXT, pname, value);
+}
+void API_ENTRY(glTexParameterIivEXT)(GLenum target, GLenum pname, const GLint *params) {
+ CALL_GL_API(glTexParameterIivEXT, target, pname, params);
+}
+void API_ENTRY(glTexParameterIuivEXT)(GLenum target, GLenum pname, const GLuint *params) {
+ CALL_GL_API(glTexParameterIuivEXT, target, pname, params);
+}
+void API_ENTRY(glGetTexParameterIivEXT)(GLenum target, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetTexParameterIivEXT, target, pname, params);
+}
+void API_ENTRY(glGetTexParameterIuivEXT)(GLenum target, GLenum pname, GLuint *params) {
+ CALL_GL_API(glGetTexParameterIuivEXT, target, pname, params);
+}
+void API_ENTRY(glSamplerParameterIivEXT)(GLuint sampler, GLenum pname, const GLint *param) {
+ CALL_GL_API(glSamplerParameterIivEXT, sampler, pname, param);
+}
+void API_ENTRY(glSamplerParameterIuivEXT)(GLuint sampler, GLenum pname, const GLuint *param) {
+ CALL_GL_API(glSamplerParameterIuivEXT, sampler, pname, param);
+}
+void API_ENTRY(glGetSamplerParameterIivEXT)(GLuint sampler, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetSamplerParameterIivEXT, sampler, pname, params);
+}
+void API_ENTRY(glGetSamplerParameterIuivEXT)(GLuint sampler, GLenum pname, GLuint *params) {
+ CALL_GL_API(glGetSamplerParameterIuivEXT, sampler, pname, params);
+}
+void API_ENTRY(glTexBufferEXT)(GLenum target, GLenum internalformat, GLuint buffer) {
+ CALL_GL_API(glTexBufferEXT, target, internalformat, buffer);
+}
+void API_ENTRY(glTexBufferRangeEXT)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size) {
+ CALL_GL_API(glTexBufferRangeEXT, target, internalformat, buffer, offset, size);
+}
+void API_ENTRY(glTexStorage1DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) {
+ CALL_GL_API(glTexStorage1DEXT, target, levels, internalformat, width);
+}
+void API_ENTRY(glTexStorage2DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) {
+ CALL_GL_API(glTexStorage2DEXT, target, levels, internalformat, width, height);
+}
+void API_ENTRY(glTexStorage3DEXT)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) {
+ CALL_GL_API(glTexStorage3DEXT, target, levels, internalformat, width, height, depth);
+}
+void API_ENTRY(glTextureStorage1DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) {
+ CALL_GL_API(glTextureStorage1DEXT, texture, target, levels, internalformat, width);
+}
+void API_ENTRY(glTextureStorage2DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) {
+ CALL_GL_API(glTextureStorage2DEXT, texture, target, levels, internalformat, width, height);
+}
+void API_ENTRY(glTextureStorage3DEXT)(GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) {
+ CALL_GL_API(glTextureStorage3DEXT, texture, target, levels, internalformat, width, height, depth);
+}
+void API_ENTRY(glTextureViewEXT)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) {
+ CALL_GL_API(glTextureViewEXT, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers);
+} \ No newline at end of file
diff --git a/libs/hwui/debug/unwrap_gles.h b/libs/hwui/debug/gles_undefine.h
index 7716a735a63b..e43829d98e38 100644
--- a/libs/hwui/debug/unwrap_gles.h
+++ b/libs/hwui/debug/gles_undefine.h
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#ifdef HWUI_GLES_WRAP_ENABLED
-#undef HWUI_GLES_WRAP_ENABLED
-
#undef glActiveShaderProgram
#undef glActiveShaderProgramEXT
#undef glActiveTexture
@@ -914,5 +911,3 @@
#undef glWaitSyncAPPLE
#undef glWeightPathsNV
#undef glWeightPointerOES
-
-#endif // HWUI_GLES_WRAP_ENABLED
diff --git a/libs/hwui/debug/nullegl.cpp b/libs/hwui/debug/nullegl.cpp
index b6cc2f247627..1ce180dd7543 100644
--- a/libs/hwui/debug/nullegl.cpp
+++ b/libs/hwui/debug/nullegl.cpp
@@ -68,6 +68,9 @@ EGLBoolean eglTerminate(EGLDisplay dpy) {
}
const char * eglQueryString(EGLDisplay dpy, EGLint name) {
+ if (name == EGL_EXTENSIONS) {
+ return "EGL_KHR_swap_buffers_with_damage";
+ }
return "";
}
@@ -148,6 +151,10 @@ EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
return EGL_TRUE;
}
+EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint rectCount) {
+ return EGL_TRUE;
+}
+
EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) {
return (EGLImageKHR) malloc(sizeof(EGLImageKHR));
}
diff --git a/libs/hwui/debug/nullgles.cpp b/libs/hwui/debug/nullgles.cpp
deleted file mode 100644
index 8689f9814f7b..000000000000
--- a/libs/hwui/debug/nullgles.cpp
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright(C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0(the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "unwrap_gles.h"
-
-#include <GLES3/gl3.h>
-#include <GLES2/gl2ext.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-struct {
- GLboolean scissorEnabled;
-} gState;
-
-void glGenCommon(GLsizei n, GLuint *buffers) {
- static GLuint nextId = 0;
- int i;
- for(i = 0; i < n; i++) {
- buffers[i] = ++nextId;
- }
-}
-
-void glGenBuffers(GLsizei n, GLuint *buffers) {
- glGenCommon(n, buffers);
-}
-
-void glGenFramebuffers(GLsizei n, GLuint *framebuffers) {
- glGenCommon(n, framebuffers);
-}
-
-void glGenRenderbuffers(GLsizei n, GLuint *renderbuffers) {
- glGenCommon(n, renderbuffers);
-}
-
-void glGenTextures(GLsizei n, GLuint *textures) {
- glGenCommon(n, textures);
-}
-
-GLuint glCreateProgram(void) {
- static GLuint nextProgram = 0;
- return ++nextProgram;
-}
-
-GLuint glCreateShader(GLenum type) {
- static GLuint nextShader = 0;
- return ++nextShader;
-}
-
-void glGetProgramiv(GLuint program, GLenum pname, GLint *params) {
- switch (pname) {
- case GL_DELETE_STATUS:
- case GL_LINK_STATUS:
- case GL_VALIDATE_STATUS:
- *params = GL_TRUE;
- break;
- case GL_INFO_LOG_LENGTH:
- *params = 16;
- break;
- }
-}
-
-void glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
- *length = snprintf(infoLog, bufSize, "success");
- if (*length >= bufSize) {
- *length = bufSize - 1;
- }
-}
-
-void glGetShaderiv(GLuint shader, GLenum pname, GLint *params) {
- switch (pname) {
- case GL_COMPILE_STATUS:
- case GL_DELETE_STATUS:
- *params = GL_TRUE;
- }
-}
-
-void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) {
- *length = snprintf(infoLog, bufSize, "success");
- if (*length >= bufSize) {
- *length = bufSize - 1;
- }
-}
-
-void setBooleanState(GLenum cap, GLboolean value) {
- switch (cap) {
- case GL_SCISSOR_TEST:
- gState.scissorEnabled = value;
- break;
- }
-}
-
-void glEnable(GLenum cap) {
- setBooleanState(cap, GL_TRUE);
-}
-
-void glDisable(GLenum cap) {
- setBooleanState(cap, GL_FALSE);
-}
-
-GLboolean glIsEnabled(GLenum cap) {
- switch (cap) {
- case GL_SCISSOR_TEST:
- return gState.scissorEnabled;
- default:
- return GL_FALSE;
- }
-}
-
-void glGetIntegerv(GLenum pname, GLint *data) {
- switch (pname) {
- case GL_MAX_TEXTURE_SIZE:
- *data = 2048;
- break;
- case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
- *data = 4;
- break;
- default:
- *data = 0;
- }
-}
-
-GLenum glCheckFramebufferStatus(GLenum target) {
- switch (target) {
- case GL_FRAMEBUFFER:
- return GL_FRAMEBUFFER_COMPLETE;
- default:
- return 0; // error case
- }
-}
-
-const char* getString(GLenum name) {
- switch (name) {
- case GL_VENDOR:
- return "android";
- case GL_RENDERER:
- return "null";
- case GL_VERSION:
- return "OpenGL ES 2.0 rev1";
- case GL_SHADING_LANGUAGE_VERSION:
- return "OpenGL ES GLSL ES 2.0 rev1";
- case GL_EXTENSIONS:
- default:
- return "";
- }
-}
-
-const GLubyte* glGetString(GLenum name) {
- return (GLubyte*) getString(name);
-}
-
-void glActiveTexture(GLenum texture) {}
-void glAttachShader(GLuint program, GLuint shader) {}
-void glBindAttribLocation(GLuint program, GLuint index, const GLchar *name) {}
-void glBindBuffer(GLenum target, GLuint buffer) {}
-void glBindFramebuffer(GLenum target, GLuint framebuffer) {}
-void glBindRenderbuffer(GLenum target, GLuint renderbuffer) {}
-void glBindTexture(GLenum target, GLuint texture) {}
-void glBlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {}
-void glBlendEquation(GLenum mode) {}
-void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) {}
-void glBlendFunc(GLenum sfactor, GLenum dfactor) {}
-void glBlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) {}
-void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage) {}
-void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void *data) {}
-void glClear(GLbitfield mask) {}
-void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {}
-void glClearDepthf(GLfloat d) {}
-void glClearStencil(GLint s) {}
-void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {}
-void glCompileShader(GLuint shader) {}
-void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) {}
-void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) {}
-void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {}
-void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {}
-void glCullFace(GLenum mode) {}
-void glDeleteBuffers(GLsizei n, const GLuint *buffers) {}
-void glDeleteFramebuffers(GLsizei n, const GLuint *framebuffers) {}
-void glDeleteProgram(GLuint program) {}
-void glDeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers) {}
-void glDeleteShader(GLuint shader) {}
-void glDeleteTextures(GLsizei n, const GLuint *textures) {}
-void glDepthFunc(GLenum func) {}
-void glDepthMask(GLboolean flag) {}
-void glDepthRangef(GLfloat n, GLfloat f) {}
-void glDetachShader(GLuint program, GLuint shader) {}
-void glDisableVertexAttribArray(GLuint index) {}
-void glDrawArrays(GLenum mode, GLint first, GLsizei count) {}
-void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices) {}
-void glEnableVertexAttribArray(GLuint index) {}
-void glFinish(void) {}
-void glFlush(void) {}
-void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {}
-void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {}
-void glFrontFace(GLenum mode) {}
-void glGenerateMipmap(GLenum target) {}
-GLint glGetAttribLocation(GLuint program, const GLchar *name) { return 1; }
-GLenum glGetError(void) { return GL_NO_ERROR; }
-GLint glGetUniformLocation(GLuint program, const GLchar *name) { return 2; }
-void glHint(GLenum target, GLenum mode) {}
-void glLineWidth(GLfloat width) {}
-void glLinkProgram(GLuint program) {}
-void glPixelStorei(GLenum pname, GLint param) {}
-void glPolygonOffset(GLfloat factor, GLfloat units) {}
-void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) {}
-void glReleaseShaderCompiler(void) {}
-void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {}
-void glSampleCoverage(GLfloat value, GLboolean invert) {}
-void glScissor(GLint x, GLint y, GLsizei width, GLsizei height) {}
-void glShaderBinary(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) {}
-void glShaderSource(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) {}
-void glStencilFunc(GLenum func, GLint ref, GLuint mask) {}
-void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) {}
-void glStencilMask(GLuint mask) {}
-void glStencilMaskSeparate(GLenum face, GLuint mask) {}
-void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) {}
-void glStencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {}
-void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {}
-void glTexParameterf(GLenum target, GLenum pname, GLfloat param) {}
-void glTexParameterfv(GLenum target, GLenum pname, const GLfloat *params) {}
-void glTexParameteri(GLenum target, GLenum pname, GLint param) {}
-void glTexParameteriv(GLenum target, GLenum pname, const GLint *params) {}
-void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {}
-void glUniform1f(GLint location, GLfloat v0) {}
-void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) {}
-void glUniform1i(GLint location, GLint v0) {}
-void glUniform1iv(GLint location, GLsizei count, const GLint *value) {}
-void glUniform2f(GLint location, GLfloat v0, GLfloat v1) {}
-void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) {}
-void glUniform2i(GLint location, GLint v0, GLint v1) {}
-void glUniform2iv(GLint location, GLsizei count, const GLint *value) {}
-void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {}
-void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) {}
-void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2) {}
-void glUniform3iv(GLint location, GLsizei count, const GLint *value) {}
-void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {}
-void glUniform4fv(GLint location, GLsizei count, const GLfloat *value) {}
-void glUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) {}
-void glUniform4iv(GLint location, GLsizei count, const GLint *value) {}
-void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {}
-void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {}
-void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) {}
-void glUseProgram(GLuint program) {}
-void glValidateProgram(GLuint program) {}
-void glVertexAttrib1f(GLuint index, GLfloat x) {}
-void glVertexAttrib1fv(GLuint index, const GLfloat *v) {}
-void glVertexAttrib2f(GLuint index, GLfloat x, GLfloat y) {}
-void glVertexAttrib2fv(GLuint index, const GLfloat *v) {}
-void glVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z) {}
-void glVertexAttrib3fv(GLuint index, const GLfloat *v) {}
-void glVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) {}
-void glVertexAttrib4fv(GLuint index, const GLfloat *v) {}
-void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) {}
-void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) {}
-
-
-// gles2 ext
-void glInsertEventMarkerEXT(GLsizei length, const GLchar *marker) {}
-void glPushGroupMarkerEXT(GLsizei length, const GLchar *marker) {}
-void glPopGroupMarkerEXT(void) {}
-void glDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, const GLenum *attachments) {}
-void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) {}
-
-// GLES3
-void* glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) {
- return 0;
-}
-
-GLboolean glUnmapBuffer(GLenum target) {
- return GL_FALSE;
-}
diff --git a/libs/hwui/debug/wrap_gles.cpp b/libs/hwui/debug/wrap_gles.cpp
index c4f2e3537fe8..8dc946e5667b 100644
--- a/libs/hwui/debug/wrap_gles.cpp
+++ b/libs/hwui/debug/wrap_gles.cpp
@@ -14,80 +14,20 @@
* limitations under the License.
*/
-#include "unwrap_gles.h"
+#include "GlesDriver.h"
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <GLES3/gl3.h>
-#include <GLES3/gl31.h>
-#include <GLES3/gl32.h>
+using namespace android::uirenderer::debug;
-#include <cutils/log.h>
+#undef API_ENTRY
+#undef CALL_GL_API
+#undef CALL_GL_API_RETURN
-void assertNoGlErrors(const char* apicall) {
- GLenum status = GL_NO_ERROR;
- GLenum lastError = GL_NO_ERROR;
- const char* lastErrorName = nullptr;
- while ((status = glGetError()) != GL_NO_ERROR) {
- lastError = status;
- switch (status) {
- case GL_INVALID_ENUM:
- ALOGE("GL error: GL_INVALID_ENUM");
- lastErrorName = "GL_INVALID_ENUM";
- break;
- case GL_INVALID_VALUE:
- ALOGE("GL error: GL_INVALID_VALUE");
- lastErrorName = "GL_INVALID_VALUE";
- break;
- case GL_INVALID_OPERATION:
- ALOGE("GL error: GL_INVALID_OPERATION");
- lastErrorName = "GL_INVALID_OPERATION";
- break;
- case GL_OUT_OF_MEMORY:
- ALOGE("GL error: Out of memory!");
- lastErrorName = "GL_OUT_OF_MEMORY";
- break;
- default:
- ALOGE("GL error: 0x%x", status);
- lastErrorName = "UNKNOWN";
- }
- }
- LOG_ALWAYS_FATAL_IF(lastError != GL_NO_ERROR,
- "%s error! %s (0x%x)", apicall, lastErrorName, lastError);
-}
+#define API_ENTRY(x) x
+#define CALL_GL_API(api, ...) GlesDriver::get()->api##_(__VA_ARGS__)
+#define CALL_GL_API_RETURN(api, ...) return GlesDriver::get()->api##_(__VA_ARGS__)
-#define API_ENTRY(x) wrap_##x
-#define CALL_GL_API(x, ...) x(__VA_ARGS__); assertNoGlErrors(#x)
-#define CALL_GL_API_RETURN(x, ...) auto ret = x(__VA_ARGS__);\
- assertNoGlErrors(#x);\
- return ret
+#include "gles_stubs.in"
-extern "C" {
-#include <gl2_api.in>
-#include <gl2ext_api.in>
-
-// libGLESv2 handles these specially, so they are not in gl2_api.in
-
-void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean *data) {
- CALL_GL_API(glGetBooleanv, pname, data);
-}
-void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat *data) {
- CALL_GL_API(glGetFloatv, pname, data);
-}
-void API_ENTRY(glGetIntegerv)(GLenum pname, GLint *data) {
- CALL_GL_API(glGetIntegerv, pname, data);
-}
-const GLubyte * API_ENTRY(glGetString)(GLenum name) {
- CALL_GL_API_RETURN(glGetString, name);
-}
-const GLubyte * API_ENTRY(glGetStringi)(GLenum name, GLuint index) {
- CALL_GL_API_RETURN(glGetStringi, name, index);
-}
-void API_ENTRY(glGetInteger64v)(GLenum pname, GLint64 *data) {
- CALL_GL_API(glGetInteger64v, pname, data);
-}
-}
+#undef API_ENTRY
+#undef CALL_GL_API
+#undef CALL_GL_API_RETURN
diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h
index 4a3537442e73..27a29aa0a6b2 100644
--- a/libs/hwui/debug/wrap_gles.h
+++ b/libs/hwui/debug/wrap_gles.h
@@ -14,905 +14,24 @@
* limitations under the License.
*/
-#ifndef HWUI_GLES_WRAP_ENABLED
+// #include'ing this file is bad, bad things should be compile errors
+#ifdef HWUI_GLES_WRAP_ENABLED
+#error wrap_gles.h should only be used as an auto-included header, don't directly #include it
+#endif
#define HWUI_GLES_WRAP_ENABLED
-#define glActiveShaderProgram wrap_glActiveShaderProgram
-#define glActiveShaderProgramEXT wrap_glActiveShaderProgramEXT
-#define glActiveTexture wrap_glActiveTexture
-#define glAlphaFunc wrap_glAlphaFunc
-#define glAlphaFuncQCOM wrap_glAlphaFuncQCOM
-#define glAlphaFuncx wrap_glAlphaFuncx
-#define glAlphaFuncxOES wrap_glAlphaFuncxOES
-#define glApplyFramebufferAttachmentCMAAINTEL wrap_glApplyFramebufferAttachmentCMAAINTEL
-#define glAttachShader wrap_glAttachShader
-#define glBeginConditionalRenderNV wrap_glBeginConditionalRenderNV
-#define glBeginPerfMonitorAMD wrap_glBeginPerfMonitorAMD
-#define glBeginPerfQueryINTEL wrap_glBeginPerfQueryINTEL
-#define glBeginQuery wrap_glBeginQuery
-#define glBeginQueryEXT wrap_glBeginQueryEXT
-#define glBeginTransformFeedback wrap_glBeginTransformFeedback
-#define glBindAttribLocation wrap_glBindAttribLocation
-#define glBindBuffer wrap_glBindBuffer
-#define glBindBufferBase wrap_glBindBufferBase
-#define glBindBufferRange wrap_glBindBufferRange
-#define glBindFragDataLocationEXT wrap_glBindFragDataLocationEXT
-#define glBindFragDataLocationIndexedEXT wrap_glBindFragDataLocationIndexedEXT
-#define glBindFramebuffer wrap_glBindFramebuffer
-#define glBindFramebufferOES wrap_glBindFramebufferOES
-#define glBindImageTexture wrap_glBindImageTexture
-#define glBindProgramPipeline wrap_glBindProgramPipeline
-#define glBindProgramPipelineEXT wrap_glBindProgramPipelineEXT
-#define glBindRenderbuffer wrap_glBindRenderbuffer
-#define glBindRenderbufferOES wrap_glBindRenderbufferOES
-#define glBindSampler wrap_glBindSampler
-#define glBindTexture wrap_glBindTexture
-#define glBindTransformFeedback wrap_glBindTransformFeedback
-#define glBindVertexArray wrap_glBindVertexArray
-#define glBindVertexArrayOES wrap_glBindVertexArrayOES
-#define glBindVertexBuffer wrap_glBindVertexBuffer
-#define glBlendBarrier wrap_glBlendBarrier
-#define glBlendBarrierKHR wrap_glBlendBarrierKHR
-#define glBlendBarrierNV wrap_glBlendBarrierNV
-#define glBlendColor wrap_glBlendColor
-#define glBlendEquation wrap_glBlendEquation
-#define glBlendEquationOES wrap_glBlendEquationOES
-#define glBlendEquationSeparate wrap_glBlendEquationSeparate
-#define glBlendEquationSeparateOES wrap_glBlendEquationSeparateOES
-#define glBlendEquationSeparatei wrap_glBlendEquationSeparatei
-#define glBlendEquationSeparateiEXT wrap_glBlendEquationSeparateiEXT
-#define glBlendEquationSeparateiOES wrap_glBlendEquationSeparateiOES
-#define glBlendEquationi wrap_glBlendEquationi
-#define glBlendEquationiEXT wrap_glBlendEquationiEXT
-#define glBlendEquationiOES wrap_glBlendEquationiOES
-#define glBlendFunc wrap_glBlendFunc
-#define glBlendFuncSeparate wrap_glBlendFuncSeparate
-#define glBlendFuncSeparateOES wrap_glBlendFuncSeparateOES
-#define glBlendFuncSeparatei wrap_glBlendFuncSeparatei
-#define glBlendFuncSeparateiEXT wrap_glBlendFuncSeparateiEXT
-#define glBlendFuncSeparateiOES wrap_glBlendFuncSeparateiOES
-#define glBlendFunci wrap_glBlendFunci
-#define glBlendFunciEXT wrap_glBlendFunciEXT
-#define glBlendFunciOES wrap_glBlendFunciOES
-#define glBlendParameteriNV wrap_glBlendParameteriNV
-#define glBlitFramebuffer wrap_glBlitFramebuffer
-#define glBlitFramebufferANGLE wrap_glBlitFramebufferANGLE
-#define glBlitFramebufferNV wrap_glBlitFramebufferNV
-#define glBufferData wrap_glBufferData
-#define glBufferStorageEXT wrap_glBufferStorageEXT
-#define glBufferSubData wrap_glBufferSubData
-#define glCheckFramebufferStatus wrap_glCheckFramebufferStatus
-#define glCheckFramebufferStatusOES wrap_glCheckFramebufferStatusOES
-#define glClear wrap_glClear
-#define glClearBufferfi wrap_glClearBufferfi
-#define glClearBufferfv wrap_glClearBufferfv
-#define glClearBufferiv wrap_glClearBufferiv
-#define glClearBufferuiv wrap_glClearBufferuiv
-#define glClearColor wrap_glClearColor
-#define glClearColorx wrap_glClearColorx
-#define glClearColorxOES wrap_glClearColorxOES
-#define glClearDepthf wrap_glClearDepthf
-#define glClearDepthfOES wrap_glClearDepthfOES
-#define glClearDepthx wrap_glClearDepthx
-#define glClearDepthxOES wrap_glClearDepthxOES
-#define glClearStencil wrap_glClearStencil
-#define glClientActiveTexture wrap_glClientActiveTexture
-#define glClientWaitSync wrap_glClientWaitSync
-#define glClientWaitSyncAPPLE wrap_glClientWaitSyncAPPLE
-#define glClipPlanef wrap_glClipPlanef
-#define glClipPlanefIMG wrap_glClipPlanefIMG
-#define glClipPlanefOES wrap_glClipPlanefOES
-#define glClipPlanex wrap_glClipPlanex
-#define glClipPlanexIMG wrap_glClipPlanexIMG
-#define glClipPlanexOES wrap_glClipPlanexOES
-#define glColor4f wrap_glColor4f
-#define glColor4ub wrap_glColor4ub
-#define glColor4x wrap_glColor4x
-#define glColor4xOES wrap_glColor4xOES
-#define glColorMask wrap_glColorMask
-#define glColorMaski wrap_glColorMaski
-#define glColorMaskiEXT wrap_glColorMaskiEXT
-#define glColorMaskiOES wrap_glColorMaskiOES
-#define glColorPointer wrap_glColorPointer
-#define glCompileShader wrap_glCompileShader
-#define glCompressedTexImage2D wrap_glCompressedTexImage2D
-#define glCompressedTexImage3D wrap_glCompressedTexImage3D
-#define glCompressedTexImage3DOES wrap_glCompressedTexImage3DOES
-#define glCompressedTexSubImage2D wrap_glCompressedTexSubImage2D
-#define glCompressedTexSubImage3D wrap_glCompressedTexSubImage3D
-#define glCompressedTexSubImage3DOES wrap_glCompressedTexSubImage3DOES
-#define glCopyBufferSubData wrap_glCopyBufferSubData
-#define glCopyBufferSubDataNV wrap_glCopyBufferSubDataNV
-#define glCopyImageSubData wrap_glCopyImageSubData
-#define glCopyImageSubDataEXT wrap_glCopyImageSubDataEXT
-#define glCopyImageSubDataOES wrap_glCopyImageSubDataOES
-#define glCopyPathNV wrap_glCopyPathNV
-#define glCopyTexImage2D wrap_glCopyTexImage2D
-#define glCopyTexSubImage2D wrap_glCopyTexSubImage2D
-#define glCopyTexSubImage3D wrap_glCopyTexSubImage3D
-#define glCopyTexSubImage3DOES wrap_glCopyTexSubImage3DOES
-#define glCopyTextureLevelsAPPLE wrap_glCopyTextureLevelsAPPLE
-#define glCoverFillPathInstancedNV wrap_glCoverFillPathInstancedNV
-#define glCoverFillPathNV wrap_glCoverFillPathNV
-#define glCoverStrokePathInstancedNV wrap_glCoverStrokePathInstancedNV
-#define glCoverStrokePathNV wrap_glCoverStrokePathNV
-#define glCoverageMaskNV wrap_glCoverageMaskNV
-#define glCoverageModulationNV wrap_glCoverageModulationNV
-#define glCoverageModulationTableNV wrap_glCoverageModulationTableNV
-#define glCoverageOperationNV wrap_glCoverageOperationNV
-#define glCreatePerfQueryINTEL wrap_glCreatePerfQueryINTEL
-#define glCreateProgram wrap_glCreateProgram
-#define glCreateShader wrap_glCreateShader
-#define glCreateShaderProgramv wrap_glCreateShaderProgramv
-#define glCreateShaderProgramvEXT wrap_glCreateShaderProgramvEXT
-#define glCullFace wrap_glCullFace
-#define glCurrentPaletteMatrixOES wrap_glCurrentPaletteMatrixOES
-#define glDebugMessageCallback wrap_glDebugMessageCallback
-#define glDebugMessageCallbackKHR wrap_glDebugMessageCallbackKHR
-#define glDebugMessageControl wrap_glDebugMessageControl
-#define glDebugMessageControlKHR wrap_glDebugMessageControlKHR
-#define glDebugMessageInsert wrap_glDebugMessageInsert
-#define glDebugMessageInsertKHR wrap_glDebugMessageInsertKHR
-#define glDeleteBuffers wrap_glDeleteBuffers
-#define glDeleteFencesNV wrap_glDeleteFencesNV
-#define glDeleteFramebuffers wrap_glDeleteFramebuffers
-#define glDeleteFramebuffersOES wrap_glDeleteFramebuffersOES
-#define glDeletePathsNV wrap_glDeletePathsNV
-#define glDeletePerfMonitorsAMD wrap_glDeletePerfMonitorsAMD
-#define glDeletePerfQueryINTEL wrap_glDeletePerfQueryINTEL
-#define glDeleteProgram wrap_glDeleteProgram
-#define glDeleteProgramPipelines wrap_glDeleteProgramPipelines
-#define glDeleteProgramPipelinesEXT wrap_glDeleteProgramPipelinesEXT
-#define glDeleteQueries wrap_glDeleteQueries
-#define glDeleteQueriesEXT wrap_glDeleteQueriesEXT
-#define glDeleteRenderbuffers wrap_glDeleteRenderbuffers
-#define glDeleteRenderbuffersOES wrap_glDeleteRenderbuffersOES
-#define glDeleteSamplers wrap_glDeleteSamplers
-#define glDeleteShader wrap_glDeleteShader
-#define glDeleteSync wrap_glDeleteSync
-#define glDeleteSyncAPPLE wrap_glDeleteSyncAPPLE
-#define glDeleteTextures wrap_glDeleteTextures
-#define glDeleteTransformFeedbacks wrap_glDeleteTransformFeedbacks
-#define glDeleteVertexArrays wrap_glDeleteVertexArrays
-#define glDeleteVertexArraysOES wrap_glDeleteVertexArraysOES
-#define glDepthFunc wrap_glDepthFunc
-#define glDepthMask wrap_glDepthMask
-#define glDepthRangeArrayfvNV wrap_glDepthRangeArrayfvNV
-#define glDepthRangeIndexedfNV wrap_glDepthRangeIndexedfNV
-#define glDepthRangef wrap_glDepthRangef
-#define glDepthRangefOES wrap_glDepthRangefOES
-#define glDepthRangex wrap_glDepthRangex
-#define glDepthRangexOES wrap_glDepthRangexOES
-#define glDetachShader wrap_glDetachShader
-#define glDisable wrap_glDisable
-#define glDisableClientState wrap_glDisableClientState
-#define glDisableDriverControlQCOM wrap_glDisableDriverControlQCOM
-#define glDisableVertexAttribArray wrap_glDisableVertexAttribArray
-#define glDisablei wrap_glDisablei
-#define glDisableiEXT wrap_glDisableiEXT
-#define glDisableiNV wrap_glDisableiNV
-#define glDisableiOES wrap_glDisableiOES
-#define glDiscardFramebufferEXT wrap_glDiscardFramebufferEXT
-#define glDispatchCompute wrap_glDispatchCompute
-#define glDispatchComputeIndirect wrap_glDispatchComputeIndirect
-#define glDrawArrays wrap_glDrawArrays
-#define glDrawArraysIndirect wrap_glDrawArraysIndirect
-#define glDrawArraysInstanced wrap_glDrawArraysInstanced
-#define glDrawArraysInstancedANGLE wrap_glDrawArraysInstancedANGLE
-#define glDrawArraysInstancedBaseInstanceEXT wrap_glDrawArraysInstancedBaseInstanceEXT
-#define glDrawArraysInstancedEXT wrap_glDrawArraysInstancedEXT
-#define glDrawArraysInstancedNV wrap_glDrawArraysInstancedNV
-#define glDrawBuffers wrap_glDrawBuffers
-#define glDrawBuffersEXT wrap_glDrawBuffersEXT
-#define glDrawBuffersIndexedEXT wrap_glDrawBuffersIndexedEXT
-#define glDrawBuffersNV wrap_glDrawBuffersNV
-#define glDrawElements wrap_glDrawElements
-#define glDrawElementsBaseVertex wrap_glDrawElementsBaseVertex
-#define glDrawElementsBaseVertexEXT wrap_glDrawElementsBaseVertexEXT
-#define glDrawElementsBaseVertexOES wrap_glDrawElementsBaseVertexOES
-#define glDrawElementsIndirect wrap_glDrawElementsIndirect
-#define glDrawElementsInstanced wrap_glDrawElementsInstanced
-#define glDrawElementsInstancedANGLE wrap_glDrawElementsInstancedANGLE
-#define glDrawElementsInstancedBaseInstanceEXT wrap_glDrawElementsInstancedBaseInstanceEXT
-#define glDrawElementsInstancedBaseVertex wrap_glDrawElementsInstancedBaseVertex
-#define glDrawElementsInstancedBaseVertexBaseInstanceEXT wrap_glDrawElementsInstancedBaseVertexBaseInstanceEXT
-#define glDrawElementsInstancedBaseVertexEXT wrap_glDrawElementsInstancedBaseVertexEXT
-#define glDrawElementsInstancedBaseVertexOES wrap_glDrawElementsInstancedBaseVertexOES
-#define glDrawElementsInstancedEXT wrap_glDrawElementsInstancedEXT
-#define glDrawElementsInstancedNV wrap_glDrawElementsInstancedNV
-#define glDrawRangeElements wrap_glDrawRangeElements
-#define glDrawRangeElementsBaseVertex wrap_glDrawRangeElementsBaseVertex
-#define glDrawRangeElementsBaseVertexEXT wrap_glDrawRangeElementsBaseVertexEXT
-#define glDrawRangeElementsBaseVertexOES wrap_glDrawRangeElementsBaseVertexOES
-#define glDrawTexfOES wrap_glDrawTexfOES
-#define glDrawTexfvOES wrap_glDrawTexfvOES
-#define glDrawTexiOES wrap_glDrawTexiOES
-#define glDrawTexivOES wrap_glDrawTexivOES
-#define glDrawTexsOES wrap_glDrawTexsOES
-#define glDrawTexsvOES wrap_glDrawTexsvOES
-#define glDrawTexxOES wrap_glDrawTexxOES
-#define glDrawTexxvOES wrap_glDrawTexxvOES
-#define glEGLImageTargetRenderbufferStorageOES wrap_glEGLImageTargetRenderbufferStorageOES
-#define glEGLImageTargetTexture2DOES wrap_glEGLImageTargetTexture2DOES
-#define glEnable wrap_glEnable
-#define glEnableClientState wrap_glEnableClientState
-#define glEnableDriverControlQCOM wrap_glEnableDriverControlQCOM
-#define glEnableVertexAttribArray wrap_glEnableVertexAttribArray
-#define glEnablei wrap_glEnablei
-#define glEnableiEXT wrap_glEnableiEXT
-#define glEnableiNV wrap_glEnableiNV
-#define glEnableiOES wrap_glEnableiOES
-#define glEndConditionalRenderNV wrap_glEndConditionalRenderNV
-#define glEndPerfMonitorAMD wrap_glEndPerfMonitorAMD
-#define glEndPerfQueryINTEL wrap_glEndPerfQueryINTEL
-#define glEndQuery wrap_glEndQuery
-#define glEndQueryEXT wrap_glEndQueryEXT
-#define glEndTilingQCOM wrap_glEndTilingQCOM
-#define glEndTransformFeedback wrap_glEndTransformFeedback
-#define glExtGetBufferPointervQCOM wrap_glExtGetBufferPointervQCOM
-#define glExtGetBuffersQCOM wrap_glExtGetBuffersQCOM
-#define glExtGetFramebuffersQCOM wrap_glExtGetFramebuffersQCOM
-#define glExtGetProgramBinarySourceQCOM wrap_glExtGetProgramBinarySourceQCOM
-#define glExtGetProgramsQCOM wrap_glExtGetProgramsQCOM
-#define glExtGetRenderbuffersQCOM wrap_glExtGetRenderbuffersQCOM
-#define glExtGetShadersQCOM wrap_glExtGetShadersQCOM
-#define glExtGetTexLevelParameterivQCOM wrap_glExtGetTexLevelParameterivQCOM
-#define glExtGetTexSubImageQCOM wrap_glExtGetTexSubImageQCOM
-#define glExtGetTexturesQCOM wrap_glExtGetTexturesQCOM
-#define glExtIsProgramBinaryQCOM wrap_glExtIsProgramBinaryQCOM
-#define glExtTexObjectStateOverrideiQCOM wrap_glExtTexObjectStateOverrideiQCOM
-#define glFenceSync wrap_glFenceSync
-#define glFenceSyncAPPLE wrap_glFenceSyncAPPLE
-#define glFinish wrap_glFinish
-#define glFinishFenceNV wrap_glFinishFenceNV
-#define glFlush wrap_glFlush
-#define glFlushMappedBufferRange wrap_glFlushMappedBufferRange
-#define glFlushMappedBufferRangeEXT wrap_glFlushMappedBufferRangeEXT
-#define glFogf wrap_glFogf
-#define glFogfv wrap_glFogfv
-#define glFogx wrap_glFogx
-#define glFogxOES wrap_glFogxOES
-#define glFogxv wrap_glFogxv
-#define glFogxvOES wrap_glFogxvOES
-#define glFragmentCoverageColorNV wrap_glFragmentCoverageColorNV
-#define glFramebufferParameteri wrap_glFramebufferParameteri
-#define glFramebufferRenderbuffer wrap_glFramebufferRenderbuffer
-#define glFramebufferRenderbufferOES wrap_glFramebufferRenderbufferOES
-#define glFramebufferSampleLocationsfvNV wrap_glFramebufferSampleLocationsfvNV
-#define glFramebufferTexture wrap_glFramebufferTexture
-#define glFramebufferTexture2D wrap_glFramebufferTexture2D
-#define glFramebufferTexture2DMultisampleEXT wrap_glFramebufferTexture2DMultisampleEXT
-#define glFramebufferTexture2DMultisampleIMG wrap_glFramebufferTexture2DMultisampleIMG
-#define glFramebufferTexture2DOES wrap_glFramebufferTexture2DOES
-#define glFramebufferTexture3DOES wrap_glFramebufferTexture3DOES
-#define glFramebufferTextureEXT wrap_glFramebufferTextureEXT
-#define glFramebufferTextureLayer wrap_glFramebufferTextureLayer
-#define glFramebufferTextureMultisampleMultiviewOVR wrap_glFramebufferTextureMultisampleMultiviewOVR
-#define glFramebufferTextureMultiviewOVR wrap_glFramebufferTextureMultiviewOVR
-#define glFramebufferTextureOES wrap_glFramebufferTextureOES
-#define glFrontFace wrap_glFrontFace
-#define glFrustumf wrap_glFrustumf
-#define glFrustumfOES wrap_glFrustumfOES
-#define glFrustumx wrap_glFrustumx
-#define glFrustumxOES wrap_glFrustumxOES
-#define glGenBuffers wrap_glGenBuffers
-#define glGenFencesNV wrap_glGenFencesNV
-#define glGenFramebuffers wrap_glGenFramebuffers
-#define glGenFramebuffersOES wrap_glGenFramebuffersOES
-#define glGenPathsNV wrap_glGenPathsNV
-#define glGenPerfMonitorsAMD wrap_glGenPerfMonitorsAMD
-#define glGenProgramPipelines wrap_glGenProgramPipelines
-#define glGenProgramPipelinesEXT wrap_glGenProgramPipelinesEXT
-#define glGenQueries wrap_glGenQueries
-#define glGenQueriesEXT wrap_glGenQueriesEXT
-#define glGenRenderbuffers wrap_glGenRenderbuffers
-#define glGenRenderbuffersOES wrap_glGenRenderbuffersOES
-#define glGenSamplers wrap_glGenSamplers
-#define glGenTextures wrap_glGenTextures
-#define glGenTransformFeedbacks wrap_glGenTransformFeedbacks
-#define glGenVertexArrays wrap_glGenVertexArrays
-#define glGenVertexArraysOES wrap_glGenVertexArraysOES
-#define glGenerateMipmap wrap_glGenerateMipmap
-#define glGenerateMipmapOES wrap_glGenerateMipmapOES
-#define glGetActiveAttrib wrap_glGetActiveAttrib
-#define glGetActiveUniform wrap_glGetActiveUniform
-#define glGetActiveUniformBlockName wrap_glGetActiveUniformBlockName
-#define glGetActiveUniformBlockiv wrap_glGetActiveUniformBlockiv
-#define glGetActiveUniformsiv wrap_glGetActiveUniformsiv
-#define glGetAttachedShaders wrap_glGetAttachedShaders
-#define glGetAttribLocation wrap_glGetAttribLocation
-#define glGetBooleani_v wrap_glGetBooleani_v
-#define glGetBooleanv wrap_glGetBooleanv
-#define glGetBufferParameteri64v wrap_glGetBufferParameteri64v
-#define glGetBufferParameteriv wrap_glGetBufferParameteriv
-#define glGetBufferPointerv wrap_glGetBufferPointerv
-#define glGetBufferPointervOES wrap_glGetBufferPointervOES
-#define glGetClipPlanef wrap_glGetClipPlanef
-#define glGetClipPlanefOES wrap_glGetClipPlanefOES
-#define glGetClipPlanex wrap_glGetClipPlanex
-#define glGetClipPlanexOES wrap_glGetClipPlanexOES
-#define glGetCoverageModulationTableNV wrap_glGetCoverageModulationTableNV
-#define glGetDebugMessageLog wrap_glGetDebugMessageLog
-#define glGetDebugMessageLogKHR wrap_glGetDebugMessageLogKHR
-#define glGetDriverControlStringQCOM wrap_glGetDriverControlStringQCOM
-#define glGetDriverControlsQCOM wrap_glGetDriverControlsQCOM
-#define glGetError wrap_glGetError
-#define glGetFenceivNV wrap_glGetFenceivNV
-#define glGetFirstPerfQueryIdINTEL wrap_glGetFirstPerfQueryIdINTEL
-#define glGetFixedv wrap_glGetFixedv
-#define glGetFixedvOES wrap_glGetFixedvOES
-#define glGetFloati_vNV wrap_glGetFloati_vNV
-#define glGetFloatv wrap_glGetFloatv
-#define glGetFragDataIndexEXT wrap_glGetFragDataIndexEXT
-#define glGetFragDataLocation wrap_glGetFragDataLocation
-#define glGetFramebufferAttachmentParameteriv wrap_glGetFramebufferAttachmentParameteriv
-#define glGetFramebufferAttachmentParameterivOES wrap_glGetFramebufferAttachmentParameterivOES
-#define glGetFramebufferParameteriv wrap_glGetFramebufferParameteriv
-#define glGetGraphicsResetStatus wrap_glGetGraphicsResetStatus
-#define glGetGraphicsResetStatusEXT wrap_glGetGraphicsResetStatusEXT
-#define glGetGraphicsResetStatusKHR wrap_glGetGraphicsResetStatusKHR
-#define glGetImageHandleNV wrap_glGetImageHandleNV
-#define glGetInteger64i_v wrap_glGetInteger64i_v
-#define glGetInteger64v wrap_glGetInteger64v
-#define glGetInteger64vAPPLE wrap_glGetInteger64vAPPLE
-#define glGetIntegeri_v wrap_glGetIntegeri_v
-#define glGetIntegeri_vEXT wrap_glGetIntegeri_vEXT
-#define glGetIntegerv wrap_glGetIntegerv
-#define glGetInternalformatSampleivNV wrap_glGetInternalformatSampleivNV
-#define glGetInternalformativ wrap_glGetInternalformativ
-#define glGetLightfv wrap_glGetLightfv
-#define glGetLightxv wrap_glGetLightxv
-#define glGetLightxvOES wrap_glGetLightxvOES
-#define glGetMaterialfv wrap_glGetMaterialfv
-#define glGetMaterialxv wrap_glGetMaterialxv
-#define glGetMaterialxvOES wrap_glGetMaterialxvOES
-#define glGetMultisamplefv wrap_glGetMultisamplefv
-#define glGetNextPerfQueryIdINTEL wrap_glGetNextPerfQueryIdINTEL
-#define glGetObjectLabel wrap_glGetObjectLabel
-#define glGetObjectLabelEXT wrap_glGetObjectLabelEXT
-#define glGetObjectLabelKHR wrap_glGetObjectLabelKHR
-#define glGetObjectPtrLabel wrap_glGetObjectPtrLabel
-#define glGetObjectPtrLabelKHR wrap_glGetObjectPtrLabelKHR
-#define glGetPathCommandsNV wrap_glGetPathCommandsNV
-#define glGetPathCoordsNV wrap_glGetPathCoordsNV
-#define glGetPathDashArrayNV wrap_glGetPathDashArrayNV
-#define glGetPathLengthNV wrap_glGetPathLengthNV
-#define glGetPathMetricRangeNV wrap_glGetPathMetricRangeNV
-#define glGetPathMetricsNV wrap_glGetPathMetricsNV
-#define glGetPathParameterfvNV wrap_glGetPathParameterfvNV
-#define glGetPathParameterivNV wrap_glGetPathParameterivNV
-#define glGetPathSpacingNV wrap_glGetPathSpacingNV
-#define glGetPerfCounterInfoINTEL wrap_glGetPerfCounterInfoINTEL
-#define glGetPerfMonitorCounterDataAMD wrap_glGetPerfMonitorCounterDataAMD
-#define glGetPerfMonitorCounterInfoAMD wrap_glGetPerfMonitorCounterInfoAMD
-#define glGetPerfMonitorCounterStringAMD wrap_glGetPerfMonitorCounterStringAMD
-#define glGetPerfMonitorCountersAMD wrap_glGetPerfMonitorCountersAMD
-#define glGetPerfMonitorGroupStringAMD wrap_glGetPerfMonitorGroupStringAMD
-#define glGetPerfMonitorGroupsAMD wrap_glGetPerfMonitorGroupsAMD
-#define glGetPerfQueryDataINTEL wrap_glGetPerfQueryDataINTEL
-#define glGetPerfQueryIdByNameINTEL wrap_glGetPerfQueryIdByNameINTEL
-#define glGetPerfQueryInfoINTEL wrap_glGetPerfQueryInfoINTEL
-#define glGetPointerv wrap_glGetPointerv
-#define glGetPointervKHR wrap_glGetPointervKHR
-#define glGetProgramBinary wrap_glGetProgramBinary
-#define glGetProgramBinaryOES wrap_glGetProgramBinaryOES
-#define glGetProgramInfoLog wrap_glGetProgramInfoLog
-#define glGetProgramInterfaceiv wrap_glGetProgramInterfaceiv
-#define glGetProgramPipelineInfoLog wrap_glGetProgramPipelineInfoLog
-#define glGetProgramPipelineInfoLogEXT wrap_glGetProgramPipelineInfoLogEXT
-#define glGetProgramPipelineiv wrap_glGetProgramPipelineiv
-#define glGetProgramPipelineivEXT wrap_glGetProgramPipelineivEXT
-#define glGetProgramResourceIndex wrap_glGetProgramResourceIndex
-#define glGetProgramResourceLocation wrap_glGetProgramResourceLocation
-#define glGetProgramResourceLocationIndexEXT wrap_glGetProgramResourceLocationIndexEXT
-#define glGetProgramResourceName wrap_glGetProgramResourceName
-#define glGetProgramResourcefvNV wrap_glGetProgramResourcefvNV
-#define glGetProgramResourceiv wrap_glGetProgramResourceiv
-#define glGetProgramiv wrap_glGetProgramiv
-#define glGetQueryObjecti64vEXT wrap_glGetQueryObjecti64vEXT
-#define glGetQueryObjectivEXT wrap_glGetQueryObjectivEXT
-#define glGetQueryObjectui64vEXT wrap_glGetQueryObjectui64vEXT
-#define glGetQueryObjectuiv wrap_glGetQueryObjectuiv
-#define glGetQueryObjectuivEXT wrap_glGetQueryObjectuivEXT
-#define glGetQueryiv wrap_glGetQueryiv
-#define glGetQueryivEXT wrap_glGetQueryivEXT
-#define glGetRenderbufferParameteriv wrap_glGetRenderbufferParameteriv
-#define glGetRenderbufferParameterivOES wrap_glGetRenderbufferParameterivOES
-#define glGetSamplerParameterIiv wrap_glGetSamplerParameterIiv
-#define glGetSamplerParameterIivEXT wrap_glGetSamplerParameterIivEXT
-#define glGetSamplerParameterIivOES wrap_glGetSamplerParameterIivOES
-#define glGetSamplerParameterIuiv wrap_glGetSamplerParameterIuiv
-#define glGetSamplerParameterIuivEXT wrap_glGetSamplerParameterIuivEXT
-#define glGetSamplerParameterIuivOES wrap_glGetSamplerParameterIuivOES
-#define glGetSamplerParameterfv wrap_glGetSamplerParameterfv
-#define glGetSamplerParameteriv wrap_glGetSamplerParameteriv
-#define glGetShaderInfoLog wrap_glGetShaderInfoLog
-#define glGetShaderPrecisionFormat wrap_glGetShaderPrecisionFormat
-#define glGetShaderSource wrap_glGetShaderSource
-#define glGetShaderiv wrap_glGetShaderiv
-#define glGetString wrap_glGetString
-#define glGetStringi wrap_glGetStringi
-#define glGetSynciv wrap_glGetSynciv
-#define glGetSyncivAPPLE wrap_glGetSyncivAPPLE
-#define glGetTexEnvfv wrap_glGetTexEnvfv
-#define glGetTexEnviv wrap_glGetTexEnviv
-#define glGetTexEnvxv wrap_glGetTexEnvxv
-#define glGetTexEnvxvOES wrap_glGetTexEnvxvOES
-#define glGetTexGenfvOES wrap_glGetTexGenfvOES
-#define glGetTexGenivOES wrap_glGetTexGenivOES
-#define glGetTexGenxvOES wrap_glGetTexGenxvOES
-#define glGetTexLevelParameterfv wrap_glGetTexLevelParameterfv
-#define glGetTexLevelParameteriv wrap_glGetTexLevelParameteriv
-#define glGetTexParameterIiv wrap_glGetTexParameterIiv
-#define glGetTexParameterIivEXT wrap_glGetTexParameterIivEXT
-#define glGetTexParameterIivOES wrap_glGetTexParameterIivOES
-#define glGetTexParameterIuiv wrap_glGetTexParameterIuiv
-#define glGetTexParameterIuivEXT wrap_glGetTexParameterIuivEXT
-#define glGetTexParameterIuivOES wrap_glGetTexParameterIuivOES
-#define glGetTexParameterfv wrap_glGetTexParameterfv
-#define glGetTexParameteriv wrap_glGetTexParameteriv
-#define glGetTexParameterxv wrap_glGetTexParameterxv
-#define glGetTexParameterxvOES wrap_glGetTexParameterxvOES
-#define glGetTextureHandleNV wrap_glGetTextureHandleNV
-#define glGetTextureSamplerHandleNV wrap_glGetTextureSamplerHandleNV
-#define glGetTransformFeedbackVarying wrap_glGetTransformFeedbackVarying
-#define glGetTranslatedShaderSourceANGLE wrap_glGetTranslatedShaderSourceANGLE
-#define glGetUniformBlockIndex wrap_glGetUniformBlockIndex
-#define glGetUniformIndices wrap_glGetUniformIndices
-#define glGetUniformLocation wrap_glGetUniformLocation
-#define glGetUniformfv wrap_glGetUniformfv
-#define glGetUniformiv wrap_glGetUniformiv
-#define glGetUniformuiv wrap_glGetUniformuiv
-#define glGetVertexAttribIiv wrap_glGetVertexAttribIiv
-#define glGetVertexAttribIuiv wrap_glGetVertexAttribIuiv
-#define glGetVertexAttribPointerv wrap_glGetVertexAttribPointerv
-#define glGetVertexAttribfv wrap_glGetVertexAttribfv
-#define glGetVertexAttribiv wrap_glGetVertexAttribiv
-#define glGetnUniformfv wrap_glGetnUniformfv
-#define glGetnUniformfvEXT wrap_glGetnUniformfvEXT
-#define glGetnUniformfvKHR wrap_glGetnUniformfvKHR
-#define glGetnUniformiv wrap_glGetnUniformiv
-#define glGetnUniformivEXT wrap_glGetnUniformivEXT
-#define glGetnUniformivKHR wrap_glGetnUniformivKHR
-#define glGetnUniformuiv wrap_glGetnUniformuiv
-#define glGetnUniformuivKHR wrap_glGetnUniformuivKHR
-#define glHint wrap_glHint
-#define glInsertEventMarkerEXT wrap_glInsertEventMarkerEXT
-#define glInterpolatePathsNV wrap_glInterpolatePathsNV
-#define glInvalidateFramebuffer wrap_glInvalidateFramebuffer
-#define glInvalidateSubFramebuffer wrap_glInvalidateSubFramebuffer
-#define glIsBuffer wrap_glIsBuffer
-#define glIsEnabled wrap_glIsEnabled
-#define glIsEnabledi wrap_glIsEnabledi
-#define glIsEnablediEXT wrap_glIsEnablediEXT
-#define glIsEnablediNV wrap_glIsEnablediNV
-#define glIsEnablediOES wrap_glIsEnablediOES
-#define glIsFenceNV wrap_glIsFenceNV
-#define glIsFramebuffer wrap_glIsFramebuffer
-#define glIsFramebufferOES wrap_glIsFramebufferOES
-#define glIsImageHandleResidentNV wrap_glIsImageHandleResidentNV
-#define glIsPathNV wrap_glIsPathNV
-#define glIsPointInFillPathNV wrap_glIsPointInFillPathNV
-#define glIsPointInStrokePathNV wrap_glIsPointInStrokePathNV
-#define glIsProgram wrap_glIsProgram
-#define glIsProgramPipeline wrap_glIsProgramPipeline
-#define glIsProgramPipelineEXT wrap_glIsProgramPipelineEXT
-#define glIsQuery wrap_glIsQuery
-#define glIsQueryEXT wrap_glIsQueryEXT
-#define glIsRenderbuffer wrap_glIsRenderbuffer
-#define glIsRenderbufferOES wrap_glIsRenderbufferOES
-#define glIsSampler wrap_glIsSampler
-#define glIsShader wrap_glIsShader
-#define glIsSync wrap_glIsSync
-#define glIsSyncAPPLE wrap_glIsSyncAPPLE
-#define glIsTexture wrap_glIsTexture
-#define glIsTextureHandleResidentNV wrap_glIsTextureHandleResidentNV
-#define glIsTransformFeedback wrap_glIsTransformFeedback
-#define glIsVertexArray wrap_glIsVertexArray
-#define glIsVertexArrayOES wrap_glIsVertexArrayOES
-#define glLabelObjectEXT wrap_glLabelObjectEXT
-#define glLightModelf wrap_glLightModelf
-#define glLightModelfv wrap_glLightModelfv
-#define glLightModelx wrap_glLightModelx
-#define glLightModelxOES wrap_glLightModelxOES
-#define glLightModelxv wrap_glLightModelxv
-#define glLightModelxvOES wrap_glLightModelxvOES
-#define glLightf wrap_glLightf
-#define glLightfv wrap_glLightfv
-#define glLightx wrap_glLightx
-#define glLightxOES wrap_glLightxOES
-#define glLightxv wrap_glLightxv
-#define glLightxvOES wrap_glLightxvOES
-#define glLineWidth wrap_glLineWidth
-#define glLineWidthx wrap_glLineWidthx
-#define glLineWidthxOES wrap_glLineWidthxOES
-#define glLinkProgram wrap_glLinkProgram
-#define glLoadIdentity wrap_glLoadIdentity
-#define glLoadMatrixf wrap_glLoadMatrixf
-#define glLoadMatrixx wrap_glLoadMatrixx
-#define glLoadMatrixxOES wrap_glLoadMatrixxOES
-#define glLoadPaletteFromModelViewMatrixOES wrap_glLoadPaletteFromModelViewMatrixOES
-#define glLogicOp wrap_glLogicOp
-#define glMakeImageHandleNonResidentNV wrap_glMakeImageHandleNonResidentNV
-#define glMakeImageHandleResidentNV wrap_glMakeImageHandleResidentNV
-#define glMakeTextureHandleNonResidentNV wrap_glMakeTextureHandleNonResidentNV
-#define glMakeTextureHandleResidentNV wrap_glMakeTextureHandleResidentNV
-#define glMapBufferOES wrap_glMapBufferOES
-#define glMapBufferRange wrap_glMapBufferRange
-#define glMapBufferRangeEXT wrap_glMapBufferRangeEXT
-#define glMaterialf wrap_glMaterialf
-#define glMaterialfv wrap_glMaterialfv
-#define glMaterialx wrap_glMaterialx
-#define glMaterialxOES wrap_glMaterialxOES
-#define glMaterialxv wrap_glMaterialxv
-#define glMaterialxvOES wrap_glMaterialxvOES
-#define glMatrixIndexPointerOES wrap_glMatrixIndexPointerOES
-#define glMatrixLoad3x2fNV wrap_glMatrixLoad3x2fNV
-#define glMatrixLoad3x3fNV wrap_glMatrixLoad3x3fNV
-#define glMatrixLoadTranspose3x3fNV wrap_glMatrixLoadTranspose3x3fNV
-#define glMatrixMode wrap_glMatrixMode
-#define glMatrixMult3x2fNV wrap_glMatrixMult3x2fNV
-#define glMatrixMult3x3fNV wrap_glMatrixMult3x3fNV
-#define glMatrixMultTranspose3x3fNV wrap_glMatrixMultTranspose3x3fNV
-#define glMemoryBarrier wrap_glMemoryBarrier
-#define glMemoryBarrierByRegion wrap_glMemoryBarrierByRegion
-#define glMinSampleShading wrap_glMinSampleShading
-#define glMinSampleShadingOES wrap_glMinSampleShadingOES
-#define glMultMatrixf wrap_glMultMatrixf
-#define glMultMatrixx wrap_glMultMatrixx
-#define glMultMatrixxOES wrap_glMultMatrixxOES
-#define glMultiDrawArraysEXT wrap_glMultiDrawArraysEXT
-#define glMultiDrawArraysIndirectEXT wrap_glMultiDrawArraysIndirectEXT
-#define glMultiDrawElementsBaseVertexEXT wrap_glMultiDrawElementsBaseVertexEXT
-#define glMultiDrawElementsBaseVertexOES wrap_glMultiDrawElementsBaseVertexOES
-#define glMultiDrawElementsEXT wrap_glMultiDrawElementsEXT
-#define glMultiDrawElementsIndirectEXT wrap_glMultiDrawElementsIndirectEXT
-#define glMultiTexCoord4f wrap_glMultiTexCoord4f
-#define glMultiTexCoord4x wrap_glMultiTexCoord4x
-#define glMultiTexCoord4xOES wrap_glMultiTexCoord4xOES
-#define glNamedFramebufferSampleLocationsfvNV wrap_glNamedFramebufferSampleLocationsfvNV
-#define glNormal3f wrap_glNormal3f
-#define glNormal3x wrap_glNormal3x
-#define glNormal3xOES wrap_glNormal3xOES
-#define glNormalPointer wrap_glNormalPointer
-#define glObjectLabel wrap_glObjectLabel
-#define glObjectLabelKHR wrap_glObjectLabelKHR
-#define glObjectPtrLabel wrap_glObjectPtrLabel
-#define glObjectPtrLabelKHR wrap_glObjectPtrLabelKHR
-#define glOrthof wrap_glOrthof
-#define glOrthofOES wrap_glOrthofOES
-#define glOrthox wrap_glOrthox
-#define glOrthoxOES wrap_glOrthoxOES
-#define glPatchParameteri wrap_glPatchParameteri
-#define glPatchParameteriEXT wrap_glPatchParameteriEXT
-#define glPatchParameteriOES wrap_glPatchParameteriOES
-#define glPathCommandsNV wrap_glPathCommandsNV
-#define glPathCoordsNV wrap_glPathCoordsNV
-#define glPathCoverDepthFuncNV wrap_glPathCoverDepthFuncNV
-#define glPathDashArrayNV wrap_glPathDashArrayNV
-#define glPathGlyphIndexArrayNV wrap_glPathGlyphIndexArrayNV
-#define glPathGlyphIndexRangeNV wrap_glPathGlyphIndexRangeNV
-#define glPathGlyphRangeNV wrap_glPathGlyphRangeNV
-#define glPathGlyphsNV wrap_glPathGlyphsNV
-#define glPathMemoryGlyphIndexArrayNV wrap_glPathMemoryGlyphIndexArrayNV
-#define glPathParameterfNV wrap_glPathParameterfNV
-#define glPathParameterfvNV wrap_glPathParameterfvNV
-#define glPathParameteriNV wrap_glPathParameteriNV
-#define glPathParameterivNV wrap_glPathParameterivNV
-#define glPathStencilDepthOffsetNV wrap_glPathStencilDepthOffsetNV
-#define glPathStencilFuncNV wrap_glPathStencilFuncNV
-#define glPathStringNV wrap_glPathStringNV
-#define glPathSubCommandsNV wrap_glPathSubCommandsNV
-#define glPathSubCoordsNV wrap_glPathSubCoordsNV
-#define glPauseTransformFeedback wrap_glPauseTransformFeedback
-#define glPixelStorei wrap_glPixelStorei
-#define glPointAlongPathNV wrap_glPointAlongPathNV
-#define glPointParameterf wrap_glPointParameterf
-#define glPointParameterfv wrap_glPointParameterfv
-#define glPointParameterx wrap_glPointParameterx
-#define glPointParameterxOES wrap_glPointParameterxOES
-#define glPointParameterxv wrap_glPointParameterxv
-#define glPointParameterxvOES wrap_glPointParameterxvOES
-#define glPointSize wrap_glPointSize
-#define glPointSizePointerOES wrap_glPointSizePointerOES
-#define glPointSizex wrap_glPointSizex
-#define glPointSizexOES wrap_glPointSizexOES
-#define glPolygonModeNV wrap_glPolygonModeNV
-#define glPolygonOffset wrap_glPolygonOffset
-#define glPolygonOffsetx wrap_glPolygonOffsetx
-#define glPolygonOffsetxOES wrap_glPolygonOffsetxOES
-#define glPopDebugGroup wrap_glPopDebugGroup
-#define glPopDebugGroupKHR wrap_glPopDebugGroupKHR
-#define glPopGroupMarkerEXT wrap_glPopGroupMarkerEXT
-#define glPopMatrix wrap_glPopMatrix
-#define glPrimitiveBoundingBox wrap_glPrimitiveBoundingBox
-#define glPrimitiveBoundingBoxEXT wrap_glPrimitiveBoundingBoxEXT
-#define glPrimitiveBoundingBoxOES wrap_glPrimitiveBoundingBoxOES
-#define glProgramBinary wrap_glProgramBinary
-#define glProgramBinaryOES wrap_glProgramBinaryOES
-#define glProgramParameteri wrap_glProgramParameteri
-#define glProgramParameteriEXT wrap_glProgramParameteriEXT
-#define glProgramPathFragmentInputGenNV wrap_glProgramPathFragmentInputGenNV
-#define glProgramUniform1f wrap_glProgramUniform1f
-#define glProgramUniform1fEXT wrap_glProgramUniform1fEXT
-#define glProgramUniform1fv wrap_glProgramUniform1fv
-#define glProgramUniform1fvEXT wrap_glProgramUniform1fvEXT
-#define glProgramUniform1i wrap_glProgramUniform1i
-#define glProgramUniform1iEXT wrap_glProgramUniform1iEXT
-#define glProgramUniform1iv wrap_glProgramUniform1iv
-#define glProgramUniform1ivEXT wrap_glProgramUniform1ivEXT
-#define glProgramUniform1ui wrap_glProgramUniform1ui
-#define glProgramUniform1uiEXT wrap_glProgramUniform1uiEXT
-#define glProgramUniform1uiv wrap_glProgramUniform1uiv
-#define glProgramUniform1uivEXT wrap_glProgramUniform1uivEXT
-#define glProgramUniform2f wrap_glProgramUniform2f
-#define glProgramUniform2fEXT wrap_glProgramUniform2fEXT
-#define glProgramUniform2fv wrap_glProgramUniform2fv
-#define glProgramUniform2fvEXT wrap_glProgramUniform2fvEXT
-#define glProgramUniform2i wrap_glProgramUniform2i
-#define glProgramUniform2iEXT wrap_glProgramUniform2iEXT
-#define glProgramUniform2iv wrap_glProgramUniform2iv
-#define glProgramUniform2ivEXT wrap_glProgramUniform2ivEXT
-#define glProgramUniform2ui wrap_glProgramUniform2ui
-#define glProgramUniform2uiEXT wrap_glProgramUniform2uiEXT
-#define glProgramUniform2uiv wrap_glProgramUniform2uiv
-#define glProgramUniform2uivEXT wrap_glProgramUniform2uivEXT
-#define glProgramUniform3f wrap_glProgramUniform3f
-#define glProgramUniform3fEXT wrap_glProgramUniform3fEXT
-#define glProgramUniform3fv wrap_glProgramUniform3fv
-#define glProgramUniform3fvEXT wrap_glProgramUniform3fvEXT
-#define glProgramUniform3i wrap_glProgramUniform3i
-#define glProgramUniform3iEXT wrap_glProgramUniform3iEXT
-#define glProgramUniform3iv wrap_glProgramUniform3iv
-#define glProgramUniform3ivEXT wrap_glProgramUniform3ivEXT
-#define glProgramUniform3ui wrap_glProgramUniform3ui
-#define glProgramUniform3uiEXT wrap_glProgramUniform3uiEXT
-#define glProgramUniform3uiv wrap_glProgramUniform3uiv
-#define glProgramUniform3uivEXT wrap_glProgramUniform3uivEXT
-#define glProgramUniform4f wrap_glProgramUniform4f
-#define glProgramUniform4fEXT wrap_glProgramUniform4fEXT
-#define glProgramUniform4fv wrap_glProgramUniform4fv
-#define glProgramUniform4fvEXT wrap_glProgramUniform4fvEXT
-#define glProgramUniform4i wrap_glProgramUniform4i
-#define glProgramUniform4iEXT wrap_glProgramUniform4iEXT
-#define glProgramUniform4iv wrap_glProgramUniform4iv
-#define glProgramUniform4ivEXT wrap_glProgramUniform4ivEXT
-#define glProgramUniform4ui wrap_glProgramUniform4ui
-#define glProgramUniform4uiEXT wrap_glProgramUniform4uiEXT
-#define glProgramUniform4uiv wrap_glProgramUniform4uiv
-#define glProgramUniform4uivEXT wrap_glProgramUniform4uivEXT
-#define glProgramUniformHandleui64NV wrap_glProgramUniformHandleui64NV
-#define glProgramUniformHandleui64vNV wrap_glProgramUniformHandleui64vNV
-#define glProgramUniformMatrix2fv wrap_glProgramUniformMatrix2fv
-#define glProgramUniformMatrix2fvEXT wrap_glProgramUniformMatrix2fvEXT
-#define glProgramUniformMatrix2x3fv wrap_glProgramUniformMatrix2x3fv
-#define glProgramUniformMatrix2x3fvEXT wrap_glProgramUniformMatrix2x3fvEXT
-#define glProgramUniformMatrix2x4fv wrap_glProgramUniformMatrix2x4fv
-#define glProgramUniformMatrix2x4fvEXT wrap_glProgramUniformMatrix2x4fvEXT
-#define glProgramUniformMatrix3fv wrap_glProgramUniformMatrix3fv
-#define glProgramUniformMatrix3fvEXT wrap_glProgramUniformMatrix3fvEXT
-#define glProgramUniformMatrix3x2fv wrap_glProgramUniformMatrix3x2fv
-#define glProgramUniformMatrix3x2fvEXT wrap_glProgramUniformMatrix3x2fvEXT
-#define glProgramUniformMatrix3x4fv wrap_glProgramUniformMatrix3x4fv
-#define glProgramUniformMatrix3x4fvEXT wrap_glProgramUniformMatrix3x4fvEXT
-#define glProgramUniformMatrix4fv wrap_glProgramUniformMatrix4fv
-#define glProgramUniformMatrix4fvEXT wrap_glProgramUniformMatrix4fvEXT
-#define glProgramUniformMatrix4x2fv wrap_glProgramUniformMatrix4x2fv
-#define glProgramUniformMatrix4x2fvEXT wrap_glProgramUniformMatrix4x2fvEXT
-#define glProgramUniformMatrix4x3fv wrap_glProgramUniformMatrix4x3fv
-#define glProgramUniformMatrix4x3fvEXT wrap_glProgramUniformMatrix4x3fvEXT
-#define glPushDebugGroup wrap_glPushDebugGroup
-#define glPushDebugGroupKHR wrap_glPushDebugGroupKHR
-#define glPushGroupMarkerEXT wrap_glPushGroupMarkerEXT
-#define glPushMatrix wrap_glPushMatrix
-#define glQueryCounterEXT wrap_glQueryCounterEXT
-#define glQueryMatrixxOES wrap_glQueryMatrixxOES
-#define glRasterSamplesEXT wrap_glRasterSamplesEXT
-#define glReadBuffer wrap_glReadBuffer
-#define glReadBufferIndexedEXT wrap_glReadBufferIndexedEXT
-#define glReadBufferNV wrap_glReadBufferNV
-#define glReadPixels wrap_glReadPixels
-#define glReadnPixels wrap_glReadnPixels
-#define glReadnPixelsEXT wrap_glReadnPixelsEXT
-#define glReadnPixelsKHR wrap_glReadnPixelsKHR
-#define glReleaseShaderCompiler wrap_glReleaseShaderCompiler
-#define glRenderbufferStorage wrap_glRenderbufferStorage
-#define glRenderbufferStorageMultisample wrap_glRenderbufferStorageMultisample
-#define glRenderbufferStorageMultisampleANGLE wrap_glRenderbufferStorageMultisampleANGLE
-#define glRenderbufferStorageMultisampleAPPLE wrap_glRenderbufferStorageMultisampleAPPLE
-#define glRenderbufferStorageMultisampleEXT wrap_glRenderbufferStorageMultisampleEXT
-#define glRenderbufferStorageMultisampleIMG wrap_glRenderbufferStorageMultisampleIMG
-#define glRenderbufferStorageMultisampleNV wrap_glRenderbufferStorageMultisampleNV
-#define glRenderbufferStorageOES wrap_glRenderbufferStorageOES
-#define glResolveDepthValuesNV wrap_glResolveDepthValuesNV
-#define glResolveMultisampleFramebufferAPPLE wrap_glResolveMultisampleFramebufferAPPLE
-#define glResumeTransformFeedback wrap_glResumeTransformFeedback
-#define glRotatef wrap_glRotatef
-#define glRotatex wrap_glRotatex
-#define glRotatexOES wrap_glRotatexOES
-#define glSampleCoverage wrap_glSampleCoverage
-#define glSampleCoveragex wrap_glSampleCoveragex
-#define glSampleCoveragexOES wrap_glSampleCoveragexOES
-#define glSampleMaski wrap_glSampleMaski
-#define glSamplerParameterIiv wrap_glSamplerParameterIiv
-#define glSamplerParameterIivEXT wrap_glSamplerParameterIivEXT
-#define glSamplerParameterIivOES wrap_glSamplerParameterIivOES
-#define glSamplerParameterIuiv wrap_glSamplerParameterIuiv
-#define glSamplerParameterIuivEXT wrap_glSamplerParameterIuivEXT
-#define glSamplerParameterIuivOES wrap_glSamplerParameterIuivOES
-#define glSamplerParameterf wrap_glSamplerParameterf
-#define glSamplerParameterfv wrap_glSamplerParameterfv
-#define glSamplerParameteri wrap_glSamplerParameteri
-#define glSamplerParameteriv wrap_glSamplerParameteriv
-#define glScalef wrap_glScalef
-#define glScalex wrap_glScalex
-#define glScalexOES wrap_glScalexOES
-#define glScissor wrap_glScissor
-#define glScissorArrayvNV wrap_glScissorArrayvNV
-#define glScissorIndexedNV wrap_glScissorIndexedNV
-#define glScissorIndexedvNV wrap_glScissorIndexedvNV
-#define glSelectPerfMonitorCountersAMD wrap_glSelectPerfMonitorCountersAMD
-#define glSetFenceNV wrap_glSetFenceNV
-#define glShadeModel wrap_glShadeModel
-#define glShaderBinary wrap_glShaderBinary
-#define glShaderSource wrap_glShaderSource
-#define glStartTilingQCOM wrap_glStartTilingQCOM
-#define glStencilFillPathInstancedNV wrap_glStencilFillPathInstancedNV
-#define glStencilFillPathNV wrap_glStencilFillPathNV
-#define glStencilFunc wrap_glStencilFunc
-#define glStencilFuncSeparate wrap_glStencilFuncSeparate
-#define glStencilMask wrap_glStencilMask
-#define glStencilMaskSeparate wrap_glStencilMaskSeparate
-#define glStencilOp wrap_glStencilOp
-#define glStencilOpSeparate wrap_glStencilOpSeparate
-#define glStencilStrokePathInstancedNV wrap_glStencilStrokePathInstancedNV
-#define glStencilStrokePathNV wrap_glStencilStrokePathNV
-#define glStencilThenCoverFillPathInstancedNV wrap_glStencilThenCoverFillPathInstancedNV
-#define glStencilThenCoverFillPathNV wrap_glStencilThenCoverFillPathNV
-#define glStencilThenCoverStrokePathInstancedNV wrap_glStencilThenCoverStrokePathInstancedNV
-#define glStencilThenCoverStrokePathNV wrap_glStencilThenCoverStrokePathNV
-#define glSubpixelPrecisionBiasNV wrap_glSubpixelPrecisionBiasNV
-#define glTestFenceNV wrap_glTestFenceNV
-#define glTexBuffer wrap_glTexBuffer
-#define glTexBufferEXT wrap_glTexBufferEXT
-#define glTexBufferOES wrap_glTexBufferOES
-#define glTexBufferRange wrap_glTexBufferRange
-#define glTexBufferRangeEXT wrap_glTexBufferRangeEXT
-#define glTexBufferRangeOES wrap_glTexBufferRangeOES
-#define glTexCoordPointer wrap_glTexCoordPointer
-#define glTexEnvf wrap_glTexEnvf
-#define glTexEnvfv wrap_glTexEnvfv
-#define glTexEnvi wrap_glTexEnvi
-#define glTexEnviv wrap_glTexEnviv
-#define glTexEnvx wrap_glTexEnvx
-#define glTexEnvxOES wrap_glTexEnvxOES
-#define glTexEnvxv wrap_glTexEnvxv
-#define glTexEnvxvOES wrap_glTexEnvxvOES
-#define glTexGenfOES wrap_glTexGenfOES
-#define glTexGenfvOES wrap_glTexGenfvOES
-#define glTexGeniOES wrap_glTexGeniOES
-#define glTexGenivOES wrap_glTexGenivOES
-#define glTexGenxOES wrap_glTexGenxOES
-#define glTexGenxvOES wrap_glTexGenxvOES
-#define glTexImage2D wrap_glTexImage2D
-#define glTexImage3D wrap_glTexImage3D
-#define glTexImage3DOES wrap_glTexImage3DOES
-#define glTexPageCommitmentEXT wrap_glTexPageCommitmentEXT
-#define glTexParameterIiv wrap_glTexParameterIiv
-#define glTexParameterIivEXT wrap_glTexParameterIivEXT
-#define glTexParameterIivOES wrap_glTexParameterIivOES
-#define glTexParameterIuiv wrap_glTexParameterIuiv
-#define glTexParameterIuivEXT wrap_glTexParameterIuivEXT
-#define glTexParameterIuivOES wrap_glTexParameterIuivOES
-#define glTexParameterf wrap_glTexParameterf
-#define glTexParameterfv wrap_glTexParameterfv
-#define glTexParameteri wrap_glTexParameteri
-#define glTexParameteriv wrap_glTexParameteriv
-#define glTexParameterx wrap_glTexParameterx
-#define glTexParameterxOES wrap_glTexParameterxOES
-#define glTexParameterxv wrap_glTexParameterxv
-#define glTexParameterxvOES wrap_glTexParameterxvOES
-#define glTexStorage1DEXT wrap_glTexStorage1DEXT
-#define glTexStorage2D wrap_glTexStorage2D
-#define glTexStorage2DEXT wrap_glTexStorage2DEXT
-#define glTexStorage2DMultisample wrap_glTexStorage2DMultisample
-#define glTexStorage3D wrap_glTexStorage3D
-#define glTexStorage3DEXT wrap_glTexStorage3DEXT
-#define glTexStorage3DMultisample wrap_glTexStorage3DMultisample
-#define glTexStorage3DMultisampleOES wrap_glTexStorage3DMultisampleOES
-#define glTexSubImage2D wrap_glTexSubImage2D
-#define glTexSubImage3D wrap_glTexSubImage3D
-#define glTexSubImage3DOES wrap_glTexSubImage3DOES
-#define glTextureStorage1DEXT wrap_glTextureStorage1DEXT
-#define glTextureStorage2DEXT wrap_glTextureStorage2DEXT
-#define glTextureStorage3DEXT wrap_glTextureStorage3DEXT
-#define glTextureViewEXT wrap_glTextureViewEXT
-#define glTextureViewOES wrap_glTextureViewOES
-#define glTransformFeedbackVaryings wrap_glTransformFeedbackVaryings
-#define glTransformPathNV wrap_glTransformPathNV
-#define glTranslatef wrap_glTranslatef
-#define glTranslatex wrap_glTranslatex
-#define glTranslatexOES wrap_glTranslatexOES
-#define glUniform1f wrap_glUniform1f
-#define glUniform1fv wrap_glUniform1fv
-#define glUniform1i wrap_glUniform1i
-#define glUniform1iv wrap_glUniform1iv
-#define glUniform1ui wrap_glUniform1ui
-#define glUniform1uiv wrap_glUniform1uiv
-#define glUniform2f wrap_glUniform2f
-#define glUniform2fv wrap_glUniform2fv
-#define glUniform2i wrap_glUniform2i
-#define glUniform2iv wrap_glUniform2iv
-#define glUniform2ui wrap_glUniform2ui
-#define glUniform2uiv wrap_glUniform2uiv
-#define glUniform3f wrap_glUniform3f
-#define glUniform3fv wrap_glUniform3fv
-#define glUniform3i wrap_glUniform3i
-#define glUniform3iv wrap_glUniform3iv
-#define glUniform3ui wrap_glUniform3ui
-#define glUniform3uiv wrap_glUniform3uiv
-#define glUniform4f wrap_glUniform4f
-#define glUniform4fv wrap_glUniform4fv
-#define glUniform4i wrap_glUniform4i
-#define glUniform4iv wrap_glUniform4iv
-#define glUniform4ui wrap_glUniform4ui
-#define glUniform4uiv wrap_glUniform4uiv
-#define glUniformBlockBinding wrap_glUniformBlockBinding
-#define glUniformHandleui64NV wrap_glUniformHandleui64NV
-#define glUniformHandleui64vNV wrap_glUniformHandleui64vNV
-#define glUniformMatrix2fv wrap_glUniformMatrix2fv
-#define glUniformMatrix2x3fv wrap_glUniformMatrix2x3fv
-#define glUniformMatrix2x3fvNV wrap_glUniformMatrix2x3fvNV
-#define glUniformMatrix2x4fv wrap_glUniformMatrix2x4fv
-#define glUniformMatrix2x4fvNV wrap_glUniformMatrix2x4fvNV
-#define glUniformMatrix3fv wrap_glUniformMatrix3fv
-#define glUniformMatrix3x2fv wrap_glUniformMatrix3x2fv
-#define glUniformMatrix3x2fvNV wrap_glUniformMatrix3x2fvNV
-#define glUniformMatrix3x4fv wrap_glUniformMatrix3x4fv
-#define glUniformMatrix3x4fvNV wrap_glUniformMatrix3x4fvNV
-#define glUniformMatrix4fv wrap_glUniformMatrix4fv
-#define glUniformMatrix4x2fv wrap_glUniformMatrix4x2fv
-#define glUniformMatrix4x2fvNV wrap_glUniformMatrix4x2fvNV
-#define glUniformMatrix4x3fv wrap_glUniformMatrix4x3fv
-#define glUniformMatrix4x3fvNV wrap_glUniformMatrix4x3fvNV
-#define glUnmapBuffer wrap_glUnmapBuffer
-#define glUnmapBufferOES wrap_glUnmapBufferOES
-#define glUseProgram wrap_glUseProgram
-#define glUseProgramStages wrap_glUseProgramStages
-#define glUseProgramStagesEXT wrap_glUseProgramStagesEXT
-#define glValidateProgram wrap_glValidateProgram
-#define glValidateProgramPipeline wrap_glValidateProgramPipeline
-#define glValidateProgramPipelineEXT wrap_glValidateProgramPipelineEXT
-#define glVertexAttrib1f wrap_glVertexAttrib1f
-#define glVertexAttrib1fv wrap_glVertexAttrib1fv
-#define glVertexAttrib2f wrap_glVertexAttrib2f
-#define glVertexAttrib2fv wrap_glVertexAttrib2fv
-#define glVertexAttrib3f wrap_glVertexAttrib3f
-#define glVertexAttrib3fv wrap_glVertexAttrib3fv
-#define glVertexAttrib4f wrap_glVertexAttrib4f
-#define glVertexAttrib4fv wrap_glVertexAttrib4fv
-#define glVertexAttribBinding wrap_glVertexAttribBinding
-#define glVertexAttribDivisor wrap_glVertexAttribDivisor
-#define glVertexAttribDivisorANGLE wrap_glVertexAttribDivisorANGLE
-#define glVertexAttribDivisorEXT wrap_glVertexAttribDivisorEXT
-#define glVertexAttribDivisorNV wrap_glVertexAttribDivisorNV
-#define glVertexAttribFormat wrap_glVertexAttribFormat
-#define glVertexAttribI4i wrap_glVertexAttribI4i
-#define glVertexAttribI4iv wrap_glVertexAttribI4iv
-#define glVertexAttribI4ui wrap_glVertexAttribI4ui
-#define glVertexAttribI4uiv wrap_glVertexAttribI4uiv
-#define glVertexAttribIFormat wrap_glVertexAttribIFormat
-#define glVertexAttribIPointer wrap_glVertexAttribIPointer
-#define glVertexAttribPointer wrap_glVertexAttribPointer
-#define glVertexBindingDivisor wrap_glVertexBindingDivisor
-#define glVertexPointer wrap_glVertexPointer
-#define glViewport wrap_glViewport
-#define glViewportArrayvNV wrap_glViewportArrayvNV
-#define glViewportIndexedfNV wrap_glViewportIndexedfNV
-#define glViewportIndexedfvNV wrap_glViewportIndexedfvNV
-#define glWaitSync wrap_glWaitSync
-#define glWaitSyncAPPLE wrap_glWaitSyncAPPLE
-#define glWeightPathsNV wrap_glWeightPathsNV
-#define glWeightPointerOES wrap_glWeightPointerOES
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl31.h>
+#include <GLES3/gl32.h>
-#endif // HWUI_GLES_WRAP_ENABLED
+// Generate stubs that route all the calls to our function table
+#include "gles_redefine.h"
+
+#define GL_ENTRY(ret, api, ...) ret api(__VA_ARGS__);
+
+#include "gles_decls.in"
+#undef GL_ENTRY
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 49e9f65582ae..e2844ad0649a 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -180,7 +180,12 @@ void CacheTexture::allocatePixelBuffer() {
mPixelBuffer = PixelBuffer::create(mFormat, getWidth(), getHeight());
}
- mTexture.resize(mWidth, mHeight, mFormat);
+ GLint internalFormat = mFormat;
+ if (mFormat == GL_RGBA) {
+ internalFormat = mCaches.rgbaInternalFormat();
+ }
+
+ mTexture.resize(mWidth, mHeight, internalFormat, mFormat);
mTexture.setFilter(getLinearFiltering() ? GL_LINEAR : GL_NEAREST);
mTexture.setWrap(GL_CLAMP_TO_EDGE);
}
diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h
index 0642d59d9b16..073d59bdea3f 100644
--- a/libs/hwui/font/CachedGlyphInfo.h
+++ b/libs/hwui/font/CachedGlyphInfo.h
@@ -17,8 +17,6 @@
#ifndef ANDROID_HWUI_CACHED_GLYPH_INFO_H
#define ANDROID_HWUI_CACHED_GLYPH_INFO_H
-#include <SkFixed.h>
-
namespace android {
namespace uirenderer {
@@ -41,14 +39,14 @@ struct CachedGlyphInfo {
float mBitmapMaxV;
// Minimize how much we call freetype
uint32_t mGlyphIndex;
- uint32_t mAdvanceX;
- uint32_t mAdvanceY;
+ float mAdvanceX;
+ float mAdvanceY;
// Values below contain a glyph's origin in the bitmap
int32_t mBitmapLeft;
int32_t mBitmapTop;
- // Auto-kerning
- SkFixed mLsbDelta;
- SkFixed mRsbDelta;
+ // Auto-kerning; represents a 2.6 fixed-point value with range [-1, 1].
+ int8_t mLsbDelta;
+ int8_t mRsbDelta;
CacheTexture* mCacheTexture;
};
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index a95454a4c010..24d497cb1860 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -304,7 +304,7 @@ void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
}
int glyphsCount = 0;
- SkFixed prevRsbDelta = 0;
+ int prevRsbDelta = 0;
float penX = 0.0f;
@@ -332,14 +332,14 @@ void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
}
CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+ penX += AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta);
prevRsbDelta = cachedGlyph->mRsbDelta;
if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) {
drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
}
- penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+ penX += cachedGlyph->mAdvanceX;
glyphsCount++;
}
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 288f73361bbc..504dabb5bcc0 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -22,14 +22,16 @@
#include <utils/KeyedVector.h>
#include <SkScalar.h>
-#include <SkGlyphCache.h>
#include <SkPaint.h>
#include <SkPathMeasure.h>
+#include <SkTypeface.h>
#include "FontUtil.h"
#include "../Rect.h"
#include "../Matrix.h"
+class SkGlyphCache;
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
index aa77d98c9343..07e8b34ac66f 100644
--- a/libs/hwui/font/FontUtil.h
+++ b/libs/hwui/font/FontUtil.h
@@ -44,6 +44,8 @@ typedef uint16_t glyph_t;
#define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph)
#define IS_END_OF_STRING(glyph) false
-#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
+// prev, next are assumed to be signed x.6 fixed-point numbers with range
+// [-1, 1]. Result is an integral float.
+#define AUTO_KERN(prev, next) static_cast<float>(((next) - (prev) + 32) >> 6)
#endif // ANDROID_HWUI_FONT_UTIL_H
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
new file mode 100644
index 000000000000..a34b61b56334
--- /dev/null
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "Bitmap.h"
+
+#include "Caches.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/RenderThread.h"
+#include "renderthread/RenderProxy.h"
+
+#include <sys/mman.h>
+
+#include <log/log.h>
+#include <cutils/ashmem.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <gui/IGraphicBufferAlloc.h>
+#include <gui/ISurfaceComposer.h>
+#include <private/gui/ComposerService.h>
+#include <binder/IServiceManager.h>
+#include <ui/PixelFormat.h>
+
+#include <SkCanvas.h>
+
+namespace android {
+
+static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) {
+ int32_t rowBytes32 = SkToS32(rowBytes);
+ int64_t bigSize = (int64_t) height * rowBytes32;
+ if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
+ return false; // allocation will be too large
+ }
+
+ *size = sk_64_asS32(bigSize);
+ return true;
+}
+
+typedef sk_sp<Bitmap> (*AllocPixeRef)(size_t allocSize, const SkImageInfo& info, size_t rowBytes,
+ SkColorTable* ctable);
+
+static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, SkColorTable* ctable, AllocPixeRef alloc) {
+ const SkImageInfo& info = bitmap->info();
+ if (info.colorType() == kUnknown_SkColorType) {
+ LOG_ALWAYS_FATAL("unknown bitmap configuration");
+ return nullptr;
+ }
+
+ size_t size;
+
+ // we must respect the rowBytes value already set on the bitmap instead of
+ // attempting to compute our own.
+ const size_t rowBytes = bitmap->rowBytes();
+ if (!computeAllocationSize(rowBytes, bitmap->height(), &size)) {
+ return nullptr;
+ }
+
+ auto wrapper = alloc(size, info, rowBytes, ctable);
+ if (wrapper) {
+ wrapper->getSkBitmap(bitmap);
+ // since we're already allocated, we lockPixels right away
+ // HeapAllocator behaves this way too
+ bitmap->lockPixels();
+ }
+ return wrapper;
+}
+
+sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
+ return allocateBitmap(bitmap, ctable, &Bitmap::allocateAshmemBitmap);
+}
+
+static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes,
+ SkColorTable* ctable) {
+ void* addr = calloc(size, 1);
+ if (!addr) {
+ return nullptr;
+ }
+ return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes, ctable));
+}
+
+#define FENCE_TIMEOUT 2000000000
+
+// TODO: handle SRGB sanely
+static PixelFormat internalFormatToPixelFormat(GLint internalFormat) {
+ switch (internalFormat) {
+ case GL_LUMINANCE:
+ return PIXEL_FORMAT_RGBA_8888;
+ case GL_SRGB8_ALPHA8:
+ return PIXEL_FORMAT_RGBA_8888;
+ case GL_RGBA:
+ return PIXEL_FORMAT_RGBA_8888;
+ case GL_RGB:
+ return PIXEL_FORMAT_RGB_565;
+ case GL_RGBA16F:
+ return PIXEL_FORMAT_RGBA_FP16;
+ default:
+ LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat);
+ return PIXEL_FORMAT_UNKNOWN;
+ }
+}
+
+class AutoEglFence {
+public:
+ AutoEglFence(EGLDisplay display)
+ : mDisplay(display) {
+ fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL);
+ }
+
+ ~AutoEglFence() {
+ if (fence != EGL_NO_SYNC_KHR) {
+ eglDestroySyncKHR(mDisplay, fence);
+ }
+ }
+
+ EGLSyncKHR fence = EGL_NO_SYNC_KHR;
+private:
+ EGLDisplay mDisplay = EGL_NO_DISPLAY;
+};
+
+class AutoEglImage {
+public:
+ AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer)
+ : mDisplay(display) {
+ EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
+ image = eglCreateImageKHR(display, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
+ }
+
+ ~AutoEglImage() {
+ if (image != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(mDisplay, image);
+ }
+ }
+
+ EGLImageKHR image = EGL_NO_IMAGE_KHR;
+private:
+ EGLDisplay mDisplay = EGL_NO_DISPLAY;
+};
+
+static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bitmap,
+ GraphicBuffer& buffer, GLint format, GLint type) {
+ SkAutoLockPixels alp(bitmap);
+ EGLDisplay display = eglGetCurrentDisplay();
+ LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
+ "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+ uirenderer::renderthread::EglManager::eglErrorString());
+ // These objects are initialized below but the default "null"
+ // values are used to cleanup properly at any point in the
+ // initialization sequenc
+ GLuint texture = 0;
+ // We use an EGLImage to access the content of the GraphicBuffer
+ // The EGL image is later bound to a 2D texture
+ EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer.getNativeBuffer();
+ AutoEglImage autoImage(display, clientBuffer);
+ if (autoImage.image == EGL_NO_IMAGE_KHR) {
+ ALOGW("Could not create EGL image, err =%s",
+ uirenderer::renderthread::EglManager::eglErrorString());
+ return false;
+ }
+ glGenTextures(1, &texture);
+ caches.textureState().bindTexture(texture);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+
+ GL_CHECKPOINT(MODERATE);
+
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
+ format, type, bitmap.getPixels());
+
+ GL_CHECKPOINT(MODERATE);
+
+ // The fence is used to wait for the texture upload to finish
+ // properly. We cannot rely on glFlush() and glFinish() as
+ // some drivers completely ignore these API calls
+ AutoEglFence autoFence(display);
+ if (autoFence.fence == EGL_NO_SYNC_KHR) {
+ LOG_ALWAYS_FATAL("Could not create sync fence %#x", eglGetError());
+ return false;
+ }
+ // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a
+ // pipeline flush (similar to what a glFlush() would do.)
+ EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence,
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT);
+ if (waitStatus != EGL_CONDITION_SATISFIED_KHR) {
+ LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError());
+ return false;
+ }
+ return true;
+}
+
+sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThread& renderThread,
+ SkBitmap& skBitmap) {
+ renderThread.eglManager().initialize();
+ uirenderer::Caches& caches = uirenderer::Caches::getInstance();
+
+ sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+ sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
+ if (alloc == NULL) {
+ ALOGW("createGraphicBufferAlloc() failed in GraphicBuffer.create()");
+ return nullptr;
+ }
+
+ const SkImageInfo& info = skBitmap.info();
+ if (info.colorType() == kUnknown_SkColorType || info.colorType() == kAlpha_8_SkColorType) {
+ ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType());
+ return nullptr;
+ }
+
+ sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
+ bool needSRGB = skBitmap.info().colorSpace() == sRGB.get();
+ bool hasSRGB = caches.extensions().hasSRGB();
+ GLint format, type, internalFormat;
+ uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(),
+ needSRGB, &internalFormat, &format, &type);
+
+ PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat);
+ status_t error;
+ sp<GraphicBuffer> buffer = alloc->createGraphicBuffer(info.width(), info.height(), pixelFormat,
+ 1, GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER
+ | GraphicBuffer::USAGE_SW_READ_NEVER , &error);
+
+ if (!buffer.get()) {
+ ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
+ return nullptr;
+ }
+
+ SkBitmap bitmap;
+ if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(),
+ hasSRGB, sRGB.get()))) {
+ bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
+ } else {
+ bitmap = skBitmap;
+ }
+
+ if (!uploadBitmapToGraphicBuffer(caches, bitmap, *buffer, format, type)) {
+ return nullptr;
+ }
+ return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
+}
+
+sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
+ return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap);
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
+ return allocateBitmap(bitmap, ctable, &android::allocateHeapBitmap);
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
+ size_t size;
+ if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
+ LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
+ return nullptr;
+ }
+ return android::allocateHeapBitmap(size, info, info.minRowBytes(), nullptr);
+}
+
+sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info,
+ size_t rowBytes, SkColorTable* ctable) {
+ // Create new ashmem region with read/write priv
+ int fd = ashmem_create_region("bitmap", size);
+ if (fd < 0) {
+ return nullptr;
+ }
+
+ void* addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ close(fd);
+ return nullptr;
+ }
+
+ if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
+ munmap(addr, size);
+ close(fd);
+ return nullptr;
+ }
+ return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes, ctable));
+}
+
+void FreePixelRef(void* addr, void* context) {
+ auto pixelRef = (SkPixelRef*) context;
+ pixelRef->unlockPixels();
+ pixelRef->unref();
+}
+
+sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) {
+ pixelRef.ref();
+ pixelRef.lockPixels();
+ return sk_sp<Bitmap>(new Bitmap((void*) pixelRef.pixels(), (void*) &pixelRef, FreePixelRef,
+ info, pixelRef.rowBytes(), pixelRef.colorTable()));
+}
+
+sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
+ PixelFormat format = graphicBuffer->getPixelFormat();
+ if (!graphicBuffer.get() ||
+ (format != PIXEL_FORMAT_RGBA_8888 && format != PIXEL_FORMAT_RGBA_FP16)) {
+ return nullptr;
+ }
+ SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType,
+ SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named));
+ return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info));
+}
+
+void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes, SkColorTable* ctable) {
+ if (kIndex_8_SkColorType != newInfo.colorType()) {
+ ctable = nullptr;
+ }
+ mRowBytes = rowBytes;
+ if (mColorTable.get() != ctable) {
+ mColorTable.reset(SkSafeRef(ctable));
+ }
+
+ // Need to validate the alpha type to filter against the color type
+ // to prevent things like a non-opaque RGB565 bitmap
+ SkAlphaType alphaType;
+ LOG_ALWAYS_FATAL_IF(!SkColorTypeValidateAlphaType(
+ newInfo.colorType(), newInfo.alphaType(), &alphaType),
+ "Failed to validate alpha type!");
+
+ // Dirty hack is dirty
+ // TODO: Figure something out here, Skia's current design makes this
+ // really hard to work with. Skia really, really wants immutable objects,
+ // but with the nested-ref-count hackery going on that's just not
+ // feasible without going insane trying to figure it out
+ SkImageInfo* myInfo = const_cast<SkImageInfo*>(&this->info());
+ *myInfo = newInfo;
+ changeAlphaType(alphaType);
+
+ // Docs say to only call this in the ctor, but we're going to call
+ // it anyway even if this isn't always the ctor.
+ // TODO: Fix this too as part of the above TODO
+ setPreLocked(getStorage(), mRowBytes, mColorTable.get());
+}
+
+Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+ : SkPixelRef(info)
+ , mPixelStorageType(PixelStorageType::Heap) {
+ mPixelStorage.heap.address = address;
+ mPixelStorage.heap.size = size;
+ reconfigure(info, rowBytes, ctable);
+}
+
+Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc,
+ const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+ : SkPixelRef(info)
+ , mPixelStorageType(PixelStorageType::External) {
+ mPixelStorage.external.address = address;
+ mPixelStorage.external.context = context;
+ mPixelStorage.external.freeFunc = freeFunc;
+ reconfigure(info, rowBytes, ctable);
+}
+
+Bitmap::Bitmap(void* address, int fd, size_t mappedSize,
+ const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+ : SkPixelRef(info)
+ , mPixelStorageType(PixelStorageType::Ashmem) {
+ mPixelStorage.ashmem.address = address;
+ mPixelStorage.ashmem.fd = fd;
+ mPixelStorage.ashmem.size = mappedSize;
+ reconfigure(info, rowBytes, ctable);
+}
+
+Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info)
+ : SkPixelRef(info)
+ , mPixelStorageType(PixelStorageType::Hardware) {
+ mPixelStorage.hardware.buffer = buffer;
+ buffer->incStrong(buffer);
+ mRowBytes = bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride();
+}
+
+Bitmap::~Bitmap() {
+ switch (mPixelStorageType) {
+ case PixelStorageType::External:
+ mPixelStorage.external.freeFunc(mPixelStorage.external.address,
+ mPixelStorage.external.context);
+ break;
+ case PixelStorageType::Ashmem:
+ munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
+ close(mPixelStorage.ashmem.fd);
+ break;
+ case PixelStorageType::Heap:
+ free(mPixelStorage.heap.address);
+ break;
+ case PixelStorageType::Hardware:
+ auto buffer = mPixelStorage.hardware.buffer;
+ buffer->decStrong(buffer);
+ mPixelStorage.hardware.buffer = nullptr;
+ break;
+
+ }
+
+ if (android::uirenderer::Caches::hasInstance()) {
+ android::uirenderer::Caches::getInstance().textureCache.releaseTexture(getStableID());
+ }
+}
+
+bool Bitmap::hasHardwareMipMap() const {
+ return mHasHardwareMipMap;
+}
+
+void Bitmap::setHasHardwareMipMap(bool hasMipMap) {
+ mHasHardwareMipMap = hasMipMap;
+}
+
+void* Bitmap::getStorage() const {
+ switch (mPixelStorageType) {
+ case PixelStorageType::External:
+ return mPixelStorage.external.address;
+ case PixelStorageType::Ashmem:
+ return mPixelStorage.ashmem.address;
+ case PixelStorageType::Heap:
+ return mPixelStorage.heap.address;
+ case PixelStorageType::Hardware:
+ return nullptr;
+ }
+}
+
+bool Bitmap::onNewLockPixels(LockRec* rec) {
+ rec->fPixels = getStorage();
+ rec->fRowBytes = mRowBytes;
+ rec->fColorTable = mColorTable.get();
+ return true;
+}
+
+size_t Bitmap::getAllocatedSizeInBytes() const {
+ return info().getSafeSize(mRowBytes);
+}
+
+int Bitmap::getAshmemFd() const {
+ switch (mPixelStorageType) {
+ case PixelStorageType::Ashmem:
+ return mPixelStorage.ashmem.fd;
+ default:
+ return -1;
+ }
+}
+
+size_t Bitmap::getAllocationByteCount() const {
+ switch (mPixelStorageType) {
+ case PixelStorageType::Heap:
+ return mPixelStorage.heap.size;
+ default:
+ return rowBytes() * height();
+ }
+}
+
+void Bitmap::reconfigure(const SkImageInfo& info) {
+ reconfigure(info, info.minRowBytes(), nullptr);
+}
+
+void Bitmap::setAlphaType(SkAlphaType alphaType) {
+ if (!SkColorTypeValidateAlphaType(info().colorType(), alphaType, &alphaType)) {
+ return;
+ }
+
+ changeAlphaType(alphaType);
+}
+
+void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
+ outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
+ if (isHardware()) {
+ ALOGW("Warning: attempt to read pixels from hardware bitmap, which is very slow operation");
+ outBitmap->allocPixels(info());
+ uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap);
+ return;
+ }
+ outBitmap->setInfo(info(), rowBytes());
+ outBitmap->setPixelRef(this);
+}
+
+void Bitmap::getSkBitmapForShaders(SkBitmap* outBitmap) {
+ outBitmap->setInfo(info(), rowBytes());
+ outBitmap->setPixelRef(this);
+ outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
+}
+
+void Bitmap::getBounds(SkRect* bounds) const {
+ SkASSERT(bounds);
+ bounds->set(0, 0, SkIntToScalar(info().width()), SkIntToScalar(info().height()));
+}
+
+GraphicBuffer* Bitmap::graphicBuffer() {
+ if (isHardware()) {
+ return mPixelStorage.hardware.buffer;
+ }
+ return nullptr;
+}
+
+} // namespace android
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
new file mode 100644
index 000000000000..518be032c0eb
--- /dev/null
+++ b/libs/hwui/hwui/Bitmap.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <SkBitmap.h>
+#include <SkColorTable.h>
+#include <SkImageInfo.h>
+#include <SkPixelRef.h>
+#include <cutils/compiler.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+
+enum class PixelStorageType {
+ External,
+ Heap,
+ Ashmem,
+ Hardware,
+};
+
+namespace uirenderer {
+namespace renderthread {
+ class RenderThread;
+}
+}
+
+class PixelStorage;
+
+typedef void (*FreeFunc)(void* addr, void* context);
+
+class ANDROID_API Bitmap : public SkPixelRef {
+public:
+ static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable);
+ static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info);
+
+ static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
+
+ static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable);
+ static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info,
+ size_t rowBytes, SkColorTable* ctable);
+
+ static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer);
+
+ static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&);
+
+ static sk_sp<Bitmap> allocateHardwareBitmap(uirenderer::renderthread::RenderThread&,
+ SkBitmap& bitmap);
+
+ Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes,
+ SkColorTable* ctable);
+ Bitmap(void* address, void* context, FreeFunc freeFunc,
+ const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
+ Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
+ size_t rowBytes, SkColorTable* ctable);
+
+ int width() const { return info().width(); }
+ int height() const { return info().height(); }
+
+ // Can't mark as override since SkPixelRef::rowBytes isn't virtual
+ // but that's OK since we just want Bitmap to be able to rely
+ // on calling rowBytes() on an unlocked pixelref, which it will be
+ // doing on a Bitmap type, not a SkPixelRef, so static
+ // dispatching will do what we want.
+ size_t rowBytes() const { return mRowBytes; }
+
+ int rowBytesAsPixels() const {
+ return mRowBytes >> info().shiftPerPixel();
+ }
+
+ void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
+ void reconfigure(const SkImageInfo& info);
+ void setAlphaType(SkAlphaType alphaType);
+
+ void getSkBitmap(SkBitmap* outBitmap);
+
+ // Ugly hack: in case of hardware bitmaps, it sets nullptr as pixels pointer
+ // so it would crash if anyone tries to render this bitmap.
+ void getSkBitmapForShaders(SkBitmap* outBitmap);
+
+ int getAshmemFd() const;
+ size_t getAllocationByteCount() const;
+
+ void setHasHardwareMipMap(bool hasMipMap);
+ bool hasHardwareMipMap() const;
+
+ bool isOpaque() const {return info().isOpaque(); }
+ SkColorType colorType() const { return info().colorType(); }
+ void getBounds(SkRect* bounds) const;
+
+ bool readyToDraw() const {
+ return this->colorType() != kIndex_8_SkColorType || mColorTable;
+ }
+
+ bool isHardware() const {
+ return mPixelStorageType == PixelStorageType::Hardware;
+ }
+
+ GraphicBuffer* graphicBuffer();
+protected:
+ virtual bool onNewLockPixels(LockRec* rec) override;
+ virtual void onUnlockPixels() override { };
+ virtual size_t getAllocatedSizeInBytes() const override;
+private:
+ Bitmap(GraphicBuffer* buffer, const SkImageInfo& info);
+ virtual ~Bitmap();
+ void* getStorage() const;
+
+ PixelStorageType mPixelStorageType;
+
+ size_t mRowBytes = 0;
+ sk_sp<SkColorTable> mColorTable;
+ bool mHasHardwareMipMap = false;
+
+ union {
+ struct {
+ void* address;
+ void* context;
+ FreeFunc freeFunc;
+ } external;
+ struct {
+ void* address;
+ int fd;
+ size_t size;
+ } ashmem;
+ struct {
+ void* address;
+ size_t size;
+ } heap;
+ struct {
+ GraphicBuffer* buffer;
+ } hardware;
+ } mPixelStorage;
+};
+
+} //namespace android \ No newline at end of file
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 7bfa15a26d56..2dc8ce8fe774 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -16,22 +16,23 @@
#include "Canvas.h"
-#include "DisplayListCanvas.h"
#include "RecordingCanvas.h"
+#include "RenderNode.h"
#include "MinikinUtils.h"
#include "Paint.h"
+#include "Properties.h"
+#include "pipeline/skia/SkiaRecordingCanvas.h"
#include "Typeface.h"
#include <SkDrawFilter.h>
namespace android {
-Canvas* Canvas::create_recording_canvas(int width, int height) {
-#if HWUI_NEW_OPS
+Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
+ if (uirenderer::Properties::isSkiaEnabled()) {
+ return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
+ }
return new uirenderer::RecordingCanvas(width, height);
-#else
- return new uirenderer::DisplayListCanvas(width, height);
-#endif
}
void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
@@ -79,8 +80,9 @@ static void simplifyPaint(int color, SkPaint* paint) {
class DrawTextFunctor {
public:
- DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
- const SkPaint& paint, float x, float y, MinikinRect& bounds, float totalAdvance)
+ DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
+ const SkPaint& paint, float x, float y, minikin::MinikinRect& bounds,
+ float totalAdvance)
: layout(layout)
, canvas(canvas)
, glyphs(glyphs)
@@ -135,14 +137,14 @@ public:
}
}
private:
- const Layout& layout;
+ const minikin::Layout& layout;
Canvas* canvas;
uint16_t* glyphs;
float* pos;
const SkPaint& paint;
float x;
float y;
- MinikinRect& bounds;
+ minikin::MinikinRect& bounds;
float totalAdvance;
};
@@ -151,7 +153,7 @@ void Canvas::drawText(const uint16_t* text, int start, int count, int contextCou
// minikin may modify the original paint
Paint paint(origPaint);
- Layout layout;
+ minikin::Layout layout;
MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
size_t nGlyphs = layout.nGlyphs();
@@ -160,7 +162,7 @@ void Canvas::drawText(const uint16_t* text, int start, int count, int contextCou
x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
- MinikinRect bounds;
+ minikin::MinikinRect bounds;
layout.getBounds(&bounds);
if (!drawTextAbsolutePos()) {
bounds.offset(x, y);
@@ -178,7 +180,7 @@ void Canvas::drawText(const uint16_t* text, int start, int count, int contextCou
class DrawTextOnPathFunctor {
public:
- DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
+ DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset,
float vOffset, const Paint& paint, const SkPath& path)
: layout(layout)
, canvas(canvas)
@@ -189,16 +191,10 @@ public:
}
void operator()(size_t start, size_t end) {
- uint16_t glyphs[1];
- for (size_t i = start; i < end; i++) {
- glyphs[0] = layout.getGlyphId(i);
- float x = hOffset + layout.getX(i);
- float y = vOffset + layout.getY(i);
- canvas->drawGlyphsOnPath(glyphs, 1, path, x, y, paint);
- }
+ canvas->drawLayoutOnPath(layout, hOffset, vOffset, paint, path, start, end);
}
private:
- const Layout& layout;
+ const minikin::Layout& layout;
Canvas* canvas;
float hOffset;
float vOffset;
@@ -209,7 +205,7 @@ private:
void Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path,
float hOffset, float vOffset, const Paint& paint, Typeface* typeface) {
Paint paintCopy(paint);
- Layout layout;
+ minikin::Layout layout;
MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 55af33e80256..969d87716ce6 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -14,19 +14,23 @@
* limitations under the License.
*/
-#ifndef ANDROID_GRAPHICS_CANVAS_H
-#define ANDROID_GRAPHICS_CANVAS_H
+#pragma once
#include <cutils/compiler.h>
#include <utils/Functor.h>
#include "GlFunctorLifecycleListener.h"
-#include "utils/NinePatch.h"
+#include "utils/Macros.h"
+#include <androidfw/ResourceTypes.h>
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkMatrix.h>
+namespace minikin {
+ class Layout;
+}
+
namespace android {
namespace uirenderer {
@@ -61,6 +65,7 @@ class Tree;
};
typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
+class Bitmap;
class Paint;
struct Typeface;
@@ -70,7 +75,23 @@ public:
static Canvas* create_canvas(const SkBitmap& bitmap);
- static Canvas* create_recording_canvas(int width, int height);
+ /**
+ * Create a new Canvas object that records view system drawing operations for deferred
+ * rendering. A canvas returned by this call supports calls to the resetRecording(...) and
+ * finishRecording() calls. The latter call returns a DisplayList that is specific to the
+ * RenderPipeline defined by Properties::getRenderPipelineType().
+ *
+ * @param width of the requested Canvas.
+ * @param height of the requested Canvas.
+ * @param renderNode is an optional parameter that specifies the node that will consume the
+ * DisplayList produced by the returned Canvas. This enables the reuse of select C++
+ * objects as a speed optimization.
+ * @return new non-null Canvas Object. The type of DisplayList produced by this canvas is
+ determined based on Properties::getRenderPipelineType().
+ *
+ */
+ static WARN_UNUSED_RESULT Canvas* create_recording_canvas(int width, int height,
+ uirenderer::RenderNode* renderNode = nullptr);
/**
* Create a new Canvas object which delegates to an SkCanvas.
@@ -79,7 +100,8 @@ public:
* delegated to this object. This function will call ref() on the
* SkCanvas, and the returned Canvas will unref() it upon
* destruction.
- * @return new Canvas object. Will not return NULL.
+ * @return new non-null Canvas Object. The type of DisplayList produced by this canvas is
+ * determined based on Properties::getRenderPipelineType().
*/
static Canvas* create_canvas(SkCanvas* skiaCanvas);
@@ -108,7 +130,8 @@ public:
// View System operations (not exposed in public Canvas API)
// ----------------------------------------------------------------------------
- virtual void resetRecording(int width, int height) = 0;
+ virtual void resetRecording(int width, int height,
+ uirenderer::RenderNode* renderNode = nullptr) = 0;
virtual uirenderer::DisplayList* finishRecording() = 0;
virtual void insertReorderBarrier(bool enableReorder) = 0;
@@ -159,9 +182,8 @@ public:
virtual bool quickRejectPath(const SkPath& path) const = 0;
virtual bool clipRect(float left, float top, float right, float bottom,
- SkRegion::Op op = SkRegion::kIntersect_Op) = 0;
- virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0;
- virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0;
+ SkClipOp op) = 0;
+ virtual bool clipPath(const SkPath* path, SkClipOp op) = 0;
// filters
virtual SkDrawFilter* getDrawFilter() = 0;
@@ -170,7 +192,7 @@ public:
// ----------------------------------------------------------------------------
// Canvas draw operations
// ----------------------------------------------------------------------------
- virtual void drawColor(int color, SkXfermode::Mode mode) = 0;
+ virtual void drawColor(int color, SkBlendMode mode) = 0;
virtual void drawPaint(const SkPaint& paint) = 0;
// Geometry
@@ -195,16 +217,16 @@ public:
const uint16_t* indices, int indexCount, const SkPaint& paint) = 0;
// Bitmap-based
- virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
+ virtual void drawBitmap(Bitmap& bitmap, float left, float top,
const SkPaint* paint) = 0;
- virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
const SkPaint* paint) = 0;
- virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, const SkPaint* paint) = 0;
- virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) = 0;
- virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk,
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) = 0;
@@ -220,7 +242,7 @@ public:
/**
* Draws a VectorDrawable onto the canvas.
*/
- virtual void drawVectorDrawable(VectorDrawableRoot* tree);
+ virtual void drawVectorDrawable(VectorDrawableRoot* tree) = 0;
/**
* Converts utf16 text to glyphs, calculating position and boundary,
@@ -243,14 +265,11 @@ protected:
const SkPaint& paint, float x, float y,
float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
float totalAdvance) = 0;
- /** drawTextOnPath: count is of glyphs */
- virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) = 0;
-
+ virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
+ const SkPaint& paint, const SkPath& path, size_t start, size_t end) = 0;
friend class DrawTextFunctor;
friend class DrawTextOnPathFunctor;
friend class uirenderer::SkiaCanvasProxy;
};
}; // namespace android
-#endif // ANDROID_GRAPHICS_CANVAS_H
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 159a1444e044..6a003794fb28 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -17,23 +17,21 @@
#include "MinikinSkia.h"
#include <log/log.h>
-
+#include <SkFontDescriptor.h>
+#include <SkFontMgr.h>
#include <SkPaint.h>
#include <SkTypeface.h>
namespace android {
-MinikinFontSkia::MinikinFontSkia(SkTypeface* typeface, const void* fontData, size_t fontSize,
+MinikinFontSkia::MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
int ttcIndex) :
- MinikinFont(typeface->uniqueID()), mTypeface(typeface), mFontData(fontData),
+ minikin::MinikinFont(typeface->uniqueID()), mTypeface(std::move(typeface)), mFontData(fontData),
mFontSize(fontSize), mTtcIndex(ttcIndex) {
}
-MinikinFontSkia::~MinikinFontSkia() {
- SkSafeUnref(mTypeface);
-}
-
-static void MinikinFontSkia_SetSkiaPaint(const MinikinFont* font, SkPaint* skPaint, const MinikinPaint& paint) {
+static void MinikinFontSkia_SetSkiaPaint(const minikin::MinikinFont* font, SkPaint* skPaint,
+ const minikin::MinikinPaint& paint) {
skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
skPaint->setTextSize(paint.size);
skPaint->setTextScaleX(paint.scaleX);
@@ -44,7 +42,7 @@ static void MinikinFontSkia_SetSkiaPaint(const MinikinFont* font, SkPaint* skPai
}
float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id,
- const MinikinPaint &paint) const {
+ const minikin::MinikinPaint &paint) const {
SkPaint skPaint;
uint16_t glyph16 = glyph_id;
SkScalar skWidth;
@@ -56,8 +54,8 @@ float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id,
return skWidth;
}
-void MinikinFontSkia::GetBounds(MinikinRect* bounds, uint32_t glyph_id,
- const MinikinPaint& paint) const {
+void MinikinFontSkia::GetBounds(minikin::MinikinRect* bounds, uint32_t glyph_id,
+ const minikin::MinikinPaint& paint) const {
SkPaint skPaint;
uint16_t glyph16 = glyph_id;
SkRect skBounds;
@@ -69,23 +67,11 @@ void MinikinFontSkia::GetBounds(MinikinRect* bounds, uint32_t glyph_id,
bounds->mBottom = skBounds.fBottom;
}
-const void* MinikinFontSkia::GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) {
- // we don't have a buffer to the font data, copy to own buffer
- const size_t tableSize = mTypeface->getTableSize(tag);
- *size = tableSize;
- if (tableSize == 0) {
- return nullptr;
- }
- void* buf = malloc(tableSize);
- if (buf == nullptr) {
- return nullptr;
- }
- mTypeface->getTableData(tag, 0, tableSize, buf);
- *destroy = free;
- return buf;
+SkTypeface *MinikinFontSkia::GetSkTypeface() const {
+ return mTypeface.get();
}
-SkTypeface *MinikinFontSkia::GetSkTypeface() const {
+sk_sp<SkTypeface> MinikinFontSkia::RefSkTypeface() const {
return mTypeface;
}
@@ -101,6 +87,28 @@ int MinikinFontSkia::GetFontIndex() const {
return mTtcIndex;
}
+minikin::MinikinFont* MinikinFontSkia::createFontWithVariation(
+ const std::vector<minikin::FontVariation>& variations) const {
+ SkFontMgr::FontParameters params;
+
+ int ttcIndex;
+ SkStreamAsset* stream = mTypeface->openStream(&ttcIndex);
+ LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed");
+
+ params.setCollectionIndex(ttcIndex);
+ std::vector<SkFontMgr::FontParameters::Axis> skAxes;
+ skAxes.resize(variations.size());
+ for (size_t i = 0; i < variations.size(); i++) {
+ skAxes[i].fTag = variations[i].axisTag;
+ skAxes[i].fStyleValue = SkFloatToScalar(variations[i].value);
+ }
+ params.setAxes(skAxes.data(), skAxes.size());
+ sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ sk_sp<SkTypeface> face(fm->createFromStream(stream, params));
+
+ return new MinikinFontSkia(std::move(face), mFontData, mFontSize, ttcIndex);
+}
+
uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) {
uint32_t flags = paint->getFlags();
SkPaint::Hinting hinting = paint->getHinting();
@@ -118,8 +126,9 @@ void MinikinFontSkia::unpackPaintFlags(SkPaint* paint, uint32_t paintFlags) {
paint->setHinting(static_cast<SkPaint::Hinting>(paintFlags >> 16));
}
-void MinikinFontSkia::populateSkPaint(SkPaint* paint, const MinikinFont* font, FontFakery fakery) {
- paint->setTypeface(reinterpret_cast<const MinikinFontSkia*>(font)->GetSkTypeface());
+void MinikinFontSkia::populateSkPaint(SkPaint* paint, const MinikinFont* font,
+ minikin::FontFakery fakery) {
+ paint->setTypeface(reinterpret_cast<const MinikinFontSkia*>(font)->RefSkTypeface());
paint->setFakeBoldText(paint->isFakeBoldText() || fakery.isFakeBold());
if (fakery.isFakeItalic()) {
paint->setTextSkewX(paint->getTextSkewX() - 0.25f);
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index a7c9fb0b09d4..249b0cbe44de 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -19,42 +19,42 @@
#include <cutils/compiler.h>
#include <minikin/MinikinFont.h>
+#include <SkRefCnt.h>
class SkPaint;
class SkTypeface;
namespace android {
-class ANDROID_API MinikinFontSkia : public MinikinFont {
+class ANDROID_API MinikinFontSkia : public minikin::MinikinFont {
public:
- // Note: this takes ownership of the reference (will unref on dtor)
- explicit MinikinFontSkia(SkTypeface *typeface, const void* fontData, size_t fontSize,
+ explicit MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
int ttcIndex);
- ~MinikinFontSkia();
-
float GetHorizontalAdvance(uint32_t glyph_id,
- const MinikinPaint &paint) const;
-
- void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
- const MinikinPaint &paint) const;
+ const minikin::MinikinPaint &paint) const;
- const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy);
+ void GetBounds(minikin::MinikinRect* bounds, uint32_t glyph_id,
+ const minikin::MinikinPaint &paint) const;
SkTypeface* GetSkTypeface() const;
+ sk_sp<SkTypeface> RefSkTypeface() const;
// Access to underlying raw font bytes
const void* GetFontData() const;
size_t GetFontSize() const;
int GetFontIndex() const;
+ minikin::MinikinFont* createFontWithVariation(
+ const std::vector<minikin::FontVariation>&) const;
static uint32_t packPaintFlags(const SkPaint* paint);
static void unpackPaintFlags(SkPaint* paint, uint32_t paintFlags);
// set typeface and fake bold/italic parameters
- static void populateSkPaint(SkPaint* paint, const MinikinFont* font, FontFakery fakery);
+ static void populateSkPaint(SkPaint* paint, const minikin::MinikinFont* font,
+ minikin::FontFakery fakery);
private:
- SkTypeface* mTypeface;
+ sk_sp<SkTypeface> mTypeface;
// A raw pointer to the font data - it should be owned by some other object with
// lifetime at least as long as this object.
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index eda94bfcd430..713e5099da26 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -26,17 +26,18 @@
namespace android {
-FontStyle MinikinUtils::prepareMinikinPaint(MinikinPaint* minikinPaint, FontCollection** pFont,
- const Paint* paint, Typeface* typeface) {
+minikin::FontStyle MinikinUtils::prepareMinikinPaint(minikin::MinikinPaint* minikinPaint,
+ minikin::FontCollection** pFont, const Paint* paint, Typeface* typeface) {
const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
*pFont = resolvedFace->fFontCollection;
- FontStyle resolved = resolvedFace->fStyle;
+ minikin::FontStyle resolved = resolvedFace->fStyle;
/* Prepare minikin FontStyle */
- FontVariant minikinVariant = (paint->getFontVariant() == VARIANT_ELEGANT) ? VARIANT_ELEGANT
- : VARIANT_COMPACT;
+ minikin::FontVariant minikinVariant = (paint->getFontVariant() == minikin::VARIANT_ELEGANT) ?
+ minikin::VARIANT_ELEGANT : minikin::VARIANT_COMPACT;
const uint32_t langListId = paint->getMinikinLangListId();
- FontStyle minikinStyle(langListId, minikinVariant, resolved.getWeight(), resolved.getItalic());
+ minikin::FontStyle minikinStyle(langListId, minikinVariant, resolved.getWeight(),
+ resolved.getItalic());
/* Prepare minikin Paint */
// Note: it would be nice to handle fractional size values (it would improve smooth zoom
@@ -46,29 +47,30 @@ FontStyle MinikinUtils::prepareMinikinPaint(MinikinPaint* minikinPaint, FontColl
minikinPaint->scaleX = paint->getTextScaleX();
minikinPaint->skewX = paint->getTextSkewX();
minikinPaint->letterSpacing = paint->getLetterSpacing();
+ minikinPaint->wordSpacing = paint->getWordSpacing();
minikinPaint->paintFlags = MinikinFontSkia::packPaintFlags(paint);
minikinPaint->fontFeatureSettings = paint->getFontFeatureSettings();
- minikinPaint->hyphenEdit = HyphenEdit(paint->getHyphenEdit());
+ minikinPaint->hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit());
return minikinStyle;
}
-void MinikinUtils::doLayout(Layout* layout, const Paint* paint, int bidiFlags,
+void MinikinUtils::doLayout(minikin::Layout* layout, const Paint* paint, int bidiFlags,
Typeface* typeface, const uint16_t* buf, size_t start, size_t count,
size_t bufSize) {
- FontCollection *font;
- MinikinPaint minikinPaint;
- FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface);
+ minikin::FontCollection *font;
+ minikin::MinikinPaint minikinPaint;
+ minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface);
layout->setFontCollection(font);
layout->doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint);
}
float MinikinUtils::measureText(const Paint* paint, int bidiFlags, Typeface* typeface,
const uint16_t* buf, size_t start, size_t count, size_t bufSize, float *advances) {
- FontCollection *font;
- MinikinPaint minikinPaint;
- FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface);
- return Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint,
- font, advances);
+ minikin::FontCollection *font;
+ minikin::MinikinPaint minikinPaint;
+ minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface);
+ return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinStyle,
+ minikinPaint, font, advances);
}
bool MinikinUtils::hasVariationSelector(Typeface* typeface, uint32_t codepoint, uint32_t vs) {
@@ -76,7 +78,7 @@ bool MinikinUtils::hasVariationSelector(Typeface* typeface, uint32_t codepoint,
return resolvedFace->fFontCollection->hasVariationSelector(codepoint, vs);
}
-float MinikinUtils::xOffsetForTextAlign(Paint* paint, const Layout& layout) {
+float MinikinUtils::xOffsetForTextAlign(Paint* paint, const minikin::Layout& layout) {
switch (paint->getTextAlign()) {
case Paint::kCenter_Align:
return layout.getAdvance() * -0.5f;
@@ -90,7 +92,8 @@ float MinikinUtils::xOffsetForTextAlign(Paint* paint, const Layout& layout) {
return 0;
}
-float MinikinUtils::hOffsetForTextAlign(Paint* paint, const Layout& layout, const SkPath& path) {
+float MinikinUtils::hOffsetForTextAlign(Paint* paint, const minikin::Layout& layout,
+ const SkPath& path) {
float align = 0;
switch (paint->getTextAlign()) {
case Paint::kCenter_Align:
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index cfaa961ac1fc..d6f64d2418d5 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -34,31 +34,33 @@ namespace android {
class MinikinUtils {
public:
- ANDROID_API static FontStyle prepareMinikinPaint(MinikinPaint* minikinPaint, FontCollection** pFont,
- const Paint* paint, Typeface* typeface);
+ ANDROID_API static minikin::FontStyle prepareMinikinPaint(minikin::MinikinPaint* minikinPaint,
+ minikin::FontCollection** pFont, const Paint* paint, Typeface* typeface);
- ANDROID_API static void doLayout(Layout* layout, const Paint* paint, int bidiFlags,
+ ANDROID_API static void doLayout(minikin::Layout* layout, const Paint* paint, int bidiFlags,
Typeface* typeface, const uint16_t* buf, size_t start, size_t count,
size_t bufSize);
ANDROID_API static float measureText(const Paint* paint, int bidiFlags, Typeface* typeface,
const uint16_t* buf, size_t start, size_t count, size_t bufSize, float *advances);
- ANDROID_API static bool hasVariationSelector(Typeface* typeface, uint32_t codepoint, uint32_t vs);
+ ANDROID_API static bool hasVariationSelector(Typeface* typeface, uint32_t codepoint,
+ uint32_t vs);
- ANDROID_API static float xOffsetForTextAlign(Paint* paint, const Layout& layout);
+ ANDROID_API static float xOffsetForTextAlign(Paint* paint, const minikin::Layout& layout);
- ANDROID_API static float hOffsetForTextAlign(Paint* paint, const Layout& layout, const SkPath& path);
+ ANDROID_API static float hOffsetForTextAlign(Paint* paint, const minikin::Layout& layout,
+ const SkPath& path);
// f is a functor of type void f(size_t start, size_t end);
template <typename F>
- ANDROID_API static void forFontRun(const Layout& layout, Paint* paint, F& f) {
+ ANDROID_API static void forFontRun(const minikin::Layout& layout, Paint* paint, F& f) {
float saveSkewX = paint->getTextSkewX();
bool savefakeBold = paint->isFakeBoldText();
- MinikinFont* curFont = NULL;
+ minikin::MinikinFont* curFont = NULL;
size_t start = 0;
size_t nGlyphs = layout.nGlyphs();
for (size_t i = 0; i < nGlyphs; i++) {
- MinikinFont* nextFont = layout.getFont(i);
+ minikin::MinikinFont* nextFont = layout.getFont(i);
if (i > 0 && nextFont != curFont) {
MinikinFontSkia::populateSkPaint(paint, curFont, layout.getFakery(start));
f(start, i);
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index f72a6321643a..c9b5f0031a7b 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -48,6 +48,14 @@ public:
return mLetterSpacing;
}
+ void setWordSpacing(float wordSpacing) {
+ mWordSpacing = wordSpacing;
+ }
+
+ float getWordSpacing() const {
+ return mWordSpacing;
+ }
+
void setFontFeatureSettings(const std::string& fontFeatureSettings) {
mFontFeatureSettings = fontFeatureSettings;
}
@@ -64,11 +72,11 @@ public:
return mMinikinLangListId;
}
- void setFontVariant(FontVariant variant) {
+ void setFontVariant(minikin::FontVariant variant) {
mFontVariant = variant;
}
- FontVariant getFontVariant() const {
+ minikin::FontVariant getFontVariant() const {
return mFontVariant;
}
@@ -82,9 +90,10 @@ public:
private:
float mLetterSpacing = 0;
+ float mWordSpacing = 0;
std::string mFontFeatureSettings;
uint32_t mMinikinLangListId;
- FontVariant mFontVariant;
+ minikin::FontVariant mFontVariant;
uint32_t mHyphenEdit = 0;
};
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index b27672ce16a0..67427433bb89 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -19,19 +19,20 @@
namespace android {
Paint::Paint() :
- SkPaint(), mLetterSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0),
- mFontVariant(VARIANT_DEFAULT) {
+ SkPaint(), mLetterSpacing(0), mWordSpacing(0), mFontFeatureSettings(),
+ mMinikinLangListId(0), mFontVariant(minikin::VARIANT_DEFAULT) {
}
Paint::Paint(const Paint& paint) : SkPaint(paint),
- mLetterSpacing(paint.mLetterSpacing), mFontFeatureSettings(paint.mFontFeatureSettings),
+ mLetterSpacing(paint.mLetterSpacing), mWordSpacing(paint.mWordSpacing),
+ mFontFeatureSettings(paint.mFontFeatureSettings),
mMinikinLangListId(paint.mMinikinLangListId), mFontVariant(paint.mFontVariant),
mHyphenEdit(paint.mHyphenEdit) {
}
Paint::Paint(const SkPaint& paint) : SkPaint(paint),
- mLetterSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0),
- mFontVariant(VARIANT_DEFAULT) {
+ mLetterSpacing(0), mWordSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0),
+ mFontVariant(minikin::VARIANT_DEFAULT) {
}
Paint::~Paint() {
@@ -40,6 +41,7 @@ Paint::~Paint() {
Paint& Paint::operator=(const Paint& other) {
SkPaint::operator=(other);
mLetterSpacing = other.mLetterSpacing;
+ mWordSpacing = other.mWordSpacing;
mFontFeatureSettings = other.mFontFeatureSettings;
mMinikinLangListId = other.mMinikinLangListId;
mFontVariant = other.mFontVariant;
@@ -50,6 +52,7 @@ Paint& Paint::operator=(const Paint& other) {
bool operator==(const Paint& a, const Paint& b) {
return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b)
&& a.mLetterSpacing == b.mLetterSpacing
+ && a.mWordSpacing == b.mWordSpacing
&& a.mFontFeatureSettings == b.mFontFeatureSettings
&& a.mMinikinLangListId == b.mMinikinLangListId
&& a.mFontVariant == b.mFontVariant
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 0b46c09936c3..b69b0cb29efe 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -23,10 +23,14 @@
#include "Typeface.h"
#include <pthread.h>
+#include <fcntl.h> // For tests.
+#include <sys/stat.h> // For tests.
+#include <sys/mman.h> // For tests.
#include "MinikinSkia.h"
#include "SkTypeface.h"
#include "SkPaint.h"
+#include "SkStream.h" // Fot tests.
#include <minikin/FontCollection.h>
#include <minikin/FontFamily.h>
@@ -45,72 +49,20 @@ static void resolveStyle(Typeface* typeface) {
weight = 9;
}
bool italic = (typeface->fSkiaStyle & SkTypeface::kItalic) != 0;
- typeface->fStyle = FontStyle(weight, italic);
+ typeface->fStyle = minikin::FontStyle(weight, italic);
}
Typeface* gDefaultTypeface = NULL;
-pthread_once_t gDefaultTypefaceOnce = PTHREAD_ONCE_INIT;
-
-// This installs a default typeface (from a hardcoded path) that allows
-// layouts to work (not crash on null pointer) before the default
-// typeface is set.
-// TODO: investigate why layouts are being created before Typeface.java
-// class initialization.
-static FontCollection *makeFontCollection() {
- std::vector<FontFamily *>typefaces;
- const char *fns[] = {
- "/system/fonts/Roboto-Regular.ttf",
- };
-
- FontFamily *family = new FontFamily();
- for (size_t i = 0; i < sizeof(fns)/sizeof(fns[0]); i++) {
- const char *fn = fns[i];
- ALOGD("makeFontCollection adding %s", fn);
- SkTypeface *skFace = SkTypeface::CreateFromFile(fn);
- if (skFace != NULL) {
- // TODO: might be a nice optimization to get access to the underlying font
- // data, but would require us opening the file ourselves and passing that
- // to the appropriate Create method of SkTypeface.
- MinikinFont *font = new MinikinFontSkia(skFace, NULL, 0, 0);
- family->addFont(font);
- font->Unref();
- } else {
- ALOGE("failed to create font %s", fn);
- }
- }
- typefaces.push_back(family);
-
- FontCollection *result = new FontCollection(typefaces);
- family->Unref();
- return result;
-}
-
-static void getDefaultTypefaceOnce() {
- Layout::init();
- if (gDefaultTypeface == NULL) {
- // We expect the client to set a default typeface, but provide a
- // default so we can make progress before that happens.
- gDefaultTypeface = new Typeface;
- gDefaultTypeface->fFontCollection = makeFontCollection();
- gDefaultTypeface->fSkiaStyle = SkTypeface::kNormal;
- gDefaultTypeface->fBaseWeight = 400;
- resolveStyle(gDefaultTypeface);
- }
-}
Typeface* Typeface::resolveDefault(Typeface* src) {
- if (src == NULL) {
- pthread_once(&gDefaultTypefaceOnce, getDefaultTypefaceOnce);
- return gDefaultTypeface;
- } else {
- return src;
- }
+ LOG_ALWAYS_FATAL_IF(gDefaultTypeface == nullptr);
+ return src == nullptr ? gDefaultTypeface : src;
}
Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) {
Typeface* resolvedFace = Typeface::resolveDefault(src);
Typeface* result = new Typeface;
- if (result != 0) {
+ if (result != nullptr) {
result->fFontCollection = resolvedFace->fFontCollection;
result->fFontCollection->Ref();
result->fSkiaStyle = style;
@@ -120,10 +72,30 @@ Typeface* Typeface::createFromTypeface(Typeface* src, SkTypeface::Style style) {
return result;
}
+Typeface* Typeface::createFromTypefaceWithVariation(Typeface* src,
+ const std::vector<minikin::FontVariation>& variations) {
+ Typeface* resolvedFace = Typeface::resolveDefault(src);
+ Typeface* result = new Typeface();
+ if (result != nullptr) {
+ result->fFontCollection =
+ resolvedFace->fFontCollection->createCollectionWithVariation(variations);
+ if (result->fFontCollection == nullptr) {
+ // None of passed axes are supported by this collection.
+ // So we will reuse the same collection with incrementing reference count.
+ result->fFontCollection = resolvedFace->fFontCollection;
+ result->fFontCollection->Ref();
+ }
+ result->fSkiaStyle = resolvedFace->fSkiaStyle;
+ result->fBaseWeight = resolvedFace->fBaseWeight;
+ resolveStyle(result);
+ }
+ return result;
+}
+
Typeface* Typeface::createWeightAlias(Typeface* src, int weight) {
Typeface* resolvedFace = Typeface::resolveDefault(src);
Typeface* result = new Typeface;
- if (result != 0) {
+ if (result != nullptr) {
result->fFontCollection = resolvedFace->fFontCollection;
result->fFontCollection->Ref();
result->fSkiaStyle = resolvedFace->fSkiaStyle;
@@ -133,16 +105,16 @@ Typeface* Typeface::createWeightAlias(Typeface* src, int weight) {
return result;
}
-Typeface* Typeface::createFromFamilies(const std::vector<FontFamily*>& families) {
+Typeface* Typeface::createFromFamilies(const std::vector<minikin::FontFamily*>& families) {
Typeface* result = new Typeface;
- result->fFontCollection = new FontCollection(families);
+ result->fFontCollection = new minikin::FontCollection(families);
if (families.empty()) {
ALOGW("createFromFamilies creating empty collection");
result->fSkiaStyle = SkTypeface::kNormal;
} else {
- const FontStyle defaultStyle;
- FontFamily* firstFamily = reinterpret_cast<FontFamily*>(families[0]);
- MinikinFont* mf = firstFamily->getClosestMatch(defaultStyle).font;
+ const minikin::FontStyle defaultStyle;
+ minikin::FontFamily* firstFamily = reinterpret_cast<minikin::FontFamily*>(families[0]);
+ minikin::MinikinFont* mf = firstFamily->getClosestMatch(defaultStyle).font;
if (mf != NULL) {
SkTypeface* skTypeface = reinterpret_cast<MinikinFontSkia*>(mf)->GetSkTypeface();
// TODO: probably better to query more precise style from family, will be important
@@ -166,4 +138,34 @@ void Typeface::setDefault(Typeface* face) {
gDefaultTypeface = face;
}
+void Typeface::setRobotoTypefaceForTest() {
+ const char* kRobotoFont = "/system/fonts/Roboto-Regular.ttf";
+
+ int fd = open(kRobotoFont, O_RDONLY);
+ LOG_ALWAYS_FATAL_IF(fd == -1, "Failed to open file %s", kRobotoFont);
+ struct stat st = {};
+ LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", kRobotoFont);
+ void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ std::unique_ptr<SkMemoryStream> fontData(new SkMemoryStream(data, st.st_size));
+ sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(fontData.release());
+ LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont);
+
+ minikin::MinikinFont* font = new MinikinFontSkia(std::move(typeface), data, st.st_size, 0);
+ minikin::FontFamily* family = new minikin::FontFamily(
+ std::vector<minikin::Font>({ minikin::Font(font, minikin::FontStyle()) }));
+ font->Unref();
+
+ std::vector<minikin::FontFamily*> typefaces = { family };
+ minikin::FontCollection *collection = new minikin::FontCollection(typefaces);
+ family->Unref();
+
+ Typeface* hwTypeface = new Typeface();
+ hwTypeface->fFontCollection = collection;
+ hwTypeface->fSkiaStyle = SkTypeface::kNormal;
+ hwTypeface->fBaseWeight = 400;
+ hwTypeface->fStyle = minikin::FontStyle(4 /* weight */, false /* italic */);
+
+ Typeface::setDefault(hwTypeface);
+}
+
}
diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h
index 8862e5a5a911..4392ebc36bad 100644
--- a/libs/hwui/hwui/Typeface.h
+++ b/libs/hwui/hwui/Typeface.h
@@ -27,7 +27,7 @@
namespace android {
struct ANDROID_API Typeface {
- FontCollection *fFontCollection;
+ minikin::FontCollection *fFontCollection;
// style used for constructing and querying Typeface objects
SkTypeface::Style fSkiaStyle;
@@ -35,7 +35,7 @@ struct ANDROID_API Typeface {
int fBaseWeight;
// resolved style actually used for rendering
- FontStyle fStyle;
+ minikin::FontStyle fStyle;
void unref();
@@ -43,11 +43,17 @@ struct ANDROID_API Typeface {
static Typeface* createFromTypeface(Typeface* src, SkTypeface::Style style);
+ static Typeface* createFromTypefaceWithVariation(Typeface* src,
+ const std::vector<minikin::FontVariation>& variations);
+
static Typeface* createWeightAlias(Typeface* src, int baseweight);
- static Typeface* createFromFamilies(const std::vector<FontFamily*>& families);
+ static Typeface* createFromFamilies(const std::vector<minikin::FontFamily*>& families);
static void setDefault(Typeface* face);
+
+ // Sets roboto font as the default typeface for testing purpose.
+ static void setRobotoTypefaceForTest();
};
}
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
index dca78b38e942..37126a69f701 100644
--- a/libs/hwui/hwui_static_deps.mk
+++ b/libs/hwui/hwui_static_deps.mk
@@ -18,6 +18,7 @@ LOCAL_SHARED_LIBRARIES += \
libutils \
libEGL \
libGLESv2 \
+ libvulkan \
libskia \
libui \
libgui \
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
new file mode 100644
index 000000000000..44c494f77231
--- /dev/null
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "CanvasProperty.h"
+#include <utils/RefBase.h>
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class AnimatedRoundRect : public SkDrawable {
+public:
+ AnimatedRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+ uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+ uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+ uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* p)
+ : mLeft(left)
+ , mTop(top)
+ , mRight(right)
+ , mBottom(bottom)
+ , mRx(rx)
+ , mRy(ry)
+ , mPaint(p) {}
+
+protected:
+ virtual SkRect onGetBounds() override {
+ return SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
+ }
+ virtual void onDraw(SkCanvas* canvas) override {
+ SkRect rect = SkRect::MakeLTRB(mLeft->value, mTop->value, mRight->value, mBottom->value);
+ canvas->drawRoundRect(rect, mRx->value, mRy->value, mPaint->value);
+ }
+
+private:
+ sp<uirenderer::CanvasPropertyPrimitive> mLeft;
+ sp<uirenderer::CanvasPropertyPrimitive> mTop;
+ sp<uirenderer::CanvasPropertyPrimitive> mRight;
+ sp<uirenderer::CanvasPropertyPrimitive> mBottom;
+ sp<uirenderer::CanvasPropertyPrimitive> mRx;
+ sp<uirenderer::CanvasPropertyPrimitive> mRy;
+ sp<uirenderer::CanvasPropertyPaint> mPaint;
+};
+
+class AnimatedCircle : public SkDrawable {
+public:
+ AnimatedCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
+ uirenderer::CanvasPropertyPrimitive* radius, uirenderer::CanvasPropertyPaint* paint)
+ : mX(x)
+ , mY(y)
+ , mRadius(radius)
+ , mPaint(paint) {}
+
+protected:
+ virtual SkRect onGetBounds() override {
+ const float x = mX->value;
+ const float y = mY->value;
+ const float radius = mRadius->value;
+ return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius);
+ }
+ virtual void onDraw(SkCanvas* canvas) override {
+ canvas->drawCircle(mX->value, mY->value, mRadius->value, mPaint->value);
+ }
+
+private:
+ sp<uirenderer::CanvasPropertyPrimitive> mX;
+ sp<uirenderer::CanvasPropertyPrimitive> mY;
+ sp<uirenderer::CanvasPropertyPrimitive> mRadius;
+ sp<uirenderer::CanvasPropertyPaint> mPaint;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
new file mode 100644
index 000000000000..419c8a99fe74
--- /dev/null
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GLFunctorDrawable.h"
+#include "GlFunctorLifecycleListener.h"
+#include "RenderNode.h"
+#include "SkClipStack.h"
+#include <private/hwui/DrawGlInfo.h>
+#include <SkPath.h>
+#include <GrContext.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+GLFunctorDrawable::~GLFunctorDrawable() {
+ if(mListener.get() != nullptr) {
+ mListener->onGlFunctorReleased(mFunctor);
+ }
+}
+
+void GLFunctorDrawable::syncFunctor() const {
+ (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
+}
+
+static void setScissor(int viewportHeight, const SkIRect& clip) {
+ SkASSERT(!clip.isEmpty());
+ // transform to Y-flipped GL space, and prevent negatives
+ GLint y = viewportHeight - clip.fBottom;
+ GLint height = (viewportHeight - clip.fTop) - y;
+ glScissor(clip.fLeft, y, clip.width(), height);
+}
+
+void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
+ if (canvas->getGrContext() == nullptr) {
+ SkDEBUGF(("Attempting to draw GLFunctor into an unsupported surface"));
+ return;
+ }
+
+ canvas->flush();
+
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ canvas->clear(SK_ColorRED);
+ return;
+ }
+
+ SkImageInfo canvasInfo = canvas->imageInfo();
+ SkMatrix44 mat4(canvas->getTotalMatrix());
+
+ SkIRect ibounds;
+ canvas->getClipDeviceBounds(&ibounds);
+
+ DrawGlInfo info;
+ info.clipLeft = ibounds.fLeft;
+ info.clipTop = ibounds.fTop;
+ info.clipRight = ibounds.fRight;
+ info.clipBottom = ibounds.fBottom;
+ // info.isLayer = hasLayer();
+ info.isLayer = false;
+ info.width = canvasInfo.width();
+ info.height = canvasInfo.height();
+ mat4.asColMajorf(&info.transform[0]);
+
+ //apply a simple clip with a scissor or a complex clip with a stencil
+ SkRegion clipRegion;
+ SkPath path;
+ canvas->getClipStack()->asPath(&path);
+ clipRegion.setPath(path, SkRegion(ibounds));
+ if (CC_UNLIKELY(clipRegion.isComplex())) {
+ //It is only a temporary solution to use a scissor to draw the stencil.
+ //There is a bug 31489986 to implement efficiently non-rectangular clips.
+ glDisable(GL_SCISSOR_TEST);
+ glDisable(GL_STENCIL_TEST);
+ glStencilMask(0xff);
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ glEnable(GL_SCISSOR_TEST);
+ SkRegion::Cliperator it(clipRegion, ibounds);
+ while (!it.done()) {
+ setScissor(info.height, it.rect());
+ glClearStencil(0x1);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ it.next();
+ }
+ glDisable(GL_SCISSOR_TEST);
+ glStencilFunc(GL_EQUAL, 0x1, 0xff);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ glEnable(GL_STENCIL_TEST);
+ } else if (clipRegion.isEmpty()) {
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ } else {
+ glDisable(GL_STENCIL_TEST);
+ glEnable(GL_SCISSOR_TEST);
+ setScissor(info.height, clipRegion.getBounds());
+ }
+
+ (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+
+ canvas->getGrContext()->resetContext();
+ }
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
new file mode 100644
index 000000000000..bf39dadbfcc5
--- /dev/null
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+#include <utils/RefBase.h>
+#include <utils/Functor.h>
+
+namespace android {
+namespace uirenderer {
+
+class GlFunctorLifecycleListener;
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a OpenGL functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class GLFunctorDrawable : public SkDrawable {
+public:
+ GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+ : mFunctor(functor)
+ , mListener(listener) {
+ canvas->getClipBounds(&mBounds);
+ }
+ virtual ~GLFunctorDrawable();
+
+ void syncFunctor() const;
+
+ protected:
+ virtual SkRect onGetBounds() override { return mBounds; }
+ virtual void onDraw(SkCanvas* canvas) override;
+
+ private:
+ Functor* mFunctor;
+ sp<GlFunctorLifecycleListener> mListener;
+ SkRect mBounds;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
new file mode 100644
index 000000000000..2ebfbcc1f18e
--- /dev/null
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GlLayer.h"
+#include "LayerDrawable.h"
+#include "VkLayer.h"
+
+#include "SkColorFilter.h"
+#include "SkSurface.h"
+#include "gl/GrGLTypes.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+void LayerDrawable::onDraw(SkCanvas* canvas) {
+ DrawLayer(canvas->getGrContext(), canvas, mLayer.get());
+}
+
+bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) {
+ // transform the matrix based on the layer
+ int saveCount = -1;
+ if (!layer->getTransform().isIdentity()) {
+ saveCount = canvas->save();
+ SkMatrix transform;
+ layer->getTransform().copyTo(transform);
+ canvas->concat(transform);
+ }
+
+ sk_sp<SkImage> layerImage;
+ if (layer->getApi() == Layer::Api::OpenGL) {
+ GlLayer* glLayer = static_cast<GlLayer*>(layer);
+ GrGLTextureInfo externalTexture;
+ externalTexture.fTarget = glLayer->getRenderTarget();
+ externalTexture.fID = glLayer->getTextureId();
+ GrBackendTextureDesc textureDescription;
+ textureDescription.fWidth = glLayer->getWidth();
+ textureDescription.fHeight = glLayer->getHeight();
+ textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
+ textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
+ textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
+ layerImage = SkImage::MakeFromTexture(context, textureDescription);
+ } else {
+ SkASSERT(layer->getApi() == Layer::Api::Vulkan);
+ VkLayer* vkLayer = static_cast<VkLayer*>(layer);
+ canvas->clear(SK_ColorGREEN);
+ layerImage = vkLayer->getImage();
+ }
+
+ if (layerImage) {
+ SkPaint paint;
+ paint.setAlpha(layer->getAlpha());
+ paint.setBlendMode(layer->getMode());
+ paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
+ canvas->drawImage(layerImage, 0, 0, &paint);
+ }
+ // restore the original matrix
+ if (saveCount >= 0) {
+ canvas->restoreToCount(saveCount);
+ }
+
+ return layerImage;
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
new file mode 100644
index 000000000000..431989519a70
--- /dev/null
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Layer.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/*
+ * Draws a layer backed by an OpenGL texture into a SkCanvas.
+ */
+class LayerDrawable : public SkDrawable {
+ public:
+ explicit LayerDrawable(Layer* layer)
+ : mLayer(layer) {}
+
+ static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer);
+ protected:
+ virtual SkRect onGetBounds() override {
+ return SkRect::MakeWH(mLayer->getWidth(), mLayer->getHeight());
+ }
+ virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+ sp<Layer> mLayer;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
new file mode 100644
index 000000000000..117395bfc75c
--- /dev/null
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "RenderNodeDrawable.h"
+#include "RenderNode.h"
+#include "SkiaDisplayList.h"
+#include "SkiaPipeline.h"
+#include "utils/TraceUtils.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList,
+ int nestLevel) {
+ LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver);
+ for (auto& child : displayList.mChildNodes) {
+ const RenderProperties& childProperties = child.getNodeProperties();
+
+ //immediate children cannot be projected on their parent
+ if (childProperties.getProjectBackwards() && nestLevel > 0) {
+ SkAutoCanvasRestore acr2(canvas, true);
+ //Apply recorded matrix, which is a total matrix saved at recording time to avoid
+ //replaying all DL commands.
+ canvas->concat(child.getRecordedMatrix());
+ child.drawContent(canvas);
+ }
+
+ //skip walking sub-nodes if current display list contains a receiver with exception of
+ //level 0, which is a known receiver
+ if (0 == nestLevel || !displayList.containsProjectionReceiver()) {
+ SkAutoCanvasRestore acr(canvas, true);
+ SkMatrix nodeMatrix;
+ mat4 hwuiMatrix(child.getRecordedMatrix());
+ auto childNode = child.getRenderNode();
+ childNode->applyViewPropertyTransforms(hwuiMatrix);
+ hwuiMatrix.copyTo(nodeMatrix);
+ canvas->concat(nodeMatrix);
+ SkiaDisplayList* childDisplayList = static_cast<SkiaDisplayList*>(
+ (const_cast<DisplayList*>(childNode->getDisplayList())));
+ if (childDisplayList) {
+ drawBackwardsProjectedNodes(canvas, *childDisplayList, nestLevel+1);
+ }
+ }
+ }
+}
+
+static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) {
+ Rect possibleRect;
+ float radius;
+ LOG_ALWAYS_FATAL_IF(!outline.getAsRoundRect(&possibleRect, &radius),
+ "clipping outlines should be at most roundedRects");
+ SkRect rect = possibleRect.toSkRect();
+ if (radius != 0.0f) {
+ if (pendingClip && !pendingClip->contains(rect)) {
+ canvas->clipRect(*pendingClip);
+ }
+ canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkClipOp::kIntersect, true);
+ } else {
+ if (pendingClip) {
+ (void)rect.intersect(*pendingClip);
+ }
+ canvas->clipRect(rect);
+ }
+}
+
+const RenderProperties& RenderNodeDrawable::getNodeProperties() const {
+ return mRenderNode->properties();
+}
+
+void RenderNodeDrawable::onDraw(SkCanvas* canvas) {
+ //negative and positive Z order are drawn out of order, if this render node drawable is in
+ //a reordering section
+ if ((!mInReorderingSection) || MathUtils::isZero(mRenderNode->properties().getZ())) {
+ this->forceDraw(canvas);
+ }
+}
+
+void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
+ RenderNode* renderNode = mRenderNode.get();
+ if (SkiaPipeline::skpCaptureEnabled()) {
+ SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
+ canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
+ }
+
+ // We only respect the nothingToDraw check when we are composing a layer. This
+ // ensures that we paint the layer even if it is not currently visible in the
+ // event that the properties change and it becomes visible.
+ if (!renderNode->isRenderable() || (renderNode->nothingToDraw() && mComposeLayer)) {
+ return;
+ }
+
+ SkASSERT(renderNode->getDisplayList()->isSkiaDL());
+ SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
+
+ SkAutoCanvasRestore acr(canvas, true);
+ const RenderProperties& properties = this->getNodeProperties();
+ //pass this outline to the children that may clip backward projected nodes
+ displayList->mProjectedOutline = displayList->containsProjectionReceiver()
+ ? &properties.getOutline() : nullptr;
+ if (!properties.getProjectBackwards()) {
+ drawContent(canvas);
+ if (mProjectedDisplayList) {
+ acr.restore(); //draw projected children using parent matrix
+ LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline);
+ const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath();
+ SkAutoCanvasRestore acr2(canvas, shouldClip);
+ canvas->setMatrix(mProjectedDisplayList->mProjectedReceiverParentMatrix);
+ if (shouldClip) {
+ clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr);
+ }
+ drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList);
+ }
+ }
+ displayList->mProjectedOutline = nullptr;
+}
+
+static bool layerNeedsPaint(const LayerProperties& properties,
+ float alphaMultiplier, SkPaint* paint) {
+ if (alphaMultiplier < 1.0f
+ || properties.alpha() < 255
+ || properties.xferMode() != SkBlendMode::kSrcOver
+ || properties.colorFilter() != nullptr) {
+ paint->setAlpha(properties.alpha() * alphaMultiplier);
+ paint->setBlendMode(properties.xferMode());
+ paint->setColorFilter(sk_ref_sp(properties.colorFilter()));
+ return true;
+ }
+ return false;
+}
+
+void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
+ RenderNode* renderNode = mRenderNode.get();
+ float alphaMultiplier = 1.0f;
+ const RenderProperties& properties = renderNode->properties();
+
+ // If we are drawing the contents of layer, we don't want to apply any of
+ // the RenderNode's properties during this pass. Those will all be applied
+ // when the layer is composited.
+ if (mComposeLayer) {
+ setViewProperties(properties, canvas, &alphaMultiplier);
+ }
+ SkiaDisplayList* displayList = (SkiaDisplayList*)mRenderNode->getDisplayList();
+ if (displayList->containsProjectionReceiver()) {
+ displayList->mProjectedReceiverParentMatrix = canvas->getTotalMatrix();
+ }
+
+ //TODO should we let the bound of the drawable do this for us?
+ const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
+ bool quickRejected = properties.getClipToBounds() && canvas->quickReject(bounds);
+ if (!quickRejected) {
+ SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
+ const LayerProperties& layerProperties = properties.layerProperties();
+ // composing a hardware layer
+ if (renderNode->getLayerSurface() && mComposeLayer) {
+ SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
+ SkPaint* paint = nullptr;
+ SkPaint tmpPaint;
+ if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) {
+ paint = &tmpPaint;
+ }
+ renderNode->getLayerSurface()->draw(canvas, 0, 0, paint);
+
+ if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
+ renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
+ if (CC_UNLIKELY(Properties::debugLayersUpdates)) {
+ SkPaint layerPaint;
+ layerPaint.setColor(0x7f00ff00);
+ canvas->drawRect(bounds, layerPaint);
+ } else if (CC_UNLIKELY(Properties::debugOverdraw)) {
+ // Render transparent rect to increment overdraw for repaint area.
+ // This can be "else if" because flashing green on layer updates
+ // will also increment the overdraw if it happens to be turned on.
+ SkPaint transparentPaint;
+ transparentPaint.setColor(SK_ColorTRANSPARENT);
+ canvas->drawRect(bounds, transparentPaint);
+ }
+ }
+
+ // composing a software layer with alpha
+ } else if (properties.effectiveLayerType() == LayerType::Software) {
+ SkPaint paint;
+ bool needsLayer = layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
+ if (needsLayer) {
+ canvas->saveLayer(bounds, &paint);
+ }
+ canvas->drawDrawable(displayList->mDrawable.get());
+ if (needsLayer) {
+ canvas->restore();
+ }
+ } else {
+ canvas->drawDrawable(displayList->mDrawable.get());
+ }
+ }
+}
+
+void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
+ float* alphaMultiplier) {
+ if (properties.getLeft() != 0 || properties.getTop() != 0) {
+ canvas->translate(properties.getLeft(), properties.getTop());
+ }
+ if (properties.getStaticMatrix()) {
+ canvas->concat(*properties.getStaticMatrix());
+ } else if (properties.getAnimationMatrix()) {
+ canvas->concat(*properties.getAnimationMatrix());
+ }
+ if (properties.hasTransformMatrix()) {
+ if (properties.isTransformTranslateOnly()) {
+ canvas->translate(properties.getTranslationX(), properties.getTranslationY());
+ } else {
+ canvas->concat(*properties.getTransformMatrix());
+ }
+ }
+ const bool isLayer = properties.effectiveLayerType() != LayerType::None;
+ int clipFlags = properties.getClippingFlags();
+ if (properties.getAlpha() < 1) {
+ if (isLayer) {
+ clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
+ }
+ if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
+ *alphaMultiplier = properties.getAlpha();
+ } else {
+ // savelayer needed to create an offscreen buffer
+ Rect layerBounds(0, 0, properties.getWidth(), properties.getHeight());
+ if (clipFlags) {
+ properties.getClippingRectForFlags(clipFlags, &layerBounds);
+ clipFlags = 0; // all clipping done by savelayer
+ }
+ SkRect bounds = SkRect::MakeLTRB(layerBounds.left, layerBounds.top,
+ layerBounds.right, layerBounds.bottom);
+ canvas->saveLayerAlpha(&bounds, (int) (properties.getAlpha() * 255));
+ }
+
+ if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
+ // pretend alpha always causes savelayer to warn about
+ // performance problem affecting old versions
+ ATRACE_FORMAT("alpha caused saveLayer %dx%d", properties.getWidth(),
+ properties.getHeight());
+ }
+ }
+
+ const SkRect* pendingClip = nullptr;
+ SkRect clipRect;
+
+ if (clipFlags) {
+ Rect tmpRect;
+ properties.getClippingRectForFlags(clipFlags, &tmpRect);
+ clipRect = tmpRect.toSkRect();
+ pendingClip = &clipRect;
+ }
+
+ if (properties.getRevealClip().willClip()) {
+ canvas->clipPath(*properties.getRevealClip().getPath(), SkClipOp::kIntersect, true);
+ } else if (properties.getOutline().willClip()) {
+ clipOutline(properties.getOutline(), canvas, pendingClip);
+ pendingClip = nullptr;
+ }
+
+ if (pendingClip) {
+ canvas->clipRect(*pendingClip);
+ }
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
new file mode 100644
index 000000000000..3eed6476c994
--- /dev/null
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+#include <SkMatrix.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+class RenderProperties;
+
+namespace skiapipeline {
+
+class SkiaDisplayList;
+
+/**
+ * This drawable wraps a RenderNode and enables it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class RenderNodeDrawable : public SkDrawable {
+public:
+ /**
+ * Creates a new RenderNodeDrawable backed by a render node.
+ *
+ * @param node that has to be drawn
+ * @param canvas is a recording canvas used to extract its matrix
+ * @param composeLayer if the node's layer type is RenderLayer this flag determines whether
+ * we should draw into the contents of the layer or compose the existing contents of the
+ * layer into the canvas.
+ */
+ explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer = true,
+ bool inReorderingSection = false)
+ : mRenderNode(node)
+ , mRecordedTransform(canvas->getTotalMatrix())
+ , mComposeLayer(composeLayer)
+ , mInReorderingSection(inReorderingSection) {}
+
+ /**
+ * Draws into the canvas this render node and its children. If the node is marked as a
+ * projection receiver then all projected children (excluding direct children) will be drawn
+ * last. Any projected node not matching those requirements will not be drawn by this function.
+ */
+ void forceDraw(SkCanvas* canvas);
+
+ /**
+ * Returns readonly render properties for this render node.
+ */
+ const RenderProperties& getNodeProperties() const;
+
+ /**
+ * The renderNode (and its properties) that is to be drawn
+ */
+ RenderNode* getRenderNode() const { return mRenderNode.get(); }
+
+ /**
+ * Returns the transform on the canvas at time of recording and is used for
+ * computing total transform without rerunning DL contents.
+ */
+ const SkMatrix& getRecordedMatrix() const { return mRecordedTransform; }
+
+ /**
+ * Sets a pointer to a display list of the parent render node. The display list is used when
+ * drawing backward projected nodes, when this node is a projection receiver.
+ */
+ void setProjectedDisplayList(SkiaDisplayList* projectedDisplayList) {
+ mProjectedDisplayList = projectedDisplayList;
+ }
+
+protected:
+ /*
+ * Return the (conservative) bounds of what the drawable will draw.
+ */
+ virtual SkRect onGetBounds() override {
+ // We don't want to enable a record time quick reject because the properties
+ // of the RenderNode may be updated on subsequent frames.
+ return SkRect::MakeLargest();
+ }
+ /**
+ * This function draws into a canvas as forceDraw, but does nothing if the render node has a
+ * non-zero elevation.
+ */
+ virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+ /*
+ * Render node that is wrapped by this class.
+ */
+ sp<RenderNode> mRenderNode;
+
+ /**
+ * Walks recursively the display list and draws the content of backward projected nodes.
+ *
+ * @param canvas used to draw the backward projected nodes
+ * @param displayList is a display list that contains a projection receiver
+ * @param nestLevel should be always 0. Used to track how far we are from the receiver.
+ */
+ void drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList,
+ int nestLevel = 0);
+
+ /**
+ * Applies the rendering properties of a view onto a SkCanvas.
+ */
+ static void setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
+ float* alphaMultiplier);
+
+ /**
+ * Stores transform on the canvas at time of recording and is used for
+ * computing total transform without rerunning DL contents.
+ */
+ const SkMatrix mRecordedTransform;
+
+ /**
+ * If mRenderNode's layer type is RenderLayer this flag determines whether we
+ * should draw into the contents of the layer or compose the existing contents
+ * of the layer into the canvas.
+ */
+ const bool mComposeLayer;
+
+ /*
+ * True if the render node is in a reordering section
+ */
+ bool mInReorderingSection;
+
+ /*
+ * Draw the content into a canvas, depending on the render node layer type and mComposeLayer.
+ */
+ void drawContent(SkCanvas* canvas) const;
+
+ /*
+ * display list that is searched for any render nodes with getProjectBackwards==true
+ */
+ SkiaDisplayList* mProjectedDisplayList = nullptr;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
new file mode 100644
index 000000000000..d05e7f660c72
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ReorderBarrierDrawables.h"
+#include "RenderNode.h"
+#include "SkiaDisplayList.h"
+#include "SkiaPipeline.h"
+
+#include <SkBlurMask.h>
+#include <SkBlurMaskFilter.h>
+#include <SkGaussianEdgeShader.h>
+#include <SkPathOps.h>
+#include <SkRRectsGaussianEdgeMaskFilter.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+StartReorderBarrierDrawable::StartReorderBarrierDrawable(SkiaDisplayList* data)
+ : mEndChildIndex(0)
+ , mBeginChildIndex(data->mChildNodes.size())
+ , mDisplayList(data) {
+}
+
+void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
+ if (mChildren.empty()) {
+ //mChildren is allocated and initialized only the first time onDraw is called and cached for
+ //subsequent calls
+ mChildren.reserve(mEndChildIndex - mBeginChildIndex + 1);
+ for (int i = mBeginChildIndex; i <= mEndChildIndex; i++) {
+ mChildren.push_back(const_cast<RenderNodeDrawable*>(&mDisplayList->mChildNodes[i]));
+ }
+ }
+ std::stable_sort(mChildren.begin(), mChildren.end(),
+ [](RenderNodeDrawable* a, RenderNodeDrawable* b) {
+ const float aZValue = a->getNodeProperties().getZ();
+ const float bZValue = b->getNodeProperties().getZ();
+ return aZValue < bZValue;
+ });
+
+ SkASSERT(!mChildren.empty());
+
+ size_t drawIndex = 0;
+ const size_t endIndex = mChildren.size();
+ while (drawIndex < endIndex) {
+ RenderNodeDrawable* childNode = mChildren[drawIndex];
+ SkASSERT(childNode);
+ const float casterZ = childNode->getNodeProperties().getZ();
+ if (casterZ >= -NON_ZERO_EPSILON) { //draw only children with negative Z
+ return;
+ }
+ childNode->forceDraw(canvas);
+ drawIndex++;
+ }
+}
+
+EndReorderBarrierDrawable::EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier)
+ : mStartBarrier(startBarrier) {
+ mStartBarrier->mEndChildIndex = mStartBarrier->mDisplayList->mChildNodes.size() - 1;
+}
+
+#define SHADOW_DELTA 0.1f
+
+void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
+ auto& zChildren = mStartBarrier->mChildren;
+ SkASSERT(!zChildren.empty());
+
+ /**
+ * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+ * with very similar Z heights to draw together.
+ *
+ * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
+ * underneath both, and neither's shadow is drawn on top of the other.
+ */
+ size_t drawIndex = 0;
+
+ const size_t endIndex = zChildren.size();
+ while (drawIndex < endIndex //draw only children with positive Z
+ && zChildren[drawIndex]->getNodeProperties().getZ() <= NON_ZERO_EPSILON) drawIndex++;
+ size_t shadowIndex = drawIndex;
+
+ float lastCasterZ = 0.0f;
+ while (shadowIndex < endIndex || drawIndex < endIndex) {
+ if (shadowIndex < endIndex) {
+ const float casterZ = zChildren[shadowIndex]->getNodeProperties().getZ();
+
+ // attempt to render the shadow if the caster about to be drawn is its caster,
+ // OR if its caster's Z value is similar to the previous potential caster
+ if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
+ this->drawShadow(canvas, zChildren[shadowIndex]);
+ lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+ shadowIndex++;
+ continue;
+ }
+ }
+
+ RenderNodeDrawable* childNode = zChildren[drawIndex];
+ SkASSERT(childNode);
+ childNode->forceDraw(canvas);
+
+ drawIndex++;
+ }
+}
+
+/**
+ * @param canvas the destination for the shadow draws
+ * @param shape the shape casting the shadow
+ * @param casterZValue the Z value of the caster RRect
+ * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow
+ * @param draw the function used to draw 'shape'
+ */
+template <typename Shape, typename F>
+static void DrawAmbientShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
+ float ambientAlpha, F&& draw) {
+ if (ambientAlpha <= 0) {
+ return;
+ }
+
+ const float kHeightFactor = 1.f/128.f;
+ const float kGeomFactor = 64;
+
+ float umbraAlpha = 1 / (1 + SkMaxScalar(casterZValue*kHeightFactor, 0));
+ float radius = casterZValue*kHeightFactor*kGeomFactor;
+
+ sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
+ SkBlurMask::ConvertRadiusToSigma(radius), SkBlurMaskFilter::kNone_BlurFlag);
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setMaskFilter(std::move(mf));
+ paint.setARGB(ambientAlpha*umbraAlpha, 0, 0, 0);
+
+ draw(shape, paint);
+}
+
+/**
+ * @param canvas the destination for the shadow draws
+ * @param shape the shape casting the shadow
+ * @param casterZValue the Z value of the caster RRect
+ * @param lightPos the position of the light casting the shadow
+ * @param lightWidth
+ * @param spotAlpha the maximum alpha value to use when drawing the spot shadow
+ * @param draw the function used to draw 'shape'
+ */
+template <typename Shape, typename F>
+static void DrawSpotShadowGeneral(SkCanvas* canvas, const Shape& shape, float casterZValue,
+ float spotAlpha, F&& draw) {
+ if (spotAlpha <= 0) {
+ return;
+ }
+
+ const Vector3 lightPos = SkiaPipeline::getLightCenter();
+ float zRatio = casterZValue / (lightPos.z - casterZValue);
+ // clamp
+ if (zRatio < 0.0f) {
+ zRatio = 0.0f;
+ } else if (zRatio > 0.95f) {
+ zRatio = 0.95f;
+ }
+
+ float blurRadius = SkiaPipeline::getLightRadius()*zRatio;
+
+ SkAutoCanvasRestore acr(canvas, true);
+
+ sk_sp<SkMaskFilter> mf = SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
+ SkBlurMask::ConvertRadiusToSigma(blurRadius), SkBlurMaskFilter::kNone_BlurFlag);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setMaskFilter(std::move(mf));
+ paint.setARGB(spotAlpha, 0, 0, 0);
+
+ // approximate projection by translating and scaling projected offset of bounds center
+ // TODO: compute the actual 2D projection
+ SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+ canvas->scale(scale, scale);
+ SkPoint center = SkPoint::Make(shape.getBounds().centerX(), shape.getBounds().centerY());
+ SkMatrix ctmInverse;
+ if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+ ALOGW("Matrix is degenerate. Will not render shadow!");
+ return;
+ }
+ SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+ ctmInverse.mapPoints(&lightPos2D, 1);
+ canvas->translate(zRatio*(center.fX - lightPos2D.fX), zRatio*(center.fY - lightPos2D.fY));
+
+ draw(shape, paint);
+}
+
+#define MAX_BLUR_RADIUS 16383.75f
+#define MAX_PAD 64
+
+/**
+ * @param casterRect the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha the maximum alpha value to use when drawing the spot shadow
+ * @param casterAlpha the alpha value of the RRect casting the shadow (0.0-1.0 range)
+ * @param casterZValue the Z value of the caster RRect
+ * @param scaleFactor the scale needed to map from src-space to device-space
+ * @param canvas the destination for the shadow draws
+ */
+static void DrawRRectShadows(const SkRect& casterRect, SkScalar casterCornerRadius,
+ SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue,
+ SkScalar scaleFactor, SkCanvas* canvas) {
+ SkASSERT(casterCornerRadius >= 0.0f);
+
+ // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
+ const SkScalar minRadius = 0.5f / scaleFactor;
+
+ const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
+ SkScalarHalf(casterRect.height()));
+ const bool isRect = casterCornerRadius <= minRadius;
+
+ sk_sp<SkShader> edgeShader(SkGaussianEdgeShader::Make());
+
+ if (ambientAlpha > 0.0f) {
+ static const float kHeightFactor = 1.0f / 128.0f;
+ static const float kGeomFactor = 64.0f;
+
+ SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
+ // the device-space radius sent to the blur shader must fit in 14.2 fixed point
+ if (srcSpaceAmbientRadius*scaleFactor > MAX_BLUR_RADIUS) {
+ srcSpaceAmbientRadius = MAX_BLUR_RADIUS/scaleFactor;
+ }
+ const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
+ const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
+
+ // For the ambient rrect, we inset the offset rect by half the srcSpaceAmbientRadius
+ // to get our stroke shape.
+ SkScalar ambientPathOutset = std::max(ambientOffset - srcSpaceAmbientRadius * 0.5f,
+ minRadius);
+
+ SkRRect ambientRRect;
+ const SkRect temp = casterRect.makeOutset(ambientPathOutset, ambientPathOutset);
+ if (isOval) {
+ ambientRRect = SkRRect::MakeOval(temp);
+ } else if (isRect) {
+ ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
+ } else {
+ ambientRRect = SkRRect::MakeRectXY(temp, casterCornerRadius + ambientPathOutset,
+ casterCornerRadius + ambientPathOutset);
+ }
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ // we outset the stroke a little to cover up AA on the interior edge
+ float pad = 0.5f;
+ paint.setStrokeWidth(srcSpaceAmbientRadius + 2.0f * pad);
+ // handle scale of radius and pad due to CTM
+ pad *= scaleFactor;
+ const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
+ SkASSERT(devSpaceAmbientRadius <= MAX_BLUR_RADIUS);
+ SkASSERT(pad < MAX_PAD);
+ // convert devSpaceAmbientRadius to 14.2 fixed point and place in the R & G components
+ // convert pad to 6.2 fixed point and place in the B component
+ uint16_t iDevSpaceAmbientRadius = (uint16_t)(4.0f * devSpaceAmbientRadius);
+ paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, iDevSpaceAmbientRadius >> 8,
+ iDevSpaceAmbientRadius & 0xff, (unsigned char)(4.0f * pad)));
+
+ paint.setShader(edgeShader);
+ canvas->drawRRect(ambientRRect, paint);
+ }
+
+ if (spotAlpha > 0.0f) {
+ const Vector3 lightPos = SkiaPipeline::getLightCenter();
+ float zRatio = casterZValue / (lightPos.z - casterZValue);
+ // clamp
+ if (zRatio < 0.0f) {
+ zRatio = 0.0f;
+ } else if (zRatio > 0.95f) {
+ zRatio = 0.95f;
+ }
+
+ const SkScalar lightWidth = SkiaPipeline::getLightRadius();
+ SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
+ // the device-space radius sent to the blur shader must fit in 14.2 fixed point
+ if (srcSpaceSpotRadius*scaleFactor > MAX_BLUR_RADIUS) {
+ srcSpaceSpotRadius = MAX_BLUR_RADIUS/scaleFactor;
+ }
+
+ SkRRect spotRRect;
+ if (isOval) {
+ spotRRect = SkRRect::MakeOval(casterRect);
+ } else if (isRect) {
+ spotRRect = SkRRect::MakeRectXY(casterRect, minRadius, minRadius);
+ } else {
+ spotRRect = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
+ }
+
+ SkRRect spotShadowRRect;
+ // Compute the scale and translation for the spot shadow.
+ const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+ spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
+
+ SkPoint center = SkPoint::Make(spotShadowRRect.rect().centerX(),
+ spotShadowRRect.rect().centerY());
+ SkMatrix ctmInverse;
+ if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+ ALOGW("Matrix is degenerate. Will not render spot shadow!");
+ return;
+ }
+ SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+ ctmInverse.mapPoints(&lightPos2D, 1);
+ const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+ zRatio*(center.fY - lightPos2D.fY));
+
+ SkAutoCanvasRestore acr(canvas, true);
+
+ // We want to extend the stroked area in so that it meets up with the caster
+ // geometry. The stroked geometry will, by definition already be inset half the
+ // stroke width but we also have to account for the scaling.
+ // We also add 1/2 to cover up AA on the interior edge.
+ SkScalar scaleOffset = (scale - 1.0f) * SkTMax(SkTMax(SkTAbs(casterRect.fLeft),
+ SkTAbs(casterRect.fRight)), SkTMax(SkTAbs(casterRect.fTop),
+ SkTAbs(casterRect.fBottom)));
+ SkScalar insetAmount = spotOffset.length() - (0.5f * srcSpaceSpotRadius) +
+ scaleOffset + 0.5f;
+
+ // Compute area
+ SkScalar strokeWidth = srcSpaceSpotRadius + insetAmount;
+ SkScalar strokedArea = 2.0f*strokeWidth * (spotShadowRRect.width()
+ + spotShadowRRect.height());
+ SkScalar filledArea = (spotShadowRRect.height() + srcSpaceSpotRadius)
+ * (spotShadowRRect.width() + srcSpaceSpotRadius);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ // If the area of the stroked geometry is larger than the fill geometry, just fill it.
+ if (strokedArea > filledArea || casterAlpha < 1.0f || insetAmount < 0.0f) {
+ paint.setStyle(SkPaint::kStrokeAndFill_Style);
+ paint.setStrokeWidth(srcSpaceSpotRadius);
+ } else {
+ // Since we can't have unequal strokes, inset the shadow rect so the inner
+ // and outer edges of the stroke will land where we want.
+ SkRect insetRect = spotShadowRRect.rect().makeInset(insetAmount/2.0f, insetAmount/2.0f);
+ SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount/2.0f,
+ minRadius);
+ spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(strokeWidth);
+ }
+
+ // handle scale of radius and pad due to CTM
+ const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
+ SkASSERT(devSpaceSpotRadius <= MAX_BLUR_RADIUS);
+
+ const SkScalar devSpaceSpotPad = 0;
+ SkASSERT(devSpaceSpotPad < MAX_PAD);
+
+ // convert devSpaceSpotRadius to 14.2 fixed point and place in the R & G
+ // components convert devSpaceSpotPad to 6.2 fixed point and place in the B component
+ uint16_t iDevSpaceSpotRadius = (uint16_t)(4.0f * devSpaceSpotRadius);
+ paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, iDevSpaceSpotRadius >> 8,
+ iDevSpaceSpotRadius & 0xff, (unsigned char)(4.0f * devSpaceSpotPad)));
+ paint.setShader(edgeShader);
+
+ canvas->translate(spotOffset.fX, spotOffset.fY);
+ canvas->drawRRect(spotShadowRRect, paint);
+ }
+}
+
+/**
+ * @param casterRect the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha the maximum alpha value to use when drawing the spot shadow
+ * @param casterZValue the Z value of the caster RRect
+ * @param scaleFactor the scale needed to map from src-space to device-space
+ * @param clipRR the oval or rect with which the drawn roundrect must be intersected
+ * @param canvas the destination for the shadow draws
+ */
+static void DrawRRectShadowsWithClip(const SkRect& casterRect, SkScalar casterCornerRadius,
+ SkScalar ambientAlpha, SkScalar spotAlpha, SkScalar casterZValue, SkScalar scaleFactor,
+ const SkRRect& clipRR, SkCanvas* canvas) {
+ SkASSERT(casterCornerRadius >= 0.0f);
+
+ const bool isOval = casterCornerRadius >= std::max(SkScalarHalf(casterRect.width()),
+ SkScalarHalf(casterRect.height()));
+
+ if (ambientAlpha > 0.0f) {
+ static const float kHeightFactor = 1.0f / 128.0f;
+ static const float kGeomFactor = 64.0f;
+
+ const SkScalar srcSpaceAmbientRadius = casterZValue * kHeightFactor * kGeomFactor;
+ const SkScalar devSpaceAmbientRadius = srcSpaceAmbientRadius * scaleFactor;
+
+ const float umbraAlpha = 1.0f / (1.0f + std::max(casterZValue * kHeightFactor, 0.0f));
+ const SkScalar ambientOffset = srcSpaceAmbientRadius * umbraAlpha;
+
+ const SkRect srcSpaceAmbientRect = casterRect.makeOutset(ambientOffset, ambientOffset);
+ SkRect devSpaceAmbientRect;
+ canvas->getTotalMatrix().mapRect(&devSpaceAmbientRect, srcSpaceAmbientRect);
+
+ SkRRect devSpaceAmbientRRect;
+ if (isOval) {
+ devSpaceAmbientRRect = SkRRect::MakeOval(devSpaceAmbientRect);
+ } else {
+ const SkScalar devSpaceCornerRadius = scaleFactor * (casterCornerRadius + ambientOffset);
+ devSpaceAmbientRRect = SkRRect::MakeRectXY(devSpaceAmbientRect, devSpaceCornerRadius,
+ devSpaceCornerRadius);
+ }
+
+ const SkRect srcSpaceAmbClipRect = clipRR.rect().makeOutset(ambientOffset, ambientOffset);
+ SkRect devSpaceAmbClipRect;
+ canvas->getTotalMatrix().mapRect(&devSpaceAmbClipRect, srcSpaceAmbClipRect);
+ SkRRect devSpaceAmbientClipRR;
+ if (clipRR.isOval()) {
+ devSpaceAmbientClipRR = SkRRect::MakeOval(devSpaceAmbClipRect);
+ } else {
+ SkASSERT(clipRR.isRect());
+ devSpaceAmbientClipRR = SkRRect::MakeRect(devSpaceAmbClipRect);
+ }
+
+ SkRect cover = srcSpaceAmbClipRect;
+ if (!cover.intersect(srcSpaceAmbientRect)) {
+ return;
+ }
+
+ SkPaint paint;
+ paint.setColor(SkColorSetARGB((unsigned char) ambientAlpha, 0, 0, 0));
+ paint.setMaskFilter(SkRRectsGaussianEdgeMaskFilter::Make(devSpaceAmbientRRect,
+ devSpaceAmbientClipRR, devSpaceAmbientRadius));
+ canvas->drawRect(cover, paint);
+ }
+
+ if (spotAlpha > 0.0f) {
+ const Vector3 lightPos = SkiaPipeline::getLightCenter();
+ float zRatio = casterZValue / (lightPos.z - casterZValue);
+ // clamp
+ if (zRatio < 0.0f) {
+ zRatio = 0.0f;
+ } else if (zRatio > 0.95f) {
+ zRatio = 0.95f;
+ }
+
+ const SkScalar lightWidth = SkiaPipeline::getLightRadius();
+ const SkScalar srcSpaceSpotRadius = 2.0f * lightWidth * zRatio;
+ const SkScalar devSpaceSpotRadius = srcSpaceSpotRadius * scaleFactor;
+
+ // Compute the scale and translation for the spot shadow.
+ const SkScalar scale = lightPos.z / (lightPos.z - casterZValue);
+ const SkMatrix spotMatrix = SkMatrix::MakeScale(scale, scale);
+
+ SkRect srcSpaceScaledRect = casterRect;
+ spotMatrix.mapRect(&srcSpaceScaledRect);
+ srcSpaceScaledRect.outset(SkScalarHalf(srcSpaceSpotRadius),
+ SkScalarHalf(srcSpaceSpotRadius));
+
+ SkRRect srcSpaceSpotRRect;
+ if (isOval) {
+ srcSpaceSpotRRect = SkRRect::MakeOval(srcSpaceScaledRect);
+ } else {
+ srcSpaceSpotRRect = SkRRect::MakeRectXY(srcSpaceScaledRect, casterCornerRadius * scale,
+ casterCornerRadius * scale);
+ }
+
+ SkPoint center = SkPoint::Make(srcSpaceSpotRRect.rect().centerX(),
+ srcSpaceSpotRRect.rect().centerY());
+ SkMatrix ctmInverse;
+ if (!canvas->getTotalMatrix().invert(&ctmInverse)) {
+ ALOGW("Matrix is degenerate. Will not render spot shadow!");
+ return;
+ }
+ SkPoint lightPos2D = SkPoint::Make(lightPos.x, lightPos.y);
+ ctmInverse.mapPoints(&lightPos2D, 1);
+ const SkPoint spotOffset = SkPoint::Make(zRatio*(center.fX - lightPos2D.fX),
+ zRatio*(center.fY - lightPos2D.fY));
+
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->translate(spotOffset.fX, spotOffset.fY);
+
+ SkRect devSpaceScaledRect;
+ canvas->getTotalMatrix().mapRect(&devSpaceScaledRect, srcSpaceScaledRect);
+
+ SkRRect devSpaceSpotRRect;
+ if (isOval) {
+ devSpaceSpotRRect = SkRRect::MakeOval(devSpaceScaledRect);
+ } else {
+ const SkScalar devSpaceScaledCornerRadius = casterCornerRadius * scale * scaleFactor;
+ devSpaceSpotRRect = SkRRect::MakeRectXY(devSpaceScaledRect, devSpaceScaledCornerRadius,
+ devSpaceScaledCornerRadius);
+ }
+
+ SkPaint paint;
+ paint.setColor(SkColorSetARGB((unsigned char) spotAlpha, 0, 0, 0));
+
+ SkRect srcSpaceScaledClipRect = clipRR.rect();
+ spotMatrix.mapRect(&srcSpaceScaledClipRect);
+ srcSpaceScaledClipRect.outset(SkScalarHalf(srcSpaceSpotRadius),
+ SkScalarHalf(srcSpaceSpotRadius));
+
+ SkRect devSpaceScaledClipRect;
+ canvas->getTotalMatrix().mapRect(&devSpaceScaledClipRect, srcSpaceScaledClipRect);
+ SkRRect devSpaceSpotClipRR;
+ if (clipRR.isOval()) {
+ devSpaceSpotClipRR = SkRRect::MakeOval(devSpaceScaledClipRect);
+ } else {
+ SkASSERT(clipRR.isRect());
+ devSpaceSpotClipRR = SkRRect::MakeRect(devSpaceScaledClipRect);
+ }
+
+ paint.setMaskFilter(SkRRectsGaussianEdgeMaskFilter::Make(devSpaceSpotRRect,
+ devSpaceSpotClipRR, devSpaceSpotRadius));
+
+ SkRect cover = srcSpaceScaledClipRect;
+ if (!cover.intersect(srcSpaceSpotRRect.rect())) {
+ return;
+ }
+
+ canvas->drawRect(cover, paint);
+ }
+}
+
+/**
+ * @param casterRect the rectangle bounds of the RRect casting the shadow
+ * @param casterCornerRadius the x&y radius for all the corners of the RRect casting the shadow
+ * @param casterClipRect a rectangular clip that must be intersected with the
+ * shadow-casting RRect prior to casting the shadow
+ * @param revealClip a circular clip that must be interested with the castClipRect
+ * and the shadow-casting rect prior to casting the shadow
+ * @param ambientAlpha the maximum alpha value to use when drawing the ambient shadow
+ * @param spotAlpha the maximum alpha value to use when drawing the spot shadow
+ * @param casterAlpha the alpha value of the RRect casting the shadow (0.0-1.0 range)
+ * @param casterZValue the Z value of the caster RRect
+ * @param canvas the destination for the shadow draws
+ *
+ * We have special cases for 4 round rect shadow draws:
+ * 1) a RRect clipped by a reveal animation
+ * 2) a RRect clipped by a rectangle
+ * 3) an unclipped RRect with non-uniform scale
+ * 4) an unclipped RRect with uniform scale
+ * 1,2 and 4 require that the scale is uniform.
+ * 1 and 2 require that rects stay rects.
+ */
+static bool DrawShadowsAsRRects(const SkRect& casterRect, SkScalar casterCornerRadius,
+ const SkRect& casterClipRect, const RevealClip& revealClip, SkScalar ambientAlpha,
+ SkScalar spotAlpha, SkScalar casterAlpha, SkScalar casterZValue, SkCanvas* canvas) {
+ SkScalar scaleFactors[2];
+ if (!canvas->getTotalMatrix().getMinMaxScales(scaleFactors)) {
+ ALOGW("Matrix is degenerate. Will not render shadow!");
+ return false;
+ }
+
+ // The casterClipRect will be empty when bounds clipping is disabled
+ bool casterIsClippedByRect = !casterClipRect.isEmpty();
+ bool uniformScale = scaleFactors[0] == scaleFactors[1];
+
+ if (revealClip.willClip()) {
+ if (casterIsClippedByRect || !uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
+ return false; // Fall back to the slow path since PathOps are required
+ }
+
+ const float revealRadius = revealClip.getRadius();
+ SkRect revealClipRect = SkRect::MakeLTRB(revealClip.getX()-revealRadius,
+ revealClip.getY()-revealRadius, revealClip.getX()+revealRadius,
+ revealClip.getY()+revealRadius);
+ SkRRect revealClipRR = SkRRect::MakeOval(revealClipRect);
+
+ DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
+ casterZValue, scaleFactors[0], revealClipRR, canvas);
+ return true;
+ }
+
+ if (casterIsClippedByRect) {
+ if (!uniformScale || !canvas->getTotalMatrix().rectStaysRect()) {
+ return false; // Fall back to the slow path since PathOps are required
+ }
+
+ SkRRect casterClipRR = SkRRect::MakeRect(casterClipRect);
+
+ DrawRRectShadowsWithClip(casterRect, casterCornerRadius, ambientAlpha, spotAlpha,
+ casterZValue, scaleFactors[0], casterClipRR, canvas);
+ return true;
+ }
+
+ // The fast path needs uniform scale
+ if (!uniformScale) {
+ SkRRect casterRR = SkRRect::MakeRectXY(casterRect, casterCornerRadius, casterCornerRadius);
+ DrawAmbientShadowGeneral(canvas, casterRR, casterZValue, ambientAlpha,
+ [&](const SkRRect& rrect, const SkPaint& paint) {
+ canvas->drawRRect(rrect, paint);
+ });
+ DrawSpotShadowGeneral(canvas, casterRR, casterZValue, spotAlpha,
+ [&](const SkRRect& rrect, const SkPaint& paint) {
+ canvas->drawRRect(rrect, paint);
+ });
+ return true;
+ }
+
+ DrawRRectShadows(casterRect, casterCornerRadius, ambientAlpha, spotAlpha, casterAlpha,
+ casterZValue, scaleFactors[0], canvas);
+ return true;
+}
+
+// copied from FrameBuilder::deferShadow
+void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster) {
+ const RenderProperties& casterProperties = caster->getNodeProperties();
+
+ if (casterProperties.getAlpha() <= 0.0f
+ || casterProperties.getOutline().getAlpha() <= 0.0f
+ || !casterProperties.getOutline().getPath()
+ || casterProperties.getScaleX() == 0
+ || casterProperties.getScaleY() == 0) {
+ // no shadow to draw
+ return;
+ }
+
+ const SkScalar casterAlpha = casterProperties.getAlpha()
+ * casterProperties.getOutline().getAlpha();
+ if (casterAlpha <= 0.0f) {
+ return;
+ }
+
+ float ambientAlpha = SkiaPipeline::getAmbientShadowAlpha()*casterAlpha;
+ float spotAlpha = SkiaPipeline::getSpotShadowAlpha()*casterAlpha;
+ const float casterZValue = casterProperties.getZ();
+
+ const RevealClip& revealClip = casterProperties.getRevealClip();
+ const SkPath* revealClipPath = revealClip.getPath();
+ if (revealClipPath && revealClipPath->isEmpty()) {
+ // An empty reveal clip means nothing is drawn
+ return;
+ }
+
+ bool clippedToBounds = casterProperties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS;
+
+ SkRect casterClipRect = SkRect::MakeEmpty();
+ if (clippedToBounds) {
+ Rect clipBounds;
+ casterProperties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
+ casterClipRect = clipBounds.toSkRect();
+ if (casterClipRect.isEmpty()) {
+ // An empty clip rect means nothing is drawn
+ return;
+ }
+ }
+
+ SkAutoCanvasRestore acr(canvas, true);
+
+ SkMatrix shadowMatrix;
+ mat4 hwuiMatrix(caster->getRecordedMatrix());
+ // TODO we don't pass the optional boolean to treat it as a 4x4 matrix
+ caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
+ hwuiMatrix.copyTo(shadowMatrix);
+ canvas->concat(shadowMatrix);
+
+ const Outline& casterOutline = casterProperties.getOutline();
+ Rect possibleRect;
+ float radius;
+ if (casterOutline.getAsRoundRect(&possibleRect, &radius)) {
+ if (DrawShadowsAsRRects(possibleRect.toSkRect(), radius, casterClipRect, revealClip,
+ ambientAlpha, spotAlpha, casterAlpha, casterZValue, canvas)) {
+ return;
+ }
+ }
+
+ // Hard cases and calls to general shadow code
+ const SkPath* casterOutlinePath = casterProperties.getOutline().getPath();
+
+ // holds temporary SkPath to store the result of intersections
+ SkPath tmpPath;
+ const SkPath* casterPath = casterOutlinePath;
+
+ // TODO: In to following course of code that calculates the final shape, is there an optimal
+ // of doing the Op calculations?
+ // intersect the shadow-casting path with the reveal, if present
+ if (revealClipPath) {
+ Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath);
+ casterPath = &tmpPath;
+ }
+
+ // intersect the shadow-casting path with the clipBounds, if present
+ if (clippedToBounds) {
+ SkPath clipBoundsPath;
+ clipBoundsPath.addRect(casterClipRect);
+ Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath);
+ casterPath = &tmpPath;
+ }
+
+ DrawAmbientShadowGeneral(canvas, *casterPath, casterZValue, ambientAlpha,
+ [&](const SkPath& path, const SkPaint& paint) {
+ canvas->drawPath(path, paint);
+ });
+
+ DrawSpotShadowGeneral(canvas, *casterPath, casterZValue, spotAlpha,
+ [&](const SkPath& path, const SkPaint& paint) {
+ canvas->drawPath(path, paint);
+ });
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
new file mode 100644
index 000000000000..9f00d23ae985
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "RenderNodeDrawable.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+#include <utils/FatVector.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaDisplayList;
+class EndReorderBarrierDrawable;
+
+/**
+ * StartReorderBarrierDrawable and EndReorderBarrierDrawable work together to define
+ * a sub-list in a display list that need to be drawn out-of-order sorted instead by render
+ * node Z index.
+ * StartReorderBarrierDrawable will sort the entire range and it will draw
+ * render nodes in the range with negative Z index.
+ */
+class StartReorderBarrierDrawable : public SkDrawable {
+public:
+ explicit StartReorderBarrierDrawable(SkiaDisplayList* data);
+
+protected:
+ virtual SkRect onGetBounds() override {
+ return SkRect::MakeLargest();
+ }
+ virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+ int mEndChildIndex;
+ int mBeginChildIndex;
+ FatVector<RenderNodeDrawable*, 16> mChildren;
+ SkiaDisplayList* mDisplayList;
+
+ friend class EndReorderBarrierDrawable;
+};
+
+/**
+ * See StartReorderBarrierDrawable.
+ * EndReorderBarrierDrawable relies on StartReorderBarrierDrawable to host and sort the render
+ * nodes by Z index. When EndReorderBarrierDrawable is drawn it will draw all render nodes in the
+ * range with positive Z index. It is also responsible for drawing shadows for the nodes
+ * corresponding to their z-index.
+ */
+class EndReorderBarrierDrawable : public SkDrawable {
+public:
+ explicit EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier);
+protected:
+ virtual SkRect onGetBounds() override {
+ return SkRect::MakeLargest();
+ }
+ virtual void onDraw(SkCanvas* canvas) override;
+private:
+ void drawShadow(SkCanvas* canvas, RenderNodeDrawable* caster);
+ StartReorderBarrierDrawable* mStartBarrier;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
new file mode 100644
index 000000000000..9db8cd3fe2e2
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaDisplayList.h"
+
+#include "renderthread/CanvasContext.h"
+#include "VectorDrawable.h"
+
+#include <SkImagePriv.h>
+
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+SkiaDisplayList::SkiaDisplayList(SkRect bounds) : mDrawable(SkLiteDL::New(bounds)) {
+ SkASSERT(projectionReceiveIndex == -1);
+}
+
+void SkiaDisplayList::syncContents() {
+ for (auto& functor : mChildFunctors) {
+ functor.syncFunctor();
+ }
+ for (auto& vectorDrawable : mVectorDrawables) {
+ vectorDrawable->syncProperties();
+ }
+}
+
+bool SkiaDisplayList::reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) {
+ reset(SkRect::MakeEmpty());
+ node->attachAvailableList(this);
+ return true;
+}
+
+void SkiaDisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) {
+ for (auto& child : mChildNodes) {
+ updateFn(child.getRenderNode());
+ }
+}
+
+bool SkiaDisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
+ std::function<void(RenderNode*, TreeInfo&, bool)> childFn) {
+ // If the prepare tree is triggered by the UI thread and no previous call to
+ // pinImages has failed then we must pin all mutable images in the GPU cache
+ // until the next UI thread draw.
+ if (info.prepareTextures && !info.canvasContext.pinImages(mMutableImages)) {
+ // In the event that pinning failed we prevent future pinImage calls for the
+ // remainder of this tree traversal and also unpin any currently pinned images
+ // to free up GPU resources.
+ info.prepareTextures = false;
+ info.canvasContext.unpinImages();
+ }
+
+ bool hasBackwardProjectedNodesHere = false;
+ bool hasBackwardProjectedNodesSubtree= false;
+
+ for (auto& child : mChildNodes) {
+ hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards();
+ RenderNode* childNode = child.getRenderNode();
+ Matrix4 mat4(child.getRecordedMatrix());
+ info.damageAccumulator->pushTransform(&mat4);
+ // TODO: a layer is needed if the canvas is rotated or has a non-rect clip
+ info.hasBackwardProjectedNodes = false;
+ childFn(childNode, info, functorsNeedLayer);
+ hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes;
+ info.damageAccumulator->popTransform();
+ }
+
+ //The purpose of next block of code is to reset projected display list if there are no
+ //backward projected nodes. This speeds up drawing, by avoiding an extra walk of the tree
+ if (mProjectionReceiver) {
+ mProjectionReceiver->setProjectedDisplayList(hasBackwardProjectedNodesSubtree ? this : nullptr);
+ info.hasBackwardProjectedNodes = hasBackwardProjectedNodesHere;
+ } else {
+ info.hasBackwardProjectedNodes = hasBackwardProjectedNodesSubtree
+ || hasBackwardProjectedNodesHere;
+ }
+
+ bool isDirty = false;
+ for (auto& vectorDrawable : mVectorDrawables) {
+ // If any vector drawable in the display list needs update, damage the node.
+ if (vectorDrawable->isDirty()) {
+ isDirty = true;
+ }
+ vectorDrawable->setPropertyChangeWillBeConsumed(true);
+ }
+ return isDirty;
+}
+
+void SkiaDisplayList::reset(SkRect bounds) {
+ mProjectionReceiver = nullptr;
+
+ mDrawable->reset(bounds);
+
+ mMutableImages.clear();
+ mVectorDrawables.clear();
+ mChildFunctors.clear();
+ mChildNodes.clear();
+
+ projectionReceiveIndex = -1;
+ allocator.~LinearAllocator();
+ new (&allocator) LinearAllocator();
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
new file mode 100644
index 000000000000..ff86fd18a9a4
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "DisplayList.h"
+#include "GLFunctorDrawable.h"
+#include "RenderNodeDrawable.h"
+
+#include <deque>
+#include <SkLiteDL.h>
+#include <SkPictureRecorder.h>
+
+namespace android {
+namespace uirenderer {
+
+class Outline;
+
+namespace skiapipeline {
+
+/**
+ * This class is intended to be self contained, but still subclasses from
+ * DisplayList to make it easier to support switching between the two at
+ * runtime. The downside of this inheritance is that we pay for the overhead
+ * of the parent class construction/destruction without any real benefit.
+ */
+class SkiaDisplayList : public DisplayList {
+public:
+ SkiaDisplayList(SkRect bounds);
+ virtual ~SkiaDisplayList() {
+ /* Given that we are using a LinearStdAllocator to store some of the
+ * SkDrawable contents we must ensure that any other object that is
+ * holding a reference to those drawables is destroyed prior to their
+ * deletion.
+ */
+ mDrawable.reset();
+ }
+
+ /**
+ * This resets the DisplayList so that it behaves as if the object were newly
+ * constructed with the provided bounds. The reuse avoids any overhead
+ * associated with destroying the SkLiteDL as well as the deques and vectors.
+ */
+ void reset(SkRect bounds);
+
+ /**
+ * Use the linear allocator to create any SkDrawables needed by the display
+ * list. This could be dangerous as these objects are ref-counted, so we
+ * need to monitor that they don't extend beyond the lifetime of the class
+ * that creates them.
+ */
+ template<class T, typename... Params>
+ SkDrawable* allocateDrawable(Params&&... params) {
+ return allocator.create<T>(std::forward<Params>(params)...);
+ }
+
+ bool isSkiaDL() const override { return true; }
+
+ /**
+ * Returns true if the DisplayList does not have any recorded content
+ */
+ bool isEmpty() const override { return mDrawable->empty(); }
+
+ /**
+ * Returns true if this list directly contains a GLFunctor drawing command.
+ */
+ bool hasFunctor() const override { return !mChildFunctors.empty(); }
+
+ /**
+ * Returns true if this list directly contains a VectorDrawable drawing command.
+ */
+ bool hasVectorDrawables() const override { return !mVectorDrawables.empty(); }
+
+ /**
+ * Attempts to reset and reuse this DisplayList.
+ *
+ * @return true if the displayList will be reused and therefore should not be deleted
+ */
+ bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) override;
+
+ /**
+ * ONLY to be called by RenderNode::syncDisplayList so that we can notify any
+ * contained VectorDrawables or GLFunctors to sync their state.
+ *
+ * NOTE: This function can be folded into RenderNode when we no longer need
+ * to subclass from DisplayList
+ */
+ void syncContents() override;
+
+ /**
+ * ONLY to be called by RenderNode::prepareTree in order to prepare this
+ * list while the UI thread is blocked. Here we can upload mutable bitmaps
+ * and notify our parent if any of our content has been invalidated and in
+ * need of a redraw. If the renderNode has any children then they are also
+ * call in order to prepare them.
+ *
+ * @return true if any content change requires the node to be invalidated
+ *
+ * NOTE: This function can be folded into RenderNode when we no longer need
+ * to subclass from DisplayList
+ */
+
+ bool prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
+ std::function<void(RenderNode*, TreeInfo&, bool)> childFn) override;
+
+ /**
+ * Calls the provided function once for each child of this DisplayList
+ */
+ void updateChildren(std::function<void(RenderNode*)> updateFn) override;
+
+ /**
+ * Returns true if there is a child render node that is a projection receiver.
+ */
+ inline bool containsProjectionReceiver() const { return mProjectionReceiver; }
+
+ /**
+ * We use std::deque here because (1) we need to iterate through these
+ * elements and (2) mDrawable holds pointers to the elements, so they cannot
+ * relocate.
+ */
+ std::deque<RenderNodeDrawable> mChildNodes;
+ std::deque<GLFunctorDrawable> mChildFunctors;
+ std::vector<SkImage*> mMutableImages;
+ std::vector<VectorDrawableRoot*> mVectorDrawables;
+ sk_sp<SkLiteDL> mDrawable;
+
+ //mProjectionReceiver points to a child node (stored in mChildNodes) that is as a projection
+ //receiver. It is set at record time and used at both prepare and draw tree traversals to
+ //make sure backward projected nodes are found and drawn immediately after mProjectionReceiver.
+ RenderNodeDrawable* mProjectionReceiver = nullptr;
+
+ //mProjectedOutline is valid only when render node tree is traversed during the draw pass.
+ //Render nodes that have a child receiver node, will store a pointer to their outline in
+ //mProjectedOutline. Child receiver node will apply the clip before any backward projected
+ //node is drawn.
+ const Outline* mProjectedOutline = nullptr;
+
+ //mProjectedReceiverParentMatrix is valid when render node tree is traversed during the draw
+ //pass. Render nodes that have a child receiver node, will store their matrix in
+ //mProjectedReceiverParentMatrix. Child receiver node will set the matrix and then clip with the
+ //outline of their parent.
+ SkMatrix mProjectedReceiverParentMatrix;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaLayer.h b/libs/hwui/pipeline/skia/SkiaLayer.h
new file mode 100644
index 000000000000..904d57e073ca
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaLayer.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <SkSurface.h>
+#include "Matrix.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/**
+ * An offscreen rendering target used to contain the contents a RenderNode.
+ */
+struct SkiaLayer
+{
+ sk_sp<SkSurface> layerSurface;
+ Matrix4 inverseTransformInWindow;
+ bool hasRenderedSinceRepaint = false;
+};
+
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
new file mode 100644
index 000000000000..65a1dc38ab8e
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaOpenGLPipeline.h"
+
+#include "DeferredLayerUpdater.h"
+#include "GlLayer.h"
+#include "LayerDrawable.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/Frame.h"
+#include "renderstate/RenderState.h"
+#include "SkiaPipeline.h"
+#include "SkiaProfileRenderer.h"
+#include "utils/TraceUtils.h"
+
+#include <android/native_window.h>
+#include <cutils/properties.h>
+#include <strings.h>
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread)
+ : SkiaPipeline(thread)
+ , mEglManager(thread.eglManager()) {
+}
+
+MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
+ // TODO: Figure out why this workaround is needed, see b/13913604
+ // In the meantime this matches the behavior of GLRenderer, so it is not a regression
+ EGLint error = 0;
+ if (!mEglManager.makeCurrent(mEglSurface, &error)) {
+ return MakeCurrentResult::AlreadyCurrent;
+ }
+ return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;
+}
+
+Frame SkiaOpenGLPipeline::getFrame() {
+ LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+ "drawRenderNode called on a context with no surface!");
+ return mEglManager.beginFrame(mEglSurface);
+}
+
+bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty,
+ const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector<sp<RenderNode>>& renderNodes,
+ FrameInfoVisualizer* profiler) {
+
+ mEglManager.damageFrame(frame, dirty);
+
+ // setup surface for fbo0
+ GrBackendRenderTargetDesc renderTargetDesc;
+ renderTargetDesc.fWidth = frame.width();
+ renderTargetDesc.fHeight = frame.height();
+ renderTargetDesc.fConfig = kRGBA_8888_GrPixelConfig;
+ renderTargetDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+ renderTargetDesc.fSampleCnt = 0;
+ renderTargetDesc.fStencilBits = STENCIL_BUFFER_SIZE;
+ renderTargetDesc.fRenderTargetHandle = 0;
+
+ SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+
+ SkASSERT(mRenderThread.getGrContext() != nullptr);
+ sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
+ mRenderThread.getGrContext(), renderTargetDesc, &props));
+
+ SkiaPipeline::updateLighting(lightGeometry, lightInfo);
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ layerUpdateQueue->clear();
+
+ // Draw visual debugging features
+ if (CC_UNLIKELY(Properties::showDirtyRegions
+ || ProfileType::None != Properties::getProfileType())) {
+ SkCanvas* profileCanvas = surface->getCanvas();
+ SkiaProfileRenderer profileRenderer(profileCanvas);
+ profiler->draw(profileRenderer);
+ profileCanvas->flush();
+ }
+
+ // Log memory statistics
+ if (CC_UNLIKELY(Properties::debugLevel != kDebugDisabled)) {
+ dumpResourceCacheUsage();
+ }
+
+ return true;
+}
+
+bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) {
+
+ GL_CHECKPOINT(LOW);
+
+ // Even if we decided to cancel the frame, from the perspective of jank
+ // metrics the frame was swapped at this point
+ currentFrameInfo->markSwapBuffers();
+
+ *requireSwap = drew || mEglManager.damageRequiresSwap();
+
+ if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
+ return false;
+ }
+
+ return *requireSwap;
+}
+
+bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
+ if (!mRenderThread.getGrContext()) {
+ return false;
+ }
+
+ deferredLayer->apply();
+
+ SkCanvas canvas(*bitmap);
+ Layer* layer = deferredLayer->backingLayer();
+ return LayerDrawable::DrawLayer(mRenderThread.getGrContext(), &canvas, layer);
+}
+
+DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
+ mEglManager.initialize();
+ GlLayer* layer = new GlLayer(mRenderThread.renderState(), 0, 0);
+ layer->generateTexture();
+ return new DeferredLayerUpdater(layer);
+}
+
+void SkiaOpenGLPipeline::onStop() {
+ if (mEglManager.isCurrent(mEglSurface)) {
+ mEglManager.makeCurrent(EGL_NO_SURFACE);
+ }
+}
+
+bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mEglManager.destroySurface(mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+ }
+
+ if (surface) {
+ mEglSurface = mEglManager.createSurface(surface);
+ }
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
+ mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
+ return true;
+ }
+
+ return false;
+}
+
+bool SkiaOpenGLPipeline::isSurfaceReady() {
+ return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);
+}
+
+bool SkiaOpenGLPipeline::isContextReady() {
+ return CC_LIKELY(mEglManager.hasEglContext());
+}
+
+void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (thread.eglManager().hasEglContext()) {
+ mode = DrawGlInfo::kModeProcess;
+ }
+
+ (*functor)(mode, nullptr);
+
+ // If there's no context we don't need to reset as there's no gl state to save/restore
+ if (mode != DrawGlInfo::kModeProcessNoContext) {
+ thread.getGrContext()->resetContext();
+ }
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
new file mode 100644
index 000000000000..36685ddb17a7
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SkiaPipeline.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaOpenGLPipeline : public SkiaPipeline {
+public:
+ SkiaOpenGLPipeline(renderthread::RenderThread& thread);
+ virtual ~SkiaOpenGLPipeline() {}
+
+ renderthread::MakeCurrentResult makeCurrent() override;
+ renderthread::Frame getFrame() override;
+ bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector< sp<RenderNode> >& renderNodes,
+ FrameInfoVisualizer* profiler) override;
+ bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
+ FrameInfo* currentFrameInfo, bool* requireSwap) override;
+ bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
+ DeferredLayerUpdater* createTextureLayer() override;
+ bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override;
+ void onStop() override;
+ bool isSurfaceReady() override;
+ bool isContextReady() override;
+
+ static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
+
+private:
+ renderthread::EglManager& mEglManager;
+ EGLSurface mEglSurface = EGL_NO_SURFACE;
+ bool mBufferPreserved = false;
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
new file mode 100644
index 000000000000..a18d26471a29
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaOpenGLReadback.h"
+
+#include "Matrix.h"
+#include "Properties.h"
+#include <SkCanvas.h>
+#include <SkSurface.h>
+#include <gl/GrGLInterface.h>
+#include <gl/GrGLTypes.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
+ int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) {
+
+ GLuint sourceTexId;
+ glGenTextures(1, &sourceTexId);
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
+
+ sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+ LOG_ALWAYS_FATAL_IF(!glInterface.get());
+ grContext.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend,
+ (GrBackendContext)glInterface.get()));
+ } else {
+ grContext->resetContext();
+ }
+
+ GrGLTextureInfo externalTexture;
+ externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
+ externalTexture.fID = sourceTexId;
+
+ GrBackendTextureDesc textureDescription;
+ textureDescription.fWidth = imgWidth;
+ textureDescription.fHeight = imgHeight;
+ textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
+ textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
+ textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
+
+ CopyResult copyResult = CopyResult::UnknownError;
+ sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), textureDescription));
+ if (image) {
+ SkAutoLockPixels alp(*bitmap);
+
+ // convert to Skia data structures
+ const SkRect bufferRect = SkRect::MakeIWH(imgWidth, imgHeight);
+ SkRect skiaSrcRect = srcRect.toSkRect();
+ SkMatrix textureMatrix;
+ imgTransform.copyTo(textureMatrix);
+
+ // remove the y-flip applied to the matrix so that we can scale the srcRect.
+ // This flip is not needed as we specify the origin of the texture when we
+ // wrap it as an SkImage.
+ SkMatrix yFlip = SkMatrix::MakeScale(1, -1);
+ yFlip.postTranslate(0,1);
+ textureMatrix.preConcat(yFlip);
+
+ // copy the entire src if the rect is empty
+ if (skiaSrcRect.isEmpty()) {
+ skiaSrcRect = bufferRect;
+ }
+
+ // since the y-flip has been removed we can simply scale & translate
+ // the source rectangle
+ textureMatrix.mapRect(&skiaSrcRect);
+
+ if (skiaSrcRect.intersect(bufferRect)) {
+ SkPoint srcOrigin = SkPoint::Make(skiaSrcRect.fLeft, skiaSrcRect.fTop);
+
+ // if we need to scale the result we must render to an offscreen buffer
+ if (bitmap->width() != skiaSrcRect.width()
+ || bitmap->height() != skiaSrcRect.height()) {
+ sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget(
+ grContext.get(), SkBudgeted::kYes, bitmap->info());
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect,
+ SkRect::MakeWH(bitmap->width(), bitmap->height()), &paint);
+ image = scaledSurface->makeImageSnapshot();
+ srcOrigin.set(0,0);
+ }
+
+ if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
+ srcOrigin.fX, srcOrigin.fY)) {
+ copyResult = CopyResult::Success;
+ }
+ }
+ }
+
+ // make sure that we have deleted the texture (in the SkImage) before we
+ // destroy the EGLImage that it was created from
+ image.reset();
+ return copyResult;
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
new file mode 100644
index 000000000000..d914409628d0
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "OpenGLReadback.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaOpenGLReadback : public OpenGLReadback {
+public:
+ SkiaOpenGLReadback(renderthread::RenderThread& thread) : OpenGLReadback(thread) {}
+protected:
+ virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
+ int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override;
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
new file mode 100644
index 000000000000..430d6bea70c1
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaPipeline.h"
+
+#include "utils/TraceUtils.h"
+#include <SkImageEncoder.h>
+#include <SkImagePriv.h>
+#include <SkOSFile.h>
+#include <SkOverdrawCanvas.h>
+#include <SkOverdrawColorFilter.h>
+#include <SkPicture.h>
+#include <SkPictureRecorder.h>
+#include <SkPixelSerializer.h>
+#include <SkStream.h>
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+float SkiaPipeline::mLightRadius = 0;
+uint8_t SkiaPipeline::mAmbientShadowAlpha = 0;
+uint8_t SkiaPipeline::mSpotShadowAlpha = 0;
+
+Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN};
+
+SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { }
+
+TaskManager* SkiaPipeline::getTaskManager() {
+ return &mTaskManager;
+}
+
+void SkiaPipeline::onDestroyHardwareResources() {
+ // No need to flush the caches here. There is a timer
+ // which will flush temporary resources over time.
+}
+
+bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) {
+ for (SkImage* image : mutableImages) {
+ if (SkImage_pinAsTexture(image, mRenderThread.getGrContext())) {
+ mPinnedImages.emplace_back(sk_ref_sp(image));
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+void SkiaPipeline::unpinImages() {
+ for (auto& image : mPinnedImages) {
+ SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext());
+ }
+ mPinnedImages.clear();
+}
+
+void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo) {
+ updateLighting(lightGeometry, lightInfo);
+ ATRACE_NAME("draw layers");
+ renderLayersImpl(*layerUpdateQueue, opaque);
+ layerUpdateQueue->clear();
+}
+
+void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
+ // Render all layers that need to be updated, in order.
+ for (size_t i = 0; i < layers.entries().size(); i++) {
+ RenderNode* layerNode = layers.entries()[i].renderNode;
+ // only schedule repaint if node still on layer - possible it may have been
+ // removed during a dropped frame, but layers may still remain scheduled so
+ // as not to lose info on what portion is damaged
+ if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) {
+ SkASSERT(layerNode->getLayerSurface());
+ SkASSERT(layerNode->getDisplayList()->isSkiaDL());
+ SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList();
+ if (!displayList || displayList->isEmpty()) {
+ SkDEBUGF(("%p drawLayers(%s) : missing drawable", this, layerNode->getName()));
+ return;
+ }
+
+ const Rect& layerDamage = layers.entries()[i].damage;
+
+ SkCanvas* layerCanvas = layerNode->getLayerSurface()->getCanvas();
+
+ int saveCount = layerCanvas->save();
+ SkASSERT(saveCount == 1);
+
+ layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
+
+ auto savedLightCenter = mLightCenter;
+ // map current light center into RenderNode's coordinate space
+ layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(mLightCenter);
+
+ const RenderProperties& properties = layerNode->properties();
+ const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
+ if (properties.getClipToBounds() && layerCanvas->quickReject(bounds)) {
+ return;
+ }
+
+ layerNode->getSkiaLayer()->hasRenderedSinceRepaint = false;
+ layerCanvas->clear(SK_ColorTRANSPARENT);
+
+ RenderNodeDrawable root(layerNode, layerCanvas, false);
+ root.forceDraw(layerCanvas);
+ layerCanvas->restoreToCount(saveCount);
+ layerCanvas->flush();
+ mLightCenter = savedLightCenter;
+ }
+ }
+}
+
+bool SkiaPipeline::createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) {
+ SkSurface* layer = node->getLayerSurface();
+ if (!layer || layer->width() != node->getWidth() || layer->height() != node->getHeight()) {
+ SkImageInfo info = SkImageInfo::MakeN32Premul(node->getWidth(), node->getHeight());
+ SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+ SkASSERT(mRenderThread.getGrContext() != nullptr);
+ node->setLayerSurface(
+ SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
+ info, 0, &props));
+ if (node->getLayerSurface()) {
+ // update the transform in window of the layer to reset its origin wrt light source
+ // position
+ Matrix4 windowTransform;
+ damageAccumulator.computeCurrentTransform(&windowTransform);
+ node->getSkiaLayer()->inverseTransformInWindow = windowTransform;
+ }
+ return true;
+ }
+ return false;
+}
+
+void SkiaPipeline::destroyLayer(RenderNode* node) {
+ node->setLayerSurface(nullptr);
+}
+
+void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
+ GrContext* context = thread.getGrContext();
+ if (context) {
+ ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
+ SkBitmap skiaBitmap;
+ bitmap->getSkBitmap(&skiaBitmap);
+ sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
+ SkImage_pinAsTexture(image.get(), context);
+ SkImage_unpinAsTexture(image.get(), context);
+ }
+}
+
+// Encodes to PNG, unless there is already encoded data, in which case that gets
+// used.
+class PngPixelSerializer : public SkPixelSerializer {
+public:
+ bool onUseEncodedData(const void*, size_t) override { return true; }
+ SkData* onEncode(const SkPixmap& pixmap) override {
+ SkDynamicMemoryWStream buf;
+ return SkEncodeImage(&buf, pixmap, SkEncodedImageFormat::kPNG, 100)
+ ? buf.detachAsData().release()
+ : nullptr;
+ }
+};
+
+void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
+ const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
+ sk_sp<SkSurface> surface) {
+
+ // draw all layers up front
+ renderLayersImpl(layers, opaque);
+
+ // initialize the canvas for the current frame
+ SkCanvas* canvas = surface->getCanvas();
+
+ std::unique_ptr<SkPictureRecorder> recorder;
+ bool recordingPicture = false;
+ char prop[PROPERTY_VALUE_MAX];
+ if (skpCaptureEnabled()) {
+ property_get("debug.hwui.capture_frame_as_skp", prop, "0");
+ recordingPicture = prop[0] != '0' && !sk_exists(prop);
+ if (recordingPicture) {
+ recorder.reset(new SkPictureRecorder());
+ canvas = recorder->beginRecording(surface->width(), surface->height(),
+ nullptr, SkPictureRecorder::kPlaybackDrawPicture_RecordFlag);
+ }
+ }
+
+ renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas);
+
+ if (skpCaptureEnabled() && recordingPicture) {
+ sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
+ if (picture->approximateOpCount() > 0) {
+ SkFILEWStream stream(prop);
+ if (stream.isValid()) {
+ PngPixelSerializer serializer;
+ picture->serialize(&stream, &serializer);
+ stream.flush();
+ SkDebugf("Captured Drawing Output (%d bytes) for frame. %s", stream.bytesWritten(), prop);
+ }
+ }
+ surface->getCanvas()->drawPicture(picture);
+ }
+
+ if (CC_UNLIKELY(Properties::debugOverdraw)) {
+ renderOverdraw(layers, clip, nodes, contentDrawBounds, surface);
+ }
+
+ ATRACE_NAME("flush commands");
+ canvas->flush();
+}
+
+namespace {
+static Rect nodeBounds(RenderNode& node) {
+ auto& props = node.properties();
+ return Rect(props.getLeft(), props.getTop(),
+ props.getRight(), props.getBottom());
+}
+}
+
+void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
+ const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
+ SkCanvas* canvas) {
+ SkAutoCanvasRestore saver(canvas, true);
+ canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
+
+ if (!opaque) {
+ canvas->clear(SK_ColorTRANSPARENT);
+ }
+
+ if (1 == nodes.size()) {
+ if (!nodes[0]->nothingToDraw()) {
+ RenderNodeDrawable root(nodes[0].get(), canvas);
+ root.draw(canvas);
+ }
+ } else if (0 == nodes.size()) {
+ //nothing to draw
+ } else {
+ // It there are multiple render nodes, they are laid out as follows:
+ // #0 - backdrop (content + caption)
+ // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
+ // #2 - additional overlay nodes
+ // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
+ // resizing however it might become partially visible. The following render loop will crop the
+ // backdrop against the content and draw the remaining part of it. It will then draw the content
+ // cropped to the backdrop (since that indicates a shrinking of the window).
+ //
+ // Additional nodes will be drawn on top with no particular clipping semantics.
+
+ // Usually the contents bounds should be mContentDrawBounds - however - we will
+ // move it towards the fixed edge to give it a more stable appearance (for the moment).
+ // If there is no content bounds we ignore the layering as stated above and start with 2.
+
+ // Backdrop bounds in render target space
+ const Rect backdrop = nodeBounds(*nodes[0]);
+
+ // Bounds that content will fill in render target space (note content node bounds may be bigger)
+ Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
+ content.translate(backdrop.left, backdrop.top);
+ if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
+ // Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
+
+ // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
+ // also fill left/top. Currently, both 2up and freeform position content at the top/left of
+ // the backdrop, so this isn't necessary.
+ RenderNodeDrawable backdropNode(nodes[0].get(), canvas);
+ if (content.right < backdrop.right) {
+ // draw backdrop to right side of content
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top,
+ backdrop.right, backdrop.bottom));
+ backdropNode.draw(canvas);
+ }
+ if (content.bottom < backdrop.bottom) {
+ // draw backdrop to bottom of content
+ // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom,
+ content.right, backdrop.bottom));
+ backdropNode.draw(canvas);
+ }
+ }
+
+ RenderNodeDrawable contentNode(nodes[1].get(), canvas);
+ if (!backdrop.isEmpty()) {
+ // content node translation to catch up with backdrop
+ float dx = backdrop.left - contentDrawBounds.left;
+ float dy = backdrop.top - contentDrawBounds.top;
+
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->translate(dx, dy);
+ const SkRect contentLocalClip = SkRect::MakeXYWH(contentDrawBounds.left,
+ contentDrawBounds.top, backdrop.getWidth(), backdrop.getHeight());
+ canvas->clipRect(contentLocalClip);
+ contentNode.draw(canvas);
+ } else {
+ SkAutoCanvasRestore acr(canvas, true);
+ contentNode.draw(canvas);
+ }
+
+ // remaining overlay nodes, simply defer
+ for (size_t index = 2; index < nodes.size(); index++) {
+ if (!nodes[index]->nothingToDraw()) {
+ SkAutoCanvasRestore acr(canvas, true);
+ RenderNodeDrawable overlayNode(nodes[index].get(), canvas);
+ overlayNode.draw(canvas);
+ }
+ }
+ }
+}
+
+void SkiaPipeline::dumpResourceCacheUsage() const {
+ int resources, maxResources;
+ size_t bytes, maxBytes;
+ mRenderThread.getGrContext()->getResourceCacheUsage(&resources, &bytes);
+ mRenderThread.getGrContext()->getResourceCacheLimits(&maxResources, &maxBytes);
+
+ SkString log("Resource Cache Usage:\n");
+ log.appendf("%8d items out of %d maximum items\n", resources, maxResources);
+ log.appendf("%8zu bytes (%.2f MB) out of %.2f MB maximum\n",
+ bytes, bytes * (1.0f / (1024.0f * 1024.0f)), maxBytes * (1.0f / (1024.0f * 1024.0f)));
+
+ ALOGD("%s", log.c_str());
+}
+
+// Overdraw debugging
+
+// These colors should be kept in sync with Caches::getOverdrawColor() with a few differences.
+// This implementation:
+// (1) Requires transparent entries for "no overdraw" and "single draws".
+// (2) Requires premul colors (instead of unpremul).
+// (3) Requires RGBA colors (instead of BGRA).
+static const uint32_t kOverdrawColors[2][6] = {
+ { 0x00000000, 0x00000000, 0x2f2f0000, 0x2f002f00, 0x3f00003f, 0x7f00007f, },
+ { 0x00000000, 0x00000000, 0x2f2f0000, 0x4f004f4f, 0x5f50335f, 0x7f00007f, },
+};
+
+void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip,
+ const std::vector<sp<RenderNode>>& nodes, const Rect &contentDrawBounds,
+ sk_sp<SkSurface> surface) {
+ // Set up the overdraw canvas.
+ SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
+ sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
+ SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas());
+
+ // Fake a redraw to replay the draw commands. This will increment the alpha channel
+ // each time a pixel would have been drawn.
+ // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero
+ // initialized.
+ renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas);
+ sk_sp<SkImage> counts = offscreen->makeImageSnapshot();
+
+ // Draw overdraw colors to the canvas. The color filter will convert counts to colors.
+ SkPaint paint;
+ const SkPMColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
+ paint.setColorFilter(SkOverdrawColorFilter::Make(colors));
+ surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint);
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
new file mode 100644
index 000000000000..c58fedf834ff
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "renderthread/CanvasContext.h"
+#include "FrameBuilder.h"
+#include "renderthread/IRenderPipeline.h"
+#include <SkSurface.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaPipeline : public renderthread::IRenderPipeline {
+public:
+ SkiaPipeline(renderthread::RenderThread& thread);
+ virtual ~SkiaPipeline() {}
+
+ TaskManager* getTaskManager() override;
+
+ void onDestroyHardwareResources() override;
+
+ bool pinImages(std::vector<SkImage*>& mutableImages) override;
+ bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override { return false; }
+ void unpinImages() override;
+
+ void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo) override;
+
+ bool createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) override;
+
+ void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
+ const std::vector< sp<RenderNode> >& nodes, bool opaque, const Rect &contentDrawBounds,
+ sk_sp<SkSurface> surface);
+
+ static void destroyLayer(RenderNode* node);
+
+ static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
+
+ static void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque);
+
+ static bool skpCaptureEnabled() { return false; }
+
+ static float getLightRadius() {
+ if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) {
+ return Properties::overrideLightRadius;
+ }
+ return mLightRadius;
+ }
+
+ static uint8_t getAmbientShadowAlpha() {
+ if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
+ return Properties::overrideAmbientShadowStrength;
+ }
+ return mAmbientShadowAlpha;
+ }
+
+ static uint8_t getSpotShadowAlpha() {
+ if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
+ return Properties::overrideSpotShadowStrength;
+ }
+ return mSpotShadowAlpha;
+ }
+
+ static Vector3 getLightCenter() {
+ if (CC_UNLIKELY(Properties::overrideLightPosY > 0 || Properties::overrideLightPosZ > 0)) {
+ Vector3 adjustedLightCenter = mLightCenter;
+ if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) {
+ // negated since this shifts up
+ adjustedLightCenter.y = - Properties::overrideLightPosY;
+ }
+ if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) {
+ adjustedLightCenter.z = Properties::overrideLightPosZ;
+ }
+ return adjustedLightCenter;
+ }
+ return mLightCenter;
+ }
+
+ static void updateLighting(const FrameBuilder::LightGeometry& lightGeometry,
+ const BakedOpRenderer::LightInfo& lightInfo) {
+ mLightRadius = lightGeometry.radius;
+ mAmbientShadowAlpha = lightInfo.ambientShadowAlpha;
+ mSpotShadowAlpha = lightInfo.spotShadowAlpha;
+ mLightCenter = lightGeometry.center;
+ }
+
+protected:
+ void dumpResourceCacheUsage() const;
+
+ renderthread::RenderThread& mRenderThread;
+
+private:
+ void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
+ const std::vector< sp<RenderNode> >& nodes, bool opaque, const Rect &contentDrawBounds,
+ SkCanvas* canvas);
+
+ /**
+ * Debugging feature. Draws a semi-transparent overlay on each pixel, indicating
+ * how many times it has been drawn.
+ */
+ void renderOverdraw(const LayerUpdateQueue& layers, const SkRect& clip,
+ const std::vector< sp<RenderNode> >& nodes, const Rect &contentDrawBounds,
+ sk_sp<SkSurface>);
+
+ TaskManager mTaskManager;
+ std::vector<sk_sp<SkImage>> mPinnedImages;
+ static float mLightRadius;
+ static uint8_t mAmbientShadowAlpha;
+ static uint8_t mSpotShadowAlpha;
+ static Vector3 mLightCenter;
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp
new file mode 100644
index 000000000000..d97fb372fe0c
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaProfileRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+void SkiaProfileRenderer::drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) {
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+ mCanvas->drawRect(rect, paint);
+}
+
+void SkiaProfileRenderer::drawRects(const float* rects, int count, const SkPaint& paint) {
+ for (int index = 0; index + 4 <= count; index += 4) {
+ SkRect rect = SkRect::MakeLTRB(rects[index + 0], rects[index + 1], rects[index + 2],
+ rects[index + 3]);
+ mCanvas->drawRect(rect, paint);
+ }
+}
+
+uint32_t SkiaProfileRenderer::getViewportWidth() {
+ return mCanvas->imageInfo().width();
+}
+
+uint32_t SkiaProfileRenderer::getViewportHeight() {
+ return mCanvas->imageInfo().height();
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
new file mode 100644
index 000000000000..e6b7f8307379
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "IProfileRenderer.h"
+
+#include "BakedOpRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+class SkiaProfileRenderer : public IProfileRenderer {
+public:
+ SkiaProfileRenderer(SkCanvas* canvas)
+ : mCanvas(canvas) {}
+
+ void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
+ void drawRects(const float* rects, int count, const SkPaint& paint) override;
+ uint32_t getViewportWidth() override;
+ uint32_t getViewportHeight() override;
+
+ virtual ~SkiaProfileRenderer() {}
+
+private:
+ // Does not have ownership.
+ SkCanvas* mCanvas;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
new file mode 100644
index 000000000000..dbe0296eae24
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaRecordingCanvas.h"
+
+#include "Layer.h"
+#include "RenderNode.h"
+#include "LayerDrawable.h"
+#include "NinePatchUtils.h"
+#include "pipeline/skia/AnimatedDrawables.h"
+#include <SkImagePriv.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+// ----------------------------------------------------------------------------
+// Recording Canvas Setup
+// ----------------------------------------------------------------------------
+
+void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, int width,
+ int height) {
+ mCurrentBarrier = nullptr;
+ SkASSERT(mDisplayList.get() == nullptr);
+
+ if (renderNode) {
+ mDisplayList = renderNode->detachAvailableList();
+ }
+ SkRect bounds = SkRect::MakeWH(width, height);
+ if (mDisplayList) {
+ mDisplayList->reset(bounds);
+ } else {
+ mDisplayList.reset(new SkiaDisplayList(bounds));
+ }
+
+ mRecorder.reset(mDisplayList->mDrawable.get());
+ SkiaCanvas::reset(&mRecorder);
+}
+
+uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
+ // close any existing chunks if necessary
+ insertReorderBarrier(false);
+ mRecorder.restoreToCount(1);
+ return mDisplayList.release();
+}
+
+// ----------------------------------------------------------------------------
+// Recording Canvas draw operations: View System
+// ----------------------------------------------------------------------------
+
+void SkiaRecordingCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+ uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+ uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+ uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) {
+ drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom,
+ rx, ry, paint));
+}
+
+void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x,
+ uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
+ uirenderer::CanvasPropertyPaint* paint) {
+ drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint));
+}
+
+void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
+ if (nullptr != mCurrentBarrier) {
+ // finish off the existing chunk
+ SkDrawable* drawable =
+ mDisplayList->allocateDrawable<EndReorderBarrierDrawable>(
+ mCurrentBarrier);
+ mCurrentBarrier = nullptr;
+ drawDrawable(drawable);
+ }
+ if (enableReorder) {
+ mCurrentBarrier = (StartReorderBarrierDrawable*)
+ mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
+ mDisplayList.get());
+ drawDrawable(mCurrentBarrier);
+ }
+}
+
+void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
+ if (layerUpdater != nullptr && layerUpdater->backingLayer() != nullptr) {
+ uirenderer::Layer* layer = layerUpdater->backingLayer();
+ sk_sp<SkDrawable> drawable(new LayerDrawable(layer));
+ drawDrawable(drawable.get());
+ }
+}
+
+void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
+ // record the child node
+ mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
+ auto& renderNodeDrawable = mDisplayList->mChildNodes.back();
+ drawDrawable(&renderNodeDrawable);
+
+ // use staging property, since recording on UI thread
+ if (renderNode->stagingProperties().isProjectionReceiver()) {
+ mDisplayList->mProjectionReceiver = &renderNodeDrawable;
+ // set projectionReceiveIndex so that RenderNode.hasProjectionReceiver returns true
+ mDisplayList->projectionReceiveIndex = mDisplayList->mChildNodes.size() - 1;
+ }
+}
+
+void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
+ uirenderer::GlFunctorLifecycleListener* listener) {
+ mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
+ drawDrawable(&mDisplayList->mChildFunctors.back());
+}
+
+class VectorDrawable : public SkDrawable {
+ public:
+ VectorDrawable(VectorDrawableRoot* tree) : mRoot(tree) {}
+
+ protected:
+ virtual SkRect onGetBounds() override {
+ return SkRect::MakeLargest();
+ }
+ virtual void onDraw(SkCanvas* canvas) override {
+ Bitmap& hwuiBitmap = mRoot->getBitmapUpdateIfDirty();
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
+ SkPaint* paint = mRoot->getPaint();
+ canvas->drawBitmapRect(bitmap, mRoot->mutateProperties()->getBounds(), paint);
+ /*
+ * TODO we can draw this directly but need to address the following...
+ *
+ * 1) Add drawDirect(SkCanvas*) to VectorDrawableRoot
+ * 2) fix VectorDrawable.cpp's Path::draw to not make a temporary path
+ * so that we don't break caching
+ * 3) figure out how to set path's as volatile during animation
+ * 4) if mRoot->getPaint() != null either promote to layer (during
+ * animation) or cache in SkSurface (for static content)
+ *
+ */
+ }
+
+ private:
+ sp<VectorDrawableRoot> mRoot;
+};
+
+void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+ drawDrawable(mDisplayList->allocateDrawable<VectorDrawable>(tree));
+ mDisplayList->mVectorDrawables.push_back(tree);
+}
+
+// ----------------------------------------------------------------------------
+// Recording Canvas draw operations: Bitmaps
+// ----------------------------------------------------------------------------
+
+inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPaint) {
+ if (origPaint && origPaint->isAntiAlias()) {
+ *tmpPaint = *origPaint;
+ tmpPaint->setAntiAlias(false);
+ return tmpPaint;
+ } else {
+ return origPaint;
+ }
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
+ SkBitmap skBitmap;
+ bitmap.getSkBitmap(&skBitmap);
+
+ sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
+ if (!skBitmap.isImmutable()) {
+ mDisplayList->mMutableImages.push_back(image.get());
+ }
+ SkPaint tmpPaint;
+ mRecorder.drawImage(image, left, top, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix,
+ const SkPaint* paint) {
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
+ SkAutoCanvasRestore acr(&mRecorder, true);
+ concat(matrix);
+ sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+ if (!bitmap.isImmutable()) {
+ mDisplayList->mMutableImages.push_back(image.get());
+ }
+ SkPaint tmpPaint;
+ mRecorder.drawImage(image, 0, 0, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight,
+ float dstBottom, const SkPaint* paint) {
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
+ SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
+ SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+ sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+ if (!bitmap.isImmutable()) {
+ mDisplayList->mMutableImages.push_back(image.get());
+ }
+ SkPaint tmpPaint;
+ mRecorder.drawImageRect(image, srcRect, dstRect, nonAAPaint(paint, &tmpPaint));
+}
+
+void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk,
+ float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
+ SkBitmap bitmap;
+ hwuiBitmap.getSkBitmap(&bitmap);
+
+ SkCanvas::Lattice lattice;
+ NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
+
+ lattice.fFlags = nullptr;
+ int numFlags = 0;
+ if (chunk.numColors > 0 && chunk.numColors == NinePatchUtils::NumDistinctRects(lattice)) {
+ // We can expect the framework to give us a color for every distinct rect.
+ // Skia requires placeholder flags for degenerate rects.
+ numFlags = (lattice.fXCount + 1) * (lattice.fYCount + 1);
+ }
+
+ SkAutoSTMalloc<25, SkCanvas::Lattice::Flags> flags(numFlags);
+ if (numFlags > 0) {
+ NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk);
+ }
+
+ lattice.fBounds = nullptr;
+ SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+ sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+ if (!bitmap.isImmutable()) {
+ mDisplayList->mMutableImages.push_back(image.get());
+ }
+
+ SkPaint tmpPaint;
+ mRecorder.drawImageLattice(image.get(), lattice, dst, nonAAPaint(paint, &tmpPaint));
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
new file mode 100644
index 000000000000..10829f87efb9
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "SkiaCanvas.h"
+#include "SkiaDisplayList.h"
+#include "ReorderBarrierDrawables.h"
+#include <SkLiteRecorder.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+/**
+ * A SkiaCanvas implementation that records drawing operations for deferred rendering backed by a
+ * SkLiteRecorder and a SkiaDisplayList.
+ */
+class SkiaRecordingCanvas : public SkiaCanvas {
+ public:
+ explicit SkiaRecordingCanvas(uirenderer::RenderNode* renderNode, int width, int height) {
+ initDisplayList(renderNode, width, height);
+ }
+
+ virtual void setBitmap(const SkBitmap& bitmap) override {
+ LOG_ALWAYS_FATAL("DisplayListCanvas is not backed by a bitmap.");
+ }
+
+ virtual void resetRecording(int width, int height,
+ uirenderer::RenderNode* renderNode) override {
+ initDisplayList(renderNode, width, height);
+ }
+
+ virtual uirenderer::DisplayList* finishRecording() override;
+
+ virtual void drawBitmap(Bitmap& bitmap, float left, float top,
+ const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) override;
+ virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) override;
+ virtual void drawNinePatch(Bitmap& hwuiBitmap, const android::Res_png_9patch& chunk,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) override;
+
+ virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
+ uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
+ uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
+ uirenderer::CanvasPropertyPrimitive* ry,
+ uirenderer::CanvasPropertyPaint* paint) override;
+ virtual void drawCircle(uirenderer::CanvasPropertyPrimitive* x,
+ uirenderer::CanvasPropertyPrimitive* y, uirenderer::CanvasPropertyPrimitive* radius,
+ uirenderer::CanvasPropertyPaint* paint) override;
+
+ virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
+
+ virtual void insertReorderBarrier(bool enableReorder) override;
+ virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
+ virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
+ virtual void callDrawGLFunction(Functor* functor,
+ uirenderer::GlFunctorLifecycleListener* listener) override;
+
+private:
+ SkLiteRecorder mRecorder;
+ std::unique_ptr<SkiaDisplayList> mDisplayList;
+ StartReorderBarrierDrawable* mCurrentBarrier;
+
+ /**
+ * A new SkiaDisplayList is created or recycled if available.
+ *
+ * @param renderNode is optional and used to recycle an old display list.
+ * @param width used to calculate recording bounds.
+ * @param height used to calculate recording bounds.
+ */
+ void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height);
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
new file mode 100644
index 000000000000..910c339c4d1c
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaVulkanPipeline.h"
+
+#include "DeferredLayerUpdater.h"
+#include "renderthread/Frame.h"
+#include "Readback.h"
+#include "renderstate/RenderState.h"
+#include "SkiaPipeline.h"
+#include "SkiaProfileRenderer.h"
+#include "VkLayer.h"
+
+#include <SkSurface.h>
+#include <SkTypes.h>
+
+#include <GrContext.h>
+#include <GrTypes.h>
+#include <vk/GrVkTypes.h>
+
+#include <android/native_window.h>
+#include <cutils/properties.h>
+#include <strings.h>
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+SkiaVulkanPipeline::SkiaVulkanPipeline(renderthread::RenderThread& thread)
+ : SkiaPipeline(thread)
+ , mVkManager(thread.vulkanManager()) {}
+
+MakeCurrentResult SkiaVulkanPipeline::makeCurrent() {
+ return MakeCurrentResult::AlreadyCurrent;
+}
+
+Frame SkiaVulkanPipeline::getFrame() {
+ LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr,
+ "drawRenderNode called on a context with no surface!");
+
+ SkSurface* backBuffer = mVkManager.getBackbufferSurface(mVkSurface);
+ if (backBuffer == nullptr) {
+ SkDebugf("failed to get backbuffer");
+ return Frame(-1, -1, 0);
+ }
+
+ Frame frame(backBuffer->width(), backBuffer->height(), mVkManager.getAge(mVkSurface));
+ return frame;
+}
+
+bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty,
+ const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector<sp<RenderNode>>& renderNodes,
+ FrameInfoVisualizer* profiler) {
+
+ sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface();
+ if (backBuffer.get() == nullptr) {
+ return false;
+ }
+ SkiaPipeline::updateLighting(lightGeometry, lightInfo);
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer);
+ layerUpdateQueue->clear();
+
+ // Draw visual debugging features
+ if (CC_UNLIKELY(Properties::showDirtyRegions
+ || ProfileType::None != Properties::getProfileType())) {
+ SkCanvas* profileCanvas = backBuffer->getCanvas();
+ SkiaProfileRenderer profileRenderer(profileCanvas);
+ profiler->draw(profileRenderer);
+ profileCanvas->flush();
+ }
+
+ // Log memory statistics
+ if (CC_UNLIKELY(Properties::debugLevel != kDebugDisabled)) {
+ dumpResourceCacheUsage();
+ }
+
+ return true;
+}
+
+bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) {
+
+ *requireSwap = drew;
+
+ // Even if we decided to cancel the frame, from the perspective of jank
+ // metrics the frame was swapped at this point
+ currentFrameInfo->markSwapBuffers();
+
+ if (*requireSwap) {
+ mVkManager.swapBuffers(mVkSurface);
+ }
+
+ return *requireSwap;
+}
+
+bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+ // TODO: implement copyLayerInto for vulkan.
+ return false;
+}
+
+DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
+ mVkManager.initialize();
+
+ VkLayer* layer = new VkLayer(mRenderThread.renderState(), 0, 0);
+ return new DeferredLayerUpdater(layer);
+}
+
+void SkiaVulkanPipeline::onStop() {
+}
+
+bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
+ if (mVkSurface) {
+ mVkManager.destroySurface(mVkSurface);
+ mVkSurface = nullptr;
+ }
+
+ if (surface) {
+ mVkSurface = mVkManager.createSurface(surface);
+ }
+
+ return mVkSurface != nullptr;
+}
+
+bool SkiaVulkanPipeline::isSurfaceReady() {
+ return CC_UNLIKELY(mVkSurface != nullptr);
+}
+
+bool SkiaVulkanPipeline::isContextReady() {
+ return CC_LIKELY(mVkManager.hasVkContext());
+}
+
+void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
+ // TODO: we currently don't support OpenGL WebView's
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ (*functor)(mode, nullptr);
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
new file mode 100644
index 000000000000..aab1d7a547c0
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SkiaPipeline.h"
+#include "renderthread/VulkanManager.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class SkiaVulkanPipeline : public SkiaPipeline {
+public:
+ SkiaVulkanPipeline(renderthread::RenderThread& thread);
+ virtual ~SkiaVulkanPipeline() {}
+
+ renderthread::MakeCurrentResult makeCurrent() override;
+ renderthread::Frame getFrame() override;
+ bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector< sp<RenderNode> >& renderNodes,
+ FrameInfoVisualizer* profiler) override;
+ bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
+ FrameInfo* currentFrameInfo, bool* requireSwap) override;
+ bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
+ DeferredLayerUpdater* createTextureLayer() override;
+ bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override;
+ void onStop() override;
+ bool isSurfaceReady() override;
+ bool isContextReady() override;
+
+ static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
+
+private:
+ renderthread::VulkanManager& mVkManager;
+ renderthread::VulkanSurface* mVkSurface = nullptr;
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp
index 93f787d31745..8865c6efce8c 100644
--- a/libs/hwui/renderstate/Blend.cpp
+++ b/libs/hwui/renderstate/Blend.cpp
@@ -25,70 +25,70 @@ namespace uirenderer {
* Structure mapping Skia xfermodes to OpenGL blending factors.
*/
struct Blender {
- SkXfermode::Mode mode;
+ SkBlendMode mode;
GLenum src;
GLenum dst;
};
// assumptions made by lookup tables in either this file or ProgramCache
-static_assert(0 == SkXfermode::kClear_Mode, "SkXfermode enums have changed");
-static_assert(1 == SkXfermode::kSrc_Mode, "SkXfermode enums have changed");
-static_assert(2 == SkXfermode::kDst_Mode, "SkXfermode enums have changed");
-static_assert(3 == SkXfermode::kSrcOver_Mode, "SkXfermode enums have changed");
-static_assert(4 == SkXfermode::kDstOver_Mode, "SkXfermode enums have changed");
-static_assert(5 == SkXfermode::kSrcIn_Mode, "SkXfermode enums have changed");
-static_assert(6 == SkXfermode::kDstIn_Mode, "SkXfermode enums have changed");
-static_assert(7 == SkXfermode::kSrcOut_Mode, "SkXfermode enums have changed");
-static_assert(8 == SkXfermode::kDstOut_Mode, "SkXfermode enums have changed");
-static_assert(9 == SkXfermode::kSrcATop_Mode, "SkXfermode enums have changed");
-static_assert(10 == SkXfermode::kDstATop_Mode, "SkXfermode enums have changed");
-static_assert(11 == SkXfermode::kXor_Mode, "SkXfermode enums have changed");
-static_assert(12 == SkXfermode::kPlus_Mode, "SkXfermode enums have changed");
-static_assert(13 == SkXfermode::kModulate_Mode, "SkXfermode enums have changed");
-static_assert(14 == SkXfermode::kScreen_Mode, "SkXfermode enums have changed");
-static_assert(15 == SkXfermode::kOverlay_Mode, "SkXfermode enums have changed");
-static_assert(16 == SkXfermode::kDarken_Mode, "SkXfermode enums have changed");
-static_assert(17 == SkXfermode::kLighten_Mode, "SkXfermode enums have changed");
+static_assert(0 == static_cast<int>(SkBlendMode::kClear), "SkBlendMode enums have changed");
+static_assert(1 == static_cast<int>(SkBlendMode::kSrc), "SkBlendMode enums have changed");
+static_assert(2 == static_cast<int>(SkBlendMode::kDst), "SkBlendMode enums have changed");
+static_assert(3 == static_cast<int>(SkBlendMode::kSrcOver), "SkBlendMode enums have changed");
+static_assert(4 == static_cast<int>(SkBlendMode::kDstOver), "SkBlendMode enums have changed");
+static_assert(5 == static_cast<int>(SkBlendMode::kSrcIn), "SkBlendMode enums have changed");
+static_assert(6 == static_cast<int>(SkBlendMode::kDstIn), "SkBlendMode enums have changed");
+static_assert(7 == static_cast<int>(SkBlendMode::kSrcOut), "SkBlendMode enums have changed");
+static_assert(8 == static_cast<int>(SkBlendMode::kDstOut), "SkBlendMode enums have changed");
+static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "SkBlendMode enums have changed");
+static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "SkBlendMode enums have changed");
+static_assert(11 == static_cast<int>(SkBlendMode::kXor), "SkBlendMode enums have changed");
+static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "SkBlendMode enums have changed");
+static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "SkBlendMode enums have changed");
+static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "SkBlendMode enums have changed");
+static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "SkBlendMode enums have changed");
+static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "SkBlendMode enums have changed");
+static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "SkBlendMode enums have changed");
// In this array, the index of each Blender equals the value of the first
-// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
+// entry. For instance, gBlends[1] == gBlends[SkBlendMode::kSrc]
const Blender kBlends[] = {
- { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_ZERO, GL_SRC_COLOR },
- { SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
+ { SkBlendMode::kClear, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kSrc, GL_ONE, GL_ZERO },
+ { SkBlendMode::kDst, GL_ZERO, GL_ONE },
+ { SkBlendMode::kSrcOver, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kDstOver, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+ { SkBlendMode::kSrcIn, GL_DST_ALPHA, GL_ZERO },
+ { SkBlendMode::kDstIn, GL_ZERO, GL_SRC_ALPHA },
+ { SkBlendMode::kSrcOut, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkBlendMode::kDstOut, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kSrcATop, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kDstATop, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+ { SkBlendMode::kXor, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kPlus, GL_ONE, GL_ONE },
+ { SkBlendMode::kModulate, GL_ZERO, GL_SRC_COLOR },
+ { SkBlendMode::kScreen, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
};
-// This array contains the swapped version of each SkXfermode. For instance
+// This array contains the swapped version of each SkBlendMode. For instance
// this array's SrcOver blending mode is actually DstOver. You can refer to
// createLayer() for more information on the purpose of this array.
const Blender kBlendsSwap[] = {
- { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_DST_COLOR, GL_ZERO },
- { SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE }
+ { SkBlendMode::kClear, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkBlendMode::kSrc, GL_ZERO, GL_ONE },
+ { SkBlendMode::kDst, GL_ONE, GL_ZERO },
+ { SkBlendMode::kSrcOver, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+ { SkBlendMode::kDstOver, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kSrcIn, GL_ZERO, GL_SRC_ALPHA },
+ { SkBlendMode::kDstIn, GL_DST_ALPHA, GL_ZERO },
+ { SkBlendMode::kSrcOut, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kDstOut, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkBlendMode::kSrcATop, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+ { SkBlendMode::kDstATop, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kXor, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkBlendMode::kPlus, GL_ONE, GL_ONE },
+ { SkBlendMode::kModulate, GL_DST_COLOR, GL_ZERO },
+ { SkBlendMode::kScreen, GL_ONE_MINUS_DST_COLOR, GL_ONE }
};
Blend::Blend()
@@ -111,9 +111,10 @@ void Blend::syncEnabled() {
}
}
-void Blend::getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage, GLenum* outSrc, GLenum* outDst) {
- *outSrc = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[mode].src : kBlends[mode].src;
- *outDst = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[mode].dst : kBlends[mode].dst;
+void Blend::getFactors(SkBlendMode mode, ModeOrderSwap modeUsage, GLenum* outSrc, GLenum* outDst) {
+ int index = static_cast<int>(mode);
+ *outSrc = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[index].src : kBlends[index].src;
+ *outDst = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[index].dst : kBlends[index].dst;
}
void Blend::setFactors(GLenum srcMode, GLenum dstMode) {
diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h
index df9e5a8af879..ec0e114c998f 100644
--- a/libs/hwui/renderstate/Blend.h
+++ b/libs/hwui/renderstate/Blend.h
@@ -20,7 +20,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-#include <SkXfermode.h>
+#include <SkBlendMode.h>
#include <memory>
namespace android {
@@ -36,7 +36,7 @@ public:
};
void syncEnabled();
- static void getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage,
+ static void getFactors(SkBlendMode mode, ModeOrderSwap modeUsage,
GLenum* outSrc, GLenum* outDst);
void setFactors(GLenum src, GLenum dst);
diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp
index b575c696586e..6d0293695412 100644
--- a/libs/hwui/renderstate/MeshState.cpp
+++ b/libs/hwui/renderstate/MeshState.cpp
@@ -17,8 +17,6 @@
#include "Program.h"
-#include "ShadowTessellator.h"
-
namespace android {
namespace uirenderer {
@@ -100,6 +98,12 @@ void MeshState::genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size,
glBufferData(GL_ARRAY_BUFFER, size, data, usage);
}
+void MeshState::updateMeshBufferSubData(GLuint buffer, GLintptr offset,
+ GLsizeiptr size, const void* data) {
+ bindMeshBuffer(buffer);
+ glBufferSubData(GL_ARRAY_BUFFER, offset, size, data);
+}
+
void MeshState::deleteMeshBuffer(GLuint buffer) {
if (buffer == mCurrentBuffer) {
// GL defines that deleting the currently bound VBO rebinds to 0 (no VBO).
diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h
index dd684686f584..17ad4622e44a 100644
--- a/libs/hwui/renderstate/MeshState.h
+++ b/libs/hwui/renderstate/MeshState.h
@@ -72,6 +72,7 @@ public:
void unbindMeshBuffer();
void genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size, const void* data, GLenum usage);
+ void updateMeshBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data);
void deleteMeshBuffer(GLuint);
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp
index 10a26e08f897..a9bbb273dbb5 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.cpp
+++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp
@@ -22,6 +22,7 @@
#include "utils/FatVector.h"
#include "utils/TraceUtils.h"
+#include <utils/Color.h>
#include <utils/Log.h>
#include <GLES2/gl2.h>
@@ -44,7 +45,7 @@ OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
uint32_t height = computeIdealDimension(viewportHeight);
ATRACE_FORMAT("Allocate %ux%u HW Layer", width, height);
caches.textureState().activateTexture(0);
- texture.resize(width, height, GL_RGBA);
+ texture.resize(width, height, caches.rgbaInternalFormat(), GL_RGBA);
texture.blend = true;
texture.setWrap(GL_CLAMP_TO_EDGE);
// not setting filter on texture, since it's set when drawing, based on transform
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 5e600644ca19..17ee390c90bd 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include "GlLayer.h"
+#include "VkLayer.h"
#include <GpuMemoryTracker.h>
#include "renderstate/RenderState.h"
@@ -40,7 +42,7 @@ RenderState::~RenderState() {
void RenderState::onGLContextCreated() {
LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
"State object lifecycle not managed correctly");
- GpuMemoryTracker::onGLContextCreated();
+ GpuMemoryTracker::onGpuContextCreated();
mBlend = new Blend();
mMeshState = new MeshState();
@@ -52,51 +54,19 @@ void RenderState::onGLContextCreated() {
mCaches = &Caches::createInstance(*this);
}
mCaches->init();
- mCaches->textureCache.setAssetAtlas(&mAssetAtlas);
}
static void layerLostGlContext(Layer* layer) {
- layer->onGlContextLost();
+ LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL,
+ "layerLostGlContext on non GL layer");
+ static_cast<GlLayer*>(layer)->onGlContextLost();
}
void RenderState::onGLContextDestroyed() {
-/*
- size_t size = mActiveLayers.size();
- if (CC_UNLIKELY(size != 0)) {
- ALOGE("Crashing, have %d contexts and %d layers at context destruction. isempty %d",
- mRegisteredContexts.size(), size, mActiveLayers.empty());
- mCaches->dumpMemoryUsage();
- for (std::set<renderthread::CanvasContext*>::iterator cit = mRegisteredContexts.begin();
- cit != mRegisteredContexts.end(); cit++) {
- renderthread::CanvasContext* context = *cit;
- ALOGE("Context: %p (root = %p)", context, context->mRootRenderNode.get());
- ALOGE(" Prefeteched layers: %zu", context->mPrefetechedLayers.size());
- for (std::set<RenderNode*>::iterator pit = context->mPrefetechedLayers.begin();
- pit != context->mPrefetechedLayers.end(); pit++) {
- (*pit)->debugDumpLayers(" ");
- }
- context->mRootRenderNode->debugDumpLayers(" ");
- }
-
-
- if (mActiveLayers.begin() == mActiveLayers.end()) {
- ALOGE("set has become empty. wat.");
- }
- for (std::set<const Layer*>::iterator lit = mActiveLayers.begin();
- lit != mActiveLayers.end(); lit++) {
- const Layer* layer = *(lit);
- ALOGE("Layer %p, state %d, texlayer %d, fbo %d, buildlayered %d",
- layer, layer->state, layer->isTextureLayer(), layer->getFbo(), layer->wasBuildLayered);
- }
- LOG_ALWAYS_FATAL("%d layers have survived gl context destruction", size);
- }
-*/
-
mLayerPool.clear();
// TODO: reset all cached state in state objects
std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext);
- mAssetAtlas.terminate();
mCaches->terminate();
@@ -109,7 +79,29 @@ void RenderState::onGLContextDestroyed() {
delete mStencil;
mStencil = nullptr;
- GpuMemoryTracker::onGLContextDestroyed();
+ GpuMemoryTracker::onGpuContextDestroyed();
+}
+
+void RenderState::onVkContextCreated() {
+ LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
+ "State object lifecycle not managed correctly");
+ GpuMemoryTracker::onGpuContextCreated();
+}
+
+static void layerDestroyedVkContext(Layer* layer) {
+ LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::Vulkan,
+ "layerLostVkContext on non Vulkan layer");
+ static_cast<VkLayer*>(layer)->onVkContextDestroyed();
+}
+
+void RenderState::onVkContextDestroyed() {
+ mLayerPool.clear();
+ std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext);
+ GpuMemoryTracker::onGpuContextDestroyed();
+}
+
+GrContext* RenderState::getGrContext() const {
+ return mRenderThread.getGrContext();
}
void RenderState::flush(Caches::FlushMode mode) {
@@ -179,9 +171,17 @@ void RenderState::interruptForFunctorInvoke() {
meshState().resetVertexPointers();
meshState().disableTexCoordsVertexArray();
debugOverdraw(false, false);
+ // TODO: We need a way to know whether the functor is sRGB aware (b/32072673)
+ if (mCaches->extensions().hasSRGBWriteControl()) {
+ glDisable(GL_FRAMEBUFFER_SRGB_EXT);
+ }
}
void RenderState::resumeFromFunctorInvoke() {
+ if (mCaches->extensions().hasSRGBWriteControl()) {
+ glEnable(GL_FRAMEBUFFER_SRGB_EXT);
+ }
+
glViewport(0, 0, mViewportWidth, mViewportHeight);
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
debugOverdraw(false, false);
@@ -304,12 +304,12 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
// texture always takes slot 0, shader samplers increment from there
mCaches->textureState().activateTexture(0);
- mCaches->textureState().bindTexture(texture.target, texture.texture->id());
+ mCaches->textureState().bindTexture(texture.texture->target(), texture.texture->id());
if (texture.clamp != GL_INVALID_ENUM) {
- texture.texture->setWrap(texture.clamp, false, false, texture.target);
+ texture.texture->setWrap(texture.clamp, false, false);
}
if (texture.filter != GL_INVALID_ENUM) {
- texture.texture->setFilter(texture.filter, false, false, texture.target);
+ texture.texture->setFilter(texture.filter, false, false);
}
if (texture.textureTransform) {
@@ -340,7 +340,7 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
glVertexAttribPointer(alphaLocation, 1, GL_FLOAT, GL_FALSE, vertices.stride, alphaCoords);
}
// Shader uniforms
- SkiaShader::apply(*mCaches, fill.skiaShaderData);
+ SkiaShader::apply(*mCaches, fill.skiaShaderData, mViewportWidth, mViewportHeight);
GL_CHECKPOINT(MODERATE);
Texture* texture = (fill.skiaShaderData.skiaShaderType & kBitmap_SkiaShaderType) ?
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 9e0fb121be65..d183a15f3842 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -16,7 +16,6 @@
#ifndef RENDERSTATE_H
#define RENDERSTATE_H
-#include "AssetAtlas.h"
#include "Caches.h"
#include "Glop.h"
#include "renderstate/Blend.h"
@@ -36,6 +35,8 @@
#include <utils/RefBase.h>
#include <private/hwui/DrawGlInfo.h>
+class GrContext;
+
namespace android {
namespace uirenderer {
@@ -57,6 +58,9 @@ public:
void onGLContextCreated();
void onGLContextDestroyed();
+ void onVkContextCreated();
+ void onVkContextDestroyed();
+
void flush(Caches::FlushMode flushMode);
void setViewport(GLsizei width, GLsizei height);
@@ -92,7 +96,6 @@ public:
void render(const Glop& glop, const Matrix4& orthoMatrix);
- AssetAtlas& assetAtlas() { return mAssetAtlas; }
Blend& blend() { return *mBlend; }
MeshState& meshState() { return *mMeshState; }
Scissor& scissor() { return *mScissor; }
@@ -100,6 +103,8 @@ public:
OffscreenBufferPool& layerPool() { return mLayerPool; }
+ GrContext* getGrContext() const;
+
void dump();
private:
@@ -120,7 +125,6 @@ private:
OffscreenBufferPool mLayerPool;
- AssetAtlas mAssetAtlas;
std::set<Layer*> mActiveLayers;
std::set<renderthread::CanvasContext*> mRegisteredContexts;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 975ac8368e3d..1b3bf96998dd 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -19,17 +19,19 @@
#include "AnimationContext.h"
#include "Caches.h"
-#include "DeferredLayerUpdater.h"
#include "EglManager.h"
+#include "Frame.h"
#include "LayerUpdateQueue.h"
-#include "LayerRenderer.h"
-#include "OpenGLRenderer.h"
#include "Properties.h"
#include "RenderThread.h"
#include "hwui/Canvas.h"
#include "renderstate/RenderState.h"
#include "renderstate/Stencil.h"
#include "protos/hwui.pb.h"
+#include "OpenGLPipeline.h"
+#include "pipeline/skia/SkiaOpenGLPipeline.h"
+#include "pipeline/skia/SkiaPipeline.h"
+#include "pipeline/skia/SkiaVulkanPipeline.h"
#include "utils/GLUtils.h"
#include "utils/TimeUtils.h"
@@ -61,15 +63,89 @@ namespace android {
namespace uirenderer {
namespace renderthread {
+CanvasContext* CanvasContext::create(RenderThread& thread,
+ bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) {
+
+ auto renderType = Properties::getRenderPipelineType();
+
+ switch (renderType) {
+ case RenderPipelineType::OpenGL:
+ return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
+ std::make_unique<OpenGLPipeline>(thread));
+ case RenderPipelineType::SkiaGL:
+ return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
+ std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
+ case RenderPipelineType::SkiaVulkan:
+ return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
+ std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
+ }
+ return nullptr;
+}
+
+void CanvasContext::destroyLayer(RenderNode* node) {
+ auto renderType = Properties::getRenderPipelineType();
+ switch (renderType) {
+ case RenderPipelineType::OpenGL:
+ OpenGLPipeline::destroyLayer(node);
+ break;
+ case RenderPipelineType::SkiaGL:
+ case RenderPipelineType::SkiaVulkan:
+ skiapipeline::SkiaPipeline::destroyLayer(node);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
+ }
+}
+
+void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor) {
+ ATRACE_CALL();
+ auto renderType = Properties::getRenderPipelineType();
+ switch (renderType) {
+ case RenderPipelineType::OpenGL:
+ OpenGLPipeline::invokeFunctor(thread, functor);
+ break;
+ case RenderPipelineType::SkiaGL:
+ skiapipeline::SkiaOpenGLPipeline::invokeFunctor(thread, functor);
+ break;
+ case RenderPipelineType::SkiaVulkan:
+ skiapipeline::SkiaVulkanPipeline::invokeFunctor(thread, functor);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
+ }
+}
+
+void CanvasContext::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
+ auto renderType = Properties::getRenderPipelineType();
+ switch (renderType) {
+ case RenderPipelineType::OpenGL:
+ OpenGLPipeline::prepareToDraw(thread, bitmap);
+ break;
+ case RenderPipelineType::SkiaGL:
+ case RenderPipelineType::SkiaVulkan:
+ skiapipeline::SkiaPipeline::prepareToDraw(thread, bitmap);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
+ }
+}
+
CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
- RenderNode* rootRenderNode, IContextFactory* contextFactory)
+ RenderNode* rootRenderNode, IContextFactory* contextFactory,
+ std::unique_ptr<IRenderPipeline> renderPipeline)
: mRenderThread(thread)
- , mEglManager(thread.eglManager())
, mOpaque(!translucent)
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
, mJankTracker(thread.mainDisplayInfo())
, mProfiler(mFrames)
- , mContentDrawBounds(0, 0, 0, 0) {
+ , mContentDrawBounds(0, 0, 0, 0)
+ , mRenderPipeline(std::move(renderPipeline)) {
mRenderNodes.emplace_back(rootRenderNode);
mRenderThread.renderState().registerCanvasContext(this);
mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
@@ -86,12 +162,6 @@ void CanvasContext::destroy(TreeObserver* observer) {
freePrefetchedLayers(observer);
destroyHardwareResources(observer);
mAnimationContext->destroy();
-#if !HWUI_NEW_OPS
- if (mCanvas) {
- delete mCanvas;
- mCanvas = nullptr;
- }
-#endif
}
void CanvasContext::setSurface(Surface* surface) {
@@ -99,24 +169,15 @@ void CanvasContext::setSurface(Surface* surface) {
mNativeSurface = surface;
- if (mEglSurface != EGL_NO_SURFACE) {
- mEglManager.destroySurface(mEglSurface);
- mEglSurface = EGL_NO_SURFACE;
- }
-
- if (surface) {
- mEglSurface = mEglManager.createSurface(surface);
- }
+ bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior);
mFrameNumber = -1;
- if (mEglSurface != EGL_NO_SURFACE) {
- const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer);
- mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
- mHaveNewSurface = true;
- mSwapHistory.clear();
+ if (hasSurface) {
+ mHaveNewSurface = true;
+ mSwapHistory.clear();
} else {
- mRenderThread.removeFrameCallback(this);
+ mRenderThread.removeFrameCallback(this);
}
}
@@ -126,11 +187,6 @@ void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
void CanvasContext::initialize(Surface* surface) {
setSurface(surface);
-#if !HWUI_NEW_OPS
- if (mCanvas) return;
- mCanvas = new OpenGLRenderer(mRenderThread.renderState());
- mCanvas->initProperties();
-#endif
}
void CanvasContext::updateSurface(Surface* surface) {
@@ -146,35 +202,22 @@ void CanvasContext::setStopped(bool stopped) {
mStopped = stopped;
if (mStopped) {
mRenderThread.removeFrameCallback(this);
- if (mEglManager.isCurrent(mEglSurface)) {
- mEglManager.makeCurrent(EGL_NO_SURFACE);
- }
+ mRenderPipeline->onStop();
} else if (mIsDirty && hasSurface()) {
mRenderThread.postFrameCallback(this);
}
}
}
-// TODO: don't pass viewport size, it's automatic via EGL
-void CanvasContext::setup(int width, int height, float lightRadius,
+void CanvasContext::setup(float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
-#if HWUI_NEW_OPS
mLightGeometry.radius = lightRadius;
mLightInfo.ambientShadowAlpha = ambientShadowAlpha;
mLightInfo.spotShadowAlpha = spotShadowAlpha;
-#else
- if (!mCanvas) return;
- mCanvas->initLight(lightRadius, ambientShadowAlpha, spotShadowAlpha);
-#endif
}
void CanvasContext::setLightCenter(const Vector3& lightCenter) {
-#if HWUI_NEW_OPS
mLightGeometry.center = lightCenter;
-#else
- if (!mCanvas) return;
- mCanvas->setLightCenter(lightCenter);
-#endif
}
void CanvasContext::setOpaque(bool opaque) {
@@ -184,14 +227,23 @@ void CanvasContext::setOpaque(bool opaque) {
bool CanvasContext::makeCurrent() {
if (mStopped) return false;
- // TODO: Figure out why this workaround is needed, see b/13913604
- // In the meantime this matches the behavior of GLRenderer, so it is not a regression
- EGLint error = 0;
- mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface, &error);
- if (error) {
- setSurface(nullptr);
+ auto result = mRenderPipeline->makeCurrent();
+ switch (result) {
+ case MakeCurrentResult::AlreadyCurrent:
+ return true;
+ case MakeCurrentResult::Failed:
+ mHaveNewSurface = true;
+ setSurface(nullptr);
+ return false;
+ case MakeCurrentResult::Succeeded:
+ mHaveNewSurface = true;
+ return true;
+ default:
+ LOG_ALWAYS_FATAL("unexpected result %d from IRenderPipeline::makeCurrent",
+ (int32_t) result);
}
- return !error;
+
+ return true;
}
static bool wasSkipped(FrameInfo* info) {
@@ -254,11 +306,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
mCurrentFrameInfo->markSyncStart();
info.damageAccumulator = &mDamageAccumulator;
-#if HWUI_NEW_OPS
info.layerUpdateQueue = &mLayerUpdateQueue;
-#else
- info.renderer = mCanvas;
-#endif
mAnimationContext->startFrame(info.mode);
for (const sp<RenderNode>& node : mRenderNodes) {
@@ -283,7 +331,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
return;
}
- if (CC_LIKELY(mSwapHistory.size())) {
+ if (CC_LIKELY(mSwapHistory.size() && !Properties::forceDrawFrame)) {
nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
SwapHistory& lastSwap = mSwapHistory.back();
nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
@@ -335,11 +383,6 @@ void CanvasContext::notifyFramePending() {
}
void CanvasContext::draw() {
-#if !HWUI_NEW_OPS
- LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
- "drawRenderNode called on a context with no canvas or surface!");
-#endif
-
SkRect dirty;
mDamageAccumulator.finish(&dirty);
@@ -351,215 +394,27 @@ void CanvasContext::draw() {
mCurrentFrameInfo->markIssueDrawCommandsStart();
- Frame frame = mEglManager.beginFrame(mEglSurface);
+ Frame frame = mRenderPipeline->getFrame();
- if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) {
- // can't rely on prior content of window if viewport size changes
- dirty.setEmpty();
- mLastFrameWidth = frame.width();
- mLastFrameHeight = frame.height();
- } else if (mHaveNewSurface || frame.bufferAge() == 0) {
- // New surface needs a full draw
- dirty.setEmpty();
- } else {
- if (!dirty.isEmpty() && !dirty.intersect(0, 0, frame.width(), frame.height())) {
- ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
- SK_RECT_ARGS(dirty), frame.width(), frame.height());
- dirty.setEmpty();
- }
- profiler().unionDirty(&dirty);
- }
+ SkRect windowDirty = computeDirtyRect(frame, &dirty);
- if (dirty.isEmpty()) {
- dirty.set(0, 0, frame.width(), frame.height());
- }
-
- // At this point dirty is the area of the screen to update. However,
- // the area of the frame we need to repaint is potentially different, so
- // stash the screen area for later
- SkRect screenDirty(dirty);
-
- // If the buffer age is 0 we do a full-screen repaint (handled above)
- // If the buffer age is 1 the buffer contents are the same as they were
- // last frame so there's nothing to union() against
- // Therefore we only care about the > 1 case.
- if (frame.bufferAge() > 1) {
- if (frame.bufferAge() > (int) mSwapHistory.size()) {
- // We don't have enough history to handle this old of a buffer
- // Just do a full-draw
- dirty.set(0, 0, frame.width(), frame.height());
- } else {
- // At this point we haven't yet added the latest frame
- // to the damage history (happens below)
- // So we need to damage
- for (int i = mSwapHistory.size() - 1;
- i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) {
- dirty.join(mSwapHistory[i].damage);
- }
- }
- }
-
- mEglManager.damageFrame(frame, dirty);
-
-#if HWUI_NEW_OPS
- auto& caches = Caches::getInstance();
- FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), mLightGeometry, caches);
-
- frameBuilder.deferLayers(mLayerUpdateQueue);
- mLayerUpdateQueue.clear();
-
- frameBuilder.deferRenderNodeScene(mRenderNodes, mContentDrawBounds);
-
- BakedOpRenderer renderer(caches, mRenderThread.renderState(),
- mOpaque, mLightInfo);
- frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
- profiler().draw(&renderer);
- bool drew = renderer.didDraw();
-
- // post frame cleanup
- caches.clearGarbage();
- caches.pathCache.trim();
- caches.tessellationCache.trim();
-
-#if DEBUG_MEMORY_USAGE
- mCaches.dumpMemoryUsage();
-#else
- if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) {
- caches.dumpMemoryUsage();
- }
-#endif
-
-#else
- mCanvas->prepareDirty(frame.width(), frame.height(),
- dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque);
-
- Rect outBounds;
- // It there are multiple render nodes, they are laid out as follows:
- // #0 - backdrop (content + caption)
- // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
- // #2 - additional overlay nodes
- // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
- // resizing however it might become partially visible. The following render loop will crop the
- // backdrop against the content and draw the remaining part of it. It will then draw the content
- // cropped to the backdrop (since that indicates a shrinking of the window).
- //
- // Additional nodes will be drawn on top with no particular clipping semantics.
-
- // The bounds of the backdrop against which the content should be clipped.
- Rect backdropBounds = mContentDrawBounds;
- // Usually the contents bounds should be mContentDrawBounds - however - we will
- // move it towards the fixed edge to give it a more stable appearance (for the moment).
- Rect contentBounds;
- // If there is no content bounds we ignore the layering as stated above and start with 2.
- int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() == 1) ? 2 : 0;
- // Draw all render nodes. Note that
- for (const sp<RenderNode>& node : mRenderNodes) {
- if (layer == 0) { // Backdrop.
- // Draw the backdrop clipped to the inverse content bounds, but assume that the content
- // was moved to the upper left corner.
- const RenderProperties& properties = node->properties();
- Rect targetBounds(properties.getLeft(), properties.getTop(),
- properties.getRight(), properties.getBottom());
- // Move the content bounds towards the fixed corner of the backdrop.
- const int x = targetBounds.left;
- const int y = targetBounds.top;
- contentBounds.set(x, y, x + mContentDrawBounds.getWidth(),
- y + mContentDrawBounds.getHeight());
- // Remember the intersection of the target bounds and the intersection bounds against
- // which we have to crop the content.
- backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
- backdropBounds.doIntersect(targetBounds);
- // Check if we have to draw something on the left side ...
- if (targetBounds.left < contentBounds.left) {
- mCanvas->save(SaveFlags::Clip);
- if (mCanvas->clipRect(targetBounds.left, targetBounds.top,
- contentBounds.left, targetBounds.bottom,
- SkRegion::kIntersect_Op)) {
- mCanvas->drawRenderNode(node.get(), outBounds);
- }
- // Reduce the target area by the area we have just painted.
- targetBounds.left = std::min(contentBounds.left, targetBounds.right);
- mCanvas->restore();
- }
- // ... or on the right side ...
- if (targetBounds.right > contentBounds.right &&
- !targetBounds.isEmpty()) {
- mCanvas->save(SaveFlags::Clip);
- if (mCanvas->clipRect(contentBounds.right, targetBounds.top,
- targetBounds.right, targetBounds.bottom,
- SkRegion::kIntersect_Op)) {
- mCanvas->drawRenderNode(node.get(), outBounds);
- }
- // Reduce the target area by the area we have just painted.
- targetBounds.right = std::max(targetBounds.left, contentBounds.right);
- mCanvas->restore();
- }
- // ... or at the top ...
- if (targetBounds.top < contentBounds.top &&
- !targetBounds.isEmpty()) {
- mCanvas->save(SaveFlags::Clip);
- if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right,
- contentBounds.top,
- SkRegion::kIntersect_Op)) {
- mCanvas->drawRenderNode(node.get(), outBounds);
- }
- // Reduce the target area by the area we have just painted.
- targetBounds.top = std::min(contentBounds.top, targetBounds.bottom);
- mCanvas->restore();
- }
- // ... or at the bottom.
- if (targetBounds.bottom > contentBounds.bottom &&
- !targetBounds.isEmpty()) {
- mCanvas->save(SaveFlags::Clip);
- if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right,
- targetBounds.bottom, SkRegion::kIntersect_Op)) {
- mCanvas->drawRenderNode(node.get(), outBounds);
- }
- mCanvas->restore();
- }
- } else if (layer == 1) { // Content
- // It gets cropped against the bounds of the backdrop to stay inside.
- mCanvas->save(SaveFlags::MatrixClip);
-
- // We shift and clip the content to match its final location in the window.
- const float left = mContentDrawBounds.left;
- const float top = mContentDrawBounds.top;
- const float dx = backdropBounds.left - left;
- const float dy = backdropBounds.top - top;
- const float width = backdropBounds.getWidth();
- const float height = backdropBounds.getHeight();
-
- mCanvas->translate(dx, dy);
- if (mCanvas->clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op)) {
- mCanvas->drawRenderNode(node.get(), outBounds);
- }
- mCanvas->restore();
- } else { // draw the rest on top at will!
- mCanvas->drawRenderNode(node.get(), outBounds);
- }
- layer++;
- }
-
- profiler().draw(mCanvas);
-
- bool drew = mCanvas->finish();
-#endif
+ bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
+ mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler()));
waitOnFences();
- GL_CHECKPOINT(LOW);
+ bool requireSwap = false;
+ bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo,
+ &requireSwap);
- // Even if we decided to cancel the frame, from the perspective of jank
- // metrics the frame was swapped at this point
- mCurrentFrameInfo->markSwapBuffers();
mIsDirty = false;
- if (drew || mEglManager.damageRequiresSwap()) {
- if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) {
+ if (requireSwap) {
+ if (!didSwap) { //some error happened
setSurface(nullptr);
}
SwapHistory& swap = mSwapHistory.next();
- swap.damage = screenDirty;
+ swap.damage = windowDirty;
swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC);
swap.vsyncTime = mRenderThread.timeLord().latestVsync();
if (mNativeSurface.get()) {
@@ -609,18 +464,18 @@ void CanvasContext::draw() {
GpuMemoryTracker::onFrameCompleted();
#ifdef BUGREPORT_FONT_CACHE_USAGE
- caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted();
+ auto renderType = Properties::getRenderPipelineType();
+ if (RenderPipelineType::OpenGL == renderType) {
+ Caches& caches = Caches::getInstance();
+ caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted();
+ }
#endif
}
// Called by choreographer to do an RT-driven animation
void CanvasContext::doFrame() {
-#if HWUI_NEW_OPS
- if (CC_UNLIKELY(mEglSurface == EGL_NO_SURFACE)) return;
-#else
- if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) return;
-#endif
+ if (!mRenderPipeline->isSurfaceReady()) return;
prepareAndDraw(nullptr);
}
@@ -643,16 +498,6 @@ void CanvasContext::prepareAndDraw(RenderNode* node) {
}
}
-void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
- ATRACE_CALL();
- DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
- if (thread.eglManager().hasEglContext()) {
- mode = DrawGlInfo::kModeProcess;
- }
-
- thread.renderState().invokeFunctor(functor, mode, nullptr);
-}
-
void CanvasContext::markLayerInUse(RenderNode* node) {
if (mPrefetchedLayers.erase(node)) {
node->decStrong(nullptr);
@@ -673,10 +518,7 @@ void CanvasContext::freePrefetchedLayers(TreeObserver* observer) {
void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) {
ATRACE_CALL();
- if (!mEglManager.hasEglContext()) return;
-#if !HWUI_NEW_OPS
- if (!mCanvas) return;
-#endif
+ if (!mRenderPipeline->isContextReady()) return;
// buildLayer() will leave the tree in an unknown state, so we must stop drawing
stopDrawing();
@@ -684,11 +526,7 @@ void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) {
TreeInfo info(TreeInfo::MODE_FULL, *this);
info.damageAccumulator = &mDamageAccumulator;
info.observer = observer;
-#if HWUI_NEW_OPS
info.layerUpdateQueue = &mLayerUpdateQueue;
-#else
- info.renderer = mCanvas;
-#endif
info.runAnimations = false;
node->prepareTree(info);
SkRect ignore;
@@ -697,41 +535,24 @@ void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) {
// purposes when the frame is actually drawn
node->setPropertyFieldsDirty(RenderNode::GENERIC);
-#if HWUI_NEW_OPS
- static const std::vector< sp<RenderNode> > emptyNodeList;
- auto& caches = Caches::getInstance();
- FrameBuilder frameBuilder(mLayerUpdateQueue, mLightGeometry, caches);
- mLayerUpdateQueue.clear();
- BakedOpRenderer renderer(caches, mRenderThread.renderState(),
- mOpaque, mLightInfo);
- LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case");
- frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
-#else
- mCanvas->markLayersAsBuildLayers();
- mCanvas->flushLayerUpdates();
-#endif
+ mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo);
node->incStrong(nullptr);
mPrefetchedLayers.insert(node);
}
bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
- layer->apply();
- return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
+ return mRenderPipeline->copyLayerInto(layer, bitmap);
}
void CanvasContext::destroyHardwareResources(TreeObserver* observer) {
stopDrawing();
- if (mEglManager.hasEglContext()) {
+ if (mRenderPipeline->isContextReady()) {
freePrefetchedLayers(observer);
for (const sp<RenderNode>& node : mRenderNodes) {
node->destroyHardwareResources(observer);
}
- Caches& caches = Caches::getInstance();
- // Make sure to release all the textures we were owning as there won't
- // be another draw
- caches.textureCache.resetMarkInUse(this);
- mRenderThread.renderState().flush(Caches::FlushMode::Layers);
+ mRenderPipeline->onDestroyHardwareResources();
}
}
@@ -748,20 +569,8 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) {
}
}
-void CanvasContext::runWithGlContext(RenderTask* task) {
- LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(),
- "GL context not initialized!");
- task->run();
-}
-
-Layer* CanvasContext::createTextureLayer() {
- mEglManager.initialize();
- return LayerRenderer::createTextureLayer(mRenderThread.renderState());
-}
-
-void CanvasContext::setTextureAtlas(RenderThread& thread,
- const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) {
- thread.eglManager().setTextureAtlas(buffer, map, mapSize);
+DeferredLayerUpdater* CanvasContext::createTextureLayer() {
+ return mRenderPipeline->createTextureLayer();
}
void CanvasContext::dumpFrames(int fd) {
@@ -836,8 +645,8 @@ void CanvasContext::waitOnFences() {
class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> {
public:
- FuncTaskProcessor(Caches& caches)
- : TaskProcessor<bool>(&caches.tasks) {}
+ explicit FuncTaskProcessor(TaskManager* taskManager)
+ : TaskProcessor<bool>(taskManager) {}
virtual void onProcess(const sp<Task<bool> >& task) override {
FuncTask* t = static_cast<FuncTask*>(task.get());
@@ -848,7 +657,7 @@ public:
void CanvasContext::enqueueFrameWork(std::function<void()>&& func) {
if (!mFrameWorkProcessor.get()) {
- mFrameWorkProcessor = new FuncTaskProcessor(Caches::getInstance());
+ mFrameWorkProcessor = new FuncTaskProcessor(mRenderPipeline->getTaskManager());
}
sp<FuncTask> task(new FuncTask());
task->func = func;
@@ -864,6 +673,56 @@ int64_t CanvasContext::getFrameNumber() {
return mFrameNumber;
}
+SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
+ if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) {
+ // can't rely on prior content of window if viewport size changes
+ dirty->setEmpty();
+ mLastFrameWidth = frame.width();
+ mLastFrameHeight = frame.height();
+ } else if (mHaveNewSurface || frame.bufferAge() == 0) {
+ // New surface needs a full draw
+ dirty->setEmpty();
+ } else {
+ if (!dirty->isEmpty() && !dirty->intersect(0, 0, frame.width(), frame.height())) {
+ ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
+ SK_RECT_ARGS(*dirty), frame.width(), frame.height());
+ dirty->setEmpty();
+ }
+ profiler().unionDirty(dirty);
+ }
+
+ if (dirty->isEmpty()) {
+ dirty->set(0, 0, frame.width(), frame.height());
+ }
+
+ // At this point dirty is the area of the window to update. However,
+ // the area of the frame we need to repaint is potentially different, so
+ // stash the screen area for later
+ SkRect windowDirty(*dirty);
+
+ // If the buffer age is 0 we do a full-screen repaint (handled above)
+ // If the buffer age is 1 the buffer contents are the same as they were
+ // last frame so there's nothing to union() against
+ // Therefore we only care about the > 1 case.
+ if (frame.bufferAge() > 1) {
+ if (frame.bufferAge() > (int) mSwapHistory.size()) {
+ // We don't have enough history to handle this old of a buffer
+ // Just do a full-draw
+ dirty->set(0, 0, frame.width(), frame.height());
+ } else {
+ // At this point we haven't yet added the latest frame
+ // to the damage history (happens below)
+ // So we need to damage
+ for (int i = mSwapHistory.size() - 1;
+ i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) {
+ dirty->join(mSwapHistory[i].damage);
+ }
+ }
+ }
+
+ return windowDirty;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index e1821751b57f..0174b86d22d5 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-#ifndef CANVASCONTEXT_H_
-#define CANVASCONTEXT_H_
+#pragma once
+#include "BakedOpDispatcher.h"
+#include "BakedOpRenderer.h"
#include "DamageAccumulator.h"
+#include "FrameBuilder.h"
#include "FrameInfo.h"
#include "FrameInfoVisualizer.h"
#include "FrameMetricsReporter.h"
#include "IContextFactory.h"
+#include "IRenderPipeline.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
#include "thread/Task.h"
@@ -30,12 +33,6 @@
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
-#if HWUI_NEW_OPS
-#include "BakedOpDispatcher.h"
-#include "BakedOpRenderer.h"
-#include "FrameBuilder.h"
-#endif
-
#include <cutils/compiler.h>
#include <EGL/egl.h>
#include <SkBitmap.h>
@@ -53,29 +50,71 @@ namespace uirenderer {
class AnimationContext;
class DeferredLayerUpdater;
-class OpenGLRenderer;
-class Rect;
class Layer;
+class Rect;
class RenderState;
namespace renderthread {
class EglManager;
-
-enum SwapBehavior {
- kSwap_default,
- kSwap_discardBuffer,
-};
+class Frame;
// This per-renderer class manages the bridge between the global EGL context
// and the render surface.
// TODO: Rename to Renderer or some other per-window, top-level manager
class CanvasContext : public IFrameCallback {
public:
- CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
- IContextFactory* contextFactory);
+ static CanvasContext* create(RenderThread& thread, bool translucent,
+ RenderNode* rootRenderNode, IContextFactory* contextFactory);
virtual ~CanvasContext();
+ /**
+ * Update or create a layer specific for the provided RenderNode. The layer
+ * attached to the node will be specific to the RenderPipeline used by this
+ * context
+ *
+ * @return true if the layer has been created or updated
+ */
+ bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator) {
+ return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator);
+ }
+
+ /**
+ * Pin any mutable images to the GPU cache. A pinned images is guaranteed to
+ * remain in the cache until it has been unpinned. We leverage this feature
+ * to avoid making a CPU copy of the pixels.
+ *
+ * @return true if all images have been successfully pinned to the GPU cache
+ * and false otherwise (e.g. cache limits have been exceeded).
+ */
+ bool pinImages(std::vector<SkImage*>& mutableImages) {
+ return mRenderPipeline->pinImages(mutableImages);
+ }
+ bool pinImages(LsaVector<sk_sp<Bitmap>>& images) {
+ return mRenderPipeline->pinImages(images);
+ }
+
+ /**
+ * Unpin any image that had be previously pinned to the GPU cache
+ */
+ void unpinImages() { mRenderPipeline->unpinImages(); }
+
+ /**
+ * Destroy any layers that have been attached to the provided RenderNode removing
+ * any state that may have been set during createOrUpdateLayer().
+ */
+ static void destroyLayer(RenderNode* node);
+
+ static void invokeFunctor(const RenderThread& thread, Functor* functor);
+
+ static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap);
+
+ /*
+ * If Properties::isSkiaEnabled() is true then this will return the Skia
+ * grContext associated with the current RenderPipeline.
+ */
+ GrContext* getGrContext() const { return mRenderThread.getGrContext(); }
+
// Won't take effect until next EGLSurface creation
void setSwapBehavior(SwapBehavior swapBehavior);
@@ -85,7 +124,7 @@ public:
void setStopped(bool stopped);
bool hasSurface() { return mNativeSurface.get(); }
- void setup(int width, int height, float lightRadius,
+ void setup(float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
void setLightCenter(const Vector3& lightCenter);
void setOpaque(bool opaque);
@@ -106,14 +145,7 @@ public:
void destroyHardwareResources(TreeObserver* observer);
static void trimMemory(RenderThread& thread, int level);
- static void invokeFunctor(RenderThread& thread, Functor* functor);
-
- void runWithGlContext(RenderTask* task);
-
- Layer* createTextureLayer();
-
- ANDROID_API static void setTextureAtlas(RenderThread& thread,
- const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
+ DeferredLayerUpdater* createTextureLayer();
void stopDrawing();
void notifyFramePending();
@@ -171,6 +203,9 @@ public:
void waitOnFences();
private:
+ CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
+ IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
+
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
// lifecycle tracking
@@ -182,21 +217,20 @@ private:
bool isSwapChainStuffed();
+ SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
+
EGLint mLastFrameWidth = 0;
EGLint mLastFrameHeight = 0;
RenderThread& mRenderThread;
- EglManager& mEglManager;
sp<Surface> mNativeSurface;
- EGLSurface mEglSurface = EGL_NO_SURFACE;
// stopped indicates the CanvasContext will reject actual redraw operations,
// and defer repaint until it is un-stopped
bool mStopped = false;
// CanvasContext is dirty if it has received an update that it has not
// painted onto its surface.
bool mIsDirty = false;
- bool mBufferPreserved = false;
- SwapBehavior mSwapBehavior = kSwap_default;
+ SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default;
struct SwapHistory {
SkRect damage;
nsecs_t vsyncTime;
@@ -212,12 +246,8 @@ private:
nsecs_t mLastDropVsync = 0;
bool mOpaque;
-#if HWUI_NEW_OPS
BakedOpRenderer::LightInfo mLightInfo;
FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 };
-#else
- OpenGLRenderer* mCanvas = nullptr;
-#endif
bool mHaveNewSurface = false;
DamageAccumulator mDamageAccumulator;
@@ -248,9 +278,9 @@ private:
std::vector< sp<FuncTask> > mFrameFences;
sp<TaskProcessor<bool> > mFrameWorkProcessor;
+ std::unique_ptr<IRenderPipeline> mRenderPipeline;
};
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
-#endif /* CANVASCONTEXT_H_ */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index e3b6dc6fd9fe..4ff54a5299f8 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -119,7 +119,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) {
int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
mRenderThread->timeLord().vsyncReceived(vsync);
bool canDraw = mContext->makeCurrent();
- Caches::getInstance().textureCache.resetMarkInUse(mContext);
+ mContext->unpinImages();
for (size_t i = 0; i < mLayers.size(); i++) {
mLayers[i]->apply();
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 99bc9a75e94b..7020be073ee0 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -24,10 +24,19 @@
#include "Caches.h"
#include "DeviceInfo.h"
+#include "Frame.h"
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
+#include "Texture.h"
+
#include <EGL/eglext.h>
+#include <GrContextOptions.h>
+#include <gl/GrGLInterface.h>
+
+#ifdef HWUI_GLES_WRAP_ENABLED
+#include "debug/GlesDriver.h"
+#endif
#define GLES_VERSION 2
@@ -60,7 +69,7 @@ static const char* egl_error_str(EGLint error) {
return "Unknown error";
}
}
-static const char* egl_error_str() {
+const char* EglManager::eglErrorString() {
return egl_error_str(eglGetError());
}
@@ -69,33 +78,13 @@ static struct {
bool setDamage = false;
} EglExtensions;
-void Frame::map(const SkRect& in, EGLint* out) const {
- /* The rectangles are specified relative to the bottom-left of the surface
- * and the x and y components of each rectangle specify the bottom-left
- * position of that rectangle.
- *
- * HWUI does everything with 0,0 being top-left, so need to map
- * the rect
- */
- SkIRect idirty;
- in.roundOut(&idirty);
- EGLint y = mHeight - (idirty.y() + idirty.height());
- // layout: {x, y, width, height}
- out[0] = idirty.x();
- out[1] = y;
- out[2] = idirty.width();
- out[3] = idirty.height();
-}
-
EglManager::EglManager(RenderThread& thread)
: mRenderThread(thread)
, mEglDisplay(EGL_NO_DISPLAY)
, mEglConfig(nullptr)
, mEglContext(EGL_NO_CONTEXT)
, mPBufferSurface(EGL_NO_SURFACE)
- , mCurrentSurface(EGL_NO_SURFACE)
- , mAtlasMap(nullptr)
- , mAtlasMapSize(0) {
+ , mCurrentSurface(EGL_NO_SURFACE) {
}
void EglManager::initialize() {
@@ -105,11 +94,11 @@ void EglManager::initialize() {
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
- "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
+ "Failed to get EGL_DEFAULT_DISPLAY! err=%s", eglErrorString());
EGLint major, minor;
LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
- "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
+ "Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());
ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
@@ -130,7 +119,22 @@ void EglManager::initialize() {
makeCurrent(mPBufferSurface);
DeviceInfo::initialize();
mRenderThread.renderState().onGLContextCreated();
- initAtlas();
+
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+#ifdef HWUI_GLES_WRAP_ENABLED
+ debug::GlesDriver* driver = debug::GlesDriver::get();
+ sk_sp<const GrGLInterface> glInterface(driver->getSkiaInterface());
+#else
+ sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+#endif
+ LOG_ALWAYS_FATAL_IF(!glInterface.get());
+
+ GrContextOptions options;
+ options.fDisableDistanceFieldPaths = true;
+ options.fAllowPathMaskCaching = true;
+ mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend,
+ (GrBackendContext)glInterface.get(), options));
+ }
}
void EglManager::initExtensions() {
@@ -178,7 +182,7 @@ void EglManager::loadConfig() {
loadConfig();
} else {
// Failed to get a valid config
- LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
+ LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString());
}
}
}
@@ -190,33 +194,7 @@ void EglManager::createContext() {
};
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
- "Failed to create context, error = %s", egl_error_str());
-}
-
-void EglManager::setTextureAtlas(const sp<GraphicBuffer>& buffer,
- int64_t* map, size_t mapSize) {
-
- // Already initialized
- if (mAtlasBuffer.get()) {
- ALOGW("Multiple calls to setTextureAtlas!");
- delete map;
- return;
- }
-
- mAtlasBuffer = buffer;
- mAtlasMap = map;
- mAtlasMapSize = mapSize;
-
- if (hasEglContext()) {
- initAtlas();
- }
-}
-
-void EglManager::initAtlas() {
- if (mAtlasBuffer.get()) {
- mRenderThread.renderState().assetAtlas().init(mAtlasBuffer,
- mAtlasMap, mAtlasMapSize);
- }
+ "Failed to create context, error = %s", eglErrorString());
}
void EglManager::createPBufferSurface() {
@@ -231,15 +209,24 @@ void EglManager::createPBufferSurface() {
EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
initialize();
- EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, nullptr);
+
+ EGLint attribs[] = {
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
+ EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
+ EGL_COLORSPACE, EGL_COLORSPACE_sRGB,
+#endif
+ EGL_NONE
+ };
+
+ EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, attribs);
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
"Failed to create EGLSurface for window %p, eglErr = %s",
- (void*) window, egl_error_str());
+ (void*) window, eglErrorString());
if (mSwapBehavior != SwapBehavior::Preserved) {
LOG_ALWAYS_FATAL_IF(eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) == EGL_FALSE,
"Failed to set swap behavior to destroyed for window %p, eglErr = %s",
- (void*) window, egl_error_str());
+ (void*) window, eglErrorString());
}
return surface;
@@ -250,13 +237,14 @@ void EglManager::destroySurface(EGLSurface surface) {
makeCurrent(EGL_NO_SURFACE);
}
if (!eglDestroySurface(mEglDisplay, surface)) {
- ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
+ ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, eglErrorString());
}
}
void EglManager::destroy() {
if (mEglDisplay == EGL_NO_DISPLAY) return;
+ mRenderThread.setGrContext(nullptr);
mRenderThread.renderState().onGLContextDestroyed();
eglDestroyContext(mEglDisplay, mEglContext);
eglDestroySurface(mEglDisplay, mPBufferSurface);
@@ -284,7 +272,7 @@ bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
(void*)surface, egl_error_str(*errOut));
} else {
LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
- (void*)surface, egl_error_str());
+ (void*)surface, eglErrorString());
}
}
mCurrentSurface = surface;
@@ -325,7 +313,7 @@ void EglManager::damageFrame(const Frame& frame, const SkRect& dirty) {
frame.map(dirty, rects);
if (!eglSetDamageRegionKHR(mEglDisplay, frame.mSurface, rects, 1)) {
LOG_ALWAYS_FATAL("Failed to set damage region on surface %p, error=%s",
- (void*)frame.mSurface, egl_error_str());
+ (void*)frame.mSurface, eglErrorString());
}
}
#endif
@@ -379,14 +367,14 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) {
preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
if (!preserved) {
ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
- (void*) surface, egl_error_str());
+ (void*) surface, eglErrorString());
// Maybe it's already set?
EGLint swapBehavior;
if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) {
preserved = (swapBehavior == EGL_BUFFER_PRESERVED);
} else {
ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
- (void*) surface, egl_error_str());
+ (void*) surface, eglErrorString());
}
}
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 41047fecf960..025192511cd9 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -26,35 +26,14 @@ namespace android {
namespace uirenderer {
namespace renderthread {
+class Frame;
class RenderThread;
-class EglManager;
-
-class Frame {
-public:
- EGLint width() const { return mWidth; }
- EGLint height() const { return mHeight; }
-
- // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt
- // for what this means
- EGLint bufferAge() const { return mBufferAge; }
-
-private:
- friend class EglManager;
-
- EGLSurface mSurface;
- EGLint mWidth;
- EGLint mHeight;
- EGLint mBufferAge;
-
- // Maps from 0,0 in top-left to 0,0 in bottom-left
- // If out is not an EGLint[4] you're going to have a bad time
- void map(const SkRect& in, EGLint* out) const;
-};
// This class contains the shared global EGL objects, such as EGLDisplay
// and EGLConfig, which are re-used by CanvasContext
class EglManager {
public:
+ static const char* eglErrorString();
// Returns true on success, false on failure
void initialize();
@@ -79,13 +58,10 @@ public:
// Returns true iff the surface is now preserving buffers.
bool setPreserveBuffer(EGLSurface surface, bool preserve);
- void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
-
void fence();
private:
friend class RenderThread;
-
explicit EglManager(RenderThread& thread);
// EglContext is never destroyed, method is purposely not implemented
~EglManager();
@@ -94,7 +70,6 @@ private:
void createPBufferSurface();
void loadConfig();
void createContext();
- void initAtlas();
EGLint queryBufferAge(EGLSurface surface);
RenderThread& mRenderThread;
@@ -106,10 +81,6 @@ private:
EGLSurface mCurrentSurface;
- sp<GraphicBuffer> mAtlasBuffer;
- int64_t* mAtlasMap;
- size_t mAtlasMapSize;
-
enum class SwapBehavior {
Discard,
Preserved,
diff --git a/libs/hwui/renderthread/Frame.cpp b/libs/hwui/renderthread/Frame.cpp
new file mode 100644
index 000000000000..126bb093b4eb
--- /dev/null
+++ b/libs/hwui/renderthread/Frame.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Frame.h"
+#include <SkRect.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+void Frame::map(const SkRect& in, int32_t* out) const {
+ /* The rectangles are specified relative to the bottom-left of the surface
+ * and the x and y components of each rectangle specify the bottom-left
+ * position of that rectangle.
+ *
+ * HWUI does everything with 0,0 being top-left, so need to map
+ * the rect
+ */
+ SkIRect idirty;
+ in.roundOut(&idirty);
+ int32_t y = mHeight - (idirty.y() + idirty.height());
+ // layout: {x, y, width, height}
+ out[0] = idirty.x();
+ out[1] = y;
+ out[2] = idirty.width();
+ out[3] = idirty.height();
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/Frame.h b/libs/hwui/renderthread/Frame.h
new file mode 100644
index 000000000000..99996fe40626
--- /dev/null
+++ b/libs/hwui/renderthread/Frame.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+struct SkRect;
+typedef void *EGLSurface;
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class Frame {
+public:
+ Frame(int32_t width, int32_t height, int32_t bufferAge)
+ : mWidth(width)
+ , mHeight(height)
+ , mBufferAge(bufferAge) { }
+
+ int32_t width() const { return mWidth; }
+ int32_t height() const { return mHeight; }
+
+ // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt
+ // for what this means
+ int32_t bufferAge() const { return mBufferAge; }
+
+private:
+ Frame() {}
+ friend class EglManager;
+
+ int32_t mWidth;
+ int32_t mHeight;
+ int32_t mBufferAge;
+
+ EGLSurface mSurface;
+
+ // Maps from 0,0 in top-left to 0,0 in bottom-left
+ // If out is not an int32_t[4] you're going to have a bad time
+ void map(const SkRect& in, int32_t* out) const;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
new file mode 100644
index 000000000000..45f6718a68fb
--- /dev/null
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FrameInfoVisualizer.h"
+
+#include <SkRect.h>
+#include <utils/RefBase.h>
+
+class GrContext;
+
+namespace android {
+
+class Surface;
+
+namespace uirenderer {
+
+class DeferredLayerUpdater;
+
+namespace renderthread {
+
+enum class SwapBehavior {
+ kSwap_default,
+ kSwap_discardBuffer,
+};
+
+enum class MakeCurrentResult {
+ AlreadyCurrent,
+ Failed,
+ Succeeded
+};
+
+class Frame;
+
+class IRenderPipeline {
+public:
+ virtual MakeCurrentResult makeCurrent() = 0;
+ virtual Frame getFrame() = 0;
+ virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector< sp<RenderNode> >& renderNodes,
+ FrameInfoVisualizer* profiler) = 0;
+ virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
+ FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
+ virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0;
+ virtual DeferredLayerUpdater* createTextureLayer() = 0;
+ virtual bool setSurface(Surface* window, SwapBehavior swapBehavior) = 0;
+ virtual void onStop() = 0;
+ virtual bool isSurfaceReady() = 0;
+ virtual bool isContextReady() = 0;
+ virtual void onDestroyHardwareResources() = 0;
+ virtual void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo) = 0;
+ virtual TaskManager* getTaskManager() = 0;
+ virtual bool createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) = 0;
+ virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0;
+ virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0;
+ virtual void unpinImages() = 0;
+
+ virtual ~IRenderPipeline() {}
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
new file mode 100644
index 000000000000..df40a44a16cb
--- /dev/null
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "OpenGLPipeline.h"
+
+#include "DeferredLayerUpdater.h"
+#include "EglManager.h"
+#include "Frame.h"
+#include "GlLayer.h"
+#include "ProfileRenderer.h"
+#include "renderstate/RenderState.h"
+#include "OpenGLReadback.h"
+
+#include <android/native_window.h>
+#include <cutils/properties.h>
+#include <strings.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+OpenGLPipeline::OpenGLPipeline(RenderThread& thread)
+ : mEglManager(thread.eglManager())
+ , mRenderThread(thread) {
+}
+
+MakeCurrentResult OpenGLPipeline::makeCurrent() {
+ // TODO: Figure out why this workaround is needed, see b/13913604
+ // In the meantime this matches the behavior of GLRenderer, so it is not a regression
+ EGLint error = 0;
+ bool haveNewSurface = mEglManager.makeCurrent(mEglSurface, &error);
+
+ Caches::getInstance().textureCache.resetMarkInUse(this);
+ if (!haveNewSurface) {
+ return MakeCurrentResult::AlreadyCurrent;
+ }
+ return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;
+}
+
+Frame OpenGLPipeline::getFrame() {
+ LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+ "drawRenderNode called on a context with no surface!");
+ return mEglManager.beginFrame(mEglSurface);
+}
+
+bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector< sp<RenderNode> >& renderNodes,
+ FrameInfoVisualizer* profiler) {
+
+ mEglManager.damageFrame(frame, dirty);
+
+ bool drew = false;
+
+
+ auto& caches = Caches::getInstance();
+ FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), lightGeometry, caches);
+
+ frameBuilder.deferLayers(*layerUpdateQueue);
+ layerUpdateQueue->clear();
+
+ frameBuilder.deferRenderNodeScene(renderNodes, contentDrawBounds);
+
+ BakedOpRenderer renderer(caches, mRenderThread.renderState(),
+ opaque, lightInfo);
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+ ProfileRenderer profileRenderer(renderer);
+ profiler->draw(profileRenderer);
+ drew = renderer.didDraw();
+
+ // post frame cleanup
+ caches.clearGarbage();
+ caches.pathCache.trim();
+ caches.tessellationCache.trim();
+
+#if DEBUG_MEMORY_USAGE
+ caches.dumpMemoryUsage();
+#else
+ if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) {
+ caches.dumpMemoryUsage();
+ }
+#endif
+
+ return drew;
+}
+
+bool OpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
+ FrameInfo* currentFrameInfo, bool* requireSwap) {
+
+ GL_CHECKPOINT(LOW);
+
+ // Even if we decided to cancel the frame, from the perspective of jank
+ // metrics the frame was swapped at this point
+ currentFrameInfo->markSwapBuffers();
+
+ *requireSwap = drew || mEglManager.damageRequiresSwap();
+
+ if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
+ return false;
+ }
+
+ return *requireSwap;
+}
+
+bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
+ ATRACE_CALL();
+ layer->apply();
+ return OpenGLReadbackImpl::copyLayerInto(mRenderThread,
+ static_cast<GlLayer&>(*layer->backingLayer()), bitmap);
+}
+
+DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() {
+ mEglManager.initialize();
+ GlLayer* layer = new GlLayer(mRenderThread.renderState(), 0, 0);
+ Caches::getInstance().textureState().activateTexture(0);
+ layer->generateTexture();
+
+ return new DeferredLayerUpdater(layer);
+}
+
+void OpenGLPipeline::onStop() {
+ if (mEglManager.isCurrent(mEglSurface)) {
+ mEglManager.makeCurrent(EGL_NO_SURFACE);
+ }
+}
+
+bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mEglManager.destroySurface(mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+ }
+
+ if (surface) {
+ mEglSurface = mEglManager.createSurface(surface);
+ }
+
+ if (mEglSurface != EGL_NO_SURFACE) {
+ const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
+ mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
+ return true;
+ }
+
+ return false;
+}
+
+bool OpenGLPipeline::isSurfaceReady() {
+ return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);
+}
+
+bool OpenGLPipeline::isContextReady() {
+ return CC_LIKELY(mEglManager.hasEglContext());
+}
+
+void OpenGLPipeline::onDestroyHardwareResources() {
+ Caches& caches = Caches::getInstance();
+ // Make sure to release all the textures we were owning as there won't
+ // be another draw
+ caches.textureCache.resetMarkInUse(this);
+ mRenderThread.renderState().flush(Caches::FlushMode::Layers);
+}
+
+void OpenGLPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo) {
+ static const std::vector< sp<RenderNode> > emptyNodeList;
+ auto& caches = Caches::getInstance();
+ FrameBuilder frameBuilder(*layerUpdateQueue, lightGeometry, caches);
+ layerUpdateQueue->clear();
+ BakedOpRenderer renderer(caches, mRenderThread.renderState(),
+ opaque, lightInfo);
+ LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case");
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+}
+
+TaskManager* OpenGLPipeline::getTaskManager() {
+ return &Caches::getInstance().tasks;
+}
+
+static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) {
+ return layer->viewportWidth == (uint32_t)width && layer->viewportHeight == (uint32_t)height;
+}
+
+bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) {
+ RenderState& renderState = mRenderThread.renderState();
+ OffscreenBufferPool& layerPool = renderState.layerPool();
+ bool transformUpdateNeeded = false;
+ if (node->getLayer() == nullptr) {
+ node->setLayer(layerPool.get(renderState, node->getWidth(), node->getHeight()));
+ transformUpdateNeeded = true;
+ } else if (!layerMatchesWH(node->getLayer(), node->getWidth(), node->getHeight())) {
+ // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
+ // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
+ if (node->properties().fitsOnLayer()) {
+ node->setLayer(layerPool.resize(node->getLayer(), node->getWidth(), node->getHeight()));
+ } else {
+ destroyLayer(node);
+ }
+ transformUpdateNeeded = true;
+ }
+
+ if (transformUpdateNeeded && node->getLayer()) {
+ // update the transform in window of the layer to reset its origin wrt light source position
+ Matrix4 windowTransform;
+ damageAccumulator.computeCurrentTransform(&windowTransform);
+ node->getLayer()->setWindowTransform(windowTransform);
+ }
+
+ return transformUpdateNeeded;
+}
+
+bool OpenGLPipeline::pinImages(LsaVector<sk_sp<Bitmap>>& images) {
+ TextureCache& cache = Caches::getInstance().textureCache;
+ bool prefetchSucceeded = true;
+ for (auto& bitmapResource : images) {
+ prefetchSucceeded &= cache.prefetchAndMarkInUse(this, bitmapResource.get());
+ }
+ return prefetchSucceeded;
+}
+
+void OpenGLPipeline::unpinImages() {
+ Caches::getInstance().textureCache.resetMarkInUse(this);
+}
+
+void OpenGLPipeline::destroyLayer(RenderNode* node) {
+ if (OffscreenBuffer* layer = node->getLayer()) {
+ layer->renderState.layerPool().putOrDelete(layer);
+ node->setLayer(nullptr);
+ }
+}
+
+void OpenGLPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
+ if (Caches::hasInstance() && thread.eglManager().hasEglContext()) {
+ ATRACE_NAME("Bitmap#prepareToDraw task");
+ Caches::getInstance().textureCache.prefetch(bitmap);
+ }
+}
+
+void OpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (thread.eglManager().hasEglContext()) {
+ mode = DrawGlInfo::kModeProcess;
+ }
+ thread.renderState().invokeFunctor(functor, mode, nullptr);
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h
new file mode 100644
index 000000000000..6df8be477e9c
--- /dev/null
+++ b/libs/hwui/renderthread/OpenGLPipeline.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "CanvasContext.h"
+#include "BakedOpDispatcher.h"
+#include "BakedOpRenderer.h"
+#include "FrameBuilder.h"
+#include "IRenderPipeline.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class OpenGLPipeline : public IRenderPipeline {
+public:
+ OpenGLPipeline(RenderThread& thread);
+ virtual ~OpenGLPipeline() {}
+
+ MakeCurrentResult makeCurrent() override;
+ Frame getFrame() override;
+ bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
+ const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo,
+ const std::vector< sp<RenderNode> >& renderNodes,
+ FrameInfoVisualizer* profiler) override;
+ bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
+ FrameInfo* currentFrameInfo, bool* requireSwap) override;
+ bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
+ DeferredLayerUpdater* createTextureLayer() override;
+ bool setSurface(Surface* window, SwapBehavior swapBehavior) override;
+ void onStop() override;
+ bool isSurfaceReady() override;
+ bool isContextReady() override;
+ void onDestroyHardwareResources() override;
+ void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ const BakedOpRenderer::LightInfo& lightInfo) override;
+ TaskManager* getTaskManager() override;
+ bool createOrUpdateLayer(RenderNode* node,
+ const DamageAccumulator& damageAccumulator) override;
+ bool pinImages(std::vector<SkImage*>& mutableImages) override { return false; }
+ bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override;
+ void unpinImages() override;
+ static void destroyLayer(RenderNode* node);
+ static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap);
+ static void invokeFunctor(const RenderThread& thread, Functor* functor);
+
+private:
+ EglManager& mEglManager;
+ EGLSurface mEglSurface = EGL_NO_SURFACE;
+ bool mBufferPreserved = false;
+ RenderThread& mRenderThread;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index a734401a2be6..022e871315a9 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -18,7 +18,6 @@
#include "DeferredLayerUpdater.h"
#include "DisplayList.h"
-#include "LayerRenderer.h"
#include "Readback.h"
#include "Rect.h"
#include "renderthread/CanvasContext.h"
@@ -28,6 +27,8 @@
#include "utils/Macros.h"
#include "utils/TimeUtils.h"
+#include <ui/GraphicBuffer.h>
+
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -59,7 +60,7 @@ namespace renderthread {
CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory) {
- return new CanvasContext(*args->thread, args->translucent,
+ return CanvasContext::create(*args->thread, args->translucent,
args->rootRenderNode, args->contextFactory);
}
@@ -184,19 +185,17 @@ void RenderProxy::setStopped(bool stopped) {
postAndWait(task);
}
-CREATE_BRIDGE6(setup, CanvasContext* context, int width, int height,
+CREATE_BRIDGE4(setup, CanvasContext* context,
float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
- args->context->setup(args->width, args->height, args->lightRadius,
+ args->context->setup(args->lightRadius,
args->ambientShadowAlpha, args->spotShadowAlpha);
return nullptr;
}
-void RenderProxy::setup(int width, int height, float lightRadius,
+void RenderProxy::setup(float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
SETUP_TASK(setup);
args->context = mContext;
- args->width = width;
- args->height = height;
args->lightRadius = lightRadius;
args->ambientShadowAlpha = ambientShadowAlpha;
args->spotShadowAlpha = spotShadowAlpha;
@@ -271,22 +270,8 @@ void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
}
}
-CREATE_BRIDGE2(runWithGlContext, CanvasContext* context, RenderTask* task) {
- args->context->runWithGlContext(args->task);
- return nullptr;
-}
-
-void RenderProxy::runWithGlContext(RenderTask* gltask) {
- SETUP_TASK(runWithGlContext);
- args->context = mContext;
- args->task = gltask;
- postAndWait(task);
-}
-
CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) {
- Layer* layer = args->context->createTextureLayer();
- if (!layer) return nullptr;
- return new DeferredLayerUpdater(layer);
+ return args->context->createTextureLayer();
}
DeferredLayerUpdater* RenderProxy::createTextureLayer() {
@@ -460,6 +445,19 @@ void RenderProxy::resetProfileInfo() {
postAndWait(task);
}
+CREATE_BRIDGE2(frameTimePercentile, RenderThread* thread, int percentile) {
+ return reinterpret_cast<void*>(static_cast<uintptr_t>(
+ args->thread->jankTracker().findPercentile(args->percentile)));
+}
+
+uint32_t RenderProxy::frameTimePercentile(int p) {
+ SETUP_TASK(frameTimePercentile);
+ args->thread = &mRenderThread;
+ args->percentile = p;
+ return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(
+ postAndWait(task)));
+}
+
CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) {
args->thread->jankTracker().dump(args->fd);
@@ -471,11 +469,7 @@ CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) {
} else {
fprintf(file, "\nNo caches instance.\n");
}
-#if HWUI_NEW_OPS
fprintf(file, "\nPipeline=FrameBuilder\n");
-#else
- fprintf(file, "\nPipeline=OpenGLRenderer\n");
-#endif
fflush(file);
return nullptr;
}
@@ -488,23 +482,6 @@ void RenderProxy::dumpGraphicsMemory(int fd) {
staticPostAndWait(task);
}
-CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map,
- size_t size) {
- CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size);
- args->buffer->decStrong(nullptr);
- return nullptr;
-}
-
-void RenderProxy::setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size) {
- SETUP_TASK(setTextureAtlas);
- args->thread = &mRenderThread;
- args->buffer = buffer.get();
- args->buffer->incStrong(nullptr);
- args->map = map;
- args->size = size;
- post(task);
-}
-
CREATE_BRIDGE2(setProcessStatsBuffer, RenderThread* thread, int fd) {
args->thread->jankTracker().switchStorageToAshmem(args->fd);
close(args->fd);
@@ -625,32 +602,31 @@ void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observer) {
post(task);
}
-CREATE_BRIDGE3(copySurfaceInto, RenderThread* thread,
- Surface* surface, SkBitmap* bitmap) {
- return (void*) Readback::copySurfaceInto(*args->thread,
- *args->surface, args->bitmap);
+CREATE_BRIDGE4(copySurfaceInto, RenderThread* thread,
+ Surface* surface, Rect srcRect, SkBitmap* bitmap) {
+ return (void*)args->thread->readback().copySurfaceInto(*args->surface,
+ args->srcRect, args->bitmap);
}
-int RenderProxy::copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap) {
+int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top,
+ int right, int bottom, SkBitmap* bitmap) {
SETUP_TASK(copySurfaceInto);
args->bitmap = bitmap;
args->surface = surface.get();
args->thread = &RenderThread::getInstance();
+ args->srcRect.set(left, top, right, bottom);
return static_cast<int>(
reinterpret_cast<intptr_t>( staticPostAndWait(task) ));
}
-CREATE_BRIDGE2(prepareToDraw, RenderThread* thread, SkBitmap* bitmap) {
- if (Caches::hasInstance() && args->thread->eglManager().hasEglContext()) {
- ATRACE_NAME("Bitmap#prepareToDraw task");
- Caches::getInstance().textureCache.prefetch(args->bitmap);
- }
- delete args->bitmap;
+CREATE_BRIDGE2(prepareToDraw, RenderThread* thread, Bitmap* bitmap) {
+ CanvasContext::prepareToDraw(*args->thread, args->bitmap);
+ args->bitmap->unref();
args->bitmap = nullptr;
return nullptr;
}
-void RenderProxy::prepareToDraw(const SkBitmap& bitmap) {
+void RenderProxy::prepareToDraw(Bitmap& bitmap) {
// If we haven't spun up a hardware accelerated window yet, there's no
// point in precaching these bitmaps as it can't impact jank.
// We also don't know if we even will spin up a hardware-accelerated
@@ -659,7 +635,8 @@ void RenderProxy::prepareToDraw(const SkBitmap& bitmap) {
RenderThread* renderThread = &RenderThread::getInstance();
SETUP_TASK(prepareToDraw);
args->thread = renderThread;
- args->bitmap = new SkBitmap(bitmap);
+ bitmap.ref();
+ args->bitmap = &bitmap;
nsecs_t lastVsync = renderThread->timeLord().latestVsync();
nsecs_t estimatedNextVsync = lastVsync + renderThread->timeLord().frameIntervalNanos();
nsecs_t timeToNextVsync = estimatedNextVsync - systemTime(CLOCK_MONOTONIC);
@@ -675,6 +652,31 @@ void RenderProxy::prepareToDraw(const SkBitmap& bitmap) {
}
}
+CREATE_BRIDGE2(allocateHardwareBitmap, RenderThread* thread, SkBitmap* bitmap) {
+ sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(*args->thread, *args->bitmap);
+ return hardwareBitmap.release();
+}
+
+sk_sp<Bitmap> RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) {
+ SETUP_TASK(allocateHardwareBitmap);
+ args->bitmap = &bitmap;
+ args->thread = &RenderThread::getInstance();
+ sk_sp<Bitmap> hardwareBitmap(reinterpret_cast<Bitmap*>(staticPostAndWait(task)));
+ return hardwareBitmap;
+}
+
+CREATE_BRIDGE3(copyGraphicBufferInto, RenderThread* thread, GraphicBuffer* buffer, SkBitmap* bitmap) {
+ return (void*) args->thread->readback().copyGraphicBufferInto(args->buffer, args->bitmap);
+}
+
+int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) {
+ SETUP_TASK(copyGraphicBufferInto);
+ args->thread = &RenderThread::getInstance();
+ args->bitmap = bitmap;
+ args->buffer = buffer;
+ return static_cast<int>(reinterpret_cast<intptr_t>(staticPostAndWait(task)));
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index bb111bd0de25..44a5a147b6da 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -35,6 +35,8 @@
#include "DrawFrameTask.h"
namespace android {
+class GraphicBuffer;
+
namespace uirenderer {
class DeferredLayerUpdater;
@@ -80,7 +82,7 @@ public:
ANDROID_API void updateSurface(const sp<Surface>& surface);
ANDROID_API bool pauseSurface(const sp<Surface>& surface);
ANDROID_API void setStopped(bool stopped);
- ANDROID_API void setup(int width, int height, float lightRadius,
+ ANDROID_API void setup(float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
ANDROID_API void setLightCenter(const Vector3& lightCenter);
ANDROID_API void setOpaque(bool opaque);
@@ -90,8 +92,6 @@ public:
ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
- ANDROID_API void runWithGlContext(RenderTask* task);
-
ANDROID_API DeferredLayerUpdater* createTextureLayer();
ANDROID_API void buildLayer(RenderNode* node, TreeObserver* observer);
ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap);
@@ -111,9 +111,9 @@ public:
ANDROID_API void dumpProfileInfo(int fd, int dumpFlags);
// Not exported, only used for testing
void resetProfileInfo();
+ uint32_t frameTimePercentile(int p);
ANDROID_API static void dumpGraphicsMemory(int fd);
- ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
ANDROID_API void setProcessStatsBuffer(int fd);
ANDROID_API int getRenderThreadTid();
@@ -128,9 +128,13 @@ public:
ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
ANDROID_API long getDroppedFrameReportCount();
- ANDROID_API static int copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap);
- ANDROID_API static void prepareToDraw(const SkBitmap& bitmap);
+ ANDROID_API static int copySurfaceInto(sp<Surface>& surface,
+ int left, int top, int right, int bottom, SkBitmap* bitmap);
+ ANDROID_API static void prepareToDraw(Bitmap& bitmap);
+
+ static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
+ static int copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap);
private:
RenderThread& mRenderThread;
CanvasContext* mContext;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 9c10c4fdbad1..e13d0ce6d688 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -17,9 +17,12 @@
#include "RenderThread.h"
#include "../renderstate/RenderState.h"
+#include "../pipeline/skia/SkiaOpenGLReadback.h"
#include "CanvasContext.h"
#include "EglManager.h"
+#include "OpenGLReadback.h"
#include "RenderProxy.h"
+#include "VulkanManager.h"
#include "utils/FatVector.h"
#include <gui/DisplayEventReceiver.h>
@@ -158,7 +161,8 @@ RenderThread::RenderThread() : Thread(true)
, mFrameCallbackTaskPending(false)
, mFrameCallbackTask(nullptr)
, mRenderState(nullptr)
- , mEglManager(nullptr) {
+ , mEglManager(nullptr)
+ , mVkManager(nullptr) {
Properties::load();
mFrameCallbackTask = new DispatchFrameCallbacks(this);
mLooper = new Looper(false);
@@ -192,6 +196,31 @@ void RenderThread::initThreadLocals() {
mEglManager = new EglManager(*this);
mRenderState = new RenderState(*this);
mJankTracker = new JankTracker(mDisplayInfo);
+ mVkManager = new VulkanManager(*this);
+}
+
+Readback& RenderThread::readback() {
+
+ if (!mReadback) {
+ auto renderType = Properties::getRenderPipelineType();
+ switch (renderType) {
+ case RenderPipelineType::OpenGL:
+ mReadback = new OpenGLReadbackImpl(*this);
+ break;
+ case RenderPipelineType::SkiaGL:
+ case RenderPipelineType::SkiaVulkan:
+ // It works to use the OpenGL pipeline for Vulkan but this is not
+ // ideal as it causes us to create an OpenGL context in addition
+ // to the Vulkan one.
+ mReadback = new skiapipeline::SkiaOpenGLReadback(*this);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
+ }
+ }
+
+ return *mReadback;
}
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 076e3d43a2c9..d121bcf5b084 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -22,6 +22,7 @@
#include "../JankTracker.h"
#include "TimeLord.h"
+#include <GrContext.h>
#include <cutils/compiler.h>
#include <ui/DisplayInfo.h>
#include <utils/Looper.h>
@@ -36,6 +37,7 @@ class DisplayEventReceiver;
namespace uirenderer {
+class Readback;
class RenderState;
class TestUtils;
@@ -45,6 +47,7 @@ class CanvasContext;
class DispatchFrameCallbacks;
class EglManager;
class RenderProxy;
+class VulkanManager;
class TaskQueue {
public:
@@ -88,12 +91,18 @@ public:
void pushBackFrameCallback(IFrameCallback* callback);
TimeLord& timeLord() { return mTimeLord; }
- RenderState& renderState() { return *mRenderState; }
- EglManager& eglManager() { return *mEglManager; }
+ RenderState& renderState() const { return *mRenderState; }
+ EglManager& eglManager() const { return *mEglManager; }
JankTracker& jankTracker() { return *mJankTracker; }
+ Readback& readback();
const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; }
+ GrContext* getGrContext() const { return mGrContext.get(); }
+ void setGrContext(GrContext* cxt) { mGrContext.reset(cxt); }
+
+ VulkanManager& vulkanManager() { return *mVkManager; }
+
protected:
virtual bool threadLoop() override;
@@ -144,6 +153,10 @@ private:
EglManager* mEglManager;
JankTracker* mJankTracker = nullptr;
+ Readback* mReadback = nullptr;
+
+ sk_sp<GrContext> mGrContext;
+ VulkanManager* mVkManager;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
new file mode 100644
index 000000000000..454ce4d9e8ff
--- /dev/null
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -0,0 +1,700 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VulkanManager.h"
+
+#include "DeviceInfo.h"
+#include "Properties.h"
+#include "RenderThread.h"
+#include "renderstate/RenderState.h"
+#include "utils/FatVector.h"
+
+#include <GrContext.h>
+#include <GrTypes.h>
+#include <vk/GrVkTypes.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+#define GET_PROC(F) m ## F = (PFN_vk ## F) vkGetInstanceProcAddr(instance, "vk" #F)
+#define GET_DEV_PROC(F) m ## F = (PFN_vk ## F) vkGetDeviceProcAddr(device, "vk" #F)
+
+VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {
+}
+
+void VulkanManager::destroy() {
+ if (!hasVkContext()) return;
+
+ mRenderThread.renderState().onVkContextDestroyed();
+ mRenderThread.setGrContext(nullptr);
+
+ if (VK_NULL_HANDLE != mCommandPool) {
+ mDestroyCommandPool(mBackendContext->fDevice, mCommandPool, nullptr);
+ mCommandPool = VK_NULL_HANDLE;
+ }
+ mBackendContext.reset();
+}
+
+void VulkanManager::initialize() {
+ if (hasVkContext()) { return; }
+
+ auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; };
+
+ mBackendContext.reset(GrVkBackendContext::Create(&mPresentQueueIndex, canPresent));
+
+ // Get all the addresses of needed vulkan functions
+ VkInstance instance = mBackendContext->fInstance;
+ VkDevice device = mBackendContext->fDevice;
+ GET_PROC(CreateAndroidSurfaceKHR);
+ GET_PROC(DestroySurfaceKHR);
+ GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
+ GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
+ GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
+ GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
+ GET_DEV_PROC(CreateSwapchainKHR);
+ GET_DEV_PROC(DestroySwapchainKHR);
+ GET_DEV_PROC(GetSwapchainImagesKHR);
+ GET_DEV_PROC(AcquireNextImageKHR);
+ GET_DEV_PROC(QueuePresentKHR);
+ GET_DEV_PROC(CreateCommandPool);
+ GET_DEV_PROC(DestroyCommandPool);
+ GET_DEV_PROC(AllocateCommandBuffers);
+ GET_DEV_PROC(FreeCommandBuffers);
+ GET_DEV_PROC(ResetCommandBuffer);
+ GET_DEV_PROC(BeginCommandBuffer);
+ GET_DEV_PROC(EndCommandBuffer);
+ GET_DEV_PROC(CmdPipelineBarrier);
+ GET_DEV_PROC(GetDeviceQueue);
+ GET_DEV_PROC(QueueSubmit);
+ GET_DEV_PROC(QueueWaitIdle);
+ GET_DEV_PROC(DeviceWaitIdle);
+ GET_DEV_PROC(CreateSemaphore);
+ GET_DEV_PROC(DestroySemaphore);
+ GET_DEV_PROC(CreateFence);
+ GET_DEV_PROC(DestroyFence);
+ GET_DEV_PROC(WaitForFences);
+ GET_DEV_PROC(ResetFences);
+
+ // create the command pool for the command buffers
+ if (VK_NULL_HANDLE == mCommandPool) {
+ VkCommandPoolCreateInfo commandPoolInfo;
+ memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo));
+ commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ // this needs to be on the render queue
+ commandPoolInfo.queueFamilyIndex = mBackendContext->fGraphicsQueueIndex;
+ commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+ SkDEBUGCODE(VkResult res =) mCreateCommandPool(mBackendContext->fDevice,
+ &commandPoolInfo, nullptr, &mCommandPool);
+ SkASSERT(VK_SUCCESS == res);
+ }
+
+ mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue);
+
+ mRenderThread.setGrContext(GrContext::Create(kVulkan_GrBackend,
+ (GrBackendContext) mBackendContext.get()));
+ DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
+
+ if (Properties::enablePartialUpdates && Properties::useBufferAge) {
+ mSwapBehavior = SwapBehavior::BufferAge;
+ }
+
+ mRenderThread.renderState().onVkContextCreated();
+}
+
+// Returns the next BackbufferInfo to use for the next draw. The function will make sure all
+// previous uses have finished before returning.
+VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurface* surface) {
+ SkASSERT(surface->mBackbuffers);
+
+ ++surface->mCurrentBackbufferIndex;
+ if (surface->mCurrentBackbufferIndex > surface->mImageCount) {
+ surface->mCurrentBackbufferIndex = 0;
+ }
+
+ VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
+ surface->mCurrentBackbufferIndex;
+
+ // Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely
+ // reuse its commands buffers.
+ VkResult res = mWaitForFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences,
+ true, UINT64_MAX);
+ if (res != VK_SUCCESS) {
+ return nullptr;
+ }
+
+ return backbuffer;
+}
+
+
+SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) {
+ VulkanSurface::BackbufferInfo* backbuffer = getAvailableBackbuffer(surface);
+ SkASSERT(backbuffer);
+
+ VkResult res;
+
+ res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
+ SkASSERT(VK_SUCCESS == res);
+
+ // The acquire will signal the attached mAcquireSemaphore. We use this to know the image has
+ // finished presenting and that it is safe to begin sending new commands to the returned image.
+ res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
+ backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, &backbuffer->mImageIndex);
+
+ if (VK_ERROR_SURFACE_LOST_KHR == res) {
+ // need to figure out how to create a new vkSurface without the platformData*
+ // maybe use attach somehow? but need a Window
+ return nullptr;
+ }
+ if (VK_ERROR_OUT_OF_DATE_KHR == res) {
+ // tear swapchain down and try again
+ if (!createSwapchain(surface)) {
+ return nullptr;
+ }
+ backbuffer = getAvailableBackbuffer(surface);
+ res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
+ SkASSERT(VK_SUCCESS == res);
+
+ // acquire the image
+ res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
+ backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, &backbuffer->mImageIndex);
+
+ if (VK_SUCCESS != res) {
+ return nullptr;
+ }
+ }
+
+ // set up layout transfer from initial to color attachment
+ VkImageLayout layout = surface->mImageInfos[backbuffer->mImageIndex].mImageLayout;
+ SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout);
+ VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT :
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkAccessFlags srcAccessMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
+ 0 : VK_ACCESS_MEMORY_READ_BIT;
+ VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+
+ VkImageMemoryBarrier imageMemoryBarrier = {
+ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
+ NULL, // pNext
+ srcAccessMask, // outputMask
+ dstAccessMask, // inputMask
+ layout, // oldLayout
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout
+ mPresentQueueIndex, // srcQueueFamilyIndex
+ mBackendContext->fGraphicsQueueIndex, // dstQueueFamilyIndex
+ surface->mImages[backbuffer->mImageIndex], // image
+ { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } // subresourceRange
+ };
+ mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[0], 0);
+
+ VkCommandBufferBeginInfo info;
+ memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
+ info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ info.flags = 0;
+ mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[0], &info);
+
+ mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[0], srcStageMask, dstStageMask, 0,
+ 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
+
+ mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[0]);
+
+ VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ // insert the layout transfer into the queue and wait on the acquire
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.waitSemaphoreCount = 1;
+ // Wait to make sure aquire semaphore set above has signaled.
+ submitInfo.pWaitSemaphores = &backbuffer->mAcquireSemaphore;
+ submitInfo.pWaitDstStageMask = &waitDstStageFlags;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[0];
+ submitInfo.signalSemaphoreCount = 0;
+
+ // Attach first fence to submission here so we can track when the command buffer finishes.
+ mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
+
+ // We need to notify Skia that we changed the layout of the wrapped VkImage
+ GrVkImageInfo* imageInfo;
+ sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface;
+ skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
+ SkSurface::kFlushRead_BackendHandleAccess);
+ imageInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+
+ surface->mBackbuffer = std::move(skSurface);
+ return surface->mBackbuffer.get();
+}
+
+void VulkanManager::destroyBuffers(VulkanSurface* surface) {
+ if (surface->mBackbuffers) {
+ for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
+ mWaitForFences(mBackendContext->fDevice, 2, surface->mBackbuffers[i].mUsageFences, true,
+ UINT64_MAX);
+ surface->mBackbuffers[i].mImageIndex = -1;
+ mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mAcquireSemaphore,
+ nullptr);
+ mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mRenderSemaphore,
+ nullptr);
+ mFreeCommandBuffers(mBackendContext->fDevice, mCommandPool, 2,
+ surface->mBackbuffers[i].mTransitionCmdBuffers);
+ mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
+ mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
+ }
+ }
+
+ delete[] surface->mBackbuffers;
+ surface->mBackbuffers = nullptr;
+ delete[] surface->mImageInfos;
+ surface->mImageInfos = nullptr;
+ delete[] surface->mImages;
+ surface->mImages = nullptr;
+}
+
+void VulkanManager::destroySurface(VulkanSurface* surface) {
+ // Make sure all submit commands have finished before starting to destroy objects.
+ if (VK_NULL_HANDLE != mPresentQueue) {
+ mQueueWaitIdle(mPresentQueue);
+ }
+ mDeviceWaitIdle(mBackendContext->fDevice);
+
+ destroyBuffers(surface);
+
+ if (VK_NULL_HANDLE != surface->mSwapchain) {
+ mDestroySwapchainKHR(mBackendContext->fDevice, surface->mSwapchain, nullptr);
+ surface->mSwapchain = VK_NULL_HANDLE;
+ }
+
+ if (VK_NULL_HANDLE != surface->mVkSurface) {
+ mDestroySurfaceKHR(mBackendContext->fInstance, surface->mVkSurface, nullptr);
+ surface->mVkSurface = VK_NULL_HANDLE;
+ }
+ delete surface;
+}
+
+void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) {
+ mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount,
+ nullptr);
+ SkASSERT(surface->mImageCount);
+ surface->mImages = new VkImage[surface->mImageCount];
+ mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain,
+ &surface->mImageCount, surface->mImages);
+
+ SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+
+ bool wantSRGB = VK_FORMAT_R8G8B8A8_SRGB == format;
+ GrPixelConfig config = wantSRGB ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig;
+
+ // set up initial image layouts and create surfaces
+ surface->mImageInfos = new VulkanSurface::ImageInfo[surface->mImageCount];
+ for (uint32_t i = 0; i < surface->mImageCount; ++i) {
+ GrBackendRenderTargetDesc desc;
+ GrVkImageInfo info;
+ info.fImage = surface->mImages[i];
+ info.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
+ info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
+ info.fFormat = format;
+ info.fLevelCount = 1;
+
+ desc.fWidth = extent.width;
+ desc.fHeight = extent.height;
+ desc.fConfig = config;
+ desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+ desc.fSampleCnt = 0;
+ desc.fStencilBits = 0;
+ desc.fRenderTargetHandle = (GrBackendObject) &info;
+
+ VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
+ imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(),
+ desc, &props);
+ }
+
+ SkASSERT(mCommandPool != VK_NULL_HANDLE);
+
+ // set up the backbuffers
+ VkSemaphoreCreateInfo semaphoreInfo;
+ memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = nullptr;
+ semaphoreInfo.flags = 0;
+ VkCommandBufferAllocateInfo commandBuffersInfo;
+ memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo));
+ commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ commandBuffersInfo.pNext = nullptr;
+ commandBuffersInfo.commandPool = mCommandPool;
+ commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ commandBuffersInfo.commandBufferCount = 2;
+ VkFenceCreateInfo fenceInfo;
+ memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
+ fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+ fenceInfo.pNext = nullptr;
+ fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+
+ // we create one additional backbuffer structure here, because we want to
+ // give the command buffers they contain a chance to finish before we cycle back
+ surface->mBackbuffers = new VulkanSurface::BackbufferInfo[surface->mImageCount + 1];
+ for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
+ SkDEBUGCODE(VkResult res);
+ surface->mBackbuffers[i].mImageIndex = -1;
+ SkDEBUGCODE(res = ) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
+ &surface->mBackbuffers[i].mAcquireSemaphore);
+ SkDEBUGCODE(res = ) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
+ &surface->mBackbuffers[i].mRenderSemaphore);
+ SkDEBUGCODE(res = ) mAllocateCommandBuffers(mBackendContext->fDevice, &commandBuffersInfo,
+ surface->mBackbuffers[i].mTransitionCmdBuffers);
+ SkDEBUGCODE(res = ) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
+ &surface->mBackbuffers[i].mUsageFences[0]);
+ SkDEBUGCODE(res = ) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
+ &surface->mBackbuffers[i].mUsageFences[1]);
+ SkASSERT(VK_SUCCESS == res);
+ }
+ surface->mCurrentBackbufferIndex = surface->mImageCount;
+}
+
+bool VulkanManager::createSwapchain(VulkanSurface* surface) {
+ // check for capabilities
+ VkSurfaceCapabilitiesKHR caps;
+ VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mBackendContext->fPhysicalDevice,
+ surface->mVkSurface, &caps);
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ uint32_t surfaceFormatCount;
+ res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
+ &surfaceFormatCount, nullptr);
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount);
+ res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
+ &surfaceFormatCount, surfaceFormats.data());
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ uint32_t presentModeCount;
+ res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+ surface->mVkSurface, &presentModeCount, nullptr);
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ FatVector<VkPresentModeKHR, VK_PRESENT_MODE_RANGE_SIZE_KHR> presentModes(presentModeCount);
+ res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+ surface->mVkSurface, &presentModeCount, presentModes.data());
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ VkExtent2D extent = caps.currentExtent;
+ // clamp width; to handle currentExtent of -1 and protect us from broken hints
+ if (extent.width < caps.minImageExtent.width) {
+ extent.width = caps.minImageExtent.width;
+ }
+ SkASSERT(extent.width <= caps.maxImageExtent.width);
+ // clamp height
+ if (extent.height < caps.minImageExtent.height) {
+ extent.height = caps.minImageExtent.height;
+ }
+ SkASSERT(extent.height <= caps.maxImageExtent.height);
+
+ uint32_t imageCount = caps.minImageCount + 2;
+ if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
+ // Application must settle for fewer images than desired:
+ imageCount = caps.maxImageCount;
+ }
+
+ // Currently Skia requires the images to be color attchments and support all transfer
+ // operations.
+ VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
+ SkASSERT(caps.supportedTransforms & caps.currentTransform);
+ SkASSERT(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
+ VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
+ VkCompositeAlphaFlagBitsKHR composite_alpha =
+ (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ?
+ VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR :
+ VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+
+ // Pick our surface format. For now, just make sure it matches our sRGB request:
+ VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
+ VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+
+ bool wantSRGB = false;
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
+ wantSRGB = true;
+#endif
+ for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
+ // We are assuming we can get either R8G8B8A8_UNORM or R8G8B8A8_SRGB
+ VkFormat desiredFormat = wantSRGB ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
+ if (desiredFormat == surfaceFormats[i].format) {
+ surfaceFormat = surfaceFormats[i].format;
+ colorSpace = surfaceFormats[i].colorSpace;
+ }
+ }
+
+ if (VK_FORMAT_UNDEFINED == surfaceFormat) {
+ return false;
+ }
+
+ // If mailbox mode is available, use it, as it is the lowest-latency non-
+ // tearing mode. If not, fall back to FIFO which is always available.
+ VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
+ for (uint32_t i = 0; i < presentModeCount; ++i) {
+ // use mailbox
+ if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) {
+ mode = presentModes[i];
+ break;
+ }
+ }
+
+ VkSwapchainCreateInfoKHR swapchainCreateInfo;
+ memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
+ swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+ swapchainCreateInfo.surface = surface->mVkSurface;
+ swapchainCreateInfo.minImageCount = imageCount;
+ swapchainCreateInfo.imageFormat = surfaceFormat;
+ swapchainCreateInfo.imageColorSpace = colorSpace;
+ swapchainCreateInfo.imageExtent = extent;
+ swapchainCreateInfo.imageArrayLayers = 1;
+ swapchainCreateInfo.imageUsage = usageFlags;
+
+ uint32_t queueFamilies[] = { mBackendContext->fGraphicsQueueIndex, mPresentQueueIndex };
+ if (mBackendContext->fGraphicsQueueIndex != mPresentQueueIndex) {
+ swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
+ swapchainCreateInfo.queueFamilyIndexCount = 2;
+ swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
+ } else {
+ swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ swapchainCreateInfo.queueFamilyIndexCount = 0;
+ swapchainCreateInfo.pQueueFamilyIndices = nullptr;
+ }
+
+ swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+ swapchainCreateInfo.compositeAlpha = composite_alpha;
+ swapchainCreateInfo.presentMode = mode;
+ swapchainCreateInfo.clipped = true;
+ swapchainCreateInfo.oldSwapchain = surface->mSwapchain;
+
+ res = mCreateSwapchainKHR(mBackendContext->fDevice, &swapchainCreateInfo, nullptr,
+ &surface->mSwapchain);
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ // destroy the old swapchain
+ if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
+ mDeviceWaitIdle(mBackendContext->fDevice);
+
+ destroyBuffers(surface);
+
+ mDestroySwapchainKHR(mBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
+ }
+
+ createBuffers(surface, surfaceFormat, extent);
+
+ return true;
+}
+
+
+VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) {
+ initialize();
+
+ if (!window) {
+ return nullptr;
+ }
+
+ VulkanSurface* surface = new VulkanSurface();
+
+ VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
+ memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.pNext = nullptr;
+ surfaceCreateInfo.flags = 0;
+ surfaceCreateInfo.window = window;
+
+ VkResult res = mCreateAndroidSurfaceKHR(mBackendContext->fInstance, &surfaceCreateInfo,
+ nullptr, &surface->mVkSurface);
+ if (VK_SUCCESS != res) {
+ delete surface;
+ return nullptr;
+ }
+
+SkDEBUGCODE(
+ VkBool32 supported;
+ res = mGetPhysicalDeviceSurfaceSupportKHR(mBackendContext->fPhysicalDevice,
+ mPresentQueueIndex, surface->mVkSurface, &supported);
+ // All physical devices and queue families on Android must be capable of presentation with any
+ // native window.
+ SkASSERT(VK_SUCCESS == res && supported);
+);
+
+ if (!createSwapchain(surface)) {
+ destroySurface(surface);
+ return nullptr;
+ }
+
+ return surface;
+}
+
+// Helper to know which src stage flags we need to set when transitioning to the present layout
+static VkPipelineStageFlags layoutToPipelineStageFlags(const VkImageLayout layout) {
+ if (VK_IMAGE_LAYOUT_GENERAL == layout) {
+ return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
+ } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout ||
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
+ return VK_PIPELINE_STAGE_TRANSFER_BIT;
+ } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout ||
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout ||
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout ||
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
+ return VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
+ } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
+ return VK_PIPELINE_STAGE_HOST_BIT;
+ }
+
+ SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout);
+ return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+}
+
+// Helper to know which src access mask we need to set when transitioning to the present layout
+static VkAccessFlags layoutToSrcAccessMask(const VkImageLayout layout) {
+ VkAccessFlags flags = 0;
+ if (VK_IMAGE_LAYOUT_GENERAL == layout) {
+ flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
+ VK_ACCESS_TRANSFER_WRITE_BIT |
+ VK_ACCESS_TRANSFER_READ_BIT |
+ VK_ACCESS_SHADER_READ_BIT |
+ VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_HOST_READ_BIT;
+ } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
+ flags = VK_ACCESS_HOST_WRITE_BIT;
+ } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) {
+ flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout) {
+ flags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ } else if (VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
+ flags = VK_ACCESS_TRANSFER_WRITE_BIT;
+ } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout) {
+ flags = VK_ACCESS_TRANSFER_READ_BIT;
+ } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
+ flags = VK_ACCESS_SHADER_READ_BIT;
+ }
+ return flags;
+}
+
+void VulkanManager::swapBuffers(VulkanSurface* surface) {
+ VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
+ surface->mCurrentBackbufferIndex;
+ GrVkImageInfo* imageInfo;
+ SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get();
+ skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
+ SkSurface::kFlushRead_BackendHandleAccess);
+ // Check to make sure we never change the actually wrapped image
+ SkASSERT(imageInfo->fImage == surface->mImages[backbuffer->mImageIndex]);
+
+ // We need to transition the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR and make sure that all
+ // previous work is complete for before presenting. So we first add the necessary barrier here.
+ VkImageLayout layout = imageInfo->fImageLayout;
+ VkPipelineStageFlags srcStageMask = layoutToPipelineStageFlags(layout);
+ VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+ VkAccessFlags srcAccessMask = layoutToSrcAccessMask(layout);
+ VkAccessFlags dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
+
+ VkImageMemoryBarrier imageMemoryBarrier = {
+ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
+ NULL, // pNext
+ srcAccessMask, // outputMask
+ dstAccessMask, // inputMask
+ layout, // oldLayout
+ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout
+ mBackendContext->fGraphicsQueueIndex, // srcQueueFamilyIndex
+ mPresentQueueIndex, // dstQueueFamilyIndex
+ surface->mImages[backbuffer->mImageIndex], // image
+ { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } // subresourceRange
+ };
+
+ mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[1], 0);
+ VkCommandBufferBeginInfo info;
+ memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
+ info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ info.flags = 0;
+ mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[1], &info);
+ mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[1], srcStageMask, dstStageMask, 0,
+ 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
+ mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[1]);
+
+ surface->mImageInfos[backbuffer->mImageIndex].mImageLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+
+ // insert the layout transfer into the queue and wait on the acquire
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.waitSemaphoreCount = 0;
+ submitInfo.pWaitDstStageMask = 0;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[1];
+ submitInfo.signalSemaphoreCount = 1;
+ // When this command buffer finishes we will signal this semaphore so that we know it is now
+ // safe to present the image to the screen.
+ submitInfo.pSignalSemaphores = &backbuffer->mRenderSemaphore;
+
+ // Attach second fence to submission here so we can track when the command buffer finishes.
+ mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
+
+ // Submit present operation to present queue. We use a semaphore here to make sure all rendering
+ // to the image is complete and that the layout has been change to present on the graphics
+ // queue.
+ const VkPresentInfoKHR presentInfo =
+ {
+ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType
+ NULL, // pNext
+ 1, // waitSemaphoreCount
+ &backbuffer->mRenderSemaphore, // pWaitSemaphores
+ 1, // swapchainCount
+ &surface->mSwapchain, // pSwapchains
+ &backbuffer->mImageIndex, // pImageIndices
+ NULL // pResults
+ };
+
+ mQueuePresentKHR(mPresentQueue, &presentInfo);
+
+ surface->mBackbuffer.reset();
+ surface->mImageInfos[backbuffer->mImageIndex].mLastUsed = surface->mCurrentTime;
+ surface->mImageInfos[backbuffer->mImageIndex].mInvalid = false;
+ surface->mCurrentTime++;
+}
+
+int VulkanManager::getAge(VulkanSurface* surface) {
+ VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
+ surface->mCurrentBackbufferIndex;
+ if (mSwapBehavior == SwapBehavior::Discard
+ || surface->mImageInfos[backbuffer->mImageIndex].mInvalid) {
+ return 0;
+ }
+ uint16_t lastUsed = surface->mImageInfos[backbuffer->mImageIndex].mLastUsed;
+ return surface->mCurrentTime - lastUsed;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
new file mode 100644
index 000000000000..d225b3fc4ec0
--- /dev/null
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VULKANMANAGER_H
+#define VULKANMANAGER_H
+
+#include <SkSurface.h>
+#include <vk/GrVkBackendContext.h>
+
+#include <vulkan/vulkan.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class RenderThread;
+
+class VulkanSurface {
+public:
+ VulkanSurface() {}
+
+ sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
+
+private:
+ friend class VulkanManager;
+ struct BackbufferInfo {
+ uint32_t mImageIndex; // image this is associated with
+ VkSemaphore mAcquireSemaphore; // we signal on this for acquisition of image
+ VkSemaphore mRenderSemaphore; // we wait on this for rendering to be done
+ VkCommandBuffer mTransitionCmdBuffers[2]; // to transition layout between present and render
+ // We use these fences to make sure the above Command buffers have finished their work
+ // before attempting to reuse them or destroy them.
+ VkFence mUsageFences[2];
+ };
+
+ struct ImageInfo {
+ VkImageLayout mImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ sk_sp<SkSurface> mSurface;
+ uint16_t mLastUsed = 0;
+ bool mInvalid = true;
+ };
+
+ sk_sp<SkSurface> mBackbuffer;
+
+ VkSurfaceKHR mVkSurface = VK_NULL_HANDLE;
+ VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
+
+ BackbufferInfo* mBackbuffers;
+ uint32_t mCurrentBackbufferIndex;
+
+ uint32_t mImageCount;
+ VkImage* mImages;
+ ImageInfo* mImageInfos;
+ uint16_t mCurrentTime = 0;
+};
+
+// This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
+// which are re-used by CanvasContext. This class is created once and should be used by all vulkan
+// windowing contexts. The VulkanManager must be initialized before use.
+class VulkanManager {
+public:
+ // Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must
+ // be call once before use of the VulkanManager. Multiple calls after the first will simiply
+ // return.
+ void initialize();
+
+ // Quick check to see if the VulkanManager has been initialized.
+ bool hasVkContext() { return mBackendContext.get() != nullptr; }
+
+ // Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
+ // VulkanSurface object which is returned.
+ VulkanSurface* createSurface(ANativeWindow* window);
+
+ // Destroy the VulkanSurface and all associated vulkan objects.
+ void destroySurface(VulkanSurface* surface);
+
+ // Cleans up all the global state in the VulkanManger.
+ void destroy();
+
+ // No work is needed to make a VulkanSurface current, and all functions require that a
+ // VulkanSurface is passed into them so we just return true here.
+ bool isCurrent(VulkanSurface* surface) { return true; }
+
+ int getAge(VulkanSurface* surface);
+
+ // Returns an SkSurface which wraps the next image returned from vkAcquireNextImageKHR. It also
+ // will transition the VkImage from a present layout to color attachment so that it can be used
+ // by the client for drawing.
+ SkSurface* getBackbufferSurface(VulkanSurface* surface);
+
+ // Presents the current VkImage.
+ void swapBuffers(VulkanSurface* surface);
+
+private:
+ friend class RenderThread;
+
+ explicit VulkanManager(RenderThread& thread);
+ ~VulkanManager() { destroy(); }
+
+ void destroyBuffers(VulkanSurface* surface);
+
+ bool createSwapchain(VulkanSurface* surface);
+ void createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent);
+
+ VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface);
+
+ // simple wrapper class that exists only to initialize a pointer to NULL
+ template <typename FNPTR_TYPE> class VkPtr {
+ public:
+ VkPtr() : fPtr(NULL) {}
+ VkPtr operator=(FNPTR_TYPE ptr) { fPtr = ptr; return *this; }
+ operator FNPTR_TYPE() const { return fPtr; }
+ private:
+ FNPTR_TYPE fPtr;
+ };
+
+ // WSI interface functions
+ VkPtr<PFN_vkCreateAndroidSurfaceKHR> mCreateAndroidSurfaceKHR;
+ VkPtr<PFN_vkDestroySurfaceKHR> mDestroySurfaceKHR;
+ VkPtr<PFN_vkGetPhysicalDeviceSurfaceSupportKHR> mGetPhysicalDeviceSurfaceSupportKHR;
+ VkPtr<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR> mGetPhysicalDeviceSurfaceCapabilitiesKHR;
+ VkPtr<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR> mGetPhysicalDeviceSurfaceFormatsKHR;
+ VkPtr<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR> mGetPhysicalDeviceSurfacePresentModesKHR;
+
+ VkPtr<PFN_vkCreateSwapchainKHR> mCreateSwapchainKHR;
+ VkPtr<PFN_vkDestroySwapchainKHR> mDestroySwapchainKHR;
+ VkPtr<PFN_vkGetSwapchainImagesKHR> mGetSwapchainImagesKHR;
+ VkPtr<PFN_vkAcquireNextImageKHR> mAcquireNextImageKHR;
+ VkPtr<PFN_vkQueuePresentKHR> mQueuePresentKHR;
+ VkPtr<PFN_vkCreateSharedSwapchainsKHR> mCreateSharedSwapchainsKHR;
+
+ // Additional vulkan functions
+ VkPtr<PFN_vkCreateCommandPool> mCreateCommandPool;
+ VkPtr<PFN_vkDestroyCommandPool> mDestroyCommandPool;
+ VkPtr<PFN_vkAllocateCommandBuffers> mAllocateCommandBuffers;
+ VkPtr<PFN_vkFreeCommandBuffers> mFreeCommandBuffers;
+ VkPtr<PFN_vkResetCommandBuffer> mResetCommandBuffer;
+ VkPtr<PFN_vkBeginCommandBuffer> mBeginCommandBuffer;
+ VkPtr<PFN_vkEndCommandBuffer> mEndCommandBuffer;
+ VkPtr<PFN_vkCmdPipelineBarrier> mCmdPipelineBarrier;
+
+ VkPtr<PFN_vkGetDeviceQueue> mGetDeviceQueue;
+ VkPtr<PFN_vkQueueSubmit> mQueueSubmit;
+ VkPtr<PFN_vkQueueWaitIdle> mQueueWaitIdle;
+ VkPtr<PFN_vkDeviceWaitIdle> mDeviceWaitIdle;
+
+ VkPtr<PFN_vkCreateSemaphore> mCreateSemaphore;
+ VkPtr<PFN_vkDestroySemaphore> mDestroySemaphore;
+ VkPtr<PFN_vkCreateFence> mCreateFence;
+ VkPtr<PFN_vkDestroyFence> mDestroyFence;
+ VkPtr<PFN_vkWaitForFences> mWaitForFences;
+ VkPtr<PFN_vkResetFences> mResetFences;
+
+ RenderThread& mRenderThread;
+
+ sk_sp<const GrVkBackendContext> mBackendContext;
+ uint32_t mPresentQueueIndex;
+ VkQueue mPresentQueue = VK_NULL_HANDLE;
+ VkCommandPool mCommandPool = VK_NULL_HANDLE;
+
+ enum class SwapBehavior {
+ Discard,
+ BufferAge,
+ };
+ SwapBehavior mSwapBehavior = SwapBehavior::Discard;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* VULKANMANAGER_H */
+
diff --git a/libs/hwui/tests/common/BitmapAllocationTestUtils.h b/libs/hwui/tests/common/BitmapAllocationTestUtils.h
new file mode 100644
index 000000000000..4892179f0d48
--- /dev/null
+++ b/libs/hwui/tests/common/BitmapAllocationTestUtils.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "TestScene.h"
+
+#include <SkBitmap.h>
+#include <string>
+
+namespace android {
+namespace uirenderer {
+namespace test {
+
+class BitmapAllocationTestUtils {
+public:
+ static sk_sp<Bitmap> allocateHeapBitmap(int width, int height,
+ SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) {
+ sk_sp<Bitmap> bitmap = TestUtils::createBitmap(width, height, colorType);
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ setup(skBitmap);
+ return bitmap;
+ }
+
+ static sk_sp<Bitmap> allocateHardwareBitmap(int width, int height,
+ SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) {
+ SkBitmap skBitmap;
+ SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType);
+ skBitmap.setInfo(info);
+ sk_sp<Bitmap> heapBitmap(Bitmap::allocateHeapBitmap(&skBitmap, nullptr));
+ setup(skBitmap);
+ return Bitmap::allocateHardwareBitmap(skBitmap);
+ }
+
+ typedef sk_sp<Bitmap> (*BitmapAllocator) (int, int, SkColorType,
+ std::function<void(SkBitmap& bitmap)> setup);
+
+ template <class T, BitmapAllocator allocator>
+ static test::TestScene* createBitmapAllocationScene(const TestScene::Options&) {
+ return new T(allocator);
+ }
+
+ template <class BaseScene>
+ static bool registerBitmapAllocationScene(std::string name, std::string description) {
+ TestScene::registerScene({
+ name + "GlTex",
+ description + " (GlTex version).",
+ createBitmapAllocationScene<BaseScene, &allocateHeapBitmap>
+ });
+
+ TestScene::registerScene({
+ name + "EglImage",
+ description + " (EglImage version).",
+ createBitmapAllocationScene<BaseScene, &allocateHardwareBitmap>
+ });
+ return true;
+ }
+};
+
+} // namespace test
+} // namespace uirenderer
+} // namespace android \ No newline at end of file
diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp
new file mode 100644
index 000000000000..d935382cc9a4
--- /dev/null
+++ b/libs/hwui/tests/common/LeakChecker.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LeakChecker.h"
+
+#include "Caches.h"
+#include "TestUtils.h"
+
+#include <cstdio>
+#include <iostream>
+#include <memunreachable/memunreachable.h>
+#include <unistd.h>
+#include <unordered_set>
+
+using namespace std;
+
+namespace android {
+namespace uirenderer {
+namespace test {
+
+static void logUnreachable(initializer_list<UnreachableMemoryInfo> infolist) {
+ // merge them all
+ UnreachableMemoryInfo merged;
+ unordered_set<uintptr_t> addrs;
+ merged.allocation_bytes = 0;
+ merged.leak_bytes = 0;
+ merged.num_allocations = 0;
+ merged.num_leaks = 0;
+ for (auto& info : infolist) {
+ // We'll be a little hazzy about these ones and just hope the biggest
+ // is the most accurate
+ merged.allocation_bytes = max(merged.allocation_bytes, info.allocation_bytes);
+ merged.num_allocations = max(merged.num_allocations, info.num_allocations);
+ for (auto& leak : info.leaks) {
+ if (addrs.find(leak.begin) == addrs.end()) {
+ merged.leaks.push_back(leak);
+ merged.num_leaks++;
+ merged.leak_bytes += leak.size;
+ addrs.insert(leak.begin);
+ }
+ }
+ }
+
+ // Now log the result
+ if (merged.num_leaks) {
+ cout << endl << "Leaked memory!" << endl;
+ if (!merged.leaks[0].backtrace.num_frames) {
+ cout << "Re-run with 'setprop libc.debug.malloc.program hwui_unit_test'"
+ << endl << "and 'setprop libc.debug.malloc.options backtrace=8'"
+ << " to get backtraces" << endl;
+ }
+ cout << merged.ToString(false);
+ }
+}
+
+void LeakChecker::checkForLeaks() {
+ // TODO: Until we can shutdown the RT thread we need to do this in
+ // two passes as GetUnreachableMemory has limited insight into
+ // thread-local caches so some leaks will not be properly tagged as leaks
+ UnreachableMemoryInfo rtMemInfo;
+ TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) {
+ if (Caches::hasInstance()) {
+ Caches::getInstance().tasks.stop();
+ }
+ // Check for leaks
+ if (!GetUnreachableMemory(rtMemInfo)) {
+ cerr << "Failed to get unreachable memory!" << endl;
+ return;
+ }
+ });
+ UnreachableMemoryInfo uiMemInfo;
+ if (!GetUnreachableMemory(uiMemInfo)) {
+ cerr << "Failed to get unreachable memory!" << endl;
+ return;
+ }
+ logUnreachable({rtMemInfo, uiMemInfo});
+}
+
+} /* namespace test */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/tests/common/LeakChecker.h b/libs/hwui/tests/common/LeakChecker.h
new file mode 100644
index 000000000000..cdf47d6bda80
--- /dev/null
+++ b/libs/hwui/tests/common/LeakChecker.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+namespace android {
+namespace uirenderer {
+namespace test {
+
+class LeakChecker {
+public:
+ static void checkForLeaks();
+}; // class TestUtils
+
+} /* namespace test */
+} /* namespace uirenderer */
+} /* namespace android */ \ No newline at end of file
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index 99569755205f..5e937f3239ff 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -61,20 +61,53 @@ TestContext::TestContext() {
TestContext::~TestContext() {}
sp<Surface> TestContext::surface() {
- if (!mSurfaceControl.get()) {
- mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"),
- gDisplay.w, gDisplay.h, PIXEL_FORMAT_RGBX_8888);
-
- SurfaceComposerClient::openGlobalTransaction();
- mSurfaceControl->setLayer(0x7FFFFFF);
- mSurfaceControl->show();
- SurfaceComposerClient::closeGlobalTransaction();
+ if (!mSurface.get()) {
+ createSurface();
}
+ return mSurface;
+}
+
+void TestContext::createSurface() {
+ if (mRenderOffscreen) {
+ createOffscreenSurface();
+ } else {
+ createWindowSurface();
+ }
+}
- return mSurfaceControl->getSurface();
+void TestContext::createWindowSurface() {
+ mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"),
+ gDisplay.w, gDisplay.h, PIXEL_FORMAT_RGBX_8888);
+
+ SurfaceComposerClient::openGlobalTransaction();
+ mSurfaceControl->setLayer(0x7FFFFFF);
+ mSurfaceControl->show();
+ SurfaceComposerClient::closeGlobalTransaction();
+ mSurface = mSurfaceControl->getSurface();
+}
+
+void TestContext::createOffscreenSurface() {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ producer->setMaxDequeuedBufferCount(3);
+ producer->setAsyncMode(true);
+ mConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_COMPOSER, 4);
+ mConsumer->setDefaultBufferSize(gDisplay.w, gDisplay.h);
+ mSurface = new Surface(producer);
}
void TestContext::waitForVsync() {
+ if (mConsumer.get()) {
+ BufferItem buffer;
+ if (mConsumer->acquireBuffer(&buffer, 0, false) == OK) {
+ // We assume the producer is internally ordered enough such that
+ // it is unneccessary to set a release fence
+ mConsumer->releaseBuffer(buffer);
+ }
+ // We running free, go go go!
+ return;
+ }
#if !HWUI_NULL_GPU
// Request vsync
mDisplayEventReceiver.requestNextVsync();
diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h
index 2bbe5dffd9b8..312988b968de 100644
--- a/libs/hwui/tests/common/TestContext.h
+++ b/libs/hwui/tests/common/TestContext.h
@@ -19,12 +19,16 @@
#include <gui/DisplayEventReceiver.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/BufferItemConsumer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
#include <gui/Surface.h>
#include <ui/DisplayInfo.h>
#include <utils/Looper.h>
+#include <thread>
+#include <atomic>
+
namespace android {
namespace uirenderer {
namespace test {
@@ -39,15 +43,29 @@ public:
TestContext();
~TestContext();
+ // Must be called before surface();
+ void setRenderOffscreen(bool renderOffscreen) {
+ LOG_ALWAYS_FATAL_IF(mSurface.get(),
+ "Must be called before surface is created");
+ mRenderOffscreen = renderOffscreen;
+ }
+
sp<Surface> surface();
void waitForVsync();
private:
+ void createSurface();
+ void createWindowSurface();
+ void createOffscreenSurface();
+
sp<SurfaceComposerClient> mSurfaceComposerClient;
sp<SurfaceControl> mSurfaceControl;
+ sp<BufferItemConsumer> mConsumer;
DisplayEventReceiver mDisplayEventReceiver;
sp<Looper> mLooper;
+ sp<Surface> mSurface;
+ bool mRenderOffscreen;
};
} // namespace test
diff --git a/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp
new file mode 100644
index 000000000000..6d2e85996444
--- /dev/null
+++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestListViewSceneBase.h"
+
+#include "TestContext.h"
+#include "TestUtils.h"
+
+#include <utils/Color.h>
+
+namespace android {
+namespace uirenderer {
+namespace test {
+
+void TestListViewSceneBase::createContent(int width, int height, Canvas& canvas) {
+ srand(0);
+ mItemHeight = dp(60);
+ mItemSpacing = dp(16);
+ mItemWidth = std::min((height - mItemSpacing * 2), (int)dp(300));
+ mItemLeft = (width - mItemWidth) / 2;
+ int heightWithSpacing = mItemHeight + mItemSpacing;
+ for (int y = 0; y < height + (heightWithSpacing - 1); y += heightWithSpacing) {
+ int id = mListItems.size();
+ auto setup = std::bind(&TestListViewSceneBase::createListItem, this, std::placeholders::_1,
+ std::placeholders::_2, id, mItemWidth, mItemHeight);
+ auto node = TestUtils::createNode(mItemLeft, y, mItemLeft + mItemWidth,
+ y + mItemHeight, setup);
+ mListItems.push_back(node);
+ }
+ mListView = TestUtils::createNode(0, 0, width, height,
+ [this](RenderProperties& props, Canvas& canvas) {
+ for (size_t ci = 0; ci < mListItems.size(); ci++) {
+ canvas.drawRenderNode(mListItems[ci].get());
+ }
+ });
+
+ canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver);
+ canvas.drawRenderNode(mListView.get());
+}
+
+void TestListViewSceneBase::doFrame(int frameNr) {
+ int scrollPx = dp(frameNr) * 3;
+ int itemIndexOffset = scrollPx / (mItemSpacing + mItemHeight);
+ int pxOffset = -(scrollPx % (mItemSpacing + mItemHeight));
+
+ std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(mListView->stagingProperties().getWidth(),
+ mListView->stagingProperties().getHeight()));
+ for (size_t ci = 0; ci < mListItems.size(); ci++) {
+ // update item position
+ auto listItem = mListItems[(ci + itemIndexOffset) % mListItems.size()];
+ int top = ((int)ci) * (mItemSpacing + mItemHeight) + pxOffset;
+ listItem->mutateStagingProperties().setLeftTopRightBottom(
+ mItemLeft, top, mItemLeft + mItemWidth, top + mItemHeight);
+ listItem->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ // draw it to parent DisplayList
+ canvas->drawRenderNode(mListItems[ci].get());
+ }
+ mListView->setStagingDisplayList(canvas->finishRecording(), nullptr);
+}
+
+} // namespace test
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/tests/common/TestListViewSceneBase.h b/libs/hwui/tests/common/TestListViewSceneBase.h
new file mode 100644
index 000000000000..ed6867ab3750
--- /dev/null
+++ b/libs/hwui/tests/common/TestListViewSceneBase.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "TestScene.h"
+#include <RenderNode.h>
+#include <RenderProperties.h>
+
+namespace android {
+namespace uirenderer {
+namespace test {
+
+class TestListViewSceneBase : public TestScene {
+public:
+ virtual void createListItem(RenderProperties& props, Canvas& canvas, int id,
+ int itemWidth, int itemHeight) = 0;
+private:
+ int mItemHeight;
+ int mItemSpacing;
+ int mItemWidth;
+ int mItemLeft;
+ sp<RenderNode> mListView;
+ std::vector< sp<RenderNode> > mListItems;
+
+ void createContent(int width, int height, Canvas& canvas) override;
+ void doFrame(int frameNr) override;
+};
+
+} // namespace test
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 060984ea9f32..f6f7c62a4f63 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -13,23 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef TESTS_TESTSCENE_H
-#define TESTS_TESTSCENE_H
+
+#pragma once
#include <string>
#include <unordered_map>
namespace android {
+
+class Canvas;
+
namespace uirenderer {
class RenderNode;
-
-#if HWUI_NEW_OPS
class RecordingCanvas;
-typedef RecordingCanvas TestCanvas;
-#else
-class DisplayListCanvas;
-typedef DisplayListCanvas TestCanvas;
-#endif
namespace test {
@@ -38,6 +34,7 @@ public:
struct Options {
int count = 0;
int reportFrametimeWeight = 0;
+ bool renderOffscreen = true;
};
template <class T>
@@ -65,7 +62,7 @@ public:
};
virtual ~TestScene() {}
- virtual void createContent(int width, int height, TestCanvas& renderer) = 0;
+ virtual void createContent(int width, int height, Canvas& renderer) = 0;
virtual void doFrame(int frameNr) = 0;
static std::unordered_map<std::string, Info>& testMap();
@@ -75,5 +72,3 @@ public:
} // namespace test
} // namespace uirenderer
} // namespace android
-
-#endif /* TESTS_TESTSCENE_H */
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index c762eed616e4..275ce166728b 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -18,7 +18,16 @@
#include "hwui/Paint.h"
#include "DeferredLayerUpdater.h"
-#include "LayerRenderer.h"
+
+#include <renderthread/EglManager.h>
+#include <renderthread/OpenGLPipeline.h>
+#include <pipeline/skia/SkiaOpenGLPipeline.h>
+#include <pipeline/skia/SkiaVulkanPipeline.h>
+#include <renderthread/VulkanManager.h>
+#include <utils/Unicode.h>
+#include <SkClipStack.h>
+
+#include <SkGlyphCache.h>
namespace android {
namespace uirenderer {
@@ -41,22 +50,30 @@ SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end)
}
sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
+ renderthread::RenderThread& renderThread) {
+ android::uirenderer::renderthread::IRenderPipeline* pipeline;
+ if (Properties::getRenderPipelineType() == RenderPipelineType::OpenGL) {
+ pipeline = new renderthread::OpenGLPipeline(renderThread);
+ } else if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+ pipeline = new skiapipeline::SkiaOpenGLPipeline(renderThread);
+ } else {
+ pipeline = new skiapipeline::SkiaVulkanPipeline(renderThread);
+ }
+ sp<DeferredLayerUpdater> layerUpdater = pipeline->createTextureLayer();
+ delete pipeline;
+ return layerUpdater;
+}
+
+sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
const SkMatrix& transform) {
- Layer* layer = LayerRenderer::createTextureLayer(renderThread.renderState());
- layer->getTransform().load(transform);
-
- sp<DeferredLayerUpdater> layerUpdater = new DeferredLayerUpdater(layer);
+ sp<DeferredLayerUpdater> layerUpdater = createTextureLayerUpdater(renderThread);
+ layerUpdater->backingLayer()->getTransform().load(transform);
layerUpdater->setSize(width, height);
layerUpdater->setTransform(&transform);
// updateLayer so it's ready to draw
- bool isOpaque = true;
- bool forceFilter = true;
- GLenum renderTarget = GL_TEXTURE_EXTERNAL_OES;
- LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, forceFilter,
- renderTarget, Matrix4::identity().data);
-
+ layerUpdater->updateLayer(true, GL_TEXTURE_EXTERNAL_OES, Matrix4::identity().data);
return layerUpdater;
}
@@ -68,7 +85,10 @@ void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text,
SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
while (*text != '\0') {
- SkUnichar unichar = SkUTF8_NextUnichar(&text);
+ size_t nextIndex = 0;
+ int32_t unichar = utf32_from_utf8_at(text, 4, 0, &nextIndex);
+ text += nextIndex;
+
glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar);
autoCache.getCache()->unicharToGlyph(unichar);
@@ -107,12 +127,21 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text,
void TestUtils::TestTask::run() {
// RenderState only valid once RenderThread is running, so queried here
- RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
+ renderthread::RenderThread& renderThread = renderthread::RenderThread::getInstance();
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ renderThread.vulkanManager().initialize();
+ } else {
+ renderThread.eglManager().initialize();
+ }
- renderState.onGLContextCreated();
- rtCallback(renderthread::RenderThread::getInstance());
- renderState.flush(Caches::FlushMode::Full);
- renderState.onGLContextDestroyed();
+ rtCallback(renderThread);
+
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ renderThread.vulkanManager().destroy();
+ } else {
+ renderThread.renderState().flush(Caches::FlushMode::Full);
+ renderThread.eglManager().destroy();
+ }
}
std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
@@ -124,5 +153,62 @@ std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
return utf16;
}
+SkColor TestUtils::getColor(const sk_sp<SkSurface>& surface, int x, int y) {
+ SkPixmap pixmap;
+ if (!surface->peekPixels(&pixmap)) {
+ return 0;
+ }
+ switch (pixmap.colorType()) {
+ case kGray_8_SkColorType: {
+ const uint8_t* addr = pixmap.addr8(x, y);
+ return SkColorSetRGB(*addr, *addr, *addr);
+ }
+ case kAlpha_8_SkColorType: {
+ const uint8_t* addr = pixmap.addr8(x, y);
+ return SkColorSetA(0, addr[0]);
+ }
+ case kRGB_565_SkColorType: {
+ const uint16_t* addr = pixmap.addr16(x, y);
+ return SkPixel16ToColor(addr[0]);
+ }
+ case kARGB_4444_SkColorType: {
+ const uint16_t* addr = pixmap.addr16(x, y);
+ SkPMColor c = SkPixel4444ToPixel32(addr[0]);
+ return SkUnPreMultiply::PMColorToColor(c);
+ }
+ case kBGRA_8888_SkColorType: {
+ const uint32_t* addr = pixmap.addr32(x, y);
+ SkPMColor c = SkSwizzle_BGRA_to_PMColor(addr[0]);
+ return SkUnPreMultiply::PMColorToColor(c);
+ }
+ case kRGBA_8888_SkColorType: {
+ const uint32_t* addr = pixmap.addr32(x, y);
+ SkPMColor c = SkSwizzle_RGBA_to_PMColor(addr[0]);
+ return SkUnPreMultiply::PMColorToColor(c);
+ }
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+SkRect TestUtils::getClipBounds(const SkCanvas* canvas) {
+ SkClipStack::BoundsType boundType;
+ SkRect clipBounds;
+ canvas->getClipStack()->getBounds(&clipBounds, &boundType);
+ return clipBounds;
+}
+
+SkRect TestUtils::getLocalClipBounds(const SkCanvas* canvas) {
+ SkMatrix invertedTotalMatrix;
+ if (!canvas->getTotalMatrix().invert(&invertedTotalMatrix)) {
+ return SkRect::MakeEmpty();
+ }
+ SkRect outlineInDeviceCoord = TestUtils::getClipBounds(canvas);
+ SkRect outlineInLocalCoord;
+ invertedTotalMatrix.mapRect(&outlineInLocalCoord, outlineInDeviceCoord);
+ return outlineInLocalCoord;
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 5a4ab99012b9..8b287de3220e 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -13,37 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef TEST_UTILS_H
-#define TEST_UTILS_H
+
+#pragma once
#include <DeviceInfo.h>
#include <DisplayList.h>
#include <Matrix.h>
+#include <Properties.h>
#include <Rect.h>
#include <RenderNode.h>
+#include <hwui/Bitmap.h>
+#include <pipeline/skia/SkiaRecordingCanvas.h>
#include <renderstate/RenderState.h>
#include <renderthread/RenderThread.h>
#include <Snapshot.h>
-#if HWUI_NEW_OPS
#include <RecordedOp.h>
#include <RecordingCanvas.h>
-#else
-#include <DisplayListOp.h>
-#include <DisplayListCanvas.h>
-#endif
#include <memory>
namespace android {
namespace uirenderer {
-#if HWUI_NEW_OPS
-typedef RecordingCanvas TestCanvas;
-#else
-typedef DisplayListCanvas TestCanvas;
-#endif
-
#define EXPECT_MATRIX_APPROX_EQ(a, b) \
EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b))
@@ -60,6 +52,31 @@ typedef DisplayListCanvas TestCanvas;
} else { \
ADD_FAILURE() << "ClipState not a rect"; \
}
+
+#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \
+ TEST(test_case_name, test_name##_##pipeline) { \
+ RenderPipelineType oldType = Properties::getRenderPipelineType(); \
+ Properties::overrideRenderPipelineType(RenderPipelineType::pipeline); \
+ functionCall; \
+ Properties::overrideRenderPipelineType(oldType); \
+ };
+
+/**
+ * Like gtests' TEST, but only runs with the OpenGL RenderPipelineType
+ */
+#define OPENGL_PIPELINE_TEST(test_case_name, test_name) \
+ class test_case_name##_##test_name##_HwuiTest { \
+ public: \
+ static void doTheThing(); \
+ }; \
+ INNER_PIPELINE_TEST(test_case_name, test_name, OpenGL, \
+ test_case_name##_##test_name##_HwuiTest::doTheThing()) \
+ void test_case_name##_##test_name##_HwuiTest::doTheThing()
+
+#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
+ INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \
+ TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing))
+
/**
* Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
* (for e.g. accessing its RenderState)
@@ -69,9 +86,32 @@ typedef DisplayListCanvas TestCanvas;
public: \
static void doTheThing(renderthread::RenderThread& renderThread); \
}; \
- TEST(test_case_name, test_name) { \
- TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \
+ INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \
+ INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
+ INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); \
+ void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
+
+/**
+ * Like RENDERTHREAD_TEST, but only runs with the OpenGL RenderPipelineType
+ */
+#define RENDERTHREAD_OPENGL_PIPELINE_TEST(test_case_name, test_name) \
+ class test_case_name##_##test_name##_RenderThreadTest { \
+ public: \
+ static void doTheThing(renderthread::RenderThread& renderThread); \
+ }; \
+ INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \
+ void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
+
+/**
+ * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
+ */
+#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \
+ class test_case_name##_##test_name##_RenderThreadTest { \
+ public: \
+ static void doTheThing(renderthread::RenderThread& renderThread); \
}; \
+ INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
+ INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); \
void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread)
/**
@@ -127,22 +167,28 @@ public:
static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) {
std::unique_ptr<Snapshot> snapshot(new Snapshot());
- snapshot->clip(clip, SkRegion::kReplace_Op); // store clip first, so it isn't transformed
+ // store clip first, so it isn't transformed
+ snapshot->setClip(clip.left, clip.top, clip.right, clip.bottom);
*(snapshot->transform) = transform;
return snapshot;
}
- static SkBitmap createSkBitmap(int width, int height,
+ static sk_sp<Bitmap> createBitmap(int width, int height,
SkColorType colorType = kN32_SkColorType) {
- SkBitmap bitmap;
- SkImageInfo info = SkImageInfo::Make(width, height,
- colorType, kPremul_SkAlphaType);
- bitmap.setInfo(info);
- bitmap.allocPixels(info);
- return bitmap;
+ SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType);
+ return Bitmap::allocateHeapBitmap(info);
+ }
+
+ static sk_sp<Bitmap> createBitmap(int width, int height, SkBitmap* outBitmap) {
+ SkImageInfo info = SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
+ outBitmap->setInfo(info);
+ return Bitmap::allocateHeapBitmap(outBitmap, nullptr);
}
static sp<DeferredLayerUpdater> createTextureLayerUpdater(
+ renderthread::RenderThread& renderThread);
+
+ static sp<DeferredLayerUpdater> createTextureLayerUpdater(
renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
const SkMatrix& transform);
@@ -155,7 +201,7 @@ public:
}
static sp<RenderNode> createNode(int left, int top, int right, int bottom,
- std::function<void(RenderProperties& props, TestCanvas& canvas)> setup) {
+ std::function<void(RenderProperties& props, Canvas& canvas)> setup) {
#if HWUI_NULL_GPU
// if RenderNodes are being sync'd/used, device info will be needed, since
// DeviceInfo::maxTextureSize() affects layer property
@@ -166,7 +212,29 @@ public:
RenderProperties& props = node->mutateStagingProperties();
props.setLeftTopRightBottom(left, top, right, bottom);
if (setup) {
- TestCanvas canvas(props.getWidth(), props.getHeight());
+ std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(props.getWidth(),
+ props.getHeight()));
+ setup(props, *canvas.get());
+ node->setStagingDisplayList(canvas->finishRecording(), nullptr);
+ }
+ node->setPropertyFieldsDirty(0xFFFFFFFF);
+ return node;
+ }
+
+ template<class RecordingCanvasType>
+ static sp<RenderNode> createNode(int left, int top, int right, int bottom,
+ std::function<void(RenderProperties& props, RecordingCanvasType& canvas)> setup) {
+#if HWUI_NULL_GPU
+ // if RenderNodes are being sync'd/used, device info will be needed, since
+ // DeviceInfo::maxTextureSize() affects layer property
+ DeviceInfo::initialize();
+#endif
+
+ sp<RenderNode> node = new RenderNode();
+ RenderProperties& props = node->mutateStagingProperties();
+ props.setLeftTopRightBottom(left, top, right, bottom);
+ if (setup) {
+ RecordingCanvasType canvas(props.getWidth(), props.getHeight());
setup(props, canvas);
node->setStagingDisplayList(canvas.finishRecording(), nullptr);
}
@@ -175,11 +243,40 @@ public:
}
static void recordNode(RenderNode& node,
- std::function<void(TestCanvas&)> contentCallback) {
- TestCanvas canvas(node.stagingProperties().getWidth(),
- node.stagingProperties().getHeight());
- contentCallback(canvas);
- node.setStagingDisplayList(canvas.finishRecording(), nullptr);
+ std::function<void(Canvas&)> contentCallback) {
+ std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
+ node.stagingProperties().getWidth(), node.stagingProperties().getHeight()));
+ contentCallback(*canvas.get());
+ node.setStagingDisplayList(canvas->finishRecording(), nullptr);
+ }
+
+ static sp<RenderNode> createSkiaNode(int left, int top, int right, int bottom,
+ std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)> setup,
+ const char* name = nullptr, skiapipeline::SkiaDisplayList* displayList = nullptr) {
+ #if HWUI_NULL_GPU
+ // if RenderNodes are being sync'd/used, device info will be needed, since
+ // DeviceInfo::maxTextureSize() affects layer property
+ DeviceInfo::initialize();
+ #endif
+ sp<RenderNode> node = new RenderNode();
+ if (name) {
+ node->setName(name);
+ }
+ RenderProperties& props = node->mutateStagingProperties();
+ props.setLeftTopRightBottom(left, top, right, bottom);
+ if (displayList) {
+ node->setStagingDisplayList(displayList, nullptr);
+ }
+ if (setup) {
+ std::unique_ptr<skiapipeline::SkiaRecordingCanvas> canvas(
+ new skiapipeline::SkiaRecordingCanvas(nullptr,
+ props.getWidth(), props.getHeight()));
+ setup(props, *canvas.get());
+ node->setStagingDisplayList(canvas->finishRecording(), nullptr);
+ }
+ node->setPropertyFieldsDirty(0xFFFFFFFF);
+ TestUtils::syncHierarchyPropertiesAndDisplayList(node);
+ return node;
}
/**
@@ -235,6 +332,22 @@ public:
static std::unique_ptr<uint16_t[]> asciiToUtf16(const char* str);
+ class MockFunctor : public Functor {
+ public:
+ virtual status_t operator ()(int what, void* data) {
+ mLastMode = what;
+ return DrawGlInfo::kStatusDone;
+ }
+ int getLastMode() const { return mLastMode; }
+ private:
+ int mLastMode = -1;
+ };
+
+ static SkColor getColor(const sk_sp<SkSurface>& surface, int x, int y);
+
+ static SkRect getClipBounds(const SkCanvas* canvas);
+ static SkRect getLocalClipBounds(const SkCanvas* canvas);
+
private:
static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
node->syncProperties();
@@ -251,5 +364,3 @@ private:
} /* namespace uirenderer */
} /* namespace android */
-
-#endif /* TEST_UTILS_H */
diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
new file mode 100644
index 000000000000..be58d09b7f4d
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+#include "tests/common/BitmapAllocationTestUtils.h"
+#include "utils/Color.h"
+
+#include <SkBitmap.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+class BitmapFillrate;
+
+static bool _BitmapFillrate(
+ BitmapAllocationTestUtils::registerBitmapAllocationScene<BitmapFillrate>(
+ "bitmapFillrate", "Draws multiple large half transparent bitmaps."));
+
+class BitmapFillrate : public TestScene {
+public:
+ BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
+ : TestScene()
+ , mAllocator(allocator) { }
+
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
+ createNode(canvas, 0x909C27B0, 0, 0, width, height);
+ createNode(canvas, 0xA0CDDC39, width / 3, height / 3, width, height);
+ createNode(canvas, 0x90009688, width / 3, 0, width, height);
+ createNode(canvas, 0xA0FF5722, 0, height / 3, width, height);
+ createNode(canvas, 0x9000796B, width / 6, height/6, width, height);
+ createNode(canvas, 0xA0FFC107, width / 6, 0, width, height);
+ }
+
+ void doFrame(int frameNr) override {
+ for (size_t ci = 0; ci < mNodes.size(); ci++) {
+ mNodes[ci]->mutateStagingProperties().setTranslationX(frameNr % 200);
+ mNodes[ci]->mutateStagingProperties().setTranslationY(frameNr % 200);
+ mNodes[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ void createNode(Canvas& canvas, SkColor color, int left, int top,
+ int width, int height) {
+ int itemWidth = 2 * width / 3;
+ int itemHeight = 2 * height / 3;
+ auto card = TestUtils::createNode(left, top, left + itemWidth , top + itemHeight,
+ [this, itemWidth, itemHeight, color](RenderProperties& props, Canvas& canvas) {
+ sk_sp<Bitmap> bitmap = mAllocator(itemWidth, itemHeight, kRGBA_8888_SkColorType,
+ [color](SkBitmap& skBitmap) {
+ skBitmap.eraseColor(color);
+ });
+ canvas.drawBitmap(*bitmap, 0, 0, nullptr);
+ });
+ canvas.drawRenderNode(card.get());
+ mNodes.push_back(card);
+ }
+
+ BitmapAllocationTestUtils::BitmapAllocator mAllocator;
+ std::vector< sp<RenderNode> > mNodes;
+}; \ No newline at end of file
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
new file mode 100644
index 000000000000..e03c9e84b739
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+#include "utils/Color.h"
+#include "tests/common/BitmapAllocationTestUtils.h"
+#include <SkImagePriv.h>
+
+class BitmapShaders;
+
+static bool _BitmapShaders(
+ BitmapAllocationTestUtils::registerBitmapAllocationScene<BitmapShaders>(
+ "bitmapShader", "Draws bitmap shaders with repeat and mirror modes."));
+
+class BitmapShaders : public TestScene {
+public:
+ BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator)
+ : TestScene()
+ , mAllocator(allocator) { }
+
+ sp<RenderNode> card;
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(Color::Grey_200, SkBlendMode::kSrcOver);
+ sk_sp<Bitmap> hwuiBitmap = mAllocator(200, 200, kRGBA_8888_SkColorType,
+ [](SkBitmap& skBitmap) {
+ skBitmap.eraseColor(Color::White);
+ SkCanvas skCanvas(skBitmap);
+ SkPaint skPaint;
+ skPaint.setColor(Color::Red_500);
+ skCanvas.drawRect(SkRect::MakeWH(100, 100), skPaint);
+ skPaint.setColor(Color::Blue_500);
+ skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint);
+ });
+
+ SkBitmap bitmap;
+ SkPaint paint;
+ hwuiBitmap->getSkBitmapForShaders(&bitmap);
+
+ sk_sp<SkShader> repeatShader = SkMakeBitmapShader(bitmap,
+ SkShader::TileMode::kRepeat_TileMode,
+ SkShader::TileMode::kRepeat_TileMode,
+ nullptr,
+ kNever_SkCopyPixelsMode,
+ nullptr);
+ paint.setShader(std::move(repeatShader));
+ canvas.drawRoundRect(0, 0, 500, 500, 50.0f, 50.0f, paint);
+
+ sk_sp<SkShader> mirrorShader = SkMakeBitmapShader(bitmap,
+ SkShader::TileMode::kMirror_TileMode,
+ SkShader::TileMode::kMirror_TileMode,
+ nullptr,
+ kNever_SkCopyPixelsMode,
+ nullptr);
+ paint.setShader(std::move(mirrorShader));
+ canvas.drawRoundRect(0, 600, 500, 1100, 50.0f, 50.0f, paint);
+ }
+
+ void doFrame(int frameNr) override { }
+
+ BitmapAllocationTestUtils::BitmapAllocator mAllocator;
+};
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index a5fd71266314..f47e05a1f3e6 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -28,18 +28,18 @@ static TestScene::Registrar _RectGrid(TestScene::Info{
class ClippingAnimation : public TestScene {
public:
sp<RenderNode> card;
- void createContent(int width, int height, TestCanvas& canvas) override {
- canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
card = TestUtils::createNode(0, 0, 200, 400,
- [](RenderProperties& props, TestCanvas& canvas) {
+ [](RenderProperties& props, Canvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
{
- canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+ canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
canvas.translate(100, 100);
canvas.rotate(45);
canvas.translate(-100, -100);
- canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
- canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode);
+ canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
+ canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
}
canvas.restore();
@@ -47,8 +47,8 @@ public:
{
SkPath clipCircle;
clipCircle.addCircle(100, 300, 100);
- canvas.clipPath(&clipCircle, SkRegion::kIntersect_Op);
- canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
+ canvas.clipPath(&clipCircle, SkClipOp::kIntersect);
+ canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
}
canvas.restore();
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index f184411b4139..c0d9450ebace 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -17,8 +17,8 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
-#include <minikin/Layout.h>
#include <hwui/Paint.h>
+#include <minikin/Layout.h>
#include <cstdio>
@@ -33,11 +33,11 @@ static TestScene::Registrar _GlyphStress(TestScene::Info{
class GlyphStressAnimation : public TestScene {
public:
sp<RenderNode> container;
- void createContent(int width, int height, TestCanvas& canvas) override {
+ void createContent(int width, int height, Canvas& canvas) override {
container = TestUtils::createNode(0, 0, width, height, nullptr);
doFrame(0); // update container
- canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
canvas.drawRenderNode(container.get());
}
@@ -46,19 +46,20 @@ public:
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
ssize_t textLength = 26 * 2;
- TestCanvas canvas(
+ std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
container->stagingProperties().getWidth(),
- container->stagingProperties().getHeight());
+ container->stagingProperties().getHeight()));
+
Paint paint;
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setAntiAlias(true);
paint.setColor(Color::Black);
for (int i = 0; i < 5; i++) {
paint.setTextSize(10 + (frameNr % 20) + i * 20);
- canvas.drawText(text.get(), 0, textLength, textLength,
- 0, 100 * (i + 2), kBidi_Force_LTR, paint, nullptr);
+ canvas->drawText(text.get(), 0, textLength, textLength,
+ 0, 100 * (i + 2), minikin::kBidi_Force_LTR, paint, nullptr);
}
- container->setStagingDisplayList(canvas.finishRecording(), nullptr);
+ container->setStagingDisplayList(canvas->finishRecording(), nullptr);
}
};
diff --git a/libs/hwui/tests/common/scenes/HwBitmap565.cpp b/libs/hwui/tests/common/scenes/HwBitmap565.cpp
new file mode 100644
index 000000000000..18fea3d1cb81
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/HwBitmap565.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+#include "utils/Color.h"
+#include "tests/common/BitmapAllocationTestUtils.h"
+
+class HwBitmap565;
+
+static TestScene::Registrar _HwBitmap565(TestScene::Info{
+ "hwBitmap565",
+ "Draws composite shader with hardware bitmap",
+ TestScene::simpleCreateScene<HwBitmap565>
+});
+
+class HwBitmap565 : public TestScene {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(Color::Grey_200, SkBlendMode::kSrcOver);
+
+ sk_sp<Bitmap> hardwareBitmap = BitmapAllocationTestUtils::allocateHardwareBitmap(200, 200,
+ kRGB_565_SkColorType, [](SkBitmap& skBitmap) {
+ skBitmap.eraseColor(Color::White);
+ SkCanvas skCanvas(skBitmap);
+ SkPaint skPaint;
+ skPaint.setColor(Color::Red_500);
+ skCanvas.drawRect(SkRect::MakeWH(100, 100), skPaint);
+ skPaint.setColor(Color::Blue_500);
+ skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint);
+ });
+ canvas.drawBitmap(*hardwareBitmap, 10.0f, 10.0f, nullptr);
+ }
+
+ void doFrame(int frameNr) override { }
+}; \ No newline at end of file
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
new file mode 100644
index 000000000000..83b01e906427
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+#include "utils/Color.h"
+
+#include <gui/IGraphicBufferAlloc.h>
+#include <gui/ISurfaceComposer.h>
+#include <private/gui/ComposerService.h>
+#include <binder/IServiceManager.h>
+#include <ui/PixelFormat.h>
+#include <SkGradientShader.h>
+#include <SkImagePriv.h>
+
+class HwBitmapInCompositeShader;
+
+static TestScene::Registrar _HwBitmapInCompositeShader(TestScene::Info{
+ "hwbitmapcompositeshader",
+ "Draws composite shader with hardware bitmap",
+ TestScene::simpleCreateScene<HwBitmapInCompositeShader>
+});
+
+class HwBitmapInCompositeShader : public TestScene {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
+
+ status_t error;
+ sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+ sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
+ uint32_t usage = GraphicBuffer::USAGE_HW_TEXTURE
+ | GraphicBuffer::USAGE_SW_READ_NEVER
+ | GRALLOC_USAGE_SW_WRITE_RARELY;
+ sp<GraphicBuffer> buffer = alloc->createGraphicBuffer(400, 200, PIXEL_FORMAT_RGBA_8888, 1,
+ usage, &error);
+
+ unsigned char* pixels = nullptr;
+ buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, ((void**)&pixels));
+ size_t size = bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride()
+ * buffer->getHeight();
+ memset(pixels, 0, size);
+ for (int i = 0; i < 6000; i++) {
+ pixels[4000 + 4 * i + 0] = 255;
+ pixels[4000 + 4 * i + 1] = 255;
+ pixels[4000 + 4 * i + 2] = 0;
+ pixels[4000 + 4 * i + 3] = 255;
+ }
+ buffer->unlock();
+ sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer));
+ sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap));
+
+ SkPoint center;
+ center.set(50, 50);
+ SkColor colors[2];
+ colors[0] = Color::Black;
+ colors[1] = Color::White;
+ sk_sp<SkShader> gradientShader = SkGradientShader::MakeRadial(center, 50, colors, nullptr,
+ 2, SkShader::TileMode::kRepeat_TileMode);
+
+ sk_sp<SkShader> compositeShader(
+ SkShader::MakeComposeShader(hardwareShader, gradientShader, SkBlendMode::kDstATop));
+
+ SkPaint paint;
+ paint.setShader(std::move(compositeShader));
+ canvas.drawRoundRect(0, 0, 400, 200, 10.0f, 10.0f, paint);
+ }
+
+ void doFrame(int frameNr) override { }
+
+ sk_sp<SkShader> createBitmapShader(Bitmap& bitmap) {
+ SkBitmap skBitmap;
+ bitmap.getSkBitmapForShaders(&skBitmap);
+ sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
+ return image->makeShader(SkShader::TileMode::kClamp_TileMode,
+ SkShader::TileMode::kClamp_TileMode);
+ }
+}; \ No newline at end of file
diff --git a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
index c212df4f978d..3a230ae6e8b7 100644
--- a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
@@ -28,13 +28,13 @@ static TestScene::Registrar _HwLayer(TestScene::Info{
class HwLayerAnimation : public TestScene {
public:
sp<RenderNode> card;
- void createContent(int width, int height, TestCanvas& canvas) override {
+ void createContent(int width, int height, Canvas& canvas) override {
card = TestUtils::createNode(0, 0, 200, 200,
- [](RenderProperties& props, TestCanvas& canvas) {
+ [](RenderProperties& props, Canvas& canvas) {
props.mutateLayerProperties().setType(LayerType::RenderLayer);
- canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFF0000FF, SkBlendMode::kSrcOver);
});
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); // background
canvas.drawRenderNode(card.get());
}
void doFrame(int frameNr) override {
diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
new file mode 100644
index 000000000000..b7357e179bfe
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+#include "tests/common/TestListViewSceneBase.h"
+
+#include <SkGradientShader.h>
+
+class ListOfFadedTextAnimation;
+
+static TestScene::Registrar _ListOfFadedTextAnimation(TestScene::Info{
+ "fadingedges",
+ "A mock ListView of scrolling text with faded edge. Doesn't re-bind/re-record views"
+ "as they are recycled, so won't upload much content (either glyphs, or bitmaps).",
+ TestScene::simpleCreateScene<ListOfFadedTextAnimation>
+});
+
+class ListOfFadedTextAnimation : public TestListViewSceneBase {
+ void createListItem(RenderProperties& props, Canvas& canvas, int id,
+ int itemWidth, int itemHeight) override {
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
+ int length = dp(100);
+ canvas.saveLayer(0, 0, length, itemHeight, nullptr, SaveFlags::HasAlphaLayer);
+ SkPaint textPaint;
+ textPaint.setTextSize(dp(20));
+ textPaint.setAntiAlias(true);
+ TestUtils::drawUtf8ToCanvas(&canvas, "not that long long text", textPaint, dp(10), dp(30));
+
+ SkPoint pts[2];
+ pts[0].set(0, 0);
+ pts[1].set(0, 1);
+
+ SkColor colors[2] = {Color::Black, Color::Transparent};
+ sk_sp<SkShader> s(SkGradientShader::MakeLinear(pts, colors, NULL, 2,
+ SkShader::kClamp_TileMode));
+
+ SkMatrix matrix;
+ matrix.setScale(1, length);
+ matrix.postRotate(-90);
+ SkPaint fadingPaint;
+ fadingPaint.setShader(s->makeWithLocalMatrix(matrix));
+ fadingPaint.setBlendMode(SkBlendMode::kDstOut);
+ canvas.drawRect(0, 0, length, itemHeight, fadingPaint);
+ canvas.restore();
+ }
+};
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index 8035dc45f23c..c1144be5b57c 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -15,7 +15,7 @@
*/
#include "TestSceneBase.h"
-#include "utils/Color.h"
+#include "tests/common/TestListViewSceneBase.h"
#include <cstdio>
@@ -28,61 +28,12 @@ static TestScene::Registrar _ListView(TestScene::Info{
TestScene::simpleCreateScene<ListViewAnimation>
});
-class ListViewAnimation : public TestScene {
-public:
- int cardHeight;
- int cardSpacing;
- int cardWidth;
- int cardLeft;
- sp<RenderNode> listView;
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, TestCanvas& canvas) override {
- srand(0);
- cardHeight = dp(60);
- cardSpacing = dp(16);
- cardWidth = std::min((height - cardSpacing * 2), (int)dp(300));
- cardLeft = (width - cardWidth) / 2;
-
- for (int y = 0; y < height + (cardHeight + cardSpacing - 1); y += (cardHeight + cardSpacing)) {
- cards.push_back(createCard(cards.size(), y));
- }
- listView = TestUtils::createNode(0, 0, width, height,
- [this](RenderProperties& props, TestCanvas& canvas) {
- for (size_t ci = 0; ci < cards.size(); ci++) {
- canvas.drawRenderNode(cards[ci].get());
- }
- });
-
- canvas.drawColor(Color::Grey_500, SkXfermode::kSrcOver_Mode);
- canvas.drawRenderNode(listView.get());
- }
-
- void doFrame(int frameNr) override {
- int scrollPx = dp(frameNr) * 3;
- int cardIndexOffset = scrollPx / (cardSpacing + cardHeight);
- int pxOffset = -(scrollPx % (cardSpacing + cardHeight));
-
- TestCanvas canvas(
- listView->stagingProperties().getWidth(),
- listView->stagingProperties().getHeight());
- for (size_t ci = 0; ci < cards.size(); ci++) {
- // update card position
- auto card = cards[(ci + cardIndexOffset) % cards.size()];
- int top = ((int)ci) * (cardSpacing + cardHeight) + pxOffset;
- card->mutateStagingProperties().setLeftTopRightBottom(
- cardLeft, top, cardLeft + cardWidth, top + cardHeight);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- // draw it to parent DisplayList
- canvas.drawRenderNode(cards[ci].get());
- }
- listView->setStagingDisplayList(canvas.finishRecording(), nullptr);
- }
-private:
- SkBitmap createRandomCharIcon() {
+class ListViewAnimation : public TestListViewSceneBase {
+ sk_sp<Bitmap> createRandomCharIcon(int cardHeight) {
+ SkBitmap skBitmap;
int size = cardHeight - (dp(10) * 2);
- SkBitmap bitmap = TestUtils::createSkBitmap(size, size);
- SkCanvas canvas(bitmap);
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(size, size, &skBitmap));
+ SkCanvas canvas(skBitmap);
canvas.clear(0);
SkPaint paint;
@@ -97,15 +48,19 @@ private:
paint.setTextAlign(SkPaint::kCenter_Align);
paint.setTextSize(size / 2);
char charToShow = 'A' + (rand() % 26);
- canvas.drawText(&charToShow, 1, size / 2, /*approximate centering*/ size * 0.7, paint);
+ const SkPoint pos[] = {{
+ SkIntToScalar(size / 2),
+ /*approximate centering*/ SkFloatToScalar(size * 0.7f)}};
+ canvas.drawPosText(&charToShow, 1, pos, paint);
return bitmap;
}
- static SkBitmap createBoxBitmap(bool filled) {
+ static sk_sp<Bitmap> createBoxBitmap(bool filled) {
int size = dp(20);
int stroke = dp(2);
- SkBitmap bitmap = TestUtils::createSkBitmap(size, size);
- SkCanvas canvas(bitmap);
+ SkBitmap skBitmap;
+ auto bitmap = TestUtils::createBitmap(size, size, &skBitmap);
+ SkCanvas canvas(skBitmap);
canvas.clear(Color::Transparent);
SkPaint paint;
@@ -117,34 +72,32 @@ private:
return bitmap;
}
- sp<RenderNode> createCard(int cardId, int top) {
- return TestUtils::createNode(cardLeft, top, cardLeft + cardWidth, top + cardHeight,
- [this, cardId](RenderProperties& props, TestCanvas& canvas) {
- static SkBitmap filledBox = createBoxBitmap(true);
- static SkBitmap strokedBox = createBoxBitmap(false);
-
- // TODO: switch to using round rect clipping, once merging correctly handles that
- SkPaint roundRectPaint;
- roundRectPaint.setAntiAlias(true);
- roundRectPaint.setColor(Color::White);
- canvas.drawRoundRect(0, 0, cardWidth, cardHeight, dp(6), dp(6), roundRectPaint);
-
- SkPaint textPaint;
- textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500);
- textPaint.setTextSize(dp(20));
- textPaint.setAntiAlias(true);
- char buf[256];
- snprintf(buf, sizeof(buf), "This card is #%d", cardId);
- TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, cardHeight, dp(25));
- textPaint.setTextSize(dp(15));
- TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint,
- cardHeight, dp(45));
-
- canvas.drawBitmap(createRandomCharIcon(), dp(10), dp(10), nullptr);
-
- const SkBitmap& boxBitmap = rand() % 2 ? filledBox : strokedBox;
- canvas.drawBitmap(boxBitmap, cardWidth - dp(10) - boxBitmap.width(), dp(10), nullptr);
- });
+ void createListItem(RenderProperties& props, Canvas& canvas, int cardId,
+ int itemWidth, int itemHeight) override {
+ static sk_sp<Bitmap> filledBox(createBoxBitmap(true));
+ static sk_sp<Bitmap> strokedBox(createBoxBitmap(false));
+ // TODO: switch to using round rect clipping, once merging correctly handles that
+ SkPaint roundRectPaint;
+ roundRectPaint.setAntiAlias(true);
+ roundRectPaint.setColor(Color::White);
+ canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint);
+
+ SkPaint textPaint;
+ textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500);
+ textPaint.setTextSize(dp(20));
+ textPaint.setAntiAlias(true);
+ char buf[256];
+ snprintf(buf, sizeof(buf), "This card is #%d", cardId);
+ TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, itemHeight, dp(25));
+ textPaint.setTextSize(dp(15));
+ TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint,
+ itemHeight, dp(45));
+
+ auto randomIcon = createRandomCharIcon(itemHeight);
+ canvas.drawBitmap(*randomIcon, dp(10), dp(10), nullptr);
+
+ auto box = rand() % 2 ? filledBox : strokedBox;
+ canvas.drawBitmap(*box, itemWidth - dp(10) - box->width(), dp(10), nullptr);
}
};
diff --git a/libs/hwui/tests/common/scenes/OpPropAnimation.cpp b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp
index 5dfb2b4a8b33..68051d63e855 100644
--- a/libs/hwui/tests/common/scenes/OpPropAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp
@@ -41,9 +41,9 @@ public:
sp<CanvasPropertyPrimitive> mCircleRadius = new CanvasPropertyPrimitive(0);
sp<RenderNode> content;
- void createContent(int width, int height, TestCanvas& canvas) override {
+ void createContent(int width, int height, Canvas& canvas) override {
content = TestUtils::createNode(0, 0, width, height,
- [this, width, height](RenderProperties& props, TestCanvas& canvas) {
+ [this, width, height](RenderProperties& props, Canvas& canvas) {
mPaint->value.setAntiAlias(true);
mPaint->value.setColor(Color::Blue_500);
@@ -53,7 +53,7 @@ public:
mCircleX->value = width * 0.75;
mCircleY->value = height * 0.75;
- canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
canvas.drawRoundRect(mRoundRectLeft.get(), mRoundRectTop.get(),
mRoundRectRight.get(), mRoundRectBottom.get(),
mRoundRectRx.get(), mRoundRectRy.get(), mPaint.get());
diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
index e56f2f97007e..d6fd60494812 100644
--- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
@@ -28,10 +28,10 @@ static TestScene::Registrar _Oval(TestScene::Info{
class OvalAnimation : public TestScene {
public:
sp<RenderNode> card;
- void createContent(int width, int height, TestCanvas& canvas) override {
- canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
card = TestUtils::createNode(0, 0, 200, 200,
- [](RenderProperties& props, TestCanvas& canvas) {
+ [](RenderProperties& props, Canvas& canvas) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(Color::Black);
diff --git a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
index 84265a4774a5..bc04d81296df 100644
--- a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
@@ -29,7 +29,7 @@ static TestScene::Registrar _PartialDamage(TestScene::Info{
class PartialDamageAnimation : public TestScene {
public:
std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, TestCanvas& canvas) override {
+ void createContent(int width, int height, Canvas& canvas) override {
static SkColor COLORS[] = {
0xFFF44336,
0xFF9C27B0,
@@ -37,15 +37,15 @@ public:
0xFF4CAF50,
};
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
SkColor color = COLORS[static_cast<int>((y / dp(116))) % 4];
sp<RenderNode> card = TestUtils::createNode(x, y,
x + dp(100), y + dp(100),
- [color](RenderProperties& props, TestCanvas& canvas) {
- canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
+ [color](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(color, SkBlendMode::kSrcOver);
});
canvas.drawRenderNode(card.get());
cards.push_back(card);
@@ -58,10 +58,10 @@ public:
cards[0]->mutateStagingProperties().setTranslationY(curFrame);
cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- TestUtils::recordNode(*cards[0], [curFrame](TestCanvas& canvas) {
+ TestUtils::recordNode(*cards[0], [curFrame](Canvas& canvas) {
SkColor color = TestUtils::interpolateColor(
curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0);
- canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(color, SkBlendMode::kSrcOver);
});
}
};
diff --git a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp
new file mode 100644
index 000000000000..bc6fc6452e90
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+
+class ReadbackFromHardware;
+
+static TestScene::Registrar _SaveLayer(TestScene::Info{
+ "readbackFromHBitmap",
+ "Allocates hardware bitmap and readback data from it.",
+ TestScene::simpleCreateScene<ReadbackFromHardware>
+});
+
+class ReadbackFromHardware : public TestScene {
+public:
+ static sk_sp<Bitmap> createHardwareBitmap() {
+ SkBitmap skBitmap;
+ SkImageInfo info = SkImageInfo::Make(400, 400, kN32_SkColorType, kPremul_SkAlphaType);
+ skBitmap.allocPixels(info);
+ skBitmap.eraseColor(Color::Red_500);
+ SkCanvas canvas(skBitmap);
+ SkPaint paint;
+ paint.setColor(Color::Blue_500);
+ canvas.drawRect(SkRect::MakeXYWH(30, 30, 30, 150), paint);
+ canvas.drawRect(SkRect::MakeXYWH(30, 30, 100, 30), paint);
+ canvas.drawRect(SkRect::MakeXYWH(30, 100, 70, 30), paint);
+ return Bitmap::allocateHardwareBitmap(skBitmap);
+ }
+
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver); // background
+
+ sk_sp<Bitmap> hardwareBitmap(createHardwareBitmap());
+
+ SkBitmap readback;
+ hardwareBitmap->getSkBitmap(&readback);
+
+ SkBitmap canvasBitmap;
+ sk_sp<Bitmap> heapBitmap(TestUtils::createBitmap(hardwareBitmap->width(),
+ hardwareBitmap->height(), &canvasBitmap));
+
+ SkCanvas skCanvas(canvasBitmap);
+ skCanvas.drawBitmap(readback, 0, 0);
+ canvas.drawBitmap(*heapBitmap, 0, 0, nullptr);
+
+ canvas.drawBitmap(*hardwareBitmap, 0, 500, nullptr);
+ }
+
+ void doFrame(int frameNr) override { }
+};
diff --git a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
index 6509edd4077f..825602466f92 100644
--- a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
@@ -28,7 +28,7 @@ static TestScene::Registrar _Recents(TestScene::Info{
class RecentsAnimation : public TestScene {
public:
- void createContent(int width, int height, TestCanvas& renderer) override {
+ void createContent(int width, int height, Canvas& renderer) override {
static SkColor COLORS[] = {
Color::Red_500,
Color::Purple_500,
@@ -39,18 +39,20 @@ public:
thumbnailSize = std::min(std::min(width, height) / 2, 720);
int cardsize = std::min(width, height) - dp(64);
- renderer.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+ renderer.drawColor(Color::White, SkBlendMode::kSrcOver);
renderer.insertReorderBarrier(true);
int x = dp(32);
for (int i = 0; i < 4; i++) {
int y = (height / 4) * i;
- SkBitmap thumb = TestUtils::createSkBitmap(thumbnailSize, thumbnailSize);
- thumb.eraseColor(COLORS[i]);
- sp<RenderNode> card = createCard(x, y, cardsize, cardsize, thumb);
+ SkBitmap bitmap;
+ sk_sp<Bitmap> thumb(TestUtils::createBitmap(thumbnailSize, thumbnailSize, &bitmap));
+
+ bitmap.eraseColor(COLORS[i]);
+ sp<RenderNode> card = createCard(x, y, cardsize, cardsize, *thumb);
card->mutateStagingProperties().setElevation(i * dp(8));
renderer.drawRenderNode(card.get());
- mThumbnail = thumb;
+ mThumbnail = bitmap;
mCards.push_back(card);
}
@@ -68,15 +70,14 @@ public:
}
private:
- sp<RenderNode> createCard(int x, int y, int width, int height,
- const SkBitmap& thumb) {
+ sp<RenderNode> createCard(int x, int y, int width, int height, Bitmap& thumb) {
return TestUtils::createNode(x, y, x + width, y + height,
- [&thumb, width, height](RenderProperties& props, TestCanvas& canvas) {
+ [&thumb, width, height](RenderProperties& props, Canvas& canvas) {
props.setElevation(dp(16));
props.mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
props.mutableOutline().setShouldClip(true);
- canvas.drawColor(Color::Grey_200, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Grey_200, SkBlendMode::kSrcOver);
canvas.drawBitmap(thumb, 0, 0, thumb.width(), thumb.height(),
0, 0, width, height, nullptr);
});
diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
index a9293ab244dd..668eec69c2d0 100644
--- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
@@ -29,13 +29,13 @@ static TestScene::Registrar _RectGrid(TestScene::Info{
class RectGridAnimation : public TestScene {
public:
sp<RenderNode> card;
- void createContent(int width, int height, TestCanvas& canvas) override {
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
canvas.insertReorderBarrier(true);
card = TestUtils::createNode(50, 50, 250, 250,
- [](RenderProperties& props, TestCanvas& canvas) {
- canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+ [](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(0xFFFF00FF, SkBlendMode::kSrcOver);
SkRegion region;
for (int xOffset = 0; xOffset < 200; xOffset+=2) {
diff --git a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
new file mode 100644
index 000000000000..4b6632d244f5
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "TestSceneBase.h"
+
+#include <vector>
+
+class RoundRectClippingAnimation : public TestScene {
+public:
+ int mSpacing, mSize;
+
+ RoundRectClippingAnimation(int spacing, int size)
+ : mSpacing(spacing), mSize(size) {}
+
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
+ canvas.insertReorderBarrier(true);
+ int ci = 0;
+
+ for (int x = 0; x < width; x += mSpacing) {
+ for (int y = 0; y < height; y += mSpacing) {
+ auto color = BrightColors[ci++ % BrightColorsCount];
+ auto card = TestUtils::createNode(x, y, x + mSize, y + mSize,
+ [&](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(color, SkBlendMode::kSrcOver);
+ props.mutableOutline().setRoundRect(0, 0,
+ props.getWidth(), props.getHeight(), mSize * .25, 1);
+ props.mutableOutline().setShouldClip(true);
+ });
+ canvas.drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ canvas.insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 50;
+ if (curFrame > 25) curFrame = 50 - curFrame;
+ for (auto& card : cards) {
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+};
+
+static TestScene::Registrar _RoundRectClippingGpu(TestScene::Info{
+ "roundRectClipping-gpu",
+ "A bunch of RenderNodes with round rect clipping outlines that's GPU limited.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new RoundRectClippingAnimation(dp(40), dp(200));
+ }
+});
+
+static TestScene::Registrar _RoundRectClippingCpu(TestScene::Info{
+ "roundRectClipping-cpu",
+ "A bunch of RenderNodes with round rect clipping outlines that's CPU limited.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new RoundRectClippingAnimation(dp(20), dp(20));
+ }
+});
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 6904bec304e3..7e8a7d9d14f2 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -28,17 +28,17 @@ static TestScene::Registrar _SaveLayer(TestScene::Info{
class SaveLayerAnimation : public TestScene {
public:
sp<RenderNode> card;
- void createContent(int width, int height, TestCanvas& canvas) override {
- canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); // background
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver); // background
card = TestUtils::createNode(0, 0, 400, 800,
- [](RenderProperties& props, TestCanvas& canvas) {
+ [](RenderProperties& props, Canvas& canvas) {
// nested clipped saveLayers
canvas.saveLayerAlpha(0, 0, 400, 400, 200, SaveFlags::ClipToLayer);
- canvas.drawColor(Color::Green_700, SkXfermode::kSrcOver_Mode);
- canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op);
+ canvas.drawColor(Color::Green_700, SkBlendMode::kSrcOver);
+ canvas.clipRect(50, 50, 350, 350, SkClipOp::kIntersect);
canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
- canvas.drawColor(Color::Blue_500, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
canvas.restore();
canvas.restore();
diff --git a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
index d3249b8f585a..0a69b62fe615 100644
--- a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
@@ -28,8 +28,8 @@ static TestScene::Registrar _ShadowGrid2(TestScene::Info{
class ShadowGrid2Animation : public TestScene {
public:
std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, TestCanvas& canvas) override {
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
canvas.insertReorderBarrier(true);
for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
@@ -53,11 +53,11 @@ public:
private:
sp<RenderNode> createCard(int x, int y, int width, int height) {
return TestUtils::createNode(x, y, x + width, y + height,
- [width, height](RenderProperties& props, TestCanvas& canvas) {
+ [width, height](RenderProperties& props, Canvas& canvas) {
props.setElevation(dp(16));
props.mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
props.mutableOutline().setShouldClip(true);
- canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFEEEEEE, SkBlendMode::kSrcOver);
});
}
};
diff --git a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
index 5ffedf09bc70..4a024295cb25 100644
--- a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
@@ -28,8 +28,8 @@ static TestScene::Registrar _ShadowGrid(TestScene::Info{
class ShadowGridAnimation : public TestScene {
public:
std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, TestCanvas& canvas) override {
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
canvas.insertReorderBarrier(true);
for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
@@ -53,11 +53,11 @@ public:
private:
sp<RenderNode> createCard(int x, int y, int width, int height) {
return TestUtils::createNode(x, y, x + width, y + height,
- [width, height](RenderProperties& props, TestCanvas& canvas) {
+ [width, height](RenderProperties& props, Canvas& canvas) {
props.setElevation(dp(16));
props.mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
props.mutableOutline().setShouldClip(true);
- canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(0xFFEEEEEE, SkBlendMode::kSrcOver);
});
}
};
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index 6d27c9d61a29..09e70ebf7b5f 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -30,17 +30,17 @@ static TestScene::Registrar _Shapes(TestScene::Info{
class ShapeAnimation : public TestScene {
public:
sp<RenderNode> card;
- void createContent(int width, int height, TestCanvas& canvas) override {
+ void createContent(int width, int height, Canvas& canvas) override {
card = TestUtils::createNode(0, 0, width, height,
- [width](RenderProperties& props, TestCanvas& canvas) {
- std::function<void(TestCanvas&, float, const SkPaint&)> ops[] = {
- [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ [width](RenderProperties& props, Canvas& canvas) {
+ std::function<void(Canvas&, float, const SkPaint&)> ops[] = {
+ [](Canvas& canvas, float size, const SkPaint& paint) {
canvas.drawArc(0, 0, size, size, 50, 189, true, paint);
},
- [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ [](Canvas& canvas, float size, const SkPaint& paint) {
canvas.drawOval(0, 0, size, size, paint);
},
- [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ [](Canvas& canvas, float size, const SkPaint& paint) {
SkPath diamondPath;
diamondPath.moveTo(size / 2, 0);
diamondPath.lineTo(size, size / 2);
@@ -49,18 +49,18 @@ public:
diamondPath.close();
canvas.drawPath(diamondPath, paint);
},
- [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ [](Canvas& canvas, float size, const SkPaint& paint) {
float data[] = {0, 0, size, size, 0, size, size, 0 };
canvas.drawLines(data, sizeof(data) / sizeof(float), paint);
},
- [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ [](Canvas& canvas, float size, const SkPaint& paint) {
float data[] = {0, 0, size, size, 0, size, size, 0 };
canvas.drawPoints(data, sizeof(data) / sizeof(float), paint);
},
- [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ [](Canvas& canvas, float size, const SkPaint& paint) {
canvas.drawRect(0, 0, size, size, paint);
},
- [](TestCanvas& canvas, float size, const SkPaint& paint) {
+ [](Canvas& canvas, float size, const SkPaint& paint) {
float rad = size / 4;
canvas.drawRoundRect(0, 0, size, size, rad, rad, paint);
}
@@ -82,8 +82,8 @@ public:
int middleCount = canvas.save(SaveFlags::MatrixClip);
for (auto op : ops) {
int innerCount = canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(0, 0, cellSize, cellSize, SkRegion::kIntersect_Op);
- canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.clipRect(0, 0, cellSize, cellSize, SkClipOp::kIntersect);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
op(canvas, cellSize, paint);
canvas.restoreToCount(innerCount);
canvas.translate(cellSize + cellSpace, 0);
@@ -94,7 +94,7 @@ public:
}
canvas.restoreToCount(outerCount);
});
- canvas.drawColor(Color::Grey_500, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver);
canvas.drawRenderNode(card.get());
}
diff --git a/libs/hwui/tests/common/scenes/TestSceneBase.h b/libs/hwui/tests/common/scenes/TestSceneBase.h
index 935ddcf9212d..792312a6a7a4 100644
--- a/libs/hwui/tests/common/scenes/TestSceneBase.h
+++ b/libs/hwui/tests/common/scenes/TestSceneBase.h
@@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef TESTS_SCENES_TESTSCENEBASE_H
-#define TESTS_SCENES_TESTSCENEBASE_H
-#include "DisplayListCanvas.h"
+#pragma once
+
#include "RecordingCanvas.h"
#include "RenderNode.h"
#include "tests/common/TestContext.h"
@@ -30,5 +29,3 @@ using namespace android;
using namespace android::uirenderer;
using namespace android::uirenderer::renderthread;
using namespace android::uirenderer::test;
-
-#endif /* TESTS_SCENES_TESTSCENEBASE_H_ */
diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp
index be8f48b9fd17..438f877deb3e 100644
--- a/libs/hwui/tests/common/scenes/TextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp
@@ -28,10 +28,10 @@ static TestScene::Registrar _Text(TestScene::Info{
class TextAnimation : public TestScene {
public:
sp<RenderNode> card;
- void createContent(int width, int height, TestCanvas& canvas) override {
- canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
card = TestUtils::createNode(0, 0, width, height,
- [](RenderProperties& props, TestCanvas& canvas) {
+ [](RenderProperties& props, Canvas& canvas) {
SkPaint paint;
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setAntiAlias(true);
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 6533c2eae305..396e896a7517 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -22,6 +22,7 @@
#include "renderthread/RenderProxy.h"
#include "renderthread/RenderTask.h"
+#include <benchmark/benchmark.h>
#include <gui/Surface.h>
#include <log/log.h>
#include <ui/PixelFormat.h>
@@ -41,7 +42,7 @@ public:
template<class T>
class ModifiedMovingAverage {
public:
- ModifiedMovingAverage(int weight) : mWeight(weight) {}
+ explicit ModifiedMovingAverage(int weight) : mWeight(weight) {}
T add(T today) {
if (!mHasValue) {
@@ -62,13 +63,63 @@ private:
T mAverage;
};
-void run(const TestScene::Info& info, const TestScene::Options& opts) {
+void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts,
+ benchmark::BenchmarkReporter* reporter, RenderProxy* proxy,
+ double durationInS) {
+ using namespace benchmark;
+
+ struct ReportInfo {
+ int percentile;
+ const char* suffix;
+ };
+
+ static std::array<ReportInfo, 4> REPORTS = {
+ ReportInfo { 50, "_50th" },
+ ReportInfo { 90, "_90th" },
+ ReportInfo { 95, "_95th" },
+ ReportInfo { 99, "_99th" },
+ };
+
+ // Although a vector is used, it must stay with only a single element
+ // otherwise the BenchmarkReporter will automatically compute
+ // mean and stddev which doesn't make sense for our usage
+ std::vector<BenchmarkReporter::Run> reports;
+ BenchmarkReporter::Run report;
+ report.benchmark_name = info.name;
+ report.iterations = static_cast<int64_t>(opts.count);
+ report.real_accumulated_time = durationInS;
+ report.cpu_accumulated_time = durationInS;
+ report.items_per_second = opts.count / durationInS;
+ reports.push_back(report);
+ reporter->ReportRuns(reports);
+
+ // Pretend the percentiles are single-iteration runs of the test
+ // If rendering offscreen skip this as it's fps that's more interesting
+ // in that test case than percentiles.
+ if (!opts.renderOffscreen) {
+ for (auto& ri : REPORTS) {
+ reports[0].benchmark_name = info.name;
+ reports[0].benchmark_name += ri.suffix;
+ durationInS = proxy->frameTimePercentile(ri.percentile) / 1000.0;
+ reports[0].real_accumulated_time = durationInS;
+ reports[0].cpu_accumulated_time = durationInS;
+ reports[0].iterations = 1;
+ reports[0].items_per_second = 0;
+ reporter->ReportRuns(reports);
+ }
+ }
+}
+
+void run(const TestScene::Info& info, const TestScene::Options& opts,
+ benchmark::BenchmarkReporter* reporter) {
// Switch to the real display
gDisplay = getBuiltInDisplay();
std::unique_ptr<TestScene> scene(info.createScene(opts));
+ Properties::forceDrawFrame = true;
TestContext testContext;
+ testContext.setRenderOffscreen(opts.renderOffscreen);
// create the native surface
const int width = gDisplay.w;
@@ -76,7 +127,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) {
sp<Surface> surface = testContext.surface();
sp<RenderNode> rootNode = TestUtils::createNode(0, 0, width, height,
- [&scene, width, height](RenderProperties& props, TestCanvas& canvas) {
+ [&scene, width, height](RenderProperties& props, Canvas& canvas) {
props.setClipToBounds(false);
scene->createContent(width, height, canvas);
});
@@ -87,11 +138,16 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) {
proxy->loadSystemProperties();
proxy->initialize(surface);
float lightX = width / 2.0;
- proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
+ proxy->setup(dp(800.0f), 255 * 0.075, 255 * 0.15);
proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
// Do a few cold runs then reset the stats so that the caches are all hot
- for (int i = 0; i < 5; i++) {
+ int warmupFrameCount = 5;
+ if (opts.renderOffscreen) {
+ // Do a few more warmups to try and boost the clocks up
+ warmupFrameCount = 10;
+ }
+ for (int i = 0; i < warmupFrameCount; i++) {
testContext.waitForVsync();
nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
@@ -103,6 +159,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) {
ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight);
+ nsecs_t start = systemTime(CLOCK_MONOTONIC);
for (int i = 0; i < opts.count; i++) {
testContext.waitForVsync();
nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
@@ -121,6 +178,13 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) {
}
}
}
+ proxy->fence();
+ nsecs_t end = systemTime(CLOCK_MONOTONIC);
- proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats);
+ if (reporter) {
+ outputBenchmarkReport(info, opts, reporter, proxy.get(),
+ (end - start) / (double) s2ns(1));
+ } else {
+ proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats);
+ }
}
diff --git a/libs/hwui/tests/macrobench/how_to_run.txt b/libs/hwui/tests/macrobench/how_to_run.txt
index b051768f3262..3c3d36a8977f 100644
--- a/libs/hwui/tests/macrobench/how_to_run.txt
+++ b/libs/hwui/tests/macrobench/how_to_run.txt
@@ -1,5 +1,5 @@
mmm -j8 frameworks/base/libs/hwui/ &&
- adb push $OUT/data/local/tmp/hwuitest /data/local/tmp/hwuitest &&
- adb shell /data/local/tmp/hwuitest
+adb push $OUT/data/benchmarktest/hwuimacro/hwuimacro /data/benchmarktest/hwuimacro/hwuimacro &&
+adb shell /data/benchmarktest/hwuimacro/hwuimacro shadowgrid2 --onscreen
Pass --help to get help
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 02a39501e647..1f5622252f6a 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -14,11 +14,15 @@
* limitations under the License.
*/
+#include "tests/common/LeakChecker.h"
#include "tests/common/TestScene.h"
+#include "hwui/Typeface.h"
#include "protos/hwui.pb.h"
#include "Properties.h"
+#include <benchmark/benchmark.h>
+#include <../src/sysinfo.h>
#include <getopt.h>
#include <stdio.h>
#include <string>
@@ -39,12 +43,14 @@ using namespace android::uirenderer::test;
static int gRepeatCount = 1;
static std::vector<TestScene::Info> gRunTests;
static TestScene::Options gOpts;
+std::unique_ptr<benchmark::BenchmarkReporter> gBenchmarkReporter;
-void run(const TestScene::Info& info, const TestScene::Options& opts);
+void run(const TestScene::Info& info, const TestScene::Options& opts,
+ benchmark::BenchmarkReporter* reporter);
static void printHelp() {
printf(R"(
-USAGE: hwuitest [OPTIONS] <TESTNAME>
+USAGE: hwuimacro [OPTIONS] <TESTNAME>
OPTIONS:
-c, --count=NUM NUM loops a test should run (example, number of frames)
@@ -58,6 +64,10 @@ OPTIONS:
moving average frametime. Weight is optional, default is 10
--cpuset=name Adds the test to the specified cpuset before running
Not supported on all devices and needs root
+ --offscreen Render tests off device screen. This option is on by default
+ --onscreen Render tests on device screen. By default tests
+ are offscreen rendered
+ --benchmark_format Set output format. Possible values are tabular, json, csv
)");
}
@@ -121,6 +131,20 @@ static void moveToCpuSet(const char* cpusetName) {
close(fd);
}
+static bool setBenchmarkFormat(const char* format) {
+ if (!strcmp(format, "tabular")) {
+ gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
+ } else if (!strcmp(format, "json")) {
+ gBenchmarkReporter.reset(new benchmark::JSONReporter());
+ } else if (!strcmp(format, "csv")) {
+ gBenchmarkReporter.reset(new benchmark::CSVReporter());
+ } else {
+ fprintf(stderr, "Unknown format '%s'", format);
+ return false;
+ }
+ return true;
+}
+
// For options that only exist in long-form. Anything in the
// 0-255 range is reserved for short options (which just use their ASCII value)
namespace LongOpts {
@@ -130,6 +154,9 @@ enum {
WaitForGpu,
ReportFrametime,
CpuSet,
+ BenchmarkFormat,
+ Onscreen,
+ Offscreen,
};
}
@@ -141,6 +168,9 @@ static const struct option LONG_OPTIONS[] = {
{ "wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu },
{ "report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime },
{ "cpuset", required_argument, nullptr, LongOpts::CpuSet },
+ { "benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat },
+ { "onscreen", no_argument, nullptr, LongOpts::Onscreen },
+ { "offscreen", no_argument, nullptr, LongOpts::Offscreen },
{ 0, 0, 0, 0 }
};
@@ -214,6 +244,24 @@ void parseOptions(int argc, char* argv[]) {
moveToCpuSet(optarg);
break;
+ case LongOpts::BenchmarkFormat:
+ if (!optarg) {
+ error = true;
+ break;
+ }
+ if (!setBenchmarkFormat(optarg)) {
+ error = true;
+ }
+ break;
+
+ case LongOpts::Onscreen:
+ gOpts.renderOffscreen = false;
+ break;
+
+ case LongOpts::Offscreen:
+ gOpts.renderOffscreen = true;
+ break;
+
case 'h':
printHelp();
exit(EXIT_SUCCESS);
@@ -246,7 +294,9 @@ void parseOptions(int argc, char* argv[]) {
}
} while (optind < argc);
} else {
- gRunTests.push_back(TestScene::testMap()["shadowgrid"]);
+ for (auto& iter : TestScene::testMap()) {
+ gRunTests.push_back(iter.second);
+ }
}
}
@@ -254,13 +304,39 @@ int main(int argc, char* argv[]) {
// set defaults
gOpts.count = 150;
+ Typeface::setRobotoTypefaceForTest();
+
parseOptions(argc, argv);
+ if (!gBenchmarkReporter && gOpts.renderOffscreen) {
+ gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
+ }
+
+ if (gBenchmarkReporter) {
+ size_t name_field_width = 10;
+ for (auto&& test : gRunTests) {
+ name_field_width = std::max<size_t>(name_field_width, test.name.size());
+ }
+ // _50th, _90th, etc...
+ name_field_width += 5;
+
+ benchmark::BenchmarkReporter::Context context;
+ context.num_cpus = benchmark::NumCPUs();
+ context.mhz_per_cpu = benchmark::CyclesPerSecond() / 1000000.0f;
+ context.cpu_scaling_enabled = benchmark::CpuScalingEnabled();
+ context.name_field_width = name_field_width;
+ gBenchmarkReporter->ReportContext(context);
+ }
for (int i = 0; i < gRepeatCount; i++) {
for (auto&& test : gRunTests) {
- run(test, gOpts);
+ run(test, gOpts, gBenchmarkReporter.get());
}
}
- printf("Success!\n");
+
+ if (gBenchmarkReporter) {
+ gBenchmarkReporter->Finalize();
+ }
+
+ LeakChecker::checkForLeaks();
return 0;
}
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 06b68d1dea8f..f166c5ceb974 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -17,22 +17,12 @@
#include <benchmark/benchmark.h>
#include "DisplayList.h"
-#if HWUI_NEW_OPS
#include "RecordingCanvas.h"
-#else
-#include "DisplayListCanvas.h"
-#endif
#include "tests/common/TestUtils.h"
using namespace android;
using namespace android::uirenderer;
-#if HWUI_NEW_OPS
-typedef RecordingCanvas TestCanvas;
-#else
-typedef DisplayListCanvas TestCanvas;
-#endif
-
void BM_DisplayList_alloc(benchmark::State& benchState) {
while (benchState.KeepRunning()) {
auto displayList = new DisplayList();
@@ -52,42 +42,42 @@ void BM_DisplayList_alloc_theoretical(benchmark::State& benchState) {
BENCHMARK(BM_DisplayList_alloc_theoretical);
void BM_DisplayListCanvas_record_empty(benchmark::State& benchState) {
- TestCanvas canvas(100, 100);
- delete canvas.finishRecording();
+ std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
+ delete canvas->finishRecording();
while (benchState.KeepRunning()) {
- canvas.resetRecording(100, 100);
- benchmark::DoNotOptimize(&canvas);
- delete canvas.finishRecording();
+ canvas->resetRecording(100, 100);
+ benchmark::DoNotOptimize(canvas.get());
+ delete canvas->finishRecording();
}
}
BENCHMARK(BM_DisplayListCanvas_record_empty);
void BM_DisplayListCanvas_record_saverestore(benchmark::State& benchState) {
- TestCanvas canvas(100, 100);
- delete canvas.finishRecording();
+ std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
+ delete canvas->finishRecording();
while (benchState.KeepRunning()) {
- canvas.resetRecording(100, 100);
- canvas.save(SaveFlags::MatrixClip);
- canvas.save(SaveFlags::MatrixClip);
- benchmark::DoNotOptimize(&canvas);
- canvas.restore();
- canvas.restore();
- delete canvas.finishRecording();
+ canvas->resetRecording(100, 100);
+ canvas->save(SaveFlags::MatrixClip);
+ canvas->save(SaveFlags::MatrixClip);
+ benchmark::DoNotOptimize(canvas.get());
+ canvas->restore();
+ canvas->restore();
+ delete canvas->finishRecording();
}
}
BENCHMARK(BM_DisplayListCanvas_record_saverestore);
void BM_DisplayListCanvas_record_translate(benchmark::State& benchState) {
- TestCanvas canvas(100, 100);
- delete canvas.finishRecording();
+ std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
+ delete canvas->finishRecording();
while (benchState.KeepRunning()) {
- canvas.resetRecording(100, 100);
- canvas.scale(10, 10);
- benchmark::DoNotOptimize(&canvas);
- delete canvas.finishRecording();
+ canvas->resetRecording(100, 100);
+ canvas->scale(10, 10);
+ benchmark::DoNotOptimize(canvas.get());
+ delete canvas->finishRecording();
}
}
BENCHMARK(BM_DisplayListCanvas_record_translate);
@@ -99,27 +89,27 @@ BENCHMARK(BM_DisplayListCanvas_record_translate);
* View system frequently produces unneeded save/restores.
*/
void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) {
- TestCanvas canvas(100, 100);
- delete canvas.finishRecording();
+ std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
+ delete canvas->finishRecording();
SkPaint rectPaint;
- SkBitmap iconBitmap = TestUtils::createSkBitmap(80, 80);
+ sk_sp<Bitmap> iconBitmap(TestUtils::createBitmap(80, 80));
while (benchState.KeepRunning()) {
- canvas.resetRecording(100, 100);
+ canvas->resetRecording(100, 100);
{
- canvas.save(SaveFlags::MatrixClip);
- canvas.drawRect(0, 0, 100, 100, rectPaint);
- canvas.restore();
+ canvas->save(SaveFlags::MatrixClip);
+ canvas->drawRect(0, 0, 100, 100, rectPaint);
+ canvas->restore();
}
{
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(10, 10);
- canvas.drawBitmap(iconBitmap, 0, 0, nullptr);
- canvas.restore();
+ canvas->save(SaveFlags::MatrixClip);
+ canvas->translate(10, 10);
+ canvas->drawBitmap(*iconBitmap, 0, 0, nullptr);
+ canvas->restore();
}
- benchmark::DoNotOptimize(&canvas);
- delete canvas.finishRecording();
+ benchmark::DoNotOptimize(canvas.get());
+ delete canvas->finishRecording();
}
}
BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView);
@@ -169,3 +159,37 @@ void BM_CanvasState_translate(benchmark::State& benchState) {
}
}
BENCHMARK(BM_CanvasState_translate);
+
+void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) {
+ sp<RenderNode> child = TestUtils::createNode(50, 50, 100, 100,
+ [](auto& props, auto& canvas) {
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
+ });
+
+ std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
+ delete canvas->finishRecording();
+
+ while (benchState.KeepRunning()) {
+ canvas->resetRecording(200, 200);
+ canvas->setHighContrastText(false);
+ canvas->translate(0, 0); // mScrollX, mScrollY
+
+ // Clip to padding
+ // Can expect ~25% of views to have clip to padding with a non-null padding
+ int clipRestoreCount = canvas->save(SaveFlags::MatrixClip);
+ canvas->clipRect(1, 1, 199, 199, SkClipOp::kIntersect);
+
+ canvas->insertReorderBarrier(true);
+
+ // Draw child loop
+ for (int i = 0; i < benchState.range(0); i++) {
+ canvas->drawRenderNode(child.get());
+ }
+
+ canvas->insertReorderBarrier(false);
+ canvas->restoreToCount(clipRestoreCount);
+
+ delete canvas->finishRecording();
+ }
+}
+BENCHMARK(BM_DisplayListCanvas_basicViewGroupDraw)->Arg(1)->Arg(5)->Arg(10);
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 362890b52b61..398e7a89be1e 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -39,9 +39,9 @@ const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
static sp<RenderNode> createTestNode() {
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(10, 10));
SkPaint paint;
// Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
@@ -50,7 +50,7 @@ static sp<RenderNode> createTestNode() {
for (int i = 0; i < 30; i++) {
canvas.translate(0, 10);
canvas.drawRect(0, 0, 10, 10, paint);
- canvas.drawBitmap(bitmap, 5, 0, nullptr);
+ canvas.drawBitmap(*bitmap, 5, 0, nullptr);
}
canvas.restore();
});
@@ -98,8 +98,8 @@ static sp<RenderNode> getSyncedSceneNode(const char* sceneName) {
TestScene::Options opts;
std::unique_ptr<TestScene> scene(TestScene::testMap()[sceneName].createScene(opts));
- sp<RenderNode> rootNode = TestUtils::createNode(0, 0, gDisplay.w, gDisplay.h,
- [&scene](RenderProperties& props, TestCanvas& canvas) {
+ sp<RenderNode> rootNode = TestUtils::createNode<RecordingCanvas>(0, 0, gDisplay.w, gDisplay.h,
+ [&scene](RenderProperties& props, RecordingCanvas& canvas) {
scene->createContent(gDisplay.w, gDisplay.h, canvas);
});
diff --git a/libs/hwui/tests/microbench/RenderNodeBench.cpp b/libs/hwui/tests/microbench/RenderNodeBench.cpp
new file mode 100644
index 000000000000..a5bed0026b1c
--- /dev/null
+++ b/libs/hwui/tests/microbench/RenderNodeBench.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <benchmark/benchmark.h>
+
+#include "RenderNode.h"
+
+using namespace android;
+using namespace android::uirenderer;
+
+void BM_RenderNode_create(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ auto node = new RenderNode();
+ node->incStrong(0);
+ benchmark::DoNotOptimize(node);
+ node->decStrong(0);
+ }
+}
+BENCHMARK(BM_RenderNode_create);
+
diff --git a/libs/hwui/tests/microbench/TaskManagerBench.cpp b/libs/hwui/tests/microbench/TaskManagerBench.cpp
index c6b9f3bca55f..cf47f273c144 100644
--- a/libs/hwui/tests/microbench/TaskManagerBench.cpp
+++ b/libs/hwui/tests/microbench/TaskManagerBench.cpp
@@ -29,7 +29,7 @@ class TrivialTask : public Task<char> {};
class TrivialProcessor : public TaskProcessor<char> {
public:
- TrivialProcessor(TaskManager* manager)
+ explicit TrivialProcessor(TaskManager* manager)
: TaskProcessor(manager) {}
virtual ~TrivialProcessor() {}
virtual void onProcess(const sp<Task<char> >& task) override {
diff --git a/libs/hwui/tests/microbench/how_to_run.txt b/libs/hwui/tests/microbench/how_to_run.txt
index e6f80b278276..915fe5d959f9 100755
--- a/libs/hwui/tests/microbench/how_to_run.txt
+++ b/libs/hwui/tests/microbench/how_to_run.txt
@@ -1,4 +1,3 @@
mmm -j8 frameworks/base/libs/hwui &&
-adb push $ANDROID_PRODUCT_OUT/data/local/tmp/hwuimicro \
- /data/local/tmp/hwuimicro &&
- adb shell /data/local/tmp/hwuimicro
+adb push $OUT/data/benchmarktest/hwuimicro/hwuimicro /data/benchmarktest/hwuimicro/hwuimicro &&
+adb shell /data/benchmarktest/hwuimicro/hwuimicro
diff --git a/libs/hwui/tests/microbench/main.cpp b/libs/hwui/tests/microbench/main.cpp
index a0157bc4f9ef..b5abf5bc5efa 100644
--- a/libs/hwui/tests/microbench/main.cpp
+++ b/libs/hwui/tests/microbench/main.cpp
@@ -14,6 +14,22 @@
* limitations under the License.
*/
+#include "debug/GlesDriver.h"
+#include "debug/NullGlesDriver.h"
+
+#include "hwui/Typeface.h"
+
#include <benchmark/benchmark.h>
-BENCHMARK_MAIN();
+#include <memory>
+
+using namespace android;
+using namespace android::uirenderer;
+
+int main(int argc, char** argv) {
+ debug::GlesDriver::replace(std::make_unique<debug::NullGlesDriver>());
+ benchmark::Initialize(&argc, argv);
+ Typeface::setRobotoTypefaceForTest();
+ benchmark::RunSpecifiedBenchmarks();
+ return 0;
+}
diff --git a/libs/hwui/tests/scripts/prep_buller.sh b/libs/hwui/tests/scripts/prep_buller.sh
new file mode 100755
index 000000000000..65292c3a8b9c
--- /dev/null
+++ b/libs/hwui/tests/scripts/prep_buller.sh
@@ -0,0 +1,64 @@
+#buller is bullhead & angler (☞゚ヮ゚)☞
+
+nr=$(adb shell cat /proc/cpuinfo | grep processor | wc -l)
+cpubase=/sys/devices/system/cpu
+gov=cpufreq/scaling_governor
+
+adb root
+adb wait-for-device
+adb shell stop thermal-engine
+adb shell stop perfd
+
+# LITTLE cores
+# 384000 460800 600000 672000 787200 864000 960000 1248000 1440000
+# BIG cores
+# 384000 480000 633600 768000 864000 960000 1248000 1344000 1440000
+# 1536000 1632000 1689600 1824000
+
+cpu=0
+S=960000
+while [ $((cpu < 4)) -eq 1 ]; do
+ echo "Setting cpu $cpu to $S hz"
+ adb shell "echo 1 > $cpubase/cpu${cpu}/online"
+ adb shell "echo userspace > $cpubase/cpu${cpu}/$gov"
+ adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_max_freq"
+ adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_min_freq"
+ adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_setspeed"
+ cpu=$(($cpu + 1))
+done
+
+while [ $((cpu < $nr)) -eq 1 ]; do
+ echo "disable cpu $cpu"
+ adb shell "echo 0 > $cpubase/cpu${cpu}/online"
+ cpu=$(($cpu + 1))
+done
+
+echo "setting GPU bus and idle timer"
+adb shell "echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split"
+adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on"
+adb shell "echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer"
+
+# angler: 0 762 1144 1525 2288 3509 4173 5271 5928 7904 9887 11863
+adb shell "echo 11863 > /sys/class/devfreq/qcom,gpubw.70/min_freq" &> /dev/null
+# bullhead: 0 762 1144 1525 2288 3509 4173 5271 5928 7102
+adb shell "echo 7102 > /sys/class/devfreq/qcom,gpubw.19/min_freq" &> /dev/null
+
+
+board=$(adb shell "getprop ro.product.board")
+freq=0
+if [ "$board" = "bullhead" ]
+then
+ #600000000 490000000 450000000 367000000 300000000 180000000
+ freq=300000000
+else
+ #600000000 510000000 450000000 390000000 305000000 180000000
+ freq=305000000
+fi
+echo "performance mode, $freq Hz"
+adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor"
+adb shell "echo $freq > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq"
+adb shell "echo $freq > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq"
+
+adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel"
+adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/max_pwrlevel"
+
diff --git a/libs/hwui/tests/scripts/prep_fugu.sh b/libs/hwui/tests/scripts/prep_fugu.sh
new file mode 100755
index 000000000000..04a3203c49a7
--- /dev/null
+++ b/libs/hwui/tests/scripts/prep_fugu.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+cpubase=/sys/devices/system/cpu
+gov=cpufreq/scaling_governor
+
+adb root
+adb wait-for-device
+thermal=$(adb shell "getprop persist.service.thermal")
+echo "thermal status: $thermal"
+if [ $thermal -eq 1 ]
+then
+ echo "Trying to setprop persist.service.thermal 0 and reboot"
+ adb shell "setprop persist.service.thermal 0"
+ adb reboot
+ adb wait-for-device
+ thermal=$(adb shell "getprop persist.service.thermal")
+ if [ $thermal -eq 1 ]
+ then
+ echo "thermal property is still 1. Abort."
+ exit -1
+ fi
+ echo "Successfully setup persist.service.thermal to 0"
+fi
+
+adb shell stop perfprod
+
+# cores
+# 1833000 1750000 1666000 1583000 1500000 1416000 1333000 1250000
+# 1166000 1083000 1000000 916000 833000 750000 666000 583000 500000
+
+cpu=0
+S=1166000
+while [ $((cpu < 3)) -eq 1 ]; do
+ echo "Setting cpu ${cpu} & $(($cpu + 1)) cluster to $S hz"
+ # cpu0/online doesn't exist, because you can't turned it off, so ignore results of this command
+ adb shell "echo 1 > $cpubase/cpu${cpu}/online" &> /dev/null
+ adb shell "echo userspace > $cpubase/cpu${cpu}/$gov"
+ adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_max_freq"
+ adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_min_freq"
+ adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_setspeed"
+ cpu=$(($cpu + 2))
+done
+
+#/sys/class/devfreq/dfrgx/available_frequencies is empty, so set to min
+echo "performance mode, 457 MHz"
+adb shell "echo performance > /sys/class/devfreq/dfrgx/governor"
+adb shell "echo 457000 > /sys/class/devfreq/dfrgx/min_freq"
+adb shell "echo 457000 > /sys/class/devfreq/dfrgx/max_freq"
diff --git a/libs/hwui/tests/scripts/prep_marlfish.sh b/libs/hwui/tests/scripts/prep_marlfish.sh
new file mode 100755
index 000000000000..f3c14d52fad5
--- /dev/null
+++ b/libs/hwui/tests/scripts/prep_marlfish.sh
@@ -0,0 +1,45 @@
+#marlfish is marlin & sailfish (☞゚ヮ゚)☞
+
+cpubase=/sys/devices/system/cpu
+
+adb root
+adb wait-for-device
+adb shell stop thermal-engine
+adb shell stop perfd
+
+# silver cores
+#307200 384000 460800 537600 614400 691200 768000 844800 902400 979200
+#1056000 1132800 1209600 1286400 1363200 1440000 1516800 1593600
+# gold cores
+#307200 384000 460800 537600 614400 691200 748800 825600 902400 979200
+#1056000 1132800 1209600 1286400 1363200 1440000 1516800 1593600 1670400
+#1747200 1824000 1900800 1977600 2054400 2150400
+
+S=979200
+cpu=0
+# Changing governor and frequency in one core will be automatically applied
+# to other cores in the cluster
+while [ $((cpu < 3)) -eq 1 ]; do
+ adb shell "echo userspace > $cpubase/cpu2/cpufreq/scaling_governor"
+ echo "Setting cpu ${cpu} & $(($cpu + 1)) cluster to $S hz"
+ adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_max_freq"
+ adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_min_freq"
+ cpu=$(($cpu + 2))
+done
+
+echo "setting GPU bus and idle timer"
+adb shell "echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split"
+adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on"
+adb shell "echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer"
+
+#0 762 1144 1525 2288 3143 4173 5195 5859 7759 9887 11863 13763
+adb shell "echo 13763 > /sys/class/devfreq/soc:qcom,gpubw/min_freq" &> /dev/null
+
+#133000000 214000000 315000000 401800000 510000000 560000000 624000000
+echo "performance mode, 315 MHz"
+adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor"
+adb shell "echo 315000000 > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq"
+adb shell "echo 315000000 > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq"
+
+adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel"
+adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/max_pwrlevel"
diff --git a/libs/hwui/tests/scripts/prep_ryu.sh b/libs/hwui/tests/scripts/prep_ryu.sh
new file mode 100644
index 000000000000..7c6c0df8d58e
--- /dev/null
+++ b/libs/hwui/tests/scripts/prep_ryu.sh
@@ -0,0 +1,31 @@
+adb root
+adb wait-for-device
+adb shell stop thermal-engine
+adb shell stop perfd
+
+# 51000 102000 204000 306000 408000 510000 612000 714000 816000 918000
+# 1020000 1122000 1224000 1326000 1428000 1530000 1632000 1734000 1836000 1912500
+S=1326000
+echo "set cpu to $S hz";
+adb shell "echo userspace > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
+adb shell "echo $S > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
+adb shell "echo $S > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq"
+
+#01: core 76 MHz emc 408 MHz
+#02: core 153 MHz emc 665 MHz
+#03: core 230 MHz emc 800 MHz *
+#04: core 307 MHz emc 1065 MHz
+#05: core 384 MHz emc 1331 MHz
+#06: core 460 MHz emc 1600 MHz
+#07: core 537 MHz emc 1600 MHz
+#08: core 614 MHz emc 1600 MHz
+#09: core 691 MHz emc 1600 MHz
+#0a: core 768 MHz emc 1600 MHz
+#0b: core 844 MHz emc 1600 MHz
+#0c: core 921 MHz emc 1600 MHz
+#0d: core 998 MHz emc 1600 MHz
+#AC: core 230 MHz emc 800 MHz a A d D
+
+echo "set gpu to core 307 MHz emc 1065 MHz"
+# it will lock gpu until you touch a screen
+adb shell "echo 04 > /sys/devices/57000000.gpu/pstate"
diff --git a/libs/hwui/tests/scripts/prep_volantis.sh b/libs/hwui/tests/scripts/prep_volantis.sh
index 0572ee55c9b9..6407844afa90 100755
--- a/libs/hwui/tests/scripts/prep_volantis.sh
+++ b/libs/hwui/tests/scripts/prep_volantis.sh
@@ -16,16 +16,8 @@
adb root
adb wait-for-device
-adb shell stop mpdecision
adb shell stop perfd
-adb shell stop
-for pid in $( adb shell ps | awk '{ if ( $9 == "surfaceflinger" ) { print $2 } }' ); do
- adb shell kill $pid
-done
-adb shell setprop debug.egl.traceGpuCompletion 1
-adb shell daemonize surfaceflinger
-sleep 3
-adb shell setprop service.bootanim.exit 1
+adb shell stop thermal-engine
# cpu possible frequencies
# 204000 229500 255000 280500 306000 331500 357000 382500 408000 433500 459000
diff --git a/libs/hwui/tests/scripts/stopruntime.sh b/libs/hwui/tests/scripts/stopruntime.sh
new file mode 100755
index 000000000000..0a796183eaf9
--- /dev/null
+++ b/libs/hwui/tests/scripts/stopruntime.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+adb root
+adb wait-for-device
+adb shell stop
+
+for pid in $( adb shell ps | awk '{ if ( $9 == "surfaceflinger" ) { print $2 } }' ); do
+ adb shell kill $pid
+done
+adb shell setprop debug.egl.traceGpuCompletion 1
+adb shell setprop debug.sf.nobootanimation 1
+# Daemonize command is available only in eng builds.
+adb shell daemonize surfaceflinger
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
index 6b7b721eaec6..9a3b81cc0138 100644
--- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -27,6 +27,7 @@
#include <SkBlurDrawLooper.h>
#include <SkDashPathEffect.h>
+#include <SkPath.h>
using namespace android::uirenderer;
@@ -79,15 +80,13 @@ static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, R
<< "Glop(s) expected";
}
-RENDERTHREAD_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
SkPaint strokePaint;
strokePaint.setStyle(SkPaint::kStroke_Style);
strokePaint.setStrokeWidth(4);
float intervals[] = {1.0f, 1.0f};
- auto dashEffect = SkDashPathEffect::Create(intervals, 2, 0);
- strokePaint.setPathEffect(dashEffect);
- dashEffect->unref();
+ strokePaint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
auto textureGlopVerifier = [] (const Glop& glop) {
// validate glop produced by renderPathTexture (so texture, unit quad)
@@ -114,7 +113,7 @@ RENDERTHREAD_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
testUnmergedGlopDispatch(renderThread, &ovalOp, textureGlopVerifier);
}
-RENDERTHREAD_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
SkPaint layerPaint;
layerPaint.setAlpha(128);
OffscreenBuffer* buffer = nullptr; // no providing a buffer, should hit rect fallback case
@@ -132,7 +131,7 @@ static int getGlopTransformFlags(renderthread::RenderThread& renderThread, Recor
return result;
}
-RENDERTHREAD_TEST(BakedOpDispatcher, offsetFlags) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, offsetFlags) {
Rect bounds(10, 15, 20, 25);
SkPaint paint;
SkPaint aaPaint;
@@ -158,15 +157,15 @@ RENDERTHREAD_TEST(BakedOpDispatcher, offsetFlags) {
<< "Expect an offset for non-AA lines.";
}
-RENDERTHREAD_TEST(BakedOpDispatcher, renderTextWithShadow) {
- auto node = TestUtils::createNode(0, 0, 100, 100,
- [](RenderProperties& props, TestCanvas& canvas) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, renderTextWithShadow) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
android::Paint shadowPaint;
shadowPaint.setColor(SK_ColorRED);
SkScalar sigma = Blur::convertRadiusToSigma(5);
- shadowPaint.setLooper(SkBlurDrawLooper::Create(SK_ColorWHITE, sigma, 3, 3))->unref();
+ shadowPaint.setLooper(SkBlurDrawLooper::Make(SK_ColorWHITE, sigma, 3, 3));
TestUtils::drawUtf8ToCanvas(&canvas, "A", shadowPaint, 25, 25);
TestUtils::drawUtf8ToCanvas(&canvas, "B", shadowPaint, 50, 50);
@@ -196,13 +195,13 @@ RENDERTHREAD_TEST(BakedOpDispatcher, renderTextWithShadow) {
static void validateLayerDraw(renderthread::RenderThread& renderThread,
std::function<void(const Glop& glop)> validator) {
- auto node = TestUtils::createNode(0, 0, 100, 100,
- [](RenderProperties& props, TestCanvas& canvas) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
props.mutateLayerProperties().setType(LayerType::RenderLayer);
// provide different blend mode, so decoration draws contrast
- props.mutateLayerProperties().setXferMode(SkXfermode::Mode::kSrc_Mode);
- canvas.drawColor(Color::Black, SkXfermode::Mode::kSrcOver_Mode);
+ props.mutateLayerProperties().setXferMode(SkBlendMode::kSrc);
+ canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
});
OffscreenBuffer** layerHandle = node->getLayerHandle();
@@ -233,7 +232,7 @@ static FloatColor makeFloatColor(uint32_t color) {
return c;
}
-RENDERTHREAD_TEST(BakedOpDispatcher, layerUpdateProperties) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, layerUpdateProperties) {
for (bool debugOverdraw : { false, true }) {
for (bool debugLayersUpdates : { false, true }) {
ScopedProperty<bool> ovdProp(Properties::debugOverdraw, debugOverdraw);
@@ -273,3 +272,17 @@ RENDERTHREAD_TEST(BakedOpDispatcher, layerUpdateProperties) {
}
}
}
+
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTextureSnapping) {
+ Rect bounds(10, 15, 20, 25);
+ SkPaint paint;
+ SkPath path;
+ path.addRect(SkRect::MakeXYWH(1.5, 3.8, 100, 90));
+ PathOp op(bounds, Matrix4::identity(), nullptr, &paint, &path);
+ testUnmergedGlopDispatch(renderThread, &op, [] (const Glop& glop) {
+ auto texture = glop.fill.texture.texture;
+ ASSERT_NE(nullptr, texture);
+ EXPECT_EQ(1, reinterpret_cast<PathTexture*>(texture)->left);
+ EXPECT_EQ(3, reinterpret_cast<PathTexture*>(texture)->top);
+ });
+}
diff --git a/libs/hwui/tests/unit/BakedOpRendererTests.cpp b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
index 59bd75ef6f62..380062a36d45 100644
--- a/libs/hwui/tests/unit/BakedOpRendererTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
@@ -23,7 +23,7 @@ using namespace android::uirenderer;
const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
-RENDERTHREAD_TEST(BakedOpRenderer, startRepaintLayer_clear) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, startRepaintLayer_clear) {
BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(), true, sLightInfo);
OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200u, 200u);
diff --git a/libs/hwui/tests/unit/BitmapTests.cpp b/libs/hwui/tests/unit/BitmapTests.cpp
new file mode 100644
index 000000000000..8c7e08183a1e
--- /dev/null
+++ b/libs/hwui/tests/unit/BitmapTests.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "hwui/Bitmap.h"
+
+#include <SkBitmap.h>
+#include <SkColorTable.h>
+#include <SkImageInfo.h>
+
+#include <tests/common/TestUtils.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(Bitmap, colorTableRefCounting) {
+ const SkPMColor c[] = { SkPackARGB32(0x80, 0x80, 0, 0) };
+ SkColorTable* ctable = new SkColorTable(c, SK_ARRAY_COUNT(c));
+
+ SkBitmap* bm = new SkBitmap();
+ bm->allocPixels(SkImageInfo::Make(1, 1, kIndex_8_SkColorType, kPremul_SkAlphaType),
+ nullptr, ctable);
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(bm, ctable);
+ EXPECT_FALSE(ctable->unique());
+ delete bm;
+ bitmap.reset();
+ EXPECT_TRUE(ctable->unique());
+ ctable->unref();
+}
+
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
new file mode 100644
index 000000000000..42ba3dbd2362
--- /dev/null
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "AnimationContext.h"
+#include "IContextFactory.h"
+#include "renderthread/CanvasContext.h"
+#include "tests/common/TestUtils.h"
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+ return new AnimationContext(clock);
+ }
+};
+
+RENDERTHREAD_TEST(CanvasContext, create) {
+ auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
+ ContextFactory contextFactory;
+ std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
+ renderThread, false, rootNode.get(), &contextFactory));
+
+ ASSERT_FALSE(canvasContext->hasSurface());
+
+ canvasContext->destroy(nullptr);
+}
+
+RENDERTHREAD_TEST(CanvasContext, invokeFunctor) {
+ TestUtils::MockFunctor functor;
+ CanvasContext::invokeFunctor(renderThread, &functor);
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ // we currently don't support OpenGL WebViews on the Vulkan backend
+ ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcessNoContext);
+ } else {
+ ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcess);
+ }
+}
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
index 0afabd83f5cc..43974f650084 100644
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ b/libs/hwui/tests/unit/CanvasStateTests.cpp
@@ -23,7 +23,7 @@
#include <gtest/gtest.h>
#include <SkPath.h>
-#include <SkRegion.h>
+#include <SkClipOp.h>
namespace android {
namespace uirenderer {
@@ -68,13 +68,13 @@ TEST(CanvasState, simpleClipping) {
state.initializeSaveStack(200, 200,
0, 0, 200, 200, Vector3());
- state.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
+ state.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100));
- state.clipRect(10, 10, 200, 200, SkRegion::kIntersect_Op);
+ state.clipRect(10, 10, 200, 200, SkClipOp::kIntersect);
ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100));
- state.clipRect(50, 50, 150, 150, SkRegion::kReplace_Op);
+ state.clipRect(50, 50, 150, 150, SkClipOp::kReplace);
ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150));
}
@@ -88,7 +88,7 @@ TEST(CanvasState, complexClipping) {
// rotated clip causes complex clip
state.rotate(10);
EXPECT_TRUE(state.clipIsSimple());
- state.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+ state.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
EXPECT_FALSE(state.clipIsSimple());
}
state.restore();
@@ -97,7 +97,7 @@ TEST(CanvasState, complexClipping) {
{
// subtracted clip causes complex clip
EXPECT_TRUE(state.clipIsSimple());
- state.clipRect(50, 50, 150, 150, SkRegion::kDifference_Op);
+ state.clipRect(50, 50, 150, 150, SkClipOp::kDifference);
EXPECT_FALSE(state.clipIsSimple());
}
state.restore();
@@ -108,7 +108,7 @@ TEST(CanvasState, complexClipping) {
SkPath path;
path.addOval(SkRect::MakeWH(200, 200));
EXPECT_TRUE(state.clipIsSimple());
- state.clipPath(&path, SkRegion::kDifference_Op);
+ state.clipPath(&path, SkClipOp::kDifference);
EXPECT_FALSE(state.clipIsSimple());
}
state.restore();
@@ -121,7 +121,7 @@ TEST(CanvasState, saveAndRestore) {
state.save(SaveFlags::Clip);
{
- state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
+ state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
}
state.restore();
@@ -145,7 +145,7 @@ TEST(CanvasState, saveAndRestoreButNotTooMuch) {
state.save(SaveFlags::Matrix); // NOTE: clip not saved
{
- state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
+ state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
}
state.restore();
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
new file mode 100644
index 000000000000..1ef9dba07c6a
--- /dev/null
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "DeferredLayerUpdater.h"
+#include "GlLayer.h"
+#include "Properties.h"
+
+#include "tests/common/TestUtils.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) {
+ sp<DeferredLayerUpdater> layerUpdater = TestUtils::createTextureLayerUpdater(renderThread);
+ layerUpdater->setSize(100, 100);
+ layerUpdater->setBlend(true);
+
+ // updates are deferred so the backing layer should still be in its default state
+ if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
+ GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
+ EXPECT_EQ((uint32_t)GL_NONE, glLayer->getRenderTarget());
+ }
+ EXPECT_EQ(0u, layerUpdater->backingLayer()->getWidth());
+ EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight());
+ EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter());
+ EXPECT_FALSE(layerUpdater->backingLayer()->isBlend());
+ EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform());
+
+ // push the deferred updates to the layer
+ Matrix4 scaledMatrix;
+ scaledMatrix.loadScale(0.5, 0.5, 0.0);
+ layerUpdater->updateLayer(true, GL_TEXTURE_EXTERNAL_OES, scaledMatrix.data);
+
+ // the backing layer should now have all the properties applied.
+ if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
+ GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
+ EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, glLayer->getRenderTarget());
+ }
+ EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth());
+ EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight());
+ EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter());
+ EXPECT_TRUE(layerUpdater->backingLayer()->isBlend());
+ EXPECT_EQ(scaledMatrix, layerUpdater->backingLayer()->getTexTransform());
+}
diff --git a/libs/hwui/tests/unit/DeviceInfoTests.cpp b/libs/hwui/tests/unit/DeviceInfoTests.cpp
index 17236bdf0bf7..af37938915e5 100644
--- a/libs/hwui/tests/unit/DeviceInfoTests.cpp
+++ b/libs/hwui/tests/unit/DeviceInfoTests.cpp
@@ -17,11 +17,12 @@
#include <DeviceInfo.h>
#include <gtest/gtest.h>
+#include "tests/common/TestUtils.h"
using namespace android;
using namespace android::uirenderer;
-TEST(DeviceInfo, basic) {
+OPENGL_PIPELINE_TEST(DeviceInfo, basic) {
// can't assert state before init - another test may have initialized the singleton
DeviceInfo::initialize();
const DeviceInfo* di = DeviceInfo::get();
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
new file mode 100644
index 000000000000..4831722b93e6
--- /dev/null
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+#include <SkCanvas.h>
+
+namespace {
+
+class TestCanvasBase : public SkCanvas {
+public:
+ TestCanvasBase(int width, int height) : SkCanvas(width, height) {
+ }
+ void onDrawAnnotation(const SkRect&, const char key[], SkData* value) {
+ ADD_FAILURE() << "onDrawAnnotation not expected in this test";
+ }
+ void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) {
+ ADD_FAILURE() << "onDrawDRRect not expected in this test";
+ }
+ void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ ADD_FAILURE() << "onDrawText not expected in this test";
+ }
+ void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+ const SkPaint& paint) {
+ ADD_FAILURE() << "onDrawPosText not expected in this test";
+ }
+ void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ ADD_FAILURE() << "onDrawPosTextH not expected in this test";
+ }
+ void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+ const SkMatrix* matrix, const SkPaint& paint) {
+ ADD_FAILURE() << "onDrawTextOnPath not expected in this test";
+ }
+ void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
+ const SkRect* cullRect, const SkPaint& paint) {
+ ADD_FAILURE() << "onDrawTextRSXform not expected in this test";
+ }
+ void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) {
+ ADD_FAILURE() << "onDrawTextBlob not expected in this test";
+ }
+ void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texCoords[4],
+ SkBlendMode, const SkPaint& paint) {
+ ADD_FAILURE() << "onDrawPatch not expected in this test";
+ }
+ void onDrawPaint(const SkPaint&) {
+ ADD_FAILURE() << "onDrawPaint not expected in this test";
+ }
+ void onDrawRect(const SkRect&, const SkPaint&) {
+ ADD_FAILURE() << "onDrawRect not expected in this test";
+ }
+ void onDrawRegion(const SkRegion& region, const SkPaint& paint) {
+ ADD_FAILURE() << "onDrawRegion not expected in this test";
+ }
+ void onDrawOval(const SkRect&, const SkPaint&) {
+ ADD_FAILURE() << "onDrawOval not expected in this test";
+ }
+ void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
+ const SkPaint&) {
+ ADD_FAILURE() << "onDrawArc not expected in this test";
+ }
+ void onDrawRRect(const SkRRect&, const SkPaint&) {
+ ADD_FAILURE() << "onDrawRRect not expected in this test";
+ }
+ void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) {
+ ADD_FAILURE() << "onDrawPoints not expected in this test";
+ }
+ void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[], const SkPoint texs[],
+ const SkColor colors[], SkBlendMode, const uint16_t indices[], int indexCount,
+ const SkPaint&) {
+ ADD_FAILURE() << "onDrawVertices not expected in this test";
+ }
+ void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int count,
+ SkBlendMode, const SkRect* cull, const SkPaint*) {
+ ADD_FAILURE() << "onDrawAtlas not expected in this test";
+ }
+ void onDrawPath(const SkPath&, const SkPaint&) {
+ ADD_FAILURE() << "onDrawPath not expected in this test";
+ }
+ void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) {
+ ADD_FAILURE() << "onDrawImage not expected in this test";
+ }
+ void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
+ SrcRectConstraint) {
+ ADD_FAILURE() << "onDrawImageRect not expected in this test";
+ }
+ void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, const SkPaint*) {
+ ADD_FAILURE() << "onDrawImageNine not expected in this test";
+ }
+ void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
+ const SkPaint*) {
+ ADD_FAILURE() << "onDrawImageLattice not expected in this test";
+ }
+ void onDrawBitmap(const SkBitmap&, SkScalar dx, SkScalar dy, const SkPaint*) {
+ ADD_FAILURE() << "onDrawBitmap not expected in this test";
+ }
+ void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*,
+ SrcRectConstraint) {
+ ADD_FAILURE() << "onDrawBitmapRect not expected in this test";
+ }
+ void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
+ const SkPaint*) {
+ ADD_FAILURE() << "onDrawBitmapNine not expected in this test";
+ }
+ void onDrawBitmapLattice(const SkBitmap&, const Lattice& lattice, const SkRect& dst,
+ const SkPaint*) {
+ ADD_FAILURE() << "onDrawBitmapLattice not expected in this test";
+ }
+ void onClipRRect(const SkRRect& rrect, SkClipOp, ClipEdgeStyle) {
+ ADD_FAILURE() << "onClipRRect not expected in this test";
+ }
+ void onClipPath(const SkPath& path, SkClipOp, ClipEdgeStyle) {
+ ADD_FAILURE() << "onClipPath not expected in this test";
+ }
+ void onClipRegion(const SkRegion& deviceRgn, SkClipOp) {
+ ADD_FAILURE() << "onClipRegion not expected in this test";
+ }
+ void onDiscard() {
+ ADD_FAILURE() << "onDiscard not expected in this test";
+ }
+ void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) {
+ ADD_FAILURE() << "onDrawPicture not expected in this test";
+ }
+
+ int mDrawCounter = 0; //counts how may draw calls of any kind were made to this canvas
+};
+
+} \ No newline at end of file
diff --git a/libs/hwui/tests/unit/FontRendererTests.cpp b/libs/hwui/tests/unit/FontRendererTests.cpp
index 99080ac938e7..ee202367d73e 100644
--- a/libs/hwui/tests/unit/FontRendererTests.cpp
+++ b/libs/hwui/tests/unit/FontRendererTests.cpp
@@ -28,7 +28,7 @@ static bool isZero(uint8_t* data, int size) {
return true;
}
-RENDERTHREAD_TEST(FontRenderer, renderDropShadow) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, renderDropShadow) {
SkPaint paint;
paint.setTextSize(10);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index e2dc3a0a2c66..6f3ed9cf9e2f 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -19,6 +19,7 @@
#include <BakedOpState.h>
#include <DeferredLayerUpdater.h>
#include <FrameBuilder.h>
+#include <GlLayer.h>
#include <LayerUpdateQueue.h>
#include <RecordedOp.h>
#include <RecordingCanvas.h>
@@ -108,7 +109,7 @@ public:
class FailRenderer : public TestRendererBase {};
-RENDERTHREAD_TEST(FrameBuilder, simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simple) {
class SimpleTestRenderer : public TestRendererBase {
public:
void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -127,11 +128,11 @@ RENDERTHREAD_TEST(FrameBuilder, simple) {
}
};
- auto node = TestUtils::createNode(0, 0, 100, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
canvas.drawRect(0, 0, 100, 200, SkPaint());
- canvas.drawBitmap(bitmap, 10, 10, nullptr);
+ canvas.drawBitmap(*bitmap, 10, 10, nullptr);
});
FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200,
sLightGeometry, Caches::getInstance());
@@ -142,7 +143,7 @@ RENDERTHREAD_TEST(FrameBuilder, simple) {
EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
}
-RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleStroke) {
class SimpleStrokeTestRenderer : public TestRendererBase {
public:
void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
@@ -155,7 +156,7 @@ RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
}
};
- auto node = TestUtils::createNode(0, 0, 100, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
SkPaint strokedPaint;
strokedPaint.setStrokeWidth(10);
@@ -170,11 +171,11 @@ RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
EXPECT_EQ(1, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
- auto node = TestUtils::createNode(0, 0, 200, 200,
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleRejection) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
+ canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); // intersection should be empty
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
});
@@ -186,7 +187,7 @@ RENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleBatching) {
const int LOOPS = 5;
class SimpleBatchingTestRenderer : public TestRendererBase {
public:
@@ -198,10 +199,11 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
}
};
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- SkBitmap bitmap = TestUtils::createSkBitmap(10, 10,
- kAlpha_8_SkColorType); // Disable merging by using alpha 8 bitmap
+
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(10, 10,
+ kAlpha_8_SkColorType)); // Disable merging by using alpha 8 bitmap
// Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
// Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
@@ -209,7 +211,7 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
for (int i = 0; i < LOOPS; i++) {
canvas.translate(0, 10);
canvas.drawRect(0, 0, 10, 10, SkPaint());
- canvas.drawBitmap(bitmap, 5, 0, nullptr);
+ canvas.drawBitmap(*bitmap, 5, 0, nullptr);
}
canvas.restore();
});
@@ -223,7 +225,7 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
<< "Expect number of ops = 2 * loop count";
}
-RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNode_translateClip) {
class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -234,7 +236,7 @@ RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) {
}
};
- auto node = TestUtils::createNode(0, 0, 100, 100,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 100, 100, SkPaint());
});
@@ -249,7 +251,7 @@ RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) {
EXPECT_EQ(1, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNodeScene) {
class DeferRenderNodeSceneTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -287,20 +289,20 @@ RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) {
transparentPaint.setAlpha(128);
// backdrop
- nodes.push_back(TestUtils::createNode(100, 100, 700, 500, // 600x400
+ nodes.push_back(TestUtils::createNode<RecordingCanvas>(100, 100, 700, 500, // 600x400
[&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 600, 400, transparentPaint);
}));
// content
Rect contentDrawBounds(150, 150, 650, 450); // 500x300
- nodes.push_back(TestUtils::createNode(0, 0, 800, 600,
+ nodes.push_back(TestUtils::createNode<RecordingCanvas>(0, 0, 800, 600,
[&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 800, 600, transparentPaint);
}));
// overlay
- nodes.push_back(TestUtils::createNode(0, 0, 800, 600,
+ nodes.push_back(TestUtils::createNode<RecordingCanvas>(0, 0, 800, 600,
[&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 800, 200, transparentPaint);
}));
@@ -318,7 +320,7 @@ RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) {
EXPECT_EQ(4, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_noFbo0) {
class EmptyNoFbo0TestRenderer : public TestRendererBase {
public:
void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -336,7 +338,7 @@ RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_withFbo0) {
class EmptyWithFbo0TestRenderer : public TestRendererBase {
public:
void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -346,7 +348,7 @@ RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
EXPECT_EQ(1, mIndex++);
}
};
- auto node = TestUtils::createNode(10, 10, 110, 110,
+ auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
[](RenderProperties& props, RecordingCanvas& canvas) {
// no drawn content
});
@@ -362,7 +364,7 @@ RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
" but fbo0 update lifecycle should still be observed";
}
-RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_rects) {
class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -371,7 +373,7 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
<< "Last rect should occlude others.";
}
};
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.drawRect(0, 0, 200, 200, SkPaint());
@@ -392,20 +394,20 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
}
-RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
- static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50,
- SkColorType::kRGB_565_SkColorType);
- static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50,
- SkColorType::kAlpha_8_SkColorType);
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
+ static sk_sp<Bitmap> opaqueBitmap(TestUtils::createBitmap(50, 50,
+ SkColorType::kRGB_565_SkColorType));
+ static sk_sp<Bitmap> transpBitmap(TestUtils::createBitmap(50, 50,
+ SkColorType::kAlpha_8_SkColorType));
class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
public:
void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
switch(mIndex++) {
case 0:
- EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef());
+ EXPECT_EQ(opaqueBitmap.get(), op.bitmap);
break;
case 1:
- EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef());
+ EXPECT_EQ(transpBitmap.get(), op.bitmap);
break;
default:
ADD_FAILURE() << "Only two ops expected.";
@@ -413,15 +415,15 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
}
};
- auto node = TestUtils::createNode(0, 0, 50, 50,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 50, 50,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 50, 50, SkPaint());
canvas.drawRect(0, 0, 50, 50, SkPaint());
- canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
+ canvas.drawBitmap(*transpBitmap, 0, 0, nullptr);
// only the below draws should remain, since they're
- canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
- canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
+ canvas.drawBitmap(*opaqueBitmap, 0, 0, nullptr);
+ canvas.drawBitmap(*transpBitmap, 0, 0, nullptr);
});
FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50,
sLightGeometry, Caches::getInstance());
@@ -435,7 +437,7 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops";
}
-RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clippedMerging) {
class ClippedMergingTestRenderer : public TestRendererBase {
public:
void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -447,25 +449,25 @@ RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
opList.clipSideFlags);
}
};
- auto node = TestUtils::createNode(0, 0, 100, 100,
- [](RenderProperties& props, TestCanvas& canvas) {
- SkBitmap bitmap = TestUtils::createSkBitmap(20, 20);
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20));
// left side clipped (to inset left half)
- canvas.clipRect(10, 0, 50, 100, SkRegion::kReplace_Op);
- canvas.drawBitmap(bitmap, 0, 40, nullptr);
+ canvas.clipRect(10, 0, 50, 100, SkClipOp::kReplace);
+ canvas.drawBitmap(*bitmap, 0, 40, nullptr);
// top side clipped (to inset top half)
- canvas.clipRect(0, 10, 100, 50, SkRegion::kReplace_Op);
- canvas.drawBitmap(bitmap, 40, 0, nullptr);
+ canvas.clipRect(0, 10, 100, 50, SkClipOp::kReplace);
+ canvas.drawBitmap(*bitmap, 40, 0, nullptr);
// right side clipped (to inset right half)
- canvas.clipRect(50, 0, 90, 100, SkRegion::kReplace_Op);
- canvas.drawBitmap(bitmap, 80, 40, nullptr);
+ canvas.clipRect(50, 0, 90, 100, SkClipOp::kReplace);
+ canvas.drawBitmap(*bitmap, 80, 40, nullptr);
// bottom not clipped, just abutting (inset bottom half)
- canvas.clipRect(0, 50, 100, 90, SkRegion::kReplace_Op);
- canvas.drawBitmap(bitmap, 40, 70, nullptr);
+ canvas.clipRect(0, 50, 100, 90, SkClipOp::kReplace);
+ canvas.drawBitmap(*bitmap, 40, 70, nullptr);
});
FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
@@ -477,17 +479,17 @@ RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
EXPECT_EQ(4, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, regionClipStopsMerge) {
class RegionClipStopsMergeTestRenderer : public TestRendererBase {
public:
void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; }
};
- auto node = TestUtils::createNode(0, 0, 400, 400,
- [](RenderProperties& props, TestCanvas& canvas) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
SkPath path;
path.addCircle(200, 200, 200, SkPath::kCW_Direction);
canvas.save(SaveFlags::MatrixClip);
- canvas.clipPath(&path, SkRegion::kIntersect_Op);
+ canvas.clipPath(&path, SkClipOp::kIntersect);
SkPaint paint;
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setAntiAlias(true);
@@ -506,7 +508,7 @@ RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) {
EXPECT_EQ(2, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, textMerging) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textMerging) {
class TextMergingTestRenderer : public TestRendererBase {
public:
void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -518,8 +520,8 @@ RENDERTHREAD_TEST(FrameBuilder, textMerging) {
EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
}
};
- auto node = TestUtils::createNode(0, 0, 400, 400,
- [](RenderProperties& props, TestCanvas& canvas) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
SkPaint paint;
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setAntiAlias(true);
@@ -536,7 +538,7 @@ RENDERTHREAD_TEST(FrameBuilder, textMerging) {
EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
}
-RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStrikethrough) {
const int LOOPS = 5;
class TextStrikethroughTestRenderer : public TestRendererBase {
public:
@@ -549,7 +551,7 @@ RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
EXPECT_EQ(5u, opList.count);
}
};
- auto node = TestUtils::createNode(0, 0, 200, 2000,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 2000,
[](RenderProperties& props, RecordingCanvas& canvas) {
SkPaint textPaint;
textPaint.setAntiAlias(true);
@@ -574,7 +576,7 @@ RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) {
static auto styles = {
SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style };
-RENDERTHREAD_TEST(FrameBuilder, textStyle) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStyle) {
class TextStyleTestRenderer : public TestRendererBase {
public:
void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -605,8 +607,8 @@ RENDERTHREAD_TEST(FrameBuilder, textStyle) {
EXPECT_EQ(stroke, outsetFill);
}
};
- auto node = TestUtils::createNode(0, 0, 400, 400,
- [](RenderProperties& props, TestCanvas& canvas) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
SkPaint paint;
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setAntiAlias(true);
@@ -628,7 +630,7 @@ RENDERTHREAD_TEST(FrameBuilder, textStyle) {
EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
}
-RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
public:
void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
@@ -645,10 +647,10 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
SkMatrix::MakeTrans(5, 5));
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(50, 50, 150, 150, SkRegion::kIntersect_Op);
+ canvas.clipRect(50, 50, 150, 150, SkClipOp::kIntersect);
canvas.drawLayer(layerUpdater.get());
canvas.restore();
});
@@ -662,7 +664,7 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
EXPECT_EQ(1, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_combineMatrices) {
class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
public:
void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
@@ -677,7 +679,7 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
SkMatrix::MakeTrans(5, 5));
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.translate(30, 40);
@@ -694,12 +696,15 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
EXPECT_EQ(1, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_reject) {
auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
SkMatrix::MakeTrans(5, 5));
- layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected
+ EXPECT_EQ(Layer::Api::OpenGL, layerUpdater->backingLayer()->getApi());
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
+ glLayer->setRenderTarget(GL_NONE); // Should be rejected
+
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
canvas.drawLayer(layerUpdater.get());
});
@@ -712,7 +717,7 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, functor_reject) {
class FunctorTestRenderer : public TestRendererBase {
public:
void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
@@ -722,7 +727,7 @@ RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
Functor noopFunctor;
// 1 million pixel tall view, scrolled down 80%
- auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000,
+ auto scrolledFunctorView = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 1000000,
[&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
canvas.translate(0, -800000);
canvas.callDrawGLFunction(&noopFunctor, nullptr);
@@ -737,7 +742,7 @@ RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
}
-RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferColorOp_unbounded) {
class ColorTestRenderer : public TestRendererBase {
public:
void onColorOp(const ColorOp& op, const BakedOpState& state) override {
@@ -747,10 +752,10 @@ RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
}
};
- auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10,
+ auto unclippedColorView = TestUtils::createNode<RecordingCanvas>(0, 0, 10, 10,
[](RenderProperties& props, RecordingCanvas& canvas) {
props.setClipToBounds(false);
- canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
});
FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
@@ -762,7 +767,7 @@ RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
}
-TEST(FrameBuilder, renderNode) {
+OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) {
class RenderNodeTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -781,14 +786,14 @@ TEST(FrameBuilder, renderNode) {
}
};
- auto child = TestUtils::createNode(10, 10, 110, 110,
+ auto child = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
[](RenderProperties& props, RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
});
- auto parent = TestUtils::createNode(0, 0, 200, 200,
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[&child](RenderProperties& props, RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorDKGRAY);
@@ -809,7 +814,7 @@ TEST(FrameBuilder, renderNode) {
EXPECT_EQ(2, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, clipped) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clipped) {
class ClippedTestRenderer : public TestRendererBase {
public:
void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
@@ -820,10 +825,10 @@ RENDERTHREAD_TEST(FrameBuilder, clipped) {
}
};
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
- SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
- canvas.drawBitmap(bitmap, 0, 0, nullptr);
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(200, 200));
+ canvas.drawBitmap(*bitmap, 0, 0, nullptr);
});
// clip to small area, should see in receiver
@@ -835,7 +840,7 @@ RENDERTHREAD_TEST(FrameBuilder, clipped) {
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_simple) {
class SaveLayerSimpleTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -869,7 +874,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
}
};
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
canvas.drawRect(10, 10, 190, 190, SkPaint());
@@ -885,7 +890,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) {
EXPECT_EQ(5, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_nested) {
/* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
* - startTemporaryLayer2, rect2 endLayer2
* - startTemporaryLayer1, rect1, drawLayer2, endLayer1
@@ -945,7 +950,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
}
};
- auto node = TestUtils::createNode(0, 0, 800, 800,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 800, 800,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
{
@@ -968,11 +973,11 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
EXPECT_EQ(12, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
- auto node = TestUtils::createNode(0, 0, 200, 200,
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_contentRejection) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
+ canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect);
canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
// draw within save layer may still be recorded, but shouldn't be drawn
@@ -991,7 +996,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_simple) {
class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
public:
void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1003,7 +1008,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
EXPECT_EQ(1, mIndex++);
ASSERT_NE(nullptr, op.paint);
- ASSERT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
+ ASSERT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint));
}
void onRectOp(const RectOp& op, const BakedOpState& state) override {
EXPECT_EQ(2, mIndex++);
@@ -1020,7 +1025,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
}
};
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
canvas.drawRect(0, 0, 200, 200, SkPaint());
@@ -1036,7 +1041,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) {
EXPECT_EQ(4, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_round) {
class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase {
public:
void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1053,7 +1058,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) {
}
};
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out
128, (SaveFlags::Flags)(0));
@@ -1070,7 +1075,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_round) {
EXPECT_EQ(2, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
public:
void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1105,7 +1110,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
}
};
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
int restoreTo = canvas.save(SaveFlags::MatrixClip);
@@ -1128,7 +1133,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
<< "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
}
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
public:
void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -1137,7 +1142,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
EXPECT_EQ(1, mIndex++);
ASSERT_NE(nullptr, op.paint);
- EXPECT_EQ(SkXfermode::kClear_Mode, PaintUtils::getXfermodeDirect(op.paint));
+ EXPECT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint));
EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
<< "Expect dirty rect as clip";
ASSERT_NE(nullptr, state.computedState.clipState);
@@ -1152,7 +1157,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
}
};
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
// save smaller than clip, so we get unclipped behavior
canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
@@ -1170,8 +1175,8 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
EXPECT_EQ(4, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
- auto node = TestUtils::createNode(0, 0, 200, 200,
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_reject) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
// unclipped savelayer + rect both in area that won't intersect with dirty
canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
@@ -1192,7 +1197,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
* - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
* - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
*/
-RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_complex) {
class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
@@ -1237,7 +1242,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
}
};
- auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 600, 600, // 500x500 triggers clipping
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
@@ -1257,7 +1262,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) {
EXPECT_EQ(13, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_simple) {
class HwLayerSimpleTestRenderer : public TestRendererBase {
public:
void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1289,7 +1294,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
}
};
- auto node = TestUtils::createNode(10, 10, 110, 110,
+ auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
[](RenderProperties& props, RecordingCanvas& canvas) {
props.mutateLayerProperties().setType(LayerType::RenderLayer);
SkPaint paint;
@@ -1321,7 +1326,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
*layerHandle = nullptr;
}
-RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) {
/* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
* - startRepaintLayer(child), rect(grey), endLayer
* - startTemporaryLayer, drawLayer(child), endLayer
@@ -1384,7 +1389,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
}
};
- auto child = TestUtils::createNode(50, 50, 150, 150,
+ auto child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150,
[](RenderProperties& props, RecordingCanvas& canvas) {
props.mutateLayerProperties().setType(LayerType::RenderLayer);
SkPaint paint;
@@ -1395,7 +1400,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
*(child->getLayerHandle()) = &childLayer;
RenderNode* childPtr = child.get();
- auto parent = TestUtils::createNode(0, 0, 200, 200,
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[childPtr](RenderProperties& props, RecordingCanvas& canvas) {
props.mutateLayerProperties().setType(LayerType::RenderLayer);
SkPaint paint;
@@ -1430,7 +1435,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
}
-RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, buildLayer) {
class BuildLayerTestRenderer : public TestRendererBase {
public:
void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1459,10 +1464,10 @@ RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
}
};
- auto node = TestUtils::createNode(10, 10, 110, 110,
+ auto node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110,
[](RenderProperties& props, RecordingCanvas& canvas) {
props.mutateLayerProperties().setType(LayerType::RenderLayer);
- canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
});
OffscreenBuffer** layerHandle = node->getLayerHandle();
@@ -1486,14 +1491,16 @@ RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
*layerHandle = nullptr;
}
-static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
+namespace {
+
+static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
SkPaint paint;
// order put in blue channel, transparent so overlapped content doesn't get rejected
paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
canvas->drawRect(0, 0, 100, 100, paint);
}
-static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) {
- auto node = TestUtils::createNode(0, 0, 100, 100,
+static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
[expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
drawOrderedRect(&canvas, expectedDrawOrder);
});
@@ -1501,17 +1508,34 @@ static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder,
node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
}
-RENDERTHREAD_TEST(FrameBuilder, zReorder) {
- class ZReorderTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
- EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
+
+static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder,
+ std::function<void(RenderProperties& props, RecordingCanvas& canvas)> setup) {
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [expectedDrawOrder, setup](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedRect(&canvas, expectedDrawOrder);
+ if (setup) {
+ setup(props, canvas);
}
- };
+ });
+ canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
+}
+
+class ZReorderTestRenderer : public TestRendererBase {
+public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
+ EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
+ }
+};
+
+} // end anonymous namespace
- auto parent = TestUtils::createNode(0, 0, 100, 100,
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, zReorder) {
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
[](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.insertReorderBarrier(true);
+ canvas.insertReorderBarrier(false);
drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
drawOrderedRect(&canvas, 1);
canvas.insertReorderBarrier(true);
@@ -1524,6 +1548,14 @@ RENDERTHREAD_TEST(FrameBuilder, zReorder) {
canvas.insertReorderBarrier(false);
drawOrderedRect(&canvas, 8);
drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
+ canvas.insertReorderBarrier(true); //reorder a node ahead of drawrect op
+ drawOrderedRect(&canvas, 11);
+ drawOrderedNode(&canvas, 10, -1.0f);
+ canvas.insertReorderBarrier(false);
+ canvas.insertReorderBarrier(true); //test with two empty reorder sections
+ canvas.insertReorderBarrier(true);
+ canvas.insertReorderBarrier(false);
+ drawOrderedRect(&canvas, 12);
});
FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
sLightGeometry, Caches::getInstance());
@@ -1531,10 +1563,10 @@ RENDERTHREAD_TEST(FrameBuilder, zReorder) {
ZReorderTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(10, renderer.getIndex());
+ EXPECT_EQ(13, renderer.getIndex());
};
-RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorder) {
static const int scrollX = 5;
static const int scrollY = 10;
class ProjectionReorderTestRenderer : public TestRendererBase {
@@ -1579,7 +1611,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
* The parent is scrolled by scrollX/scrollY, but this does not affect the background
* (which isn't affected by scroll).
*/
- auto receiverBackground = TestUtils::createNode(0, 0, 100, 100,
+ auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
[](RenderProperties& properties, RecordingCanvas& canvas) {
properties.setProjectionReceiver(true);
// scroll doesn't apply to background, so undone via translationX/Y
@@ -1591,7 +1623,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
});
- auto projectingRipple = TestUtils::createNode(50, 0, 100, 50,
+ auto projectingRipple = TestUtils::createNode<RecordingCanvas>(50, 0, 100, 50,
[](RenderProperties& properties, RecordingCanvas& canvas) {
properties.setProjectBackwards(true);
properties.setClipToBounds(false);
@@ -1599,14 +1631,14 @@ RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
paint.setColor(SK_ColorDKGRAY);
canvas.drawRect(-10, -10, 60, 60, paint);
});
- auto child = TestUtils::createNode(0, 50, 100, 100,
+ auto child = TestUtils::createNode<RecordingCanvas>(0, 50, 100, 100,
[&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorBLUE);
canvas.drawRect(0, 0, 100, 50, paint);
canvas.drawRenderNode(projectingRipple.get());
});
- auto parent = TestUtils::createNode(0, 0, 100, 100,
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
[&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
// Set a rect outline for the projecting ripple to be masked against.
properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
@@ -1627,7 +1659,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
EXPECT_EQ(3, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionHwLayer) {
static const int scrollX = 5;
static const int scrollY = 10;
class ProjectionHwLayerTestRenderer : public TestRendererBase {
@@ -1660,7 +1692,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
}
};
- auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
+ auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
[](RenderProperties& properties, RecordingCanvas& canvas) {
properties.setProjectionReceiver(true);
// scroll doesn't apply to background, so undone via translationX/Y
@@ -1670,19 +1702,19 @@ RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
canvas.drawRect(0, 0, 400, 400, SkPaint());
});
- auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
+ auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& properties, RecordingCanvas& canvas) {
properties.setProjectBackwards(true);
properties.setClipToBounds(false);
canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
});
- auto child = TestUtils::createNode(100, 100, 300, 300,
+ auto child = TestUtils::createNode<RecordingCanvas>(100, 100, 300, 300,
[&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
properties.mutateLayerProperties().setType(LayerType::RenderLayer);
canvas.drawRenderNode(projectingRipple.get());
canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
});
- auto parent = TestUtils::createNode(0, 0, 400, 400,
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
[&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
// Set a rect outline for the projecting ripple to be masked against.
properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
@@ -1718,7 +1750,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) {
*layerHandle = nullptr;
}
-RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionChildScroll) {
static const int scrollX = 500000;
static const int scrollY = 0;
class ProjectionChildScrollTestRenderer : public TestRendererBase {
@@ -1735,12 +1767,12 @@ RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
EXPECT_TRUE(state.computedState.transform.isIdentity());
}
};
- auto receiverBackground = TestUtils::createNode(0, 0, 400, 400,
+ auto receiverBackground = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
[](RenderProperties& properties, RecordingCanvas& canvas) {
properties.setProjectionReceiver(true);
canvas.drawRect(0, 0, 400, 400, SkPaint());
});
- auto projectingRipple = TestUtils::createNode(0, 0, 200, 200,
+ auto projectingRipple = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& properties, RecordingCanvas& canvas) {
// scroll doesn't apply to background, so undone via translationX/Y
// NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
@@ -1750,15 +1782,15 @@ RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
properties.setClipToBounds(false);
canvas.drawOval(0, 0, 200, 200, SkPaint());
});
- auto child = TestUtils::createNode(0, 0, 400, 400,
+ auto child = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
[&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
// Record time clip will be ignored by projectee
- canvas.clipRect(100, 100, 300, 300, SkRegion::kIntersect_Op);
+ canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
canvas.drawRenderNode(projectingRipple.get());
});
- auto parent = TestUtils::createNode(0, 0, 400, 400,
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
[&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
canvas.drawRenderNode(receiverBackground.get());
canvas.drawRenderNode(child.get());
@@ -1775,7 +1807,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
// creates a 100x100 shadow casting node with provided translationZ
static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
- return TestUtils::createNode(0, 0, 100, 100,
+ return TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
[translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
properties.setTranslationZ(translationZ);
properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
@@ -1785,7 +1817,7 @@ static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
});
}
-RENDERTHREAD_TEST(FrameBuilder, shadow) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadow) {
class ShadowTestRenderer : public TestRendererBase {
public:
void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1803,7 +1835,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadow) {
}
};
- auto parent = TestUtils::createNode(0, 0, 200, 200,
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.insertReorderBarrier(true);
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
@@ -1818,7 +1850,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadow) {
EXPECT_EQ(2, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowSaveLayer) {
class ShadowSaveLayerTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -1844,7 +1876,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
}
};
- auto parent = TestUtils::createNode(0, 0, 200, 200,
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
// save/restore outside of reorderBarrier, so they don't get moved out of place
canvas.translate(20, 10);
@@ -1864,7 +1896,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) {
EXPECT_EQ(6, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowHwLayer) {
class ShadowHwLayerTestRenderer : public TestRendererBase {
public:
void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1887,7 +1919,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
}
};
- auto parent = TestUtils::createNode(50, 60, 150, 160,
+ auto parent = TestUtils::createNode<RecordingCanvas>(50, 60, 150, 160,
[](RenderProperties& props, RecordingCanvas& canvas) {
props.mutateLayerProperties().setType(LayerType::RenderLayer);
canvas.insertReorderBarrier(true);
@@ -1922,7 +1954,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
*layerHandle = nullptr;
}
-RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowLayering) {
class ShadowLayeringTestRenderer : public TestRendererBase {
public:
void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1934,7 +1966,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
EXPECT_TRUE(index == 2 || index == 3);
}
};
- auto parent = TestUtils::createNode(0, 0, 200, 200,
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.insertReorderBarrier(true);
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
@@ -1949,7 +1981,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowLayering) {
EXPECT_EQ(4, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameBuilder, shadowClipping) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowClipping) {
class ShadowClippingTestRenderer : public TestRendererBase {
public:
void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1961,11 +1993,11 @@ RENDERTHREAD_TEST(FrameBuilder, shadowClipping) {
EXPECT_EQ(1, mIndex++);
}
};
- auto parent = TestUtils::createNode(0, 0, 100, 100,
+ auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
[](RenderProperties& props, RecordingCanvas& canvas) {
// Apply a clip before the reorder barrier/shadow casting child is drawn.
// This clip must be applied to the shadow cast by the child.
- canvas.clipRect(25, 25, 75, 75, SkRegion::kIntersect_Op);
+ canvas.clipRect(25, 25, 75, 75, SkClipOp::kIntersect);
canvas.insertReorderBarrier(true);
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
});
@@ -1983,7 +2015,7 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac
std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
class PropertyTestRenderer : public TestRendererBase {
public:
- PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
+ explicit PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
: mCallback(callback) {}
void onRectOp(const RectOp& op, const BakedOpState& state) override {
EXPECT_EQ(mIndex++, 0);
@@ -1992,7 +2024,7 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac
std::function<void(const RectOp&, const BakedOpState&)> mCallback;
};
- auto node = TestUtils::createNode(0, 0, 100, 100,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
[propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
propSetupCallback(props);
SkPaint paint;
@@ -2009,7 +2041,7 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac
EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
}
-RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
testProperty([](RenderProperties& properties) {
properties.setAlpha(0.5f);
properties.setHasOverlappingRendering(false);
@@ -2018,7 +2050,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
});
}
-RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropClipping) {
testProperty([](RenderProperties& properties) {
properties.setClipToBounds(true);
properties.setClipBounds(Rect(10, 20, 300, 400));
@@ -2028,7 +2060,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropClipping) {
});
}
-RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropRevealClip) {
testProperty([](RenderProperties& properties) {
properties.mutableRevealClip().set(true, 50, 50, 25);
}, [](const RectOp& op, const BakedOpState& state) {
@@ -2039,7 +2071,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropRevealClip) {
});
}
-RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOutlineClip) {
testProperty([](RenderProperties& properties) {
properties.mutableOutline().setShouldClip(true);
properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
@@ -2051,7 +2083,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropOutlineClip) {
});
}
-RENDERTHREAD_TEST(FrameBuilder, renderPropTransform) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropTransform) {
testProperty([](RenderProperties& properties) {
properties.setLeftTopRightBottom(10, 10, 110, 110);
@@ -2105,7 +2137,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
std::function<void(RenderProperties&)> propSetupCallback) {
class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
public:
- SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
+ explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData)
: mOutData(outData) {}
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -2136,7 +2168,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
<< "Node must be bigger than max texture size to exercise saveLayer codepath";
- auto node = TestUtils::createNode(0, 0, 10000, 10000,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 10000, 10000,
[&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
properties.setHasOverlappingRendering(true);
properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
@@ -2160,7 +2192,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
}
-RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
properties.setTranslationX(10); // offset rendering content
@@ -2179,7 +2211,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
<< "expect drawLayer to be translated as part of being clipped";
}
-RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
// Translate and rotate the view so that the only visible part is the top left corner of
@@ -2198,7 +2230,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
}
-RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
properties.setPivotX(0);
@@ -2212,7 +2244,7 @@ RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
}
-RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clip_replace) {
class ClipReplaceTestRenderer : public TestRendererBase {
public:
void onColorOp(const ColorOp& op, const BakedOpState& state) override {
@@ -2222,10 +2254,10 @@ RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
<< "Expect resolved clip to be intersection of viewport clip and clip op";
}
};
- auto node = TestUtils::createNode(20, 20, 30, 30,
+ auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30,
[](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.clipRect(0, -20, 10, 30, SkRegion::kReplace_Op);
- canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace);
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
});
FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
@@ -2237,5 +2269,349 @@ RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
EXPECT_EQ(1, renderer.getIndex());
}
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) {
+ /* R is backward projected on B
+ A
+ / \
+ B C
+ |
+ R
+ */
+ auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
+
+ ZReorderTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(3, renderer.getIndex());
+}
+
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) {
+ /* R is backward projected on E
+ A
+ / | \
+ / | \
+ B C E
+ |
+ R
+ */
+ auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, nullptr); //nodeB
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //drawn as 2
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //drawn as 3
+ props.setProjectionReceiver(true);
+ } ); //nodeE
+ }); //nodeA
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
+
+ ZReorderTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(4, renderer.getIndex());
+}
+
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) {
+ /* R is backward projected without receiver
+ A
+ / \
+ B C
+ |
+ R
+ */
+ auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, nullptr); //nodeB
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) {
+ //not having a projection receiver is an undefined behavior
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
+
+ ZReorderTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex());
+}
+
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) {
+ /* R is backward projected on C
+ A
+ / \
+ B C
+ |
+ R
+ */
+ auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, nullptr); //nodeB
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.setProjectionReceiver(true);
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
+
+ ZReorderTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(3, renderer.getIndex());
+}
+
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) {
+ auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, nullptr); //nodeB
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) {
+ //having a node that is projected on itself is an undefined/unexpected behavior
+ props.setProjectionReceiver(true);
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
+
+ ZReorderTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex());
+}
+
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) {
+ //TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a
+ //bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical
+ //tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling
+ /* R is backward projected on B. R is not expected to be drawn (see Sibling2 outcome below),
+ but for some reason it is drawn.
+ A
+ /|\
+ / | \
+ B C R
+ */
+ auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) {
+ } ); //nodeC
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ }); //nodeA
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
+
+ ZReorderTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(3, renderer.getIndex());
+}
+
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) {
+ /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
+ A
+ |
+ G
+ /|\
+ / | \
+ B C R
+ */
+ auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //G
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //B
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C
+ } ); //nodeC
+ drawOrderedNode(&canvas, 255, [](RenderProperties& props, RecordingCanvas& canvas) { //R
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeG
+ }); //nodeA
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
+
+ ZReorderTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(3, renderer.getIndex());
+}
+
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) {
+ /* R is backward projected on B
+ A
+ |
+ B
+ |
+ C
+ |
+ R
+ */
+ auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.setProjectionReceiver(true);
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ } ); //nodeB
+ }); //nodeA
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
+
+ ZReorderTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(3, renderer.getIndex());
+}
+
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) {
+ /* B and G are receivables, R is backward projected
+ A
+ / \
+ B C
+ / \
+ G R
+ */
+ auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C
+ drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //G
+ props.setProjectionReceiver(true);
+ } ); //nodeG
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //R
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
+
+ ZReorderTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(4, renderer.getIndex());
+}
+
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) {
+ /* B and G are receivables, G is backward projected
+ A
+ / \
+ B C
+ / \
+ G R
+ */
+ auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //C
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //G
+ props.setProjectionReceiver(true);
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeG
+ drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //R
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
+
+ ZReorderTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(4, renderer.getIndex());
+}
+
+OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) {
+ /* B and G are receivables, R is backward projected
+ A
+ / \
+ B C
+ / \
+ G D
+ |
+ R
+ */
+ auto nodeA = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) { //B
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) { //C
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) { //G
+ props.setProjectionReceiver(true);
+ } ); //nodeG
+ drawOrderedNode(&canvas, 4, [](RenderProperties& props, RecordingCanvas& canvas) { //D
+ drawOrderedNode(&canvas, 3, [](RenderProperties& props, RecordingCanvas& canvas) { //R
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeD
+ } ); //nodeC
+ }); //nodeA
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
+
+ ZReorderTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(5, renderer.getIndex());
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp
index 95543d33b1ef..caeb6bf0081b 100644
--- a/libs/hwui/tests/unit/GlopBuilderTests.cpp
+++ b/libs/hwui/tests/unit/GlopBuilderTests.cpp
@@ -45,7 +45,11 @@ static void expectFillEq(Glop::Fill& expectedFill, Glop::Fill& builtFill) {
EXPECT_EQ(expectedFill.skiaShaderData.skiaShaderType, builtFill.skiaShaderData.skiaShaderType);
EXPECT_EQ(expectedFill.texture.clamp, builtFill.texture.clamp);
EXPECT_EQ(expectedFill.texture.filter, builtFill.texture.filter);
- EXPECT_EQ(expectedFill.texture.target, builtFill.texture.target);
+ EXPECT_TRUE((expectedFill.texture.texture && builtFill.texture.texture)
+ || (!expectedFill.texture.texture && !builtFill.texture.texture));
+ if (expectedFill.texture.texture) {
+ EXPECT_EQ(expectedFill.texture.texture->target(), builtFill.texture.texture->target());
+ }
EXPECT_EQ(expectedFill.texture.textureTransform, builtFill.texture.textureTransform);
}
@@ -85,9 +89,6 @@ static void expectTransformEq(Glop::Transform& expectedTransform, Glop::Transfor
}
static void expectGlopEq(Glop& expectedGlop, Glop& builtGlop) {
-#if !HWUI_NEW_OPS
- EXPECT_EQ(expectedGlop.bounds, builtGlop.bounds);
-#endif
expectBlendEq(expectedGlop.blend, builtGlop.blend);
expectFillEq(expectedGlop.fill, builtGlop.fill);
expectMeshEq(expectedGlop.mesh, builtGlop.mesh);
@@ -111,11 +112,11 @@ static std::unique_ptr<Glop> blackUnitQuadGlop(RenderState& renderState) {
glop->fill.color.set(Color::Black);
glop->fill.skiaShaderData.skiaShaderType = kNone_SkiaShaderType;
glop->fill.filterMode = ProgramDescription::ColorFilterMode::None;
- glop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+ glop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
return glop;
}
-RENDERTHREAD_TEST(GlopBuilder, rectSnapTest) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(GlopBuilder, rectSnapTest) {
RenderState& renderState = renderThread.renderState();
Caches& caches = Caches::getInstance();
SkPaint paint;
@@ -138,9 +139,6 @@ RENDERTHREAD_TEST(GlopBuilder, rectSnapTest) {
// unit quad also should be translate by additional (0.3, 0.3) to snap to exact pixels.
goldenGlop->transform.modelView.loadTranslate(1.3, 1.3, 0);
goldenGlop->transform.modelView.scale(99, 99, 1);
-#if !HWUI_NEW_OPS
- goldenGlop->bounds = android::uirenderer::Rect(1.70, 1.70, 100.70, 100.70);
-#endif
goldenGlop->transform.canvas = simpleTranslate;
goldenGlop->fill.texture.filter = GL_NEAREST;
expectGlopEq(*goldenGlop, glop);
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
index aa1dcb2ea51b..8cbd24edbde2 100644
--- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
+++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
@@ -51,7 +51,7 @@ static void destroyEglContext() {
TEST(GpuMemoryTracker, sizeCheck) {
destroyEglContext();
- GpuMemoryTracker::onGLContextCreated();
+ GpuMemoryTracker::onGpuContextCreated();
ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
{
@@ -66,5 +66,5 @@ TEST(GpuMemoryTracker, sizeCheck) {
}
ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
- GpuMemoryTracker::onGLContextDestroyed();
+ GpuMemoryTracker::onGpuContextDestroyed();
}
diff --git a/libs/hwui/tests/unit/GradientCacheTests.cpp b/libs/hwui/tests/unit/GradientCacheTests.cpp
index 0ee96470fc57..a3b346f11a87 100644
--- a/libs/hwui/tests/unit/GradientCacheTests.cpp
+++ b/libs/hwui/tests/unit/GradientCacheTests.cpp
@@ -23,7 +23,7 @@
using namespace android;
using namespace android::uirenderer;
-RENDERTHREAD_TEST(GradientCache, addRemove) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(GradientCache, addRemove) {
Extensions extensions;
GradientCache cache(extensions);
ASSERT_LT(1000u, cache.getMaxSize()) << "Expect non-trivial size";
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index 6148b33eceb8..6c42ca1f2c2e 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -29,9 +29,9 @@ using namespace android::uirenderer;
const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50};
const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
-RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayer_overdrawRejection) {
auto node = TestUtils::createNode(0, 0, 100, 100,
- [](RenderProperties& props, RecordingCanvas& canvas) {
+ [](RenderProperties& props, Canvas& canvas) {
canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 100, 100, SkPaint());
canvas.restore();
@@ -49,9 +49,9 @@ RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) {
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
}
-RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayerUnclipped_simple) {
auto node = TestUtils::createNode(0, 0, 200, 200,
- [](RenderProperties& props, RecordingCanvas& canvas) {
+ [](RenderProperties& props, Canvas& canvas) {
canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.restore();
diff --git a/libs/hwui/tests/unit/MeshStateTests.cpp b/libs/hwui/tests/unit/MeshStateTests.cpp
new file mode 100644
index 000000000000..511d6d25fbaf
--- /dev/null
+++ b/libs/hwui/tests/unit/MeshStateTests.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <debug/MockGlesDriver.h>
+#include <debug/ScopedReplaceDriver.h>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <renderstate/MeshState.h>
+#include <tests/common/TestUtils.h>
+
+using namespace android::uirenderer;
+using namespace testing;
+
+RENDERTHREAD_OPENGL_PIPELINE_TEST(MeshState, genOrUpdate) {
+ debug::ScopedReplaceDriver<debug::MockGlesDriver> driverRef;
+ auto& mockGlDriver = driverRef.get();
+ EXPECT_CALL(mockGlDriver, glGenBuffers_(_, _)).WillOnce(SetArgPointee<1>(35));
+ EXPECT_CALL(mockGlDriver, glBindBuffer_(_, 35));
+ EXPECT_CALL(mockGlDriver, glBufferData_(_, _, _, _));
+
+ GLuint buffer = 0;
+ renderThread.renderState().meshState().genOrUpdateMeshBuffer(&buffer, 10, nullptr, GL_DYNAMIC_DRAW);
+}
diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
index b7950aab5662..6cd595af6d2f 100644
--- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
@@ -30,7 +30,7 @@ TEST(OffscreenBuffer, computeIdealDimension) {
EXPECT_EQ(1024u, OffscreenBuffer::computeIdealDimension(1000));
}
-RENDERTHREAD_TEST(OffscreenBuffer, construct) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, construct) {
OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 49u, 149u);
EXPECT_EQ(49u, layer.viewportWidth);
EXPECT_EQ(149u, layer.viewportHeight);
@@ -41,7 +41,7 @@ RENDERTHREAD_TEST(OffscreenBuffer, construct) {
EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
}
-RENDERTHREAD_TEST(OffscreenBuffer, getTextureCoordinates) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, getTextureCoordinates) {
OffscreenBuffer layerAligned(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
EXPECT_EQ(Rect(0, 1, 1, 0),
layerAligned.getTextureCoordinates());
@@ -51,7 +51,7 @@ RENDERTHREAD_TEST(OffscreenBuffer, getTextureCoordinates) {
layerUnaligned.getTextureCoordinates());
}
-RENDERTHREAD_TEST(OffscreenBuffer, dirty) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, dirty) {
OffscreenBuffer buffer(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
buffer.dirty(Rect(-100, -100, 100, 100));
EXPECT_EQ(android::Rect(100, 100), buffer.region.getBounds());
@@ -65,7 +65,7 @@ RENDERTHREAD_TEST(OffscreenBufferPool, construct) {
<< "pool must read size from Properties";
}
-RENDERTHREAD_TEST(OffscreenBufferPool, getPutClear) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, getPutClear) {
OffscreenBufferPool pool;
auto layer = pool.get(renderThread.renderState(), 100u, 200u);
@@ -88,7 +88,7 @@ RENDERTHREAD_TEST(OffscreenBufferPool, getPutClear) {
EXPECT_EQ(0u, pool.getCount());
}
-RENDERTHREAD_TEST(OffscreenBufferPool, resize) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, resize) {
OffscreenBufferPool pool;
auto layer = pool.get(renderThread.renderState(), 64u, 64u);
@@ -123,7 +123,7 @@ RENDERTHREAD_TEST(OffscreenBufferPool, resize) {
pool.putOrDelete(layer2);
}
-RENDERTHREAD_TEST(OffscreenBufferPool, putAndDestroy) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, putAndDestroy) {
OffscreenBufferPool pool;
// layer too big to return to the pool
// Note: this relies on the fact that the pool won't reject based on max texture size
@@ -133,7 +133,7 @@ RENDERTHREAD_TEST(OffscreenBufferPool, putAndDestroy) {
EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead)
}
-RENDERTHREAD_TEST(OffscreenBufferPool, clear) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, clear) {
EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
OffscreenBufferPool pool;
diff --git a/libs/hwui/tests/unit/PathInterpolatorTests.cpp b/libs/hwui/tests/unit/PathInterpolatorTests.cpp
new file mode 100644
index 000000000000..d7cb23a4b793
--- /dev/null
+++ b/libs/hwui/tests/unit/PathInterpolatorTests.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <Interpolator.h>
+
+namespace android {
+namespace uirenderer {
+
+struct TestData {
+ const std::vector<float> x;
+ const std::vector<float> y;
+ const std::vector<float> inFraction;
+ const std::vector<float> outFraction;
+};
+
+const static TestData sTestDataSet[] = {
+ {
+ // Straight line as a path.
+ {0.0f, 1.0f},
+ {0.0f, 1.0f},
+ {0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f},
+ {0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f},
+ },
+
+ {
+ {
+ 0.0f, 0.5f, 0.5178955f, 0.5341797f, 0.5489991f, 0.5625f, 0.5748291f,
+ 0.5861328f, 0.60625005f, 0.62402344f, 0.640625f, 0.675f, 0.6951172f,
+ 0.71875f, 0.7470703f, 0.78125f, 0.82246095f, 0.84606934f, 0.871875f,
+ 0.9000244f, 0.93066406f, 0.96394044f, 1.0f
+ },
+ {
+ 0.0f, 0.0f, 0.0028686523f, 0.011230469f, 0.024719238f, 0.04296875f,
+ 0.06561279f, 0.092285156f, 0.15625f, 0.2319336f, 0.31640625f, 0.5f,
+ 0.5932617f, 0.68359375f, 0.7680664f, 0.84375f, 0.90771484f, 0.9343872f,
+ 0.95703125f, 0.97528076f, 0.98876953f, 0.99713135f, 1.0f
+ },
+ {
+ 0.0f, 0.03375840187072754f, 0.13503384590148926f, 0.23630905151367188f,
+ 0.336834192276001f, 0.4508626461029053f, 0.564141035079956f,
+ 0.6781694889068604f, 0.7921979427337646f, 0.9054763317108154f, 1.0f
+ },
+ {
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0459827296435833f,
+ 0.5146934390068054f, 0.8607426285743713f, 0.9776809215545654f, 1.0f
+
+ }
+
+ },
+ {
+ {
+ 0.0f, 0.017895509f, 0.034179688f, 0.048999026f, 0.0625f, 0.0748291f,
+ 0.08613282f, 0.10625f, 0.12402344f, 0.140625f, 0.17500001f, 0.19511719f,
+ 0.21875f, 0.24707031f, 0.28125f, 0.32246095f, 0.34606934f, 0.371875f,
+ 0.4000244f, 0.43066406f, 0.46394044f, 0.5f, 1.0f
+ },
+ {
+ 0.0f, 0.0028686523f, 0.011230469f, 0.024719238f, 0.04296875f, 0.06561279f,
+ 0.092285156f, 0.15625f, 0.2319336f, 0.31640625f, 0.5f, 0.5932617f,
+ 0.68359375f, 0.7680664f, 0.84375f, 0.90771484f, 0.9343872f, 0.95703125f,
+ 0.97528076f, 0.98876953f, 0.99713135f, 1.0f, 1.0f
+ },
+ {
+ 0.0f, 0.102020263671875f, 0.20330810546875f, 0.3165740966796875f,
+ 0.43060302734375f, 0.5318756103515625f, 0.6331634521484375f,
+ 0.746429443359375f, 0.84771728515625f, 0.9617462158203125f, 1.0f
+ },
+ {
+ 0.0f, 0.14280107617378235f, 0.6245699524879456f, 0.8985776901245117f,
+ 0.9887426495552063f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
+ }
+ },
+
+
+};
+
+static std::vector<float> getX(const TestData& data) {
+ return data.x;
+}
+
+static std::vector<float> getY(const TestData& data) {
+ return data.y;
+}
+
+TEST(Interpolator, pathInterpolation) {
+ for (const TestData& data: sTestDataSet) {
+ PathInterpolator interpolator(getX(data), getY(data));
+ for (size_t i = 0; i < data.inFraction.size(); i++) {
+ EXPECT_FLOAT_EQ(data.outFraction[i], interpolator.interpolate(data.inFraction[i]));
+ }
+ }
+}
+
+}
+}
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index c072d0b80135..124f5face2cb 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -25,6 +25,7 @@
#include <utils/Color.h>
#include <SkGradientShader.h>
+#include <SkImagePriv.h>
#include <SkShader.h>
namespace android {
@@ -46,7 +47,13 @@ static void validateSingleOp(std::unique_ptr<DisplayList>& dl,
opValidator(*(dl->getOps()[0]));
}
-TEST(RecordingCanvas, emptyPlayback) {
+// The RecordingCanvas is only ever used by the OpenGL RenderPipeline and never when Skia is in use.
+// Thus, even though many of these test are not directly dependent on the current RenderPipeline, we
+// set them all to be OPENGL_PIPELINE_TESTs in case the underlying code in RecordingCanvas ever
+// changes to require the use of the OPENGL_PIPELINE. Currently the textureLayer test is the only
+// test that requires being an OPENGL_PIPELINE_TEST.
+
+OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPlayback) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.restore();
@@ -54,10 +61,10 @@ TEST(RecordingCanvas, emptyPlayback) {
playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
}
-TEST(RecordingCanvas, clipRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, clipRect) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
+ canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
canvas.drawRect(0, 0, 50, 50, SkPaint());
canvas.drawRect(50, 50, 100, 100, SkPaint());
canvas.restore();
@@ -70,18 +77,18 @@ TEST(RecordingCanvas, clipRect) {
<< "Clip should be serialized once";
}
-TEST(RecordingCanvas, emptyClipRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, emptyClipRect) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
- canvas.clipRect(100, 100, 200, 200, SkRegion::kIntersect_Op);
+ canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
+ canvas.clipRect(100, 100, 200, 200, SkClipOp::kIntersect);
canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time
canvas.restore();
});
ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected.";
}
-TEST(RecordingCanvas, emptyPaintRejection) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPaintRejection) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkPaint emptyPaint;
emptyPaint.setColor(Color::Transparent);
@@ -102,7 +109,7 @@ TEST(RecordingCanvas, emptyPaintRejection) {
EXPECT_EQ(0u, dl->getOps().size()) << "Op should be rejected";
}
-TEST(RecordingCanvas, drawArc) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawArc) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint());
canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint());
@@ -118,7 +125,7 @@ TEST(RecordingCanvas, drawArc) {
EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds);
}
-TEST(RecordingCanvas, drawLines) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawLines) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time
@@ -135,7 +142,7 @@ TEST(RecordingCanvas, drawLines) {
<< "unmapped bounds must be size of line, and not outset for stroke width";
}
-TEST(RecordingCanvas, drawRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRect) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
canvas.drawRect(10, 20, 90, 180, SkPaint());
});
@@ -147,7 +154,7 @@ TEST(RecordingCanvas, drawRect) {
EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
}
-TEST(RecordingCanvas, drawRoundRect) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRoundRect) {
// Round case - stays rounded
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
canvas.drawRoundRect(0, 0, 100, 100, 10, 10, SkPaint());
@@ -164,7 +171,7 @@ TEST(RecordingCanvas, drawRoundRect) {
<< "Non-rounded rects should be converted";
}
-TEST(RecordingCanvas, drawGlyphs) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setAntiAlias(true);
@@ -185,7 +192,7 @@ TEST(RecordingCanvas, drawGlyphs) {
ASSERT_EQ(1, count);
}
-TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setAntiAlias(true);
@@ -217,7 +224,7 @@ TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
}
-TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setAntiAlias(true);
@@ -247,9 +254,9 @@ TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
ASSERT_EQ(3, count);
}
-TEST(RecordingCanvas, drawColor) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawColor) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.drawColor(Color::Black, SkXfermode::kSrcOver_Mode);
+ canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
});
ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
@@ -259,10 +266,9 @@ TEST(RecordingCanvas, drawColor) {
EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds";
}
-TEST(RecordingCanvas, backgroundAndImage) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, backgroundAndImage) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
- SkBitmap bitmap;
- bitmap.setInfo(SkImageInfo::MakeUnknown(25, 25));
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
SkPaint paint;
paint.setColor(SK_ColorBLUE);
@@ -278,7 +284,7 @@ TEST(RecordingCanvas, backgroundAndImage) {
canvas.save(SaveFlags::MatrixClip);
canvas.translate(25, 25);
canvas.scale(2, 2);
- canvas.drawBitmap(bitmap, 0, 0, nullptr);
+ canvas.drawBitmap(*bitmap, 0, 0, nullptr);
canvas.restore();
}
canvas.restore();
@@ -312,7 +318,7 @@ TEST(RecordingCanvas, backgroundAndImage) {
ASSERT_EQ(2, count);
}
-RENDERTHREAD_TEST(RecordingCanvas, textureLayer) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(RecordingCanvas, textureLayer) {
auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
SkMatrix::MakeTrans(5, 5));
@@ -327,7 +333,7 @@ RENDERTHREAD_TEST(RecordingCanvas, textureLayer) {
});
}
-TEST(RecordingCanvas, saveLayer_simple) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simple) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
canvas.drawRect(10, 20, 190, 180, SkPaint());
@@ -361,7 +367,7 @@ TEST(RecordingCanvas, saveLayer_simple) {
EXPECT_EQ(3, count);
}
-TEST(RecordingCanvas, saveLayer_rounding) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rounding) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10.25f, 10.75f, 89.25f, 89.75f, 128, SaveFlags::ClipToLayer);
canvas.drawRect(20, 20, 80, 80, SkPaint());
@@ -391,7 +397,7 @@ TEST(RecordingCanvas, saveLayer_rounding) {
EXPECT_EQ(3, count);
}
-TEST(RecordingCanvas, saveLayer_missingRestore) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_missingRestore) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 200, 200, SkPaint());
@@ -406,7 +412,7 @@ TEST(RecordingCanvas, saveLayer_missingRestore) {
EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer";
}
-TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
canvas.drawRect(10, 20, 190, 180, SkPaint());
@@ -438,10 +444,10 @@ TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
EXPECT_EQ(3, count);
}
-TEST(RecordingCanvas, saveLayer_addClipFlag) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_addClipFlag) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(10, 20, 190, 180, SkRegion::kIntersect_Op);
+ canvas.clipRect(10, 20, 190, 180, SkClipOp::kIntersect);
canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
canvas.drawRect(10, 20, 190, 180, SkPaint());
canvas.restore();
@@ -457,10 +463,10 @@ TEST(RecordingCanvas, saveLayer_addClipFlag) {
EXPECT_EQ(3, count);
}
-TEST(RecordingCanvas, saveLayer_viewportCrop) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_viewportCrop) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
// shouldn't matter, since saveLayer will clip to its bounds
- canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
+ canvas.clipRect(-1000, -1000, 1000, 1000, SkClipOp::kReplace);
canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 400, 400, SkPaint());
@@ -481,7 +487,7 @@ TEST(RecordingCanvas, saveLayer_viewportCrop) {
EXPECT_EQ(3, count);
}
-TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.translate(100, 100);
@@ -507,7 +513,7 @@ TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
EXPECT_EQ(3, count);
}
-TEST(RecordingCanvas, saveLayer_rotateClipped) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateClipped) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.translate(100, 100);
@@ -545,12 +551,12 @@ TEST(RecordingCanvas, saveLayer_rotateClipped) {
EXPECT_EQ(3, count);
}
-TEST(RecordingCanvas, saveLayer_rejectBegin) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rejectBegin) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.translate(0, -20); // avoid identity case
// empty clip rect should force layer + contents to be rejected
- canvas.clipRect(0, -20, 200, -20, SkRegion::kIntersect_Op);
+ canvas.clipRect(0, -20, 200, -20, SkClipOp::kIntersect);
canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.restore();
@@ -560,24 +566,24 @@ TEST(RecordingCanvas, saveLayer_rejectBegin) {
ASSERT_EQ(0u, dl->getOps().size()) << "Begin/Rect/End should all be rejected.";
}
-TEST(RecordingCanvas, drawRenderNode_rejection) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_rejection) {
auto child = TestUtils::createNode(50, 50, 150, 150,
- [](RenderProperties& props, RecordingCanvas& canvas) {
+ [](RenderProperties& props, Canvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
});
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&child](RecordingCanvas& canvas) {
- canvas.clipRect(0, 0, 0, 0, SkRegion::kIntersect_Op); // empty clip, reject node
+ canvas.clipRect(0, 0, 0, 0, SkClipOp::kIntersect); // empty clip, reject node
canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node...
});
ASSERT_TRUE(dl->isEmpty());
}
-TEST(RecordingCanvas, drawRenderNode_projection) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_projection) {
sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150,
- [](RenderProperties& props, RecordingCanvas& canvas) {
+ [](RenderProperties& props, Canvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
@@ -618,11 +624,11 @@ TEST(RecordingCanvas, drawRenderNode_projection) {
}
}
-TEST(RecordingCanvas, firstClipWillReplace) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, firstClipWillReplace) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
// since no explicit clip set on canvas, this should be the one observed on op:
- canvas.clipRect(-100, -100, 300, 300, SkRegion::kIntersect_Op);
+ canvas.clipRect(-100, -100, 300, 300, SkClipOp::kIntersect);
SkPaint paint;
paint.setColor(SK_ColorWHITE);
@@ -635,11 +641,11 @@ TEST(RecordingCanvas, firstClipWillReplace) {
EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
}
-TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(-10, -10, 110, 110, SkRegion::kReplace_Op);
- canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.clipRect(-10, -10, 110, 110, SkClipOp::kReplace);
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
canvas.restore();
});
ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
@@ -648,7 +654,7 @@ TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
EXPECT_TRUE(dl->getOps()[0]->localClip->intersectWithRoot);
}
-TEST(RecordingCanvas, insertReorderBarrier) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.insertReorderBarrier(true);
@@ -669,14 +675,14 @@ TEST(RecordingCanvas, insertReorderBarrier) {
EXPECT_TRUE(chunks[1].reorderChildren);
}
-TEST(RecordingCanvas, insertReorderBarrier_clip) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier_clip) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
// first chunk: no recorded clip
canvas.insertReorderBarrier(true);
canvas.drawRect(0, 0, 400, 400, SkPaint());
// second chunk: no recorded clip, since inorder region
- canvas.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+ canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
canvas.insertReorderBarrier(false);
canvas.drawRect(0, 0, 400, 400, SkPaint());
@@ -699,7 +705,7 @@ TEST(RecordingCanvas, insertReorderBarrier_clip) {
EXPECT_EQ(Rect(200, 200), chunks[2].reorderClip->rect);
}
-TEST(RecordingCanvas, refPaint) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, refPaint) {
SkPaint paint;
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
@@ -727,62 +733,72 @@ TEST(RecordingCanvas, refPaint) {
EXPECT_NE(&paint, ops[2]->paint);
}
-TEST(RecordingCanvas, refBitmap) {
- SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmap) {
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(100, 100));
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
- canvas.drawBitmap(bitmap, 0, 0, nullptr);
+ canvas.drawBitmap(*bitmap, 0, 0, nullptr);
});
auto& bitmaps = dl->getBitmapResources();
EXPECT_EQ(1u, bitmaps.size());
}
-TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
- SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
+ sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
SkPaint paint;
- SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bitmap,
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ sk_sp<SkShader> shader = SkMakeBitmapShader(skBitmap,
+ SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode,
- SkShader::TileMode::kClamp_TileMode));
- paint.setShader(shader);
+ nullptr,
+ kNever_SkCopyPixelsMode,
+ nullptr);
+ paint.setShader(std::move(shader));
canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
});
auto& bitmaps = dl->getBitmapResources();
EXPECT_EQ(1u, bitmaps.size());
}
-TEST(RecordingCanvas, refBitmapInShader_composeShader) {
- SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_composeShader) {
+ sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
SkPaint paint;
- SkAutoTUnref<SkShader> shader1(SkShader::CreateBitmapShader(bitmap,
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ sk_sp<SkShader> shader1 = SkMakeBitmapShader(skBitmap,
+ SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode,
- SkShader::TileMode::kClamp_TileMode));
+ nullptr,
+ kNever_SkCopyPixelsMode,
+ nullptr);
SkPoint center;
center.set(50, 50);
SkColor colors[2];
colors[0] = Color::Black;
colors[1] = Color::White;
- SkAutoTUnref<SkShader> shader2(SkGradientShader::CreateRadial(center, 50, colors, nullptr, 2,
- SkShader::TileMode::kRepeat_TileMode));
+ sk_sp<SkShader> shader2 = SkGradientShader::MakeRadial(center, 50, colors, nullptr, 2,
+ SkShader::TileMode::kRepeat_TileMode);
- SkAutoTUnref<SkShader> composeShader(SkShader::CreateComposeShader(shader1, shader2,
- SkXfermode::Mode::kMultiply_Mode));
- paint.setShader(composeShader);
+ sk_sp<SkShader> composeShader = SkShader::MakeComposeShader(std::move(shader1), std::move(shader2),
+ SkBlendMode::kMultiply);
+ paint.setShader(std::move(composeShader));
canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
});
auto& bitmaps = dl->getBitmapResources();
EXPECT_EQ(1u, bitmaps.size());
}
-TEST(RecordingCanvas, drawText) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawText) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
Paint paint;
paint.setAntiAlias(true);
paint.setTextSize(20);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
- canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
+ canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::kBidi_Force_LTR, paint, NULL);
});
int count = 0;
@@ -797,7 +813,7 @@ TEST(RecordingCanvas, drawText) {
ASSERT_EQ(1, count);
}
-TEST(RecordingCanvas, drawTextInHighContrast) {
+OPENGL_PIPELINE_TEST(RecordingCanvas, drawTextInHighContrast) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.setHighContrastText(true);
Paint paint;
@@ -806,7 +822,7 @@ TEST(RecordingCanvas, drawTextInHighContrast) {
paint.setTextSize(20);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
- canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
+ canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::kBidi_Force_LTR, paint, NULL);
});
int count = 0;
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
new file mode 100644
index 000000000000..f5ff05849793
--- /dev/null
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -0,0 +1,949 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <VectorDrawable.h>
+
+#include "AnimationContext.h"
+#include "DamageAccumulator.h"
+#include "IContextFactory.h"
+#include "pipeline/skia/SkiaDisplayList.h"
+#include "pipeline/skia/SkiaPipeline.h"
+#include "pipeline/skia/SkiaRecordingCanvas.h"
+#include "renderthread/CanvasContext.h"
+#include "tests/common/TestUtils.h"
+#include "SkiaCanvas.h"
+#include <SkSurface_Base.h>
+#include <SkLiteRecorder.h>
+#include <SkClipStack.h>
+#include "FatalTestCanvas.h"
+#include <string.h>
+
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::skiapipeline;
+
+TEST(RenderNodeDrawable, create) {
+ auto rootNode = TestUtils::createNode(0, 0, 200, 400,
+ [](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
+ });
+
+ auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1));
+ SkLiteRecorder canvas;
+ canvas.reset(skLiteDL.get());
+ canvas.translate(100, 100);
+ RenderNodeDrawable drawable(rootNode.get(), &canvas);
+
+ ASSERT_EQ(drawable.getRenderNode(), rootNode.get());
+ ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties());
+ ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix());
+}
+
+namespace {
+
+static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
+ SkPaint paint;
+ // order put in blue channel, transparent so overlapped content doesn't get rejected
+ paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
+ canvas->drawRect(0, 0, 100, 100, paint);
+}
+
+static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
+ auto node = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [expectedDrawOrder, z](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedRect(&canvas, expectedDrawOrder);
+ props.setTranslationZ(z);
+ });
+ canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
+}
+
+static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder,
+ std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup) {
+ auto node = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [expectedDrawOrder, setup](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedRect(&canvas, expectedDrawOrder);
+ if (setup) {
+ setup(props, canvas);
+ }
+ });
+ canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
+}
+
+class ZReorderCanvas : public SkCanvas {
+public:
+ ZReorderCanvas(int width, int height) : SkCanvas(width, height) {}
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ int expectedOrder = SkColorGetB(paint.getColor()); // extract order from blue channel
+ EXPECT_EQ(expectedOrder, mDrawCounter++) << "An op was drawn out of order";
+ }
+ int getIndex() { return mDrawCounter; }
+protected:
+ int mDrawCounter = 0;
+};
+
+} // end anonymous namespace
+
+TEST(RenderNodeDrawable, zReorder) {
+
+ auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ canvas.insertReorderBarrier(true);
+ canvas.insertReorderBarrier(false);
+ drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
+ drawOrderedRect(&canvas, 1);
+ canvas.insertReorderBarrier(true);
+ drawOrderedNode(&canvas, 6, 2.0f);
+ drawOrderedRect(&canvas, 3);
+ drawOrderedNode(&canvas, 4, 0.0f);
+ drawOrderedRect(&canvas, 5);
+ drawOrderedNode(&canvas, 2, -2.0f);
+ drawOrderedNode(&canvas, 7, 2.0f);
+ canvas.insertReorderBarrier(false);
+ drawOrderedRect(&canvas, 8);
+ drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
+ canvas.insertReorderBarrier(true); //reorder a node ahead of drawrect op
+ drawOrderedRect(&canvas, 11);
+ drawOrderedNode(&canvas, 10, -1.0f);
+ canvas.insertReorderBarrier(false);
+ canvas.insertReorderBarrier(true); //test with two empty reorder sections
+ canvas.insertReorderBarrier(true);
+ canvas.insertReorderBarrier(false);
+ drawOrderedRect(&canvas, 12);
+ });
+
+ //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
+ ZReorderCanvas canvas(100, 100);
+ RenderNodeDrawable drawable(parent.get(), &canvas, false);
+ canvas.drawDrawable(&drawable);
+ EXPECT_EQ(13, canvas.getIndex());
+}
+
+TEST(RenderNodeDrawable, composeOnLayer)
+{
+ auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+ SkCanvas& canvas = *surface->getCanvas();
+ canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+
+ auto rootNode = TestUtils::createSkiaNode(0, 0, 1, 1,
+ [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
+ recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+ });
+
+ //attach a layer to the render node
+ auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1);
+ auto canvas2 = surfaceLayer->getCanvas();
+ canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+ rootNode->setLayerSurface(surfaceLayer);
+
+ RenderNodeDrawable drawable1(rootNode.get(), &canvas, false);
+ canvas.drawDrawable(&drawable1);
+ ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
+
+ RenderNodeDrawable drawable2(rootNode.get(), &canvas, true);
+ canvas.drawDrawable(&drawable2);
+ ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0));
+
+ RenderNodeDrawable drawable3(rootNode.get(), &canvas, false);
+ canvas.drawDrawable(&drawable3);
+ ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0));
+
+ rootNode->setLayerSurface(sk_sp<SkSurface>());
+}
+
+namespace {
+static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) {
+ SkRect clipBounds;
+ recorder.getClipBounds(&clipBounds);
+ return clipBounds;
+}
+
+static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) {
+ SkMatrix matrix;
+ recorder.getMatrix(&matrix);
+ return matrix;
+}
+}
+
+TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore)
+{
+ auto surface = SkSurface::MakeRasterN32Premul(400, 800);
+ SkCanvas& canvas = *surface->getCanvas();
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
+
+ auto rootNode = TestUtils::createSkiaNode(0, 0, 400, 800,
+ [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
+ SkPaint layerPaint;
+ ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
+ EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
+
+ //note we don't pass SaveFlags::MatrixClip, but matrix and clip will be saved
+ recorder.saveLayer(0, 0, 400, 400, &layerPaint, SaveFlags::ClipToLayer);
+ ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 400), getRecorderClipBounds(recorder));
+ EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
+
+ recorder.clipRect(50, 50, 350, 350, SkClipOp::kIntersect);
+ ASSERT_EQ(SkRect::MakeLTRB(50, 50, 350, 350), getRecorderClipBounds(recorder));
+
+ recorder.translate(300.0f, 400.0f);
+ EXPECT_EQ(SkMatrix::MakeTrans(300.0f, 400.0f), getRecorderMatrix(recorder));
+
+ recorder.restore();
+ ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
+ EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(SK_ColorGREEN);
+ recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint);
+ });
+
+ RenderNodeDrawable drawable(rootNode.get(), &canvas, true);
+ canvas.drawDrawable(&drawable);
+ ASSERT_EQ(SK_ColorGREEN, TestUtils::getColor(surface, 200, 600));
+}
+
+namespace {
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+ return new AnimationContext(clock);
+ }
+};
+} // end anonymous namespace
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
+ static const int SCROLL_X = 5;
+ static const int SCROLL_Y = 10;
+ class ProjectionTestCanvas : public SkCanvas {
+ public:
+ ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ const int index = mDrawCounter++;
+ SkMatrix expectedMatrix;;
+ switch (index) {
+ case 0: //this is node "B"
+ EXPECT_EQ(SkRect::MakeWH(100, 100), rect);
+ EXPECT_EQ(SK_ColorWHITE, paint.getColor());
+ expectedMatrix.reset();
+ EXPECT_EQ(SkRect::MakeLTRB(0, 0, 100, 100), TestUtils::getClipBounds(this));
+ break;
+ case 1: //this is node "P"
+ EXPECT_EQ(SkRect::MakeLTRB(-10, -10, 60, 60), rect);
+ EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
+ expectedMatrix.setTranslate(50 - SCROLL_X, 50 - SCROLL_Y);
+ EXPECT_EQ(SkRect::MakeLTRB(-35, -30, 45, 50), TestUtils::getLocalClipBounds(this));
+ break;
+ case 2: //this is node "C"
+ EXPECT_EQ(SkRect::MakeWH(100, 50), rect);
+ EXPECT_EQ(SK_ColorBLUE, paint.getColor());
+ expectedMatrix.setTranslate(-SCROLL_X, 50 - SCROLL_Y);
+ EXPECT_EQ(SkRect::MakeLTRB(0, 40, 95, 90), TestUtils::getClipBounds(this));
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ EXPECT_EQ(expectedMatrix, getTotalMatrix());
+ }
+
+ int getIndex() { return mDrawCounter; }
+ protected:
+ int mDrawCounter = 0;
+ };
+
+ /**
+ * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
+ * with a projecting child (P) of its own. P would normally draw between B and C's "background"
+ * draw, but because it is projected backwards, it's drawn in between B and C.
+ *
+ * The parent is scrolled by SCROLL_X/SCROLL_Y, but this does not affect the background
+ * (which isn't affected by scroll).
+ */
+ auto receiverBackground = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+ properties.setProjectionReceiver(true);
+ // scroll doesn't apply to background, so undone via translationX/Y
+ // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
+ properties.setTranslationX(SCROLL_X);
+ properties.setTranslationY(SCROLL_Y);
+
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ }, "B");
+
+ auto projectingRipple = TestUtils::createSkiaNode(50, 0, 100, 50,
+ [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+ properties.setProjectBackwards(true);
+ properties.setClipToBounds(false);
+ SkPaint paint;
+ paint.setColor(SK_ColorDKGRAY);
+ canvas.drawRect(-10, -10, 60, 60, paint);
+ }, "P");
+ auto child = TestUtils::createSkiaNode(0, 50, 100, 100,
+ [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorBLUE);
+ canvas.drawRect(0, 0, 100, 50, paint);
+ canvas.drawRenderNode(projectingRipple.get());
+ }, "C");
+ auto parent = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [&receiverBackground, &child](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+ // Set a rect outline for the projecting ripple to be masked against.
+ properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
+
+ canvas.save(SaveFlags::MatrixClip);
+ canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
+ canvas.drawRenderNode(receiverBackground.get());
+ canvas.drawRenderNode(child.get());
+ canvas.restore();
+ }, "A");
+ ContextFactory contextFactory;
+ std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
+ renderThread, false, parent.get(), &contextFactory));
+ TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
+ DamageAccumulator damageAccumulator;
+ info.damageAccumulator = &damageAccumulator;
+ info.observer = nullptr;
+ parent->prepareTree(info);
+
+ //parent(A) -> (receiverBackground, child)
+ //child(C) -> (rect[0, 0, 100, 50], projectingRipple)
+ //projectingRipple(P) -> (rect[-10, -10, 60, 60]) -> projects backwards
+ //receiverBackground(B) -> (rect[0, 0, 100, 100]) -> projection receiver
+
+ //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
+ ProjectionTestCanvas canvas(100, 100);
+ RenderNodeDrawable drawable(parent.get(), &canvas, true);
+ canvas.drawDrawable(&drawable);
+ EXPECT_EQ(3, canvas.getIndex());
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
+ /* R is backward projected on B and C is a layer.
+ A
+ / \
+ B C
+ |
+ R
+ */
+ static const int SCROLL_X = 5;
+ static const int SCROLL_Y = 10;
+ static const int CANVAS_WIDTH = 400;
+ static const int CANVAS_HEIGHT = 400;
+ static const int LAYER_WIDTH = 200;
+ static const int LAYER_HEIGHT = 200;
+ class ProjectionTestCanvas : public SkCanvas {
+ public:
+ ProjectionTestCanvas(int* drawCounter)
+ : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT)
+ , mDrawCounter(drawCounter)
+ {}
+ void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
+ const SkPaint&) override {
+ EXPECT_EQ(0, (*mDrawCounter)++); //part of painting the layer
+ EXPECT_EQ(SkRect::MakeLTRB(0, 0, LAYER_WIDTH, LAYER_HEIGHT), TestUtils::getClipBounds(this));
+ }
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ EXPECT_EQ(1, (*mDrawCounter)++);
+ EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
+ }
+ void onDrawOval(const SkRect&, const SkPaint&) override {
+ EXPECT_EQ(2, (*mDrawCounter)++);
+ SkMatrix expectedMatrix;
+ expectedMatrix.setTranslate(100 - SCROLL_X, 100 - SCROLL_Y);
+ EXPECT_EQ(expectedMatrix, getTotalMatrix());
+ EXPECT_EQ(SkRect::MakeLTRB(-85, -80, 295, 300), TestUtils::getLocalClipBounds(this));
+ }
+ int* mDrawCounter;
+ };
+
+ class ProjectionLayer : public SkSurface_Base {
+ public:
+ ProjectionLayer(int* drawCounter)
+ : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
+ , mDrawCounter(drawCounter) {
+ }
+ void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
+ EXPECT_EQ(3, (*mDrawCounter)++);
+ EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
+ 300 - SCROLL_Y), TestUtils::getClipBounds(this->getCanvas()));
+ }
+ SkCanvas* onNewCanvas() override {
+ return new ProjectionTestCanvas(mDrawCounter);
+ }
+ sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
+ return sk_sp<SkSurface>();
+ }
+ sk_sp<SkImage> onNewImageSnapshot(SkBudgeted) override {
+ return sk_sp<SkImage>();
+ }
+ void onCopyOnWrite(ContentChangeMode) override {}
+ int* mDrawCounter;
+ };
+
+ auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+ properties.setProjectionReceiver(true);
+ // scroll doesn't apply to background, so undone via translationX/Y
+ // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
+ properties.setTranslationX(SCROLL_X);
+ properties.setTranslationY(SCROLL_Y);
+
+ canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
+ }, "B"); //B
+ auto projectingRipple = TestUtils::createSkiaNode(0, 0, LAYER_WIDTH, LAYER_HEIGHT,
+ [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+ properties.setProjectBackwards(true);
+ properties.setClipToBounds(false);
+ canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
+ }, "R"); //R
+ auto child = TestUtils::createSkiaNode(100, 100, 300, 300,
+ [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+ canvas.drawRenderNode(projectingRipple.get());
+ canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
+ }, "C"); //C
+ auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [&receiverBackground, &child](RenderProperties& properties,
+ SkiaRecordingCanvas& canvas) {
+ // Set a rect outline for the projecting ripple to be masked against.
+ properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
+ canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
+ canvas.drawRenderNode(receiverBackground.get());
+ canvas.drawRenderNode(child.get());
+ }, "A"); //A
+
+ //prepareTree is required to find, which receivers have backward projected nodes
+ ContextFactory contextFactory;
+ std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
+ renderThread, false, parent.get(), &contextFactory));
+ TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
+ DamageAccumulator damageAccumulator;
+ info.damageAccumulator = &damageAccumulator;
+ info.observer = nullptr;
+ parent->prepareTree(info);
+
+ int drawCounter = 0;
+ //set a layer after prepareTree to avoid layer logic there
+ child->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+ sk_sp<SkSurface> surfaceLayer1(new ProjectionLayer(&drawCounter));
+ child->setLayerSurface(surfaceLayer1);
+ Matrix4 windowTransform;
+ windowTransform.loadTranslate(100, 100, 0);
+ child->getSkiaLayer()->inverseTransformInWindow.loadInverse(windowTransform);
+
+ LayerUpdateQueue layerUpdateQueue;
+ layerUpdateQueue.enqueueLayerWithDamage(child.get(),
+ android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
+ SkiaPipeline::renderLayersImpl(layerUpdateQueue, true);
+ EXPECT_EQ(1, drawCounter); //assert index 0 is drawn on the layer
+
+ RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
+ surfaceLayer1->getCanvas()->drawDrawable(&drawable);
+ EXPECT_EQ(4, drawCounter);
+
+ // clean up layer pointer, so we can safely destruct RenderNode
+ child->setLayerSurface(nullptr);
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) {
+ /* R is backward projected on B.
+ A
+ / \
+ B C
+ |
+ R
+ */
+ static const int SCROLL_X = 500000;
+ static const int SCROLL_Y = 0;
+ static const int CANVAS_WIDTH = 400;
+ static const int CANVAS_HEIGHT = 400;
+ class ProjectionChildScrollTestCanvas : public SkCanvas {
+ public:
+ ProjectionChildScrollTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ EXPECT_EQ(0, mDrawCounter++);
+ EXPECT_TRUE(getTotalMatrix().isIdentity());
+ }
+ void onDrawOval(const SkRect&, const SkPaint&) override {
+ EXPECT_EQ(1, mDrawCounter++);
+ EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
+ EXPECT_TRUE(getTotalMatrix().isIdentity());
+ }
+ int mDrawCounter = 0;
+ };
+
+ auto receiverBackground = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+ properties.setProjectionReceiver(true);
+ canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
+ }, "B"); //B
+ auto projectingRipple = TestUtils::createSkiaNode(0, 0, 200, 200,
+ [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+ // scroll doesn't apply to background, so undone via translationX/Y
+ // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver!
+ properties.setTranslationX(SCROLL_X);
+ properties.setTranslationY(SCROLL_Y);
+ properties.setProjectBackwards(true);
+ properties.setClipToBounds(false);
+ canvas.drawOval(0, 0, 200, 200, SkPaint());
+ }, "R"); //R
+ auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+ // Record time clip will be ignored by projectee
+ canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
+
+ canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
+ canvas.drawRenderNode(projectingRipple.get());
+ }, "C"); //C
+ auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [&receiverBackground, &child](RenderProperties& properties,
+ SkiaRecordingCanvas& canvas) {
+ canvas.drawRenderNode(receiverBackground.get());
+ canvas.drawRenderNode(child.get());
+ }, "A"); //A
+
+ //prepareTree is required to find, which receivers have backward projected nodes
+ ContextFactory contextFactory;
+ std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
+ renderThread, false, parent.get(), &contextFactory));
+ TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
+ DamageAccumulator damageAccumulator;
+ info.damageAccumulator = &damageAccumulator;
+ info.observer = nullptr;
+ parent->prepareTree(info);
+
+ std::unique_ptr<ProjectionChildScrollTestCanvas> canvas(new ProjectionChildScrollTestCanvas());
+ RenderNodeDrawable drawable(parent.get(), canvas.get(), true);
+ canvas->drawDrawable(&drawable);
+ EXPECT_EQ(2, canvas->mDrawCounter);
+}
+
+namespace {
+static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode)
+{
+ ContextFactory contextFactory;
+ std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
+ renderThread, false, renderNode.get(), &contextFactory));
+ TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
+ DamageAccumulator damageAccumulator;
+ info.damageAccumulator = &damageAccumulator;
+ info.observer = nullptr;
+ renderNode->prepareTree(info);
+
+ //create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection
+ ZReorderCanvas canvas(100, 100);
+ RenderNodeDrawable drawable(renderNode.get(), &canvas, false);
+ canvas.drawDrawable(&drawable);
+ return canvas.getIndex();
+}
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedInMiddle) {
+ /* R is backward projected on B
+ A
+ / \
+ B C
+ |
+ R
+ */
+ auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+ EXPECT_EQ(3, drawNode(renderThread, nodeA));
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectLast) {
+ /* R is backward projected on E
+ A
+ / | \
+ / | \
+ B C E
+ |
+ R
+ */
+ auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, nullptr); //nodeB
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 2
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //drawn as 3
+ props.setProjectionReceiver(true);
+ } ); //nodeE
+ }); //nodeA
+ EXPECT_EQ(4, drawNode(renderThread, nodeA));
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderNoReceivable) {
+ /* R is backward projected without receiver
+ A
+ / \
+ B C
+ |
+ R
+ */
+ auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, nullptr); //nodeB
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ //not having a projection receiver is an undefined behavior
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+ EXPECT_EQ(2, drawNode(renderThread, nodeA));
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderParentReceivable) {
+ /* R is backward projected on C
+ A
+ / \
+ B C
+ |
+ R
+ */
+ auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, nullptr); //nodeB
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ props.setProjectionReceiver(true);
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+ EXPECT_EQ(3, drawNode(renderThread, nodeA));
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderSameNodeReceivable) {
+ /* R is backward projected on R
+ A
+ / \
+ B C
+ |
+ R
+ */
+ auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, nullptr); //nodeB
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ //having a node that is projected on itself is an undefined/unexpected behavior
+ props.setProjectionReceiver(true);
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+ EXPECT_EQ(2, drawNode(renderThread, nodeA));
+}
+
+//Note: the outcome for this test is different in HWUI
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling) {
+ /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
+ A
+ /|\
+ / | \
+ B C R
+ */
+ auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ } ); //nodeC
+ drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ }); //nodeA
+ EXPECT_EQ(2, drawNode(renderThread, nodeA));
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderProjectedSibling2) {
+ /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
+ A
+ |
+ G
+ /|\
+ / | \
+ B C R
+ */
+ auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ } ); //nodeC
+ drawOrderedNode(&canvas, 255, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeG
+ }); //nodeA
+ EXPECT_EQ(3, drawNode(renderThread, nodeA));
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderGrandparentReceivable) {
+ /* R is backward projected on B
+ A
+ |
+ B
+ |
+ C
+ |
+ R
+ */
+ auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ props.setProjectionReceiver(true);
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ } ); //nodeB
+ }); //nodeA
+ EXPECT_EQ(3, drawNode(renderThread, nodeA));
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivables) {
+ /* B and G are receivables, R is backward projected
+ A
+ / \
+ B C
+ / \
+ G R
+ */
+ auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
+ drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
+ props.setProjectionReceiver(true);
+ } ); //nodeG
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+ EXPECT_EQ(4, drawNode(renderThread, nodeA));
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesLikelyScenario) {
+ /* B and G are receivables, G is backward projected
+ A
+ / \
+ B C
+ / \
+ G R
+ */
+ auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
+ props.setProjectionReceiver(true);
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeG
+ drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
+ } ); //nodeR
+ } ); //nodeC
+ }); //nodeA
+ EXPECT_EQ(4, drawNode(renderThread, nodeA));
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorderTwoReceivablesDeeper) {
+ /* B and G are receivables, R is backward projected
+ A
+ / \
+ B C
+ / \
+ G D
+ |
+ R
+ */
+ auto nodeA = TestUtils::createSkiaNode(0, 0, 100, 100,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ drawOrderedNode(&canvas, 0, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //B
+ props.setProjectionReceiver(true);
+ } ); //nodeB
+ drawOrderedNode(&canvas, 1, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //C
+ drawOrderedNode(&canvas, 2, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //G
+ props.setProjectionReceiver(true);
+ } ); //nodeG
+ drawOrderedNode(&canvas, 4, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //D
+ drawOrderedNode(&canvas, 3, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { //R
+ props.setProjectBackwards(true);
+ props.setClipToBounds(false);
+ } ); //nodeR
+ } ); //nodeD
+ } ); //nodeC
+ }); //nodeA
+ EXPECT_EQ(5, drawNode(renderThread, nodeA));
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, simple) {
+ static const int CANVAS_WIDTH = 100;
+ static const int CANVAS_HEIGHT = 200;
+ class SimpleTestCanvas : public TestCanvasBase {
+ public:
+ SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
+ }
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ EXPECT_EQ(0, mDrawCounter++);
+ }
+ void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
+ EXPECT_EQ(1, mDrawCounter++);
+ }
+ };
+
+ auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
+ canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
+ canvas.drawBitmap(*bitmap, 10, 10, nullptr);
+ });
+
+ SimpleTestCanvas canvas;
+ RenderNodeDrawable drawable(node.get(), &canvas, true);
+ canvas.drawDrawable(&drawable);
+ EXPECT_EQ(2, canvas.mDrawCounter);
+}
+
+RENDERTHREAD_TEST(RenderNodeDrawable, colorOp_unbounded) {
+ static const int CANVAS_WIDTH = 200;
+ static const int CANVAS_HEIGHT = 200;
+ class ColorTestCanvas : public TestCanvasBase {
+ public:
+ ColorTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
+ }
+ void onDrawPaint(const SkPaint&) {
+ switch (mDrawCounter++) {
+ case 0:
+ // While this mirrors FrameBuilder::colorOp_unbounded, this value is different
+ // because there is no root (root is clipped in SkiaPipeline::renderFrame).
+ // SkiaPipeline.clipped and clip_replace verify the root clip.
+ EXPECT_TRUE(TestUtils::getClipBounds(this).isEmpty());
+ break;
+ case 1:
+ EXPECT_EQ(SkRect::MakeWH(10, 10), TestUtils::getClipBounds(this));
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ }
+ };
+
+ auto unclippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ props.setClipToBounds(false);
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+ });
+
+ auto clippedColorView = TestUtils::createSkiaNode(0, 0, 10, 10,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+ });
+
+ ColorTestCanvas canvas;
+ RenderNodeDrawable drawable(unclippedColorView.get(), &canvas, true);
+ canvas.drawDrawable(&drawable);
+ EXPECT_EQ(1, canvas.mDrawCounter);
+ RenderNodeDrawable drawable2(clippedColorView.get(), &canvas, true);
+ canvas.drawDrawable(&drawable2);
+ EXPECT_EQ(2, canvas.mDrawCounter);
+}
+
+TEST(RenderNodeDrawable, renderNode) {
+ static const int CANVAS_WIDTH = 200;
+ static const int CANVAS_HEIGHT = 200;
+ class RenderNodeTestCanvas : public TestCanvasBase {
+ public:
+ RenderNodeTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {
+ }
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ switch(mDrawCounter++) {
+ case 0:
+ EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), TestUtils::getClipBounds(this));
+ EXPECT_EQ(SK_ColorDKGRAY, paint.getColor());
+ break;
+ case 1:
+ EXPECT_EQ(SkRect::MakeLTRB(50, 50, 150, 150), TestUtils::getClipBounds(this));
+ EXPECT_EQ(SK_ColorWHITE, paint.getColor());
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ }
+ };
+
+ auto child = TestUtils::createSkiaNode(10, 10, 110, 110,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+
+ auto parent = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorDKGRAY);
+ canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
+
+ canvas.save(SaveFlags::MatrixClip);
+ canvas.translate(40, 40);
+ canvas.drawRenderNode(child.get());
+ canvas.restore();
+ });
+
+ RenderNodeTestCanvas canvas;
+ RenderNodeDrawable drawable(parent.get(), &canvas, true);
+ canvas.drawDrawable(&drawable);
+ EXPECT_EQ(2, canvas.mDrawCounter);
+}
+
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index cf76a8691dcd..ab8e4e106d3e 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -15,6 +15,7 @@
*/
#include <gtest/gtest.h>
+#include <VectorDrawable.h>
#include "AnimationContext.h"
#include "DamageAccumulator.h"
@@ -39,11 +40,11 @@ public:
TEST(RenderNode, hasParents) {
auto child = TestUtils::createNode(0, 0, 200, 400,
- [](RenderProperties& props, TestCanvas& canvas) {
- canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
+ [](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
});
auto parent = TestUtils::createNode(0, 0, 200, 400,
- [&child](RenderProperties& props, TestCanvas& canvas) {
+ [&child](RenderProperties& props, Canvas& canvas) {
canvas.drawRenderNode(child.get());
});
@@ -52,8 +53,8 @@ TEST(RenderNode, hasParents) {
EXPECT_TRUE(child->hasParents()) << "Child node has no parent";
EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents";
- TestUtils::recordNode(*parent, [](TestCanvas& canvas) {
- canvas.drawColor(Color::Amber_500, SkXfermode::kSrcOver_Mode);
+ TestUtils::recordNode(*parent, [](Canvas& canvas) {
+ canvas.drawColor(Color::Amber_500, SkBlendMode::kSrcOver);
});
EXPECT_TRUE(child->hasParents()) << "Child should still have a parent";
@@ -68,7 +69,7 @@ TEST(RenderNode, hasParents) {
TEST(RenderNode, releasedCallback) {
class DecRefOnReleased : public GlFunctorLifecycleListener {
public:
- DecRefOnReleased(int* refcnt) : mRefCnt(refcnt) {}
+ explicit DecRefOnReleased(int* refcnt) : mRefCnt(refcnt) {}
void onGlFunctorReleased(Functor* functor) override {
*mRefCnt -= 1;
}
@@ -81,14 +82,14 @@ TEST(RenderNode, releasedCallback) {
Functor noopFunctor;
auto node = TestUtils::createNode(0, 0, 200, 400,
- [&](RenderProperties& props, TestCanvas& canvas) {
+ [&](RenderProperties& props, Canvas& canvas) {
refcnt++;
canvas.callDrawGLFunction(&noopFunctor, listener.get());
});
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
EXPECT_EQ(1, refcnt);
- TestUtils::recordNode(*node, [&](TestCanvas& canvas) {
+ TestUtils::recordNode(*node, [&](Canvas& canvas) {
refcnt++;
canvas.callDrawGLFunction(&noopFunctor, listener.get());
});
@@ -97,24 +98,26 @@ TEST(RenderNode, releasedCallback) {
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
EXPECT_EQ(1, refcnt);
- TestUtils::recordNode(*node, [](TestCanvas& canvas) {});
+ TestUtils::recordNode(*node, [](Canvas& canvas) {});
EXPECT_EQ(1, refcnt);
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
EXPECT_EQ(0, refcnt);
}
RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) {
+ auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
- CanvasContext canvasContext(renderThread, false, nullptr, &contextFactory);
- TreeInfo info(TreeInfo::MODE_RT_ONLY, canvasContext);
+ std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
+ renderThread, false, rootNode.get(), &contextFactory));
+ TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
info.observer = nullptr;
{
auto nonNullDLNode = TestUtils::createNode(0, 0, 200, 400,
- [](RenderProperties& props, TestCanvas& canvas) {
- canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
+ [](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
});
TestUtils::syncHierarchyPropertiesAndDisplayList(nonNullDLNode);
EXPECT_TRUE(nonNullDLNode->getDisplayList());
@@ -128,5 +131,39 @@ RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) {
nullDLNode->prepareTree(info);
}
- canvasContext.destroy(nullptr);
+ canvasContext->destroy(nullptr);
+}
+
+RENDERTHREAD_TEST(RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
+
+ VectorDrawable::Group* group = new VectorDrawable::Group();
+ sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
+
+ auto rootNode = TestUtils::createNode(0, 0, 200, 400,
+ [&](RenderProperties& props, Canvas& canvas) {
+ canvas.drawVectorDrawable(vectorDrawable.get());
+ });
+ ContextFactory contextFactory;
+ std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
+ renderThread, false, rootNode.get(), &contextFactory));
+ TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
+ DamageAccumulator damageAccumulator;
+ LayerUpdateQueue layerUpdateQueue;
+ info.damageAccumulator = &damageAccumulator;
+ info.layerUpdateQueue = &layerUpdateQueue;
+ info.observer = nullptr;
+
+ // Put node on HW layer
+ rootNode->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+
+ TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode);
+ rootNode->prepareTree(info);
+
+ // Check that the VD is in the dislay list, and the layer update queue contains the correct
+ // damage rect.
+ EXPECT_TRUE(rootNode->getDisplayList()->hasVectorDrawables());
+ EXPECT_FALSE(info.layerUpdateQueue->entries().empty());
+ EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode);
+ EXPECT_EQ(uirenderer::Rect(0, 0, 200, 400), info.layerUpdateQueue->entries().at(0).damage);
+ canvasContext->destroy(nullptr);
}
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index cd759cb3d3b6..f32d97a3d809 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -17,22 +17,35 @@
#include "tests/common/TestUtils.h"
#include <gtest/gtest.h>
-#include <SkShader.h>
#include <SkColorMatrixFilter.h>
+#include <SkColorSpace.h>
+#include <SkImagePriv.h>
+#include <SkShader.h>
using namespace android;
using namespace android::uirenderer;
+SkBitmap createSkBitmap(int width, int height) {
+ SkBitmap bitmap;
+ SkImageInfo info = SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
+ bitmap.setInfo(info);
+ bitmap.allocPixels(info);
+ return bitmap;
+}
+
/**
* 1x1 bitmaps must not be optimized into solid color shaders, since HWUI can't
* compose/render color shaders
*/
TEST(SkiaBehavior, CreateBitmapShader1x1) {
- SkBitmap origBitmap = TestUtils::createSkBitmap(1, 1);
- SkAutoTUnref<SkShader> s(SkShader::CreateBitmapShader(
+ SkBitmap origBitmap = createSkBitmap(1, 1);
+ sk_sp<SkShader> s = SkMakeBitmapShader(
origBitmap,
SkShader::kClamp_TileMode,
- SkShader::kRepeat_TileMode));
+ SkShader::kRepeat_TileMode,
+ nullptr,
+ kNever_SkCopyPixelsMode,
+ nullptr);
SkBitmap bitmap;
SkShader::TileMode xy[2];
@@ -44,7 +57,7 @@ TEST(SkiaBehavior, CreateBitmapShader1x1) {
}
TEST(SkiaBehavior, genIds) {
- SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ SkBitmap bitmap = createSkBitmap(100, 100);
uint32_t genId = bitmap.getGenerationID();
bitmap.notifyPixelsChanged();
EXPECT_NE(genId, bitmap.getGenerationID());
@@ -52,19 +65,35 @@ TEST(SkiaBehavior, genIds) {
TEST(SkiaBehavior, lightingColorFilter_simplify) {
{
- SkAutoTUnref<SkColorFilter> filter(
- SkColorMatrixFilter::CreateLightingFilter(0x11223344, 0));
+ sk_sp<SkColorFilter> filter(
+ SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0));
SkColor observedColor;
- SkXfermode::Mode observedMode;
+ SkBlendMode observedMode;
ASSERT_TRUE(filter->asColorMode(&observedColor, &observedMode));
EXPECT_EQ(0xFF223344, observedColor);
- EXPECT_EQ(SkXfermode::Mode::kModulate_Mode, observedMode);
+ EXPECT_EQ(SkBlendMode::kModulate, observedMode);
}
{
- SkAutoTUnref<SkColorFilter> failFilter(
- SkColorMatrixFilter::CreateLightingFilter(0x11223344, 0x1));
+ sk_sp<SkColorFilter> failFilter(
+ SkColorMatrixFilter::MakeLightingFilter(0x11223344, 0x1));
EXPECT_FALSE(failFilter->asColorMode(nullptr, nullptr));
}
}
+
+TEST(SkiaBehavior, porterDuffCreateIsCached) {
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kOverlay);
+ auto expected = paint.getBlendMode();
+ paint.setBlendMode(SkBlendMode::kClear);
+ ASSERT_NE(expected, paint.getBlendMode());
+ paint.setBlendMode(SkBlendMode::kOverlay);
+ ASSERT_EQ(expected, paint.getBlendMode());
+}
+
+TEST(SkiaBehavior, srgbColorSpaceIsSingleton) {
+ sk_sp<SkColorSpace> sRGB1 = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
+ sk_sp<SkColorSpace> sRGB2 = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
+ ASSERT_EQ(sRGB1.get(), sRGB2.get());
+}
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 5a011938e2bb..0ac09ac49f5d 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -28,7 +28,7 @@ using namespace android::uirenderer;
* Verify that we get the same culling bounds for text for (1) drawing glyphs
* directly to a Canvas or (2) going through a SkPicture as an intermediate step.
*/
-TEST(SkiaCanvasProxy, drawGlyphsViaPicture) {
+OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
// setup test variables
SkPaint paint;
@@ -45,7 +45,7 @@ TEST(SkiaCanvasProxy, drawGlyphsViaPicture) {
SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0);
std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas));
TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25);
- SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
+ sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
canvas.asSkCanvas()->drawPicture(picture);
});
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
new file mode 100644
index 000000000000..8f6fc8b2e960
--- /dev/null
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <VectorDrawable.h>
+
+#include "AnimationContext.h"
+#include "DamageAccumulator.h"
+#include "IContextFactory.h"
+#include "pipeline/skia/SkiaDisplayList.h"
+#include "renderthread/CanvasContext.h"
+#include "tests/common/TestUtils.h"
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::skiapipeline;
+
+TEST(SkiaDisplayList, create) {
+ SkRect bounds = SkRect::MakeWH(200, 200);
+ SkiaDisplayList skiaDL(bounds);
+ ASSERT_TRUE(skiaDL.isEmpty());
+ ASSERT_FALSE(skiaDL.mProjectionReceiver);
+ ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
+}
+
+TEST(SkiaDisplayList, reset) {
+ SkRect bounds = SkRect::MakeWH(200, 200);
+ SkiaDisplayList skiaDL(bounds);
+
+ SkCanvas dummyCanvas;
+ RenderNodeDrawable drawable(nullptr, &dummyCanvas);
+ skiaDL.mChildNodes.emplace_back(nullptr, &dummyCanvas);
+ skiaDL.mChildFunctors.emplace_back(nullptr, nullptr, &dummyCanvas);
+ skiaDL.mMutableImages.push_back(nullptr);
+ skiaDL.mVectorDrawables.push_back(nullptr);
+ skiaDL.mDrawable->drawAnnotation(bounds, "testAnnotation", nullptr);
+ skiaDL.mProjectionReceiver = &drawable;
+
+ ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
+ ASSERT_FALSE(skiaDL.mChildNodes.empty());
+ ASSERT_FALSE(skiaDL.mChildFunctors.empty());
+ ASSERT_FALSE(skiaDL.mMutableImages.empty());
+ ASSERT_FALSE(skiaDL.mVectorDrawables.empty());
+ ASSERT_FALSE(skiaDL.isEmpty());
+ ASSERT_TRUE(skiaDL.mProjectionReceiver);
+
+ bounds = SkRect::MakeWH(100, 100);
+ skiaDL.reset(bounds);
+
+ ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
+ ASSERT_TRUE(skiaDL.mChildNodes.empty());
+ ASSERT_TRUE(skiaDL.mChildFunctors.empty());
+ ASSERT_TRUE(skiaDL.mMutableImages.empty());
+ ASSERT_TRUE(skiaDL.mVectorDrawables.empty());
+ ASSERT_TRUE(skiaDL.isEmpty());
+ ASSERT_FALSE(skiaDL.mProjectionReceiver);
+}
+
+TEST(SkiaDisplayList, reuseDisplayList) {
+ sp<RenderNode> renderNode = new RenderNode();
+ std::unique_ptr<SkiaDisplayList> availableList;
+
+ // no list has been attached so it should return a nullptr
+ availableList = renderNode->detachAvailableList();
+ ASSERT_EQ(availableList.get(), nullptr);
+
+ // attach a displayList for reuse
+ SkiaDisplayList skiaDL(SkRect::MakeWH(200, 200));
+ ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get(), nullptr));
+
+ // detach the list that you just attempted to reuse
+ availableList = renderNode->detachAvailableList();
+ ASSERT_EQ(availableList.get(), &skiaDL);
+ availableList.release(); // prevents an invalid free since our DL is stack allocated
+
+ // after detaching there should return no available list
+ availableList = renderNode->detachAvailableList();
+ ASSERT_EQ(availableList.get(), nullptr);
+}
+
+TEST(SkiaDisplayList, syncContexts) {
+ SkRect bounds = SkRect::MakeWH(200, 200);
+ SkiaDisplayList skiaDL(bounds);
+
+ SkCanvas dummyCanvas;
+ TestUtils::MockFunctor functor;
+ skiaDL.mChildFunctors.emplace_back(&functor, nullptr, &dummyCanvas);
+
+ VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
+ vectorDrawable.mutateStagingProperties()->setBounds(bounds);
+ skiaDL.mVectorDrawables.push_back(&vectorDrawable);
+
+ // ensure that the functor and vectorDrawable are properly synced
+ skiaDL.syncContents();
+
+ ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
+ ASSERT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
+}
+
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+ return new AnimationContext(clock);
+ }
+};
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
+ auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
+ ContextFactory contextFactory;
+ std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create(
+ renderThread, false, rootNode.get(), &contextFactory));
+ TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
+ DamageAccumulator damageAccumulator;
+ info.damageAccumulator = &damageAccumulator;
+ info.observer = nullptr;
+
+ SkiaDisplayList skiaDL(SkRect::MakeWH(200, 200));
+
+ // prepare with a clean VD
+ VectorDrawableRoot cleanVD(new VectorDrawable::Group());
+ skiaDL.mVectorDrawables.push_back(&cleanVD);
+ cleanVD.getBitmapUpdateIfDirty(); // this clears the dirty bit
+
+ ASSERT_FALSE(cleanVD.isDirty());
+ ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
+ ASSERT_FALSE(skiaDL.prepareListAndChildren(info, false, [](RenderNode*, TreeInfo&, bool) {}));
+ ASSERT_TRUE(cleanVD.getPropertyChangeWillBeConsumed());
+
+ // prepare again this time adding a dirty VD
+ VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
+ skiaDL.mVectorDrawables.push_back(&dirtyVD);
+
+ ASSERT_TRUE(dirtyVD.isDirty());
+ ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
+ ASSERT_TRUE(skiaDL.prepareListAndChildren(info, false, [](RenderNode*, TreeInfo&, bool) {}));
+ ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
+
+ // prepare again this time adding a RenderNode and a callback
+ sp<RenderNode> renderNode = new RenderNode();
+ TreeInfo* infoPtr = &info;
+ SkCanvas dummyCanvas;
+ skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
+ bool hasRun = false;
+ ASSERT_TRUE(skiaDL.prepareListAndChildren(info, false,
+ [&hasRun, renderNode, infoPtr](RenderNode* n, TreeInfo& i, bool r) {
+ hasRun = true;
+ ASSERT_EQ(renderNode.get(), n);
+ ASSERT_EQ(infoPtr, &i);
+ ASSERT_FALSE(r);
+ }));
+ ASSERT_TRUE(hasRun);
+
+ canvasContext->destroy(nullptr);
+}
+
+TEST(SkiaDisplayList, updateChildren) {
+ SkRect bounds = SkRect::MakeWH(200, 200);
+ SkiaDisplayList skiaDL(bounds);
+
+ sp<RenderNode> renderNode = new RenderNode();
+ SkCanvas dummyCanvas;
+ skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
+ skiaDL.updateChildren([renderNode](RenderNode* n) {
+ ASSERT_EQ(renderNode.get(), n);
+ });
+}
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
new file mode 100644
index 000000000000..0b8c2a98fab5
--- /dev/null
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <VectorDrawable.h>
+
+#include "AnimationContext.h"
+#include "DamageAccumulator.h"
+#include "IContextFactory.h"
+#include "pipeline/skia/SkiaDisplayList.h"
+#include "pipeline/skia/SkiaRecordingCanvas.h"
+#include "pipeline/skia/SkiaOpenGLPipeline.h"
+#include "renderthread/CanvasContext.h"
+#include "tests/common/TestUtils.h"
+#include "SkiaCanvas.h"
+#include <SkClipStack.h>
+#include <SkLiteRecorder.h>
+#include <SkSurface_Base.h>
+#include <string.h>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::skiapipeline;
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
+ auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
+ [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+ redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+ });
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeLargest();
+ std::vector<sp<RenderNode>> renderNodes;
+ renderNodes.push_back(redNode);
+ bool opaque = true;
+ android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+ surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
+}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
+ auto halfGreenNode = TestUtils::createSkiaNode(0, 0, 2, 2,
+ [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
+ SkPaint greenPaint;
+ greenPaint.setColor(SK_ColorGREEN);
+ greenPaint.setStyle(SkPaint::kFill_Style);
+ bottomHalfGreenCanvas.drawRect(0, 1, 2, 2, greenPaint);
+ });
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeLargest();
+ std::vector<sp<RenderNode>> renderNodes;
+ renderNodes.push_back(halfGreenNode);
+ android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ auto surface = SkSurface::MakeRasterN32Premul(2, 2);
+ surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
+}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
+ auto redNode = TestUtils::createSkiaNode(0, 0, 2, 2,
+ [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+ redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+ });
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeXYWH(0, 1, 2, 1);
+ std::vector<sp<RenderNode>> renderNodes;
+ renderNodes.push_back(redNode);
+ android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ auto surface = SkSurface::MakeRasterN32Premul(2, 2);
+ surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
+}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
+ auto redNode = TestUtils::createSkiaNode(0, 0, 1, 1,
+ [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+ redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+ });
+ auto surfaceLayer1 = SkSurface::MakeRasterN32Premul(1, 1);
+ surfaceLayer1->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorWHITE);
+ redNode->setLayerSurface(surfaceLayer1);
+
+ //create a 2nd 2x2 layer and add it to the queue as well.
+ //make the layer's dirty area one half of the layer and verify only the dirty half is updated.
+ auto blueNode = TestUtils::createSkiaNode(0, 0, 2, 2,
+ [](RenderProperties& props, SkiaRecordingCanvas& blueCanvas) {
+ blueCanvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ });
+ auto surfaceLayer2 = SkSurface::MakeRasterN32Premul(2, 2);
+ surfaceLayer2->getCanvas()->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorWHITE);
+ blueNode->setLayerSurface(surfaceLayer2);
+
+ //attach both layers to the update queue
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeLargest();
+ layerUpdateQueue.enqueueLayerWithDamage(redNode.get(), dirty);
+ layerUpdateQueue.enqueueLayerWithDamage(blueNode.get(), SkRect::MakeWH(2, 1));
+ ASSERT_EQ(layerUpdateQueue.entries().size(), 2UL);
+
+ bool opaque = true;
+ FrameBuilder::LightGeometry lightGeometry;
+ lightGeometry.radius = 1.0f;
+ lightGeometry.center = { 0.0f, 0.0f, 0.0f };
+ BakedOpRenderer::LightInfo lightInfo;
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, lightInfo);
+ ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 1), SK_ColorWHITE);
+ ASSERT_TRUE(layerUpdateQueue.entries().empty());
+ redNode->setLayerSurface(sk_sp<SkSurface>());
+ blueNode->setLayerSurface(sk_sp<SkSurface>());
+}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
+ ScopedProperty<bool> prop(Properties::debugOverdraw, true);
+
+ auto whiteNode = TestUtils::createSkiaNode(0, 0, 1, 1,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+ });
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeXYWH(0, 0, 1, 1);
+ std::vector<sp<RenderNode>> renderNodes;
+ renderNodes.push_back(whiteNode);
+ bool opaque = true;
+ //empty contentDrawBounds is avoiding backdrop/content logic, which would lead to less overdraw
+ android::uirenderer::Rect contentDrawBounds(0, 0, 0, 0);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+
+ // Initialize the canvas to blue.
+ surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+
+ // Single draw, should be white.
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
+
+ // 1 Overdraw, should be blue blended onto white.
+ renderNodes.push_back(whiteNode); //this is the "content" node
+ renderNodes.push_back(whiteNode); //the "content" node above does not cause an overdraw, because
+ //it clips the first "background" node
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffd0d0ff);
+
+ // 2 Overdraw, should be green blended onto white
+ renderNodes.push_back(whiteNode);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffd0ffd0);
+
+ // 3 Overdraw, should be pink blended onto white.
+ renderNodes.push_back(whiteNode);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffffc0c0);
+
+ // 4 Overdraw, should be red blended onto white.
+ renderNodes.push_back(whiteNode);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffff8080);
+
+ // 5 Overdraw, should be red blended onto white.
+ renderNodes.push_back(whiteNode);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffff8080);
+}
+
+namespace {
+template <typename T>
+class DeferLayer : public SkSurface_Base {
+public:
+ DeferLayer() : SkSurface_Base(T().imageInfo(), nullptr) {}
+ virtual ~DeferLayer() {}
+
+ SkCanvas* onNewCanvas() override {
+ return new T();
+ }
+ sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
+ return sk_sp<SkSurface>();
+ }
+ sk_sp<SkImage> onNewImageSnapshot(SkBudgeted) override {
+ return sk_sp<SkImage>();
+ }
+ T* canvas() { return static_cast<T*>(getCanvas()); }
+ void onCopyOnWrite(ContentChangeMode) override {}
+};
+}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) {
+ class DeferTestCanvas : public SkCanvas {
+ public:
+ DeferTestCanvas() : SkCanvas(800, 600) {}
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ SkMatrix expected;
+ switch (mDrawCounter++) {
+ case 0:
+ // background - left side
+ EXPECT_EQ(SkRect::MakeLTRB(600, 100, 700, 500), TestUtils::getClipBounds(this));
+ expected.setTranslate(100, 100);
+ break;
+ case 1:
+ // background - top side
+ EXPECT_EQ(SkRect::MakeLTRB(100, 400, 600, 500), TestUtils::getClipBounds(this));
+ expected.setTranslate(100, 100);
+ break;
+ case 2:
+ // content
+ EXPECT_EQ(SkRect::MakeLTRB(100, 100, 700, 500), TestUtils::getClipBounds(this));
+ expected.setTranslate(-50, -50);
+ break;
+ case 3:
+ // overlay
+ EXPECT_EQ(SkRect::MakeLTRB(0, 0, 800, 600), TestUtils::getClipBounds(this));
+ expected.reset();
+ break;
+ default:
+ ADD_FAILURE() << "Too many rects observed";
+ }
+ EXPECT_EQ(expected, getTotalMatrix());
+ }
+ int mDrawCounter = 0;
+ };
+
+ std::vector<sp<RenderNode>> nodes;
+ SkPaint transparentPaint;
+ transparentPaint.setAlpha(128);
+
+ // backdrop
+ nodes.push_back(TestUtils::createSkiaNode(100, 100, 700, 500, // 600x400
+ [&transparentPaint](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 600, 400, transparentPaint);
+ }));
+
+ // content
+ android::uirenderer::Rect contentDrawBounds(150, 150, 650, 450); // 500x300
+ nodes.push_back(TestUtils::createSkiaNode(0, 0, 800, 600,
+ [&transparentPaint](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 800, 600, transparentPaint);
+ }));
+
+ // overlay
+ nodes.push_back(TestUtils::createSkiaNode(0, 0, 800, 600,
+ [&transparentPaint](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 800, 200, transparentPaint);
+ }));
+
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeWH(800, 600);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ sk_sp<DeferLayer<DeferTestCanvas>> surface(new DeferLayer<DeferTestCanvas>());
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface);
+ EXPECT_EQ(4, surface->canvas()->mDrawCounter);
+}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) {
+ static const int CANVAS_WIDTH = 200;
+ static const int CANVAS_HEIGHT = 200;
+ class ClippedTestCanvas : public SkCanvas {
+ public:
+ ClippedTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {
+ }
+ void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
+ EXPECT_EQ(0, mDrawCounter++);
+ EXPECT_EQ(SkRect::MakeLTRB(10, 20, 30, 40), TestUtils::getClipBounds(this));
+ EXPECT_TRUE(getTotalMatrix().isIdentity());
+ }
+ int mDrawCounter = 0;
+ };
+
+ std::vector<sp<RenderNode>> nodes;
+ nodes.push_back(TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ sk_sp<Bitmap> bitmap(TestUtils::createBitmap(CANVAS_WIDTH, CANVAS_HEIGHT));
+ canvas.drawBitmap(*bitmap, 0, 0, nullptr);
+ }));
+
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeLTRB(10, 20, 30, 40);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ sk_sp<DeferLayer<ClippedTestCanvas>> surface(new DeferLayer<ClippedTestCanvas>());
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
+ SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
+ EXPECT_EQ(1, surface->canvas()->mDrawCounter);
+}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
+ static const int CANVAS_WIDTH = 50;
+ static const int CANVAS_HEIGHT = 50;
+ class ClipReplaceTestCanvas : public SkCanvas {
+ public:
+ ClipReplaceTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {
+ }
+ void onDrawPaint(const SkPaint&) {
+ EXPECT_EQ(0, mDrawCounter++);
+ EXPECT_EQ(SkRect::MakeLTRB(20, 10, 30, 40), TestUtils::getClipBounds(this))
+ << "Expect resolved clip to be intersection of viewport clip and clip op";
+ }
+ int mDrawCounter = 0;
+ };
+
+ std::vector<sp<RenderNode>> nodes;
+ nodes.push_back(TestUtils::createSkiaNode(20, 20, 30, 30,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace);
+ canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+ }));
+
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeLTRB(10, 10, 40, 40);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ sk_sp<DeferLayer<ClipReplaceTestCanvas>> surface(new DeferLayer<ClipReplaceTestCanvas>());
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
+ SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
+ EXPECT_EQ(1, surface->canvas()->mDrawCounter);
+}
diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
new file mode 100644
index 000000000000..92d9d3d0d5fe
--- /dev/null
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <VectorDrawable.h>
+
+#include "AnimationContext.h"
+#include "DamageAccumulator.h"
+#include "IContextFactory.h"
+#include "pipeline/skia/SkiaDisplayList.h"
+#include "pipeline/skia/SkiaPipeline.h"
+#include "pipeline/skia/SkiaRecordingCanvas.h"
+#include "renderthread/CanvasContext.h"
+#include "tests/common/TestUtils.h"
+#include "SkiaCanvas.h"
+#include <SkSurface_Base.h>
+#include <SkLiteRecorder.h>
+#include <SkClipStack.h>
+#include "FatalTestCanvas.h"
+#include <string.h>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::skiapipeline;
+
+namespace {
+
+static void testProperty(std::function<void(RenderProperties&)> propSetupCallback,
+ std::function<void(const SkCanvas&)> opValidateCallback) {
+ static const int CANVAS_WIDTH = 100;
+ static const int CANVAS_HEIGHT = 100;
+ class PropertyTestCanvas : public TestCanvasBase {
+ public:
+ PropertyTestCanvas(std::function<void(const SkCanvas&)> callback)
+ : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT), mCallback(callback) {}
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ EXPECT_EQ(mDrawCounter++, 0);
+ mCallback(*this);
+ }
+ void onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle style) {
+ SkCanvas::onClipRRect(rrect, op, style);
+ }
+ std::function<void(const SkCanvas&)> mCallback;
+ };
+
+ auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [propSetupCallback](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ propSetupCallback(props);
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
+ });
+
+ PropertyTestCanvas canvas(opValidateCallback);
+ RenderNodeDrawable drawable(node.get(), &canvas, true);
+ canvas.drawDrawable(&drawable);
+ EXPECT_EQ(1, canvas.mDrawCounter);
+}
+
+}
+
+TEST(RenderNodeDrawable, renderPropClipping) {
+ testProperty([](RenderProperties& properties) {
+ properties.setClipToBounds(true);
+ properties.setClipBounds(android::uirenderer::Rect(10, 20, 300, 400));
+ }, [](const SkCanvas& canvas) {
+ EXPECT_EQ(SkRect::MakeLTRB(10, 20, 100, 100), TestUtils::getClipBounds(&canvas))
+ << "Clip rect should be intersection of node bounds and clip bounds";
+ });
+}
+
+TEST(RenderNodeDrawable, renderPropRevealClip) {
+ testProperty([](RenderProperties& properties) {
+ properties.mutableRevealClip().set(true, 50, 50, 25);
+ }, [](const SkCanvas& canvas) {
+ SkClipStack::Iter it(*canvas.getClipStack(), SkClipStack::Iter::kBottom_IterStart);
+ const SkClipStack::Element *top = it.next();
+ ASSERT_NE(nullptr, top);
+ SkPath clip;
+ top->asPath(&clip);
+ SkRect rect;
+ EXPECT_TRUE(clip.isOval(&rect));
+ EXPECT_EQ(SkRect::MakeLTRB(25, 25, 75, 75), rect);
+ });
+}
+
+TEST(RenderNodeDrawable, renderPropOutlineClip) {
+ testProperty([](RenderProperties& properties) {
+ properties.mutableOutline().setShouldClip(true);
+ properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
+ }, [](const SkCanvas& canvas) {
+ SkClipStack::Iter it(*canvas.getClipStack(), SkClipStack::Iter::kBottom_IterStart);
+ const SkClipStack::Element *top = it.next();
+ ASSERT_NE(nullptr, top);
+ SkPath clip;
+ top->asPath(&clip);
+ SkRRect rrect;
+ EXPECT_TRUE(clip.isRRect(&rrect));
+ EXPECT_EQ(SkRRect::MakeRectXY(SkRect::MakeLTRB(10, 20, 30, 40), 5.0f, 5.0f), rrect);
+ });
+}
+
+TEST(RenderNodeDrawable, renderPropTransform) {
+ testProperty([](RenderProperties& properties) {
+ properties.setLeftTopRightBottom(10, 10, 110, 110);
+
+ SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
+ properties.setStaticMatrix(&staticMatrix);
+
+ // ignored, since static overrides animation
+ SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
+ properties.setAnimationMatrix(&animationMatrix);
+
+ properties.setTranslationX(10);
+ properties.setTranslationY(20);
+ properties.setScaleX(0.5f);
+ properties.setScaleY(0.7f);
+ }, [](const SkCanvas& canvas) {
+ Matrix4 matrix;
+ matrix.loadTranslate(10, 10, 0); // left, top
+ matrix.scale(1.2f, 1.2f, 1); // static matrix
+ // ignore animation matrix, since static overrides it
+
+ // translation xy
+ matrix.translate(10, 20);
+
+ // scale xy (from default pivot - center)
+ matrix.translate(50, 50);
+ matrix.scale(0.5f, 0.7f, 1);
+ matrix.translate(-50, -50);
+ Matrix4 actual(canvas.getTotalMatrix());
+ EXPECT_MATRIX_APPROX_EQ(matrix, actual)
+ << "Op draw matrix must match expected combination of transformation properties";
+ });
+}
diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
index 0d26df203f02..8312bda8d67d 100644
--- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
+++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
@@ -26,7 +26,7 @@
using namespace android;
using namespace android::uirenderer;
-RENDERTHREAD_TEST(TextDropShadowCache, addRemove) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) {
SkPaint paint;
paint.setTextSize(20);
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 409a12d37693..cea84c057b63 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -15,19 +15,16 @@
*/
#include "gtest/gtest.h"
+#include "gmock/gmock.h"
#include "Caches.h"
+#include "debug/GlesDriver.h"
+#include "debug/NullGlesDriver.h"
+#include "hwui/Typeface.h"
#include "thread/TaskManager.h"
-#include "tests/common/TestUtils.h"
+#include "tests/common/LeakChecker.h"
-#include <memunreachable/memunreachable.h>
-
-#include <cstdio>
-#include <iostream>
-#include <map>
-#include <unordered_set>
#include <signal.h>
-#include <unistd.h>
using namespace std;
using namespace android;
@@ -54,66 +51,12 @@ static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) {
raise(sig);
}
-static void logUnreachable(initializer_list<UnreachableMemoryInfo> infolist) {
- // merge them all
- UnreachableMemoryInfo merged;
- unordered_set<uintptr_t> addrs;
- merged.allocation_bytes = 0;
- merged.leak_bytes = 0;
- merged.num_allocations = 0;
- merged.num_leaks = 0;
- for (auto& info : infolist) {
- // We'll be a little hazzy about these ones and just hope the biggest
- // is the most accurate
- merged.allocation_bytes = max(merged.allocation_bytes, info.allocation_bytes);
- merged.num_allocations = max(merged.num_allocations, info.num_allocations);
- for (auto& leak : info.leaks) {
- if (addrs.find(leak.begin) == addrs.end()) {
- merged.leaks.push_back(leak);
- merged.num_leaks++;
- merged.leak_bytes += leak.size;
- addrs.insert(leak.begin);
- }
- }
- }
-
- // Now log the result
- if (merged.num_leaks) {
- cout << endl << "Leaked memory!" << endl;
- if (!merged.leaks[0].backtrace.num_frames) {
- cout << "Re-run with 'setprop libc.debug.malloc.program hwui_unit_test'"
- << endl << "and 'setprop libc.debug.malloc.options backtrace=8'"
- << " to get backtraces" << endl;
- }
- cout << merged.ToString(false);
- }
-}
-
-static void checkForLeaks() {
- // TODO: Until we can shutdown the RT thread we need to do this in
- // two passes as GetUnreachableMemory has limited insight into
- // thread-local caches so some leaks will not be properly tagged as leaks
- nsecs_t before = systemTime();
- UnreachableMemoryInfo rtMemInfo;
- TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) {
- if (Caches::hasInstance()) {
- Caches::getInstance().tasks.stop();
- }
- // Check for leaks
- if (!GetUnreachableMemory(rtMemInfo)) {
- cerr << "Failed to get unreachable memory!" << endl;
- return;
- }
- });
- UnreachableMemoryInfo uiMemInfo;
- if (!GetUnreachableMemory(uiMemInfo)) {
- cerr << "Failed to get unreachable memory!" << endl;
- return;
+class TypefaceEnvironment : public testing::Environment {
+public:
+ virtual void SetUp() {
+ Typeface::setRobotoTypefaceForTest();
}
- logUnreachable({rtMemInfo, uiMemInfo});
- nsecs_t after = systemTime();
- cout << "Leak check took " << ns2ms(after - before) << "ms" << endl;
-}
+};
int main(int argc, char* argv[]) {
// Register a crash handler
@@ -127,10 +70,17 @@ int main(int argc, char* argv[]) {
gSigChain.insert(pair<int, struct sigaction>(sig, old_sa));
}
+ // Replace the default GLES driver
+ debug::GlesDriver::replace(std::make_unique<debug::NullGlesDriver>());
+
// Run the tests
testing::InitGoogleTest(&argc, argv);
+ testing::InitGoogleMock(&argc, argv);
+
+ testing::AddGlobalTestEnvironment(new TypefaceEnvironment());
+
int ret = RUN_ALL_TESTS();
- checkForLeaks();
+ test::LeakChecker::checkForLeaks();
return ret;
}
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index b5157f401438..f9cc46d1cf30 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -16,6 +16,8 @@
#ifndef COLOR_H
#define COLOR_H
+#include <math.h>
+
#include <SkColor.h>
namespace android {
@@ -80,6 +82,42 @@ namespace uirenderer {
};
static constexpr int BrightColorsCount = sizeof(BrightColors) / sizeof(Color::Color);
+ // Opto-electronic conversion function for the sRGB color space
+ // Takes a gamma-encoded sRGB value and converts it to a linear sRGB value
+ static constexpr float OECF_sRGB(float linear) {
+ // IEC 61966-2-1:1999
+ return linear <= 0.0031308f ?
+ linear * 12.92f : (powf(linear, 1.0f / 2.4f) * 1.055f) - 0.055f;
+ }
+
+ // Opto-electronic conversion function for the sRGB color space
+ // Takes a gamma-encoded sRGB value and converts it to a linear sRGB value
+ // This function returns the input unmodified if linear blending is not enabled
+ static constexpr float OECF(float linear) {
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
+ return OECF_sRGB(linear);
+#else
+ return linear;
+#endif
+ }
+
+ // Electro-optical conversion function for the sRGB color space
+ // Takes a linear sRGB value and converts it to a gamma-encoded sRGB value
+ static constexpr float EOCF_sRGB(float srgb) {
+ // IEC 61966-2-1:1999
+ return srgb <= 0.04045f ? srgb / 12.92f : powf((srgb + 0.055f) / 1.055f, 2.4f);
+ }
+
+ // Electro-optical conversion function for the sRGB color space
+ // Takes a linear sRGB value and converts it to a gamma-encoded sRGB value
+ // This function returns the input unmodified if linear blending is not enabled
+ static constexpr float EOCF(float srgb) {
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
+ return EOCF_sRGB(srgb);
+#else
+ return srgb;
+#endif
+ }
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index f8dfe1040063..c12747805293 100644
--- a/libs/hwui/utils/GLUtils.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -27,7 +27,7 @@ namespace uirenderer {
#if DEBUG_OPENGL
#define GL_CHECKPOINT(LEVEL) \
do { if (DEBUG_OPENGL >= DEBUG_LEVEL_##LEVEL) {\
- LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\
+ LOG_ALWAYS_FATAL_IF(android::uirenderer::GLUtils::dumpGLErrors(),\
"GL errors! %s:%d", __FILE__, __LINE__);\
} } while (0)
#else
diff --git a/libs/hwui/utils/NinePatch.h b/libs/hwui/utils/NinePatch.h
deleted file mode 100644
index 323e56312fb7..000000000000
--- a/libs/hwui/utils/NinePatch.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifndef ANDROID_GRAPHICS_NINEPATCH_H
-#define ANDROID_GRAPHICS_NINEPATCH_H
-
-#include <androidfw/ResourceTypes.h>
-#include <cutils/compiler.h>
-
-#include "SkCanvas.h"
-#include "SkRegion.h"
-
-namespace android {
-
-class ANDROID_API NinePatch {
-public:
- static void Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap,
- const Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion);
-};
-
-} // namespace android
-
-#endif // ANDROID_GRAPHICS_NINEPATCH_H
diff --git a/libs/hwui/utils/NinePatchImpl.cpp b/libs/hwui/utils/NinePatchImpl.cpp
deleted file mode 100644
index 985f3fb66814..000000000000
--- a/libs/hwui/utils/NinePatchImpl.cpp
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include "utils/NinePatch.h"
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkColorPriv.h"
-#include "SkNinePatch.h"
-#include "SkPaint.h"
-#include "SkUnPreMultiply.h"
-
-#include <utils/Log.h>
-
-namespace android {
-
-static const bool kUseTrace = true;
-static bool gTrace = false;
-
-static bool getColor(const SkBitmap& bitmap, int x, int y, SkColor* c) {
- switch (bitmap.colorType()) {
- case kN32_SkColorType:
- *c = SkUnPreMultiply::PMColorToColor(*bitmap.getAddr32(x, y));
- break;
- case kRGB_565_SkColorType:
- *c = SkPixel16ToPixel32(*bitmap.getAddr16(x, y));
- break;
- case kARGB_4444_SkColorType:
- *c = SkUnPreMultiply::PMColorToColor(
- SkPixel4444ToPixel32(*bitmap.getAddr16(x, y)));
- break;
- case kIndex_8_SkColorType: {
- SkColorTable* ctable = bitmap.getColorTable();
- *c = SkUnPreMultiply::PMColorToColor(
- (*ctable)[*bitmap.getAddr8(x, y)]);
- break;
- }
- default:
- return false;
- }
- return true;
-}
-
-static SkColor modAlpha(SkColor c, int alpha) {
- int scale = alpha + (alpha >> 7);
- int a = SkColorGetA(c) * scale >> 8;
- return SkColorSetA(c, a);
-}
-
-static void drawStretchyPatch(SkCanvas* canvas, SkIRect& src, const SkRect& dst,
- const SkBitmap& bitmap, const SkPaint& paint,
- SkColor initColor, uint32_t colorHint,
- bool hasXfer) {
- if (colorHint != android::Res_png_9patch::NO_COLOR) {
- ((SkPaint*)&paint)->setColor(modAlpha(colorHint, paint.getAlpha()));
- canvas->drawRect(dst, paint);
- ((SkPaint*)&paint)->setColor(initColor);
- } else if (src.width() == 1 && src.height() == 1) {
- SkColor c;
- if (!getColor(bitmap, src.fLeft, src.fTop, &c)) {
- goto SLOW_CASE;
- }
- if (0 != c || hasXfer) {
- SkColor prev = paint.getColor();
- ((SkPaint*)&paint)->setColor(c);
- canvas->drawRect(dst, paint);
- ((SkPaint*)&paint)->setColor(prev);
- }
- } else {
- SLOW_CASE:
- canvas->drawBitmapRect(bitmap, SkRect::Make(src), dst, &paint);
- }
-}
-
-SkScalar calculateStretch(SkScalar boundsLimit, SkScalar startingPoint,
- int srcSpace, int numStrechyPixelsRemaining,
- int numFixedPixelsRemaining) {
- SkScalar spaceRemaining = boundsLimit - startingPoint;
- SkScalar stretchySpaceRemaining =
- spaceRemaining - SkIntToScalar(numFixedPixelsRemaining);
- return srcSpace * stretchySpaceRemaining / numStrechyPixelsRemaining;
-}
-
-void NinePatch::Draw(SkCanvas* canvas, const SkRect& bounds,
- const SkBitmap& bitmap, const Res_png_9patch& chunk,
- const SkPaint* paint, SkRegion** outRegion) {
- if (canvas && canvas->quickReject(bounds)) {
- return;
- }
-
- SkPaint defaultPaint;
- if (NULL == paint) {
- // matches default dither in NinePatchDrawable.java.
- defaultPaint.setDither(true);
- paint = &defaultPaint;
- }
-
- const int32_t* xDivs = chunk.getXDivs();
- const int32_t* yDivs = chunk.getYDivs();
- // if our SkCanvas were back by GL we should enable this and draw this as
- // a mesh, which will be faster in most cases.
- if ((false)) {
- SkNinePatch::DrawMesh(canvas, bounds, bitmap,
- xDivs, chunk.numXDivs,
- yDivs, chunk.numYDivs,
- paint);
- return;
- }
-
- if (kUseTrace) {
- gTrace = true;
- }
-
- SkASSERT(canvas || outRegion);
-
- if (kUseTrace) {
- if (canvas) {
- const SkMatrix& m = canvas->getTotalMatrix();
- ALOGV("ninepatch [%g %g %g] [%g %g %g]\n",
- SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]),
- SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5]));
- }
-
- ALOGV("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()),
- SkScalarToFloat(bounds.height()));
- ALOGV("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height());
- ALOGV("======== ninepatch xDivs [%d,%d]\n", xDivs[0], xDivs[1]);
- ALOGV("======== ninepatch yDivs [%d,%d]\n", yDivs[0], yDivs[1]);
- }
-
- if (bounds.isEmpty() ||
- bitmap.width() == 0 || bitmap.height() == 0 ||
- (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0))
- {
- if (kUseTrace) {
- ALOGV("======== abort ninepatch draw\n");
- }
- return;
- }
-
- // should try a quick-reject test before calling lockPixels
-
- SkAutoLockPixels alp(bitmap);
- // after the lock, it is valid to check getPixels()
- if (bitmap.getPixels() == NULL)
- return;
-
- const bool hasXfer = paint->getXfermode() != NULL;
- SkRect dst;
- SkIRect src;
-
- const int32_t x0 = xDivs[0];
- const int32_t y0 = yDivs[0];
- const SkColor initColor = ((SkPaint*)paint)->getColor();
- const uint8_t numXDivs = chunk.numXDivs;
- const uint8_t numYDivs = chunk.numYDivs;
- int i;
- int j;
- int colorIndex = 0;
- uint32_t color;
- bool xIsStretchable;
- const bool initialXIsStretchable = (x0 == 0);
- bool yIsStretchable = (y0 == 0);
- const int bitmapWidth = bitmap.width();
- const int bitmapHeight = bitmap.height();
-
- // Number of bytes needed for dstRights array.
- // Need to cast numXDivs to a larger type to avoid overflow.
- const size_t dstBytes = ((size_t) numXDivs + 1) * sizeof(SkScalar);
- SkScalar* dstRights = (SkScalar*) alloca(dstBytes);
- bool dstRightsHaveBeenCached = false;
-
- int numStretchyXPixelsRemaining = 0;
- for (i = 0; i < numXDivs; i += 2) {
- numStretchyXPixelsRemaining += xDivs[i + 1] - xDivs[i];
- }
- int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining;
- int numStretchyYPixelsRemaining = 0;
- for (i = 0; i < numYDivs; i += 2) {
- numStretchyYPixelsRemaining += yDivs[i + 1] - yDivs[i];
- }
- int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining;
-
- if (kUseTrace) {
- ALOGV("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n",
- bitmap.width(), bitmap.height(),
- SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
- SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()),
- numXDivs, numYDivs);
- }
-
- src.fTop = 0;
- dst.fTop = bounds.fTop;
- // The first row always starts with the top being at y=0 and the bottom
- // being either yDivs[1] (if yDivs[0]=0) or yDivs[0]. In the former case
- // the first row is stretchable along the Y axis, otherwise it is fixed.
- // The last row always ends with the bottom being bitmap.height and the top
- // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
- // yDivs[numYDivs-1]. In the former case the last row is stretchable along
- // the Y axis, otherwise it is fixed.
- //
- // The first and last columns are similarly treated with respect to the X
- // axis.
- //
- // The above is to help explain some of the special casing that goes on the
- // code below.
-
- // The initial yDiv and whether the first row is considered stretchable or
- // not depends on whether yDiv[0] was zero or not.
- for (j = yIsStretchable ? 1 : 0;
- j <= numYDivs && src.fTop < bitmapHeight;
- j++, yIsStretchable = !yIsStretchable) {
- src.fLeft = 0;
- dst.fLeft = bounds.fLeft;
- if (j == numYDivs) {
- src.fBottom = bitmapHeight;
- dst.fBottom = bounds.fBottom;
- } else {
- src.fBottom = yDivs[j];
- const int srcYSize = src.fBottom - src.fTop;
- if (yIsStretchable) {
- dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop,
- srcYSize,
- numStretchyYPixelsRemaining,
- numFixedYPixelsRemaining);
- numStretchyYPixelsRemaining -= srcYSize;
- } else {
- dst.fBottom = dst.fTop + SkIntToScalar(srcYSize);
- numFixedYPixelsRemaining -= srcYSize;
- }
- }
-
- xIsStretchable = initialXIsStretchable;
- // The initial xDiv and whether the first column is considered
- // stretchable or not depends on whether xDiv[0] was zero or not.
- const uint32_t* colors = chunk.getColors();
- for (i = xIsStretchable ? 1 : 0;
- i <= numXDivs && src.fLeft < bitmapWidth;
- i++, xIsStretchable = !xIsStretchable) {
- color = colors[colorIndex++];
- if (i == numXDivs) {
- src.fRight = bitmapWidth;
- dst.fRight = bounds.fRight;
- } else {
- src.fRight = xDivs[i];
- if (dstRightsHaveBeenCached) {
- dst.fRight = dstRights[i];
- } else {
- const int srcXSize = src.fRight - src.fLeft;
- if (xIsStretchable) {
- dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft,
- srcXSize,
- numStretchyXPixelsRemaining,
- numFixedXPixelsRemaining);
- numStretchyXPixelsRemaining -= srcXSize;
- } else {
- dst.fRight = dst.fLeft + SkIntToScalar(srcXSize);
- numFixedXPixelsRemaining -= srcXSize;
- }
- dstRights[i] = dst.fRight;
- }
- }
- // If this horizontal patch is too small to be displayed, leave
- // the destination left edge where it is and go on to the next patch
- // in the source.
- if (src.fLeft >= src.fRight) {
- src.fLeft = src.fRight;
- continue;
- }
- // Make sure that we actually have room to draw any bits
- if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) {
- goto nextDiv;
- }
- // If this patch is transparent, skip and don't draw.
- if (color == android::Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) {
- if (outRegion) {
- if (*outRegion == NULL) {
- *outRegion = new SkRegion();
- }
- SkIRect idst;
- dst.round(&idst);
- //ALOGI("Adding trans rect: (%d,%d)-(%d,%d)\n",
- // idst.fLeft, idst.fTop, idst.fRight, idst.fBottom);
- (*outRegion)->op(idst, SkRegion::kUnion_Op);
- }
- goto nextDiv;
- }
- if (canvas) {
- if (kUseTrace) {
- ALOGV("-- src [%d %d %d %d] dst [%g %g %g %g]\n",
- src.fLeft, src.fTop, src.width(), src.height(),
- SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop),
- SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height()));
- if (2 == src.width() && SkIntToScalar(5) == dst.width()) {
- ALOGV("--- skip patch\n");
- }
- }
- drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor,
- color, hasXfer);
- }
-
-nextDiv:
- src.fLeft = src.fRight;
- dst.fLeft = dst.fRight;
- }
- src.fTop = src.fBottom;
- dst.fTop = dst.fBottom;
- dstRightsHaveBeenCached = true;
- }
-}
-
-} // namespace android
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index 4faab9a5f648..845a3eac35f8 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -21,7 +21,6 @@
#include <SkColorFilter.h>
#include <SkDrawLooper.h>
#include <SkShader.h>
-#include <SkXfermode.h>
namespace android {
namespace uirenderer {
@@ -33,18 +32,6 @@ namespace uirenderer {
class PaintUtils {
public:
- /**
- * Safely retrieves the mode from the specified xfermode. If the specified
- * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode.
- */
- static inline SkXfermode::Mode getXfermode(SkXfermode* mode) {
- SkXfermode::Mode resultMode;
- if (!SkXfermode::AsMode(mode, &resultMode)) {
- resultMode = SkXfermode::kSrcOver_Mode;
- }
- return resultMode;
- }
-
static inline GLenum getFilter(const SkPaint* paint) {
if (!paint || paint->getFilterQuality() != kNone_SkFilterQuality) {
return GL_LINEAR;
@@ -56,7 +43,7 @@ public:
static inline bool paintWillNotDraw(const SkPaint& paint) {
return paint.getAlpha() == 0
&& !paint.getColorFilter()
- && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
+ && paint.getBlendMode() == SkBlendMode::kSrcOver;
}
// TODO: move to a method on android:Paint? replace with SkPaint::nothingToDraw()?
@@ -64,7 +51,7 @@ public:
return paint.getAlpha() == 0
&& paint.getLooper() == nullptr
&& !paint.getColorFilter()
- && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
+ && paint.getBlendMode() == SkBlendMode::kSrcOver;
}
static bool isOpaquePaint(const SkPaint* paint) {
@@ -77,9 +64,9 @@ public:
}
// Only let simple srcOver / src blending modes declare opaque, since behavior is clear.
- SkXfermode::Mode mode = getXfermode(paint->getXfermode());
- return mode == SkXfermode::Mode::kSrcOver_Mode
- || mode == SkXfermode::Mode::kSrc_Mode;
+ SkBlendMode mode = paint->getBlendMode();
+ return mode == SkBlendMode::kSrcOver
+ || mode == SkBlendMode::kSrc;
}
static bool isBlendedShader(const SkShader* shader) {
@@ -121,8 +108,8 @@ public:
return getTextShadow(paint, nullptr);
}
- static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
- return paint ? getXfermode(paint->getXfermode()) : SkXfermode::kSrcOver_Mode;
+ static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) {
+ return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver;
}
static inline int getAlphaDirect(const SkPaint* paint) {
diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h
index 5add95711f2d..af5d10f8522b 100644
--- a/libs/hwui/utils/StringUtils.h
+++ b/libs/hwui/utils/StringUtils.h
@@ -16,10 +16,14 @@
#ifndef STRING_UTILS_H
#define STRING_UTILS_H
+#include <iomanip>
+#include <iostream>
+#include <ostream>
+#include <sstream>
#include <string>
#include <unordered_set>
-#include <ostream>
-#include <iomanip>
+
+#include <utils/Log.h>
namespace android {
namespace uirenderer {
@@ -51,6 +55,22 @@ struct SizePrinter {
}
};
+class LogcatStream: public std::ostream {
+ class LogcatStreamBuf: public std::stringbuf {
+ virtual int sync() {
+ ALOGD("%s", str().c_str());
+ str("");
+ return 0;
+ }
+ };
+
+ LogcatStreamBuf buffer;
+public:
+ LogcatStream()
+ :std::ostream(&buffer) {
+ }
+};
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index b3195c4bbbfb..8b80d690b39f 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -16,7 +16,6 @@
#include "TestWindowContext.h"
#include "AnimationContext.h"
-#include "DisplayListCanvas.h"
#include "IContextFactory.h"
#include "RecordingCanvas.h"
#include "RenderNode.h"
@@ -28,6 +27,7 @@
#include "gui/Surface.h"
#include "renderthread/RenderProxy.h"
+#include <cutils/memory.h>
namespace {
@@ -57,7 +57,7 @@ class TestWindowContext::TestWindowData {
public:
- TestWindowData(SkISize size) : mSize(size) {
+ explicit TestWindowData(SkISize size) : mSize(size) {
android::BufferQueue::createBufferQueue(&mProducer, &mConsumer);
mCpuConsumer = new android::CpuConsumer(mConsumer, 1);
mCpuConsumer->setName(android::String8("TestWindowContext"));
@@ -86,20 +86,14 @@ public:
mProxy->initialize(mAndroidSurface.get());
float lightX = mSize.width() / 2.0f;
android::uirenderer::Vector3 lightVector { lightX, -200.0f, 800.0f };
- mProxy->setup(mSize.width(), mSize.height(), 800.0f,
- 255 * 0.075f, 255 * 0.15f);
+ mProxy->setup(800.0f, 255 * 0.075f, 255 * 0.15f);
mProxy->setLightCenter(lightVector);
-#if HWUI_NEW_OPS
mCanvas.reset(new android::uirenderer::RecordingCanvas(mSize.width(), mSize.height()));
-#else
- mCanvas.reset(new android::uirenderer::DisplayListCanvas(mSize.width(), mSize.height()));
-#endif
}
SkCanvas* prepareToDraw() {
//mCanvas->reset(mSize.width(), mSize.height());
- mCanvas->clipRect(0, 0, mSize.width(), mSize.height(),
- SkRegion::Op::kReplace_Op);
+ mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), SkClipOp::kReplace);
return mCanvas->asSkCanvas();
}
@@ -115,12 +109,13 @@ public:
}
bool capturePixels(SkBitmap* bmp) {
+ sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
SkImageInfo destinationConfig =
SkImageInfo::Make(mSize.width(), mSize.height(),
- kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType, colorSpace);
bmp->allocPixels(destinationConfig);
- sk_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED,
- mSize.width() * mSize.height());
+ android_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED,
+ mSize.width() * mSize.height() * 4);
android::CpuConsumer::LockedBuffer nativeBuffer;
android::status_t retval = mCpuConsumer->lockNextBuffer(&nativeBuffer);
@@ -171,11 +166,7 @@ private:
std::unique_ptr<android::uirenderer::RenderNode> mRootNode;
std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy;
-#if HWUI_NEW_OPS
std::unique_ptr<android::uirenderer::RecordingCanvas> mCanvas;
-#else
- std::unique_ptr<android::uirenderer::DisplayListCanvas> mCanvas;
-#endif
android::sp<android::IGraphicBufferProducer> mProducer;
android::sp<android::IGraphicBufferConsumer> mConsumer;
android::sp<android::CpuConsumer> mCpuConsumer;
diff --git a/libs/hwui/utils/VectorDrawableUtils.cpp b/libs/hwui/utils/VectorDrawableUtils.cpp
index ca75c5945b7f..6f0c96db4b1e 100644
--- a/libs/hwui/utils/VectorDrawableUtils.cpp
+++ b/libs/hwui/utils/VectorDrawableUtils.cpp
@@ -198,12 +198,12 @@ static void drawArc(SkPath* p,
/* Solve for intersecting unit circles */
double dsq = dx * dx + dy * dy;
if (dsq == 0.0) {
- ALOGW("Points are coincident");
+ VECTOR_DRAWABLE_LOGD("Points are coincident");
return; /* Points are coincident */
}
double disc = 1.0 / dsq - 1.0 / 4.0;
if (disc < 0.0) {
- ALOGW("Points are too far apart %f", dsq);
+ VECTOR_DRAWABLE_LOGD("Points are too far apart %f", dsq);
float adjust = (float) (sqrt(dsq) / 1.99999);
drawArc(p, x0, y0, x1, y1, a * adjust,
b * adjust, theta, isMoreThanHalf, isPositiveArc);
diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk
new file mode 100644
index 000000000000..439e86deba5d
--- /dev/null
+++ b/libs/incident/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libincident
+
+LOCAL_CFLAGS := \
+ -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ liblog \
+ libutils
+
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/../../core/java
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+
+LOCAL_SRC_FILES := \
+ ../../core/java/android/os/IIncidentManager.aidl \
+ ../../core/java/android/os/IIncidentReportCompletedListener.aidl \
+ ../../core/java/android/os/IIncidentReportStatusListener.aidl \
+ src/IncidentReportArgs.cpp
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
new file mode 100644
index 000000000000..956ef6c39b99
--- /dev/null
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_DUMPSTATE_ARGS_H_
+#define ANDROID_OS_DUMPSTATE_ARGS_H_
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+
+#include <set>
+#include <vector>
+
+namespace android {
+namespace os {
+
+using namespace std;
+
+class IncidentReportArgs : public Parcelable {
+public:
+ IncidentReportArgs();
+ explicit IncidentReportArgs(const IncidentReportArgs& that);
+ virtual ~IncidentReportArgs();
+
+ virtual status_t writeToParcel(Parcel* out) const;
+ virtual status_t readFromParcel(const Parcel* in);
+
+ void setAll(bool all);
+ void addSection(int section);
+ void addHeader(const vector<int8_t>& header);
+
+ inline bool all() const { return mAll; };
+ bool containsSection(int section) const;
+
+ inline const set<int>& sections() const { return mSections; }
+ inline const vector<vector<int8_t>>& headers() const { return mHeaders; }
+
+ void merge(const IncidentReportArgs& that);
+
+private:
+ set<int> mSections;
+ vector<vector<int8_t>> mHeaders;
+ bool mAll;
+};
+
+}
+}
+
+#endif // ANDROID_OS_DUMPSTATE_ARGS_H_
diff --git a/libs/incident/proto/android/privacy.proto b/libs/incident/proto/android/privacy.proto
new file mode 100644
index 000000000000..ae5af0e4afa7
--- /dev/null
+++ b/libs/incident/proto/android/privacy.proto
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+option java_package = "android";
+option java_multiple_files = true;
+
+import "google/protobuf/descriptor.proto";
+
+package android;
+
+// TODO: It's better to track this by semantic types and set policy for those.
+// Do this for now to bootstrap the tools.
+enum Destination {
+ // Fields or messages annotated with DEST_LOCAL must never be
+ // extracted from the device automatically. They can be accessed
+ // by tools on the developer's workstation, and if they are sent
+ // to another device that must be by the user, with a PII warning. (TBD)
+ DEST_LOCAL = 0;
+
+ // Fields or messages annotated with DEST_EXPLICIT can be sent
+ // off the device with an explicit user action.
+ DEST_EXPLICIT = 1;
+
+ // Fields or messages annotated with DEST_LOCAL can be sent by
+ // automatic means, without per-sending user consent. The user
+ // still must have previously accepted a consent to share this
+ // information.
+ DEST_AUTOMATIC = 2;
+
+ // There is no more permissive option than DEST_AUTOMATIC.
+}
+
+message PrivacyFlags {
+ optional Destination dest = 1 [
+ default = DEST_LOCAL
+ ];
+}
+
+extend google.protobuf.FieldOptions {
+ // Flags for automatically filtering statistics
+ optional PrivacyFlags privacy = 102672883;
+}
diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp
new file mode 100644
index 000000000000..f60490911aed
--- /dev/null
+++ b/libs/incident/src/IncidentReportArgs.cpp
@@ -0,0 +1,172 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include <android/os/IncidentReportArgs.h>
+
+#include <cutils/log.h>
+
+namespace android {
+namespace os {
+
+IncidentReportArgs::IncidentReportArgs()
+ :mSections(),
+ mAll(false)
+{
+}
+
+IncidentReportArgs::IncidentReportArgs(const IncidentReportArgs& that)
+ :mSections(that.mSections),
+ mHeaders(that.mHeaders),
+ mAll(that.mAll)
+{
+}
+
+IncidentReportArgs::~IncidentReportArgs()
+{
+}
+
+status_t
+IncidentReportArgs::writeToParcel(Parcel* out) const
+{
+ status_t err;
+
+ err = out->writeInt32(mAll);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = out->writeInt32(mSections.size());
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ for (set<int>::const_iterator it=mSections.begin(); it!=mSections.end(); it++) {
+ err = out->writeInt32(*it);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ err = out->writeInt32(mHeaders.size());
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ for (vector<vector<int8_t>>::const_iterator it = mHeaders.begin(); it != mHeaders.end(); it++) {
+ err = out->writeByteVector(*it);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t
+IncidentReportArgs::readFromParcel(const Parcel* in)
+{
+ status_t err;
+
+ int32_t all;
+ err = in->readInt32(&all);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ if (all != 0) {
+ mAll = all;
+ }
+
+ mSections.clear();
+ int32_t sectionCount;
+ err = in->readInt32(&sectionCount);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ for (int i=0; i<sectionCount; i++) {
+ int32_t section;
+ err = in->readInt32(&section);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ mSections.insert(section);
+ }
+
+ int32_t headerCount;
+ err = in->readInt32(&headerCount);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ mHeaders.resize(headerCount);
+ for (int i=0; i<headerCount; i++) {
+ err = in->readByteVector(&mHeaders[i]);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+void
+IncidentReportArgs::setAll(bool all)
+{
+ mAll = all;
+ if (all) {
+ mSections.clear();
+ }
+}
+
+void
+IncidentReportArgs::addSection(int section)
+{
+ if (!mAll) {
+ mSections.insert(section);
+ }
+}
+
+void
+IncidentReportArgs::addHeader(const vector<int8_t>& header)
+{
+ mHeaders.push_back(header);
+}
+
+bool
+IncidentReportArgs::containsSection(int section) const
+{
+ return mAll || mSections.find(section) != mSections.end();
+}
+
+void
+IncidentReportArgs::merge(const IncidentReportArgs& that)
+{
+ if (mAll) {
+ return;
+ } else if (that.mAll) {
+ mAll = true;
+ mSections.clear();
+ } else {
+ for (set<int>::const_iterator it=that.mSections.begin();
+ it!=that.mSections.end(); it++) {
+ mSections.insert(*it);
+ }
+ }
+}
+
+}
+}
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 07bcbd35819d..7c6046789cdc 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -31,7 +31,7 @@
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkPaint.h>
-#include <SkXfermode.h>
+#include <SkBlendMode.h>
#pragma GCC diagnostic pop
namespace android {
@@ -108,7 +108,7 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>&
mLocked.pointerAlpha = 0.0f; // pointer is initially faded
mLocked.pointerSprite = mSpriteController->createSprite();
mLocked.pointerIconChanged = false;
- mLocked.requestedPointerType= mPolicy->getDefaultPointerIconId();
+ mLocked.requestedPointerType = mPolicy->getDefaultPointerIconId();
mLocked.animationFrameIndex = 0;
mLocked.lastFrameUpdatedTime = 0;
@@ -631,7 +631,7 @@ void PointerController::updatePointerLocked() {
if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
if (mLocked.presentation == PRESENTATION_POINTER) {
- if (mLocked.requestedPointerType== mPolicy->getDefaultPointerIconId()) {
+ if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) {
mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
} else {
std::map<int32_t, SpriteIcon>::const_iterator iter =
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 049b76e6986e..4991f0434bc2 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -30,7 +30,6 @@
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkPaint.h>
-#include <SkXfermode.h>
#pragma GCC diagnostic pop
#include <android/native_window.h>
@@ -216,7 +215,7 @@ void SpriteController::doUpdateSprites() {
SkCanvas surfaceCanvas(surfaceBitmap);
SkPaint paint;
- paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ paint.setBlendMode(SkBlendMode::kSrc);
surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
if (outBuffer.width > update.state.icon.bitmap.width()) {
diff --git a/libs/services/Android.mk b/libs/services/Android.mk
new file mode 100644
index 000000000000..cbfd4b3f9f10
--- /dev/null
+++ b/libs/services/Android.mk
@@ -0,0 +1,43 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+# Provides C++ wrappers for system services.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libservices
+LOCAL_SRC_FILES := \
+ ../../core/java/com/android/internal/os/IDropBoxManagerService.aidl \
+ src/os/DropBoxManager.cpp
+
+LOCAL_AIDL_INCLUDES := \
+ $(LOCAL_PATH)/../../core/java
+LOCAL_C_INCLUDES := \
+ system/core/include
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ liblog \
+ libcutils \
+ libutils
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
+
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+
+include $(BUILD_SHARED_LIBRARY)
+
+
diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h
new file mode 100644
index 000000000000..8717178bb7d6
--- /dev/null
+++ b/libs/services/include/android/os/DropBoxManager.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_OS_DROPBOXMANAGER_H
+#define _ANDROID_OS_DROPBOXMANAGER_H
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/Status.h>
+#include <utils/RefBase.h>
+
+#include <vector>
+
+namespace android {
+namespace os {
+
+using namespace android;
+using namespace android::base;
+using namespace android::binder;
+using namespace std;
+
+class DropBoxManager : public virtual RefBase
+{
+public:
+ enum {
+ IS_EMPTY = 1,
+ IS_TEXT = 2,
+ IS_GZIPPED = 4
+ };
+
+ DropBoxManager();
+ virtual ~DropBoxManager();
+
+ static sp<DropBoxManager> create();
+
+ // Create a new entry with plain text contents.
+ Status addText(const String16& tag, const string& text);
+
+ // Create a new Entry with byte array contents. Makes a copy of the data.
+ Status addData(const String16& tag, uint8_t const* data, size_t size, int flags);
+
+ // Create a new Entry from a file. The file will be opened in this process
+ // and a handle will be passed to the system process, so no additional permissions
+ // are required from the system process. Returns NULL if the file can't be opened.
+ Status addFile(const String16& tag, const string& filename, int flags);
+
+ class Entry : public virtual RefBase, public Parcelable {
+ public:
+ Entry();
+ virtual ~Entry();
+
+ virtual status_t writeToParcel(Parcel* out) const;
+ virtual status_t readFromParcel(const Parcel* in);
+
+ private:
+ Entry(const String16& tag, int32_t flags);
+ Entry(const String16& tag, int32_t flags, int fd);
+
+ String16 mTag;
+ int64_t mTimeMillis;
+ int32_t mFlags;
+
+ vector<uint8_t> mData;
+ unique_fd mFd;
+
+ friend class DropBoxManager;
+ };
+
+private:
+ enum {
+ HAS_BYTE_ARRAY = 8
+ };
+
+ Status add(const Entry& entry);
+};
+
+}} // namespace android::os
+
+#endif // _ANDROID_OS_DROPBOXMANAGER_H
+
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
new file mode 100644
index 000000000000..bbb45f022a87
--- /dev/null
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DropBoxManager"
+
+#include <android/os/DropBoxManager.h>
+
+#include <binder/IServiceManager.h>
+#include <com/android/internal/os/IDropBoxManagerService.h>
+#include <cutils/log.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+namespace android {
+namespace os {
+
+using namespace ::com::android::internal::os;
+
+DropBoxManager::Entry::Entry()
+ :mTag(),
+ mTimeMillis(0),
+ mFlags(IS_EMPTY),
+ mData(),
+ mFd()
+{
+ mFlags = IS_EMPTY;
+}
+
+DropBoxManager::Entry::Entry(const String16& tag, int32_t flags)
+ :mTag(tag),
+ mTimeMillis(0),
+ mFlags(flags),
+ mData(),
+ mFd()
+{
+}
+
+DropBoxManager::Entry::Entry(const String16& tag, int32_t flags, int fd)
+ :mTag(tag),
+ mTimeMillis(0),
+ mFlags(flags),
+ mData(),
+ mFd(fd)
+{
+}
+
+DropBoxManager::Entry::~Entry()
+{
+}
+
+status_t
+DropBoxManager::Entry::writeToParcel(Parcel* out) const
+{
+ status_t err;
+
+ err = out->writeString16(mTag);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = out->writeInt64(mTimeMillis);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (mFd.get() != -1) {
+ err = out->writeInt32(mFlags & ~HAS_BYTE_ARRAY); // Clear bit just to be safe
+ if (err != NO_ERROR) {
+ return err;
+ }
+ ALOGD("writing fd %d\n", mFd.get());
+ err = out->writeParcelFileDescriptor(mFd);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ } else {
+ err = out->writeInt32(mFlags | HAS_BYTE_ARRAY);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = out->writeByteVector(mData);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t
+DropBoxManager::Entry::readFromParcel(const Parcel* in)
+{
+ status_t err;
+
+ err = in->readString16(&mTag);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = in->readInt64(&mTimeMillis);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = in->readInt32(&mFlags);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if ((mFlags & HAS_BYTE_ARRAY) != 0) {
+ err = in->readByteVector(&mData);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ mFlags &= ~HAS_BYTE_ARRAY;
+ } else {
+ int fd;
+ fd = in->readParcelFileDescriptor();
+ if (fd == -1) {
+ return EBADF;
+ }
+ fd = dup(fd);
+ if (fd == -1) {
+ return errno;
+ }
+ mFd.reset(fd);
+ }
+
+ return NO_ERROR;
+}
+
+
+DropBoxManager::DropBoxManager()
+{
+}
+
+DropBoxManager::~DropBoxManager()
+{
+}
+
+Status
+DropBoxManager::addText(const String16& tag, const string& text)
+{
+ Entry entry(tag, IS_TEXT);
+ entry.mData.assign(text.c_str(), text.c_str() + text.size());
+ return add(entry);
+}
+
+Status
+DropBoxManager::addData(const String16& tag, uint8_t const* data,
+ size_t size, int flags)
+{
+ Entry entry(tag, flags);
+ entry.mData.assign(data, data+size);
+ return add(entry);
+}
+
+Status
+DropBoxManager::addFile(const String16& tag, const string& filename, int flags)
+{
+ int fd = open(filename.c_str(), O_RDONLY);
+ if (fd == -1) {
+ string message("addFile can't open file: ");
+ message += filename;
+ ALOGW("DropboxManager: %s", message.c_str());
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, message.c_str());
+ }
+
+ Entry entry(tag, flags, fd);
+ return add(entry);
+}
+
+Status
+DropBoxManager::add(const Entry& entry)
+{
+ sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>(
+ defaultServiceManager()->getService(android::String16("dropbox")));
+ if (service == NULL) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service");
+ }
+ return service->add(entry);
+}
+
+}} // namespace android::os
+
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 74638e7eccc3..fa3d8bd0930f 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -553,7 +553,7 @@ public:
}
};
-IMPLEMENT_META_INTERFACE(MountService, "IMountService")
+IMPLEMENT_META_INTERFACE(MountService, "android.os.storage.IStorageManager")
// ----------------------------------------------------------------------
diff --git a/libs/storage/IMountServiceListener.cpp b/libs/storage/IMountServiceListener.cpp
index 11b53fdc1027..033d70d5694f 100644
--- a/libs/storage/IMountServiceListener.cpp
+++ b/libs/storage/IMountServiceListener.cpp
@@ -24,6 +24,20 @@ enum {
TRANSACTION_onStorageStateChanged,
};
+class BpMountServiceListener: public BpInterface<IMountServiceListener> {
+public:
+ explicit BpMountServiceListener(const sp<IBinder>& impl)
+ : BpInterface<IMountServiceListener>(impl) { }
+
+ virtual void onUsbMassStorageConnectionChanged(const bool /* connected */) { }
+ virtual void onStorageStateChanged(const String16& /* path */,
+ const String16& /* oldState */, const String16& /* newState */) { }
+};
+
+IMPLEMENT_META_INTERFACE(MountServiceListener, "android.os.storage.IStorageEventListener")
+
+// ----------------------------------------------------------------------
+
status_t BnMountServiceListener::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/libs/storage/IMountShutdownObserver.cpp b/libs/storage/IMountShutdownObserver.cpp
index a74a768409bc..e5de603cd5ee 100644
--- a/libs/storage/IMountShutdownObserver.cpp
+++ b/libs/storage/IMountShutdownObserver.cpp
@@ -23,6 +23,16 @@ enum {
TRANSACTION_onShutDownComplete = IBinder::FIRST_CALL_TRANSACTION,
};
+class BpMountShutdownObserver: public BpInterface<IMountShutdownObserver> {
+public:
+ explicit BpMountShutdownObserver(const sp<IBinder>& impl)
+ : BpInterface<IMountShutdownObserver>(impl) { }
+
+ virtual void onShutDownComplete(const int32_t /* statusCode */) {}
+};
+
+IMPLEMENT_META_INTERFACE(MountShutdownObserver, "android.os.storage.IStorageShutdownObserver")
+
status_t BnMountShutdownObserver::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/libs/storage/IObbActionListener.cpp b/libs/storage/IObbActionListener.cpp
index a71341bc1364..797393ac6f5b 100644
--- a/libs/storage/IObbActionListener.cpp
+++ b/libs/storage/IObbActionListener.cpp
@@ -34,7 +34,7 @@ public:
const int32_t /* state */) { }
};
-IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener")
+IMPLEMENT_META_INTERFACE(ObbActionListener, "android.os.storage.IObbActionListener")
// ----------------------------------------------------------------------
diff --git a/libs/usb/tests/accessorytest/audio.c b/libs/usb/tests/accessorytest/audio.c
index d23d9b3fa5d3..36ee6b81839c 100644
--- a/libs/usb/tests/accessorytest/audio.c
+++ b/libs/usb/tests/accessorytest/audio.c
@@ -164,7 +164,6 @@ static void* capture_thread(void* arg)
static void* play_thread(void* arg)
{
struct pcm *pcm = arg;
- char *buffer;
int index, err;
fprintf(stderr, "play_thread start\n");
@@ -181,7 +180,6 @@ static void* play_thread(void* arg)
fprintf(stderr, "play_thread done\n");
pcm_close(pcm);
- free(buffer);
return NULL;
}