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.bp74
-rw-r--r--libs/androidfw/Android.mk67
-rw-r--r--libs/androidfw/AssetManager.cpp650
-rw-r--r--libs/androidfw/AttributeFinder.h206
-rw-r--r--libs/androidfw/AttributeResolution.cpp477
-rw-r--r--libs/androidfw/ResourceTypes.cpp119
-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/AttributeFinder.h206
-rw-r--r--libs/androidfw/include/androidfw/AttributeResolution.h56
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h9
-rw-r--r--libs/androidfw/include/androidfw/TypeWrappers.h1
-rw-r--r--libs/androidfw/tests/Android.mk13
-rw-r--r--libs/androidfw/tests/AttributeFinder_test.cpp166
-rw-r--r--libs/androidfw/tests/AttributeResolution_test.cpp209
-rw-r--r--libs/androidfw/tests/ConfigLocale_test.cpp46
-rw-r--r--libs/androidfw/tests/Main.cpp69
-rw-r--r--libs/androidfw/tests/TestHelpers.cpp64
-rw-r--r--libs/androidfw/tests/TestHelpers.h61
-rw-r--r--libs/androidfw/tests/data/.gitignore2
-rw-r--r--libs/androidfw/tests/data/styles/AndroidManifest.xml19
-rw-r--r--libs/androidfw/tests/data/styles/R.h35
-rwxr-xr-xlibs/androidfw/tests/data/styles/build.sh7
-rw-r--r--libs/androidfw/tests/data/styles/layout.xmlbin0 -> 380 bytes
-rw-r--r--libs/androidfw/tests/data/styles/res/layout/layout.xml5
-rw-r--r--libs/androidfw/tests/data/styles/res/values/styles.xml52
-rw-r--r--libs/androidfw/tests/data/styles/resources.arscbin0 -> 1544 bytes
-rw-r--r--libs/hwui/Android.mk156
-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.cpp15
-rw-r--r--libs/hwui/Caches.h22
-rw-r--r--libs/hwui/CanvasState.cpp19
-rw-r--r--libs/hwui/CanvasState.h28
-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.cpp32
-rw-r--r--libs/hwui/DeferredLayerUpdater.h12
-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.cpp36
-rw-r--r--libs/hwui/FontRenderer.h23
-rw-r--r--libs/hwui/FrameBuilder.cpp12
-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/Glop.h17
-rw-r--r--libs/hwui/GlopBuilder.cpp140
-rw-r--r--libs/hwui/GlopBuilder.h26
-rw-r--r--libs/hwui/GradientCache.cpp82
-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.cpp209
-rw-r--r--libs/hwui/Layer.h256
-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)198
-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.cpp35
-rw-r--r--libs/hwui/PatchCache.h11
-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.h35
-rw-r--r--libs/hwui/ProgramCache.cpp194
-rw-r--r--libs/hwui/ProgramCache.h3
-rw-r--r--libs/hwui/Properties.cpp24
-rw-r--r--libs/hwui/Properties.h26
-rw-r--r--libs/hwui/PropertyValuesHolder.cpp23
-rw-r--r--libs/hwui/Readback.h16
-rw-r--r--libs/hwui/RecordedOp.h33
-rw-r--r--libs/hwui/RecordingCanvas.cpp55
-rw-r--r--libs/hwui/RecordingCanvas.h36
-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.cpp553
-rw-r--r--libs/hwui/SkiaCanvas.h193
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp95
-rw-r--r--libs/hwui/SkiaCanvasProxy.h8
-rw-r--r--libs/hwui/SkiaShader.cpp46
-rw-r--r--libs/hwui/SkiaShader.h6
-rw-r--r--libs/hwui/Snapshot.cpp86
-rw-r--r--libs/hwui/Snapshot.h67
-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.cpp164
-rw-r--r--libs/hwui/Texture.h56
-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.cpp60
-rw-r--r--libs/hwui/VectorDrawable.h24
-rw-r--r--libs/hwui/Vertex.h17
-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/FontUtil.h4
-rw-r--r--libs/hwui/hwui/Bitmap.cpp499
-rw-r--r--libs/hwui/hwui/Bitmap.h148
-rw-r--r--libs/hwui/hwui/Canvas.cpp40
-rw-r--r--libs/hwui/hwui/Canvas.h56
-rw-r--r--libs/hwui/hwui/MinikinSkia.cpp40
-rw-r--r--libs/hwui/hwui/MinikinSkia.h22
-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.cpp102
-rw-r--r--libs/hwui/hwui/Typeface.h9
-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.cpp113
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.h59
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp65
-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.cpp193
-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.cpp335
-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.cpp247
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h93
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp159
-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.cpp50
-rw-r--r--libs/hwui/renderstate/RenderState.h3
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp504
-rw-r--r--libs/hwui/renderthread/CanvasContext.h100
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp2
-rw-r--r--libs/hwui/renderthread/EglManager.cpp97
-rw-r--r--libs/hwui/renderthread/EglManager.h15
-rw-r--r--libs/hwui/renderthread/IRenderPipeline.h85
-rw-r--r--libs/hwui/renderthread/OpenGLPipeline.cpp263
-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.cpp675
-rw-r--r--libs/hwui/renderthread/VulkanManager.h171
-rw-r--r--libs/hwui/tests/common/BitmapAllocationTestUtils.h74
-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.cpp74
-rw-r--r--libs/hwui/tests/common/TestUtils.h113
-rw-r--r--libs/hwui/tests/common/scenes/BitmapFillrate.cpp74
-rw-r--r--libs/hwui/tests/common/scenes/BitmapShaders.cpp73
-rw-r--r--libs/hwui/tests/common/scenes/ClippingAnimation.cpp10
-rw-r--r--libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp17
-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.cpp10
-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.cpp24
-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
-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.sh26
-rw-r--r--libs/hwui/tests/unit/BakedOpDispatcherTests.cpp33
-rw-r--r--libs/hwui/tests/unit/BitmapTests.cpp44
-rw-r--r--libs/hwui/tests/unit/CanvasContextTests.cpp50
-rw-r--r--libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp54
-rw-r--r--libs/hwui/tests/unit/FrameBuilderTests.cpp576
-rw-r--r--libs/hwui/tests/unit/GlopBuilderTests.cpp14
-rw-r--r--libs/hwui/tests/unit/LeakCheckTests.cpp4
-rw-r--r--libs/hwui/tests/unit/MeshStateTests.cpp36
-rw-r--r--libs/hwui/tests/unit/PathInterpolatorTests.cpp110
-rw-r--r--libs/hwui/tests/unit/RecordingCanvasTests.cpp55
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp789
-rw-r--r--libs/hwui/tests/unit/RenderNodeTests.cpp66
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp51
-rw-r--r--libs/hwui/tests/unit/SkiaCanvasTests.cpp2
-rw-r--r--libs/hwui/tests/unit/SkiaDisplayListTests.cpp180
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp231
-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.cpp22
-rw-r--r--libs/hwui/utils/VectorDrawableUtils.cpp4
-rw-r--r--libs/input/PointerController.cpp6
-rw-r--r--libs/input/SpriteController.cpp3
-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
284 files changed, 17524 insertions, 15003 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
new file mode 100644
index 000000000000..d501d251f789
--- /dev/null
+++ b/libs/androidfw/Android.bp
@@ -0,0 +1,74 @@
+// 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.
+
+// libandroidfw is partially built for the host (used by obbtool, aapt, and others)
+
+cc_library {
+ name: "libandroidfw",
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+ srcs: [
+ "Asset.cpp",
+ "AssetDir.cpp",
+ "AssetManager.cpp",
+ "AttributeResolution.cpp",
+ "LocaleData.cpp",
+ "misc.cpp",
+ "ObbFile.cpp",
+ "ResourceTypes.cpp",
+ "StreamingZipInflater.cpp",
+ "TypeWrappers.cpp",
+ "ZipFileRO.cpp",
+ "ZipUtils.cpp",
+ ],
+ export_include_dirs: ["include"],
+ target: {
+ android: {
+ srcs: [
+ "BackupData.cpp",
+ "BackupHelpers.cpp",
+ "CursorWindow.cpp",
+ "DisplayEventDispatcher.cpp",
+ ],
+ shared_libs: [
+ "libziparchive",
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libcutils",
+ "libgui",
+ "libutils",
+ "libz",
+ ],
+ static: {
+ enabled: false,
+ },
+ },
+ host: {
+ cflags: ["-DSTATIC_ANDROIDFW_FOR_TOOLS"],
+ shared: {
+ enabled: false,
+ },
+ shared_libs: ["libz-host"],
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+}
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index 9636ea72f47d..68c51effd79d 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -14,73 +14,6 @@
LOCAL_PATH:= $(call my-dir)
-# libandroidfw is partially built for the host (used by obbtool, aapt, and others)
-# These files are common to host and target builds.
-
-commonSources := \
- Asset.cpp \
- AssetDir.cpp \
- AssetManager.cpp \
- LocaleData.cpp \
- misc.cpp \
- ObbFile.cpp \
- ResourceTypes.cpp \
- StreamingZipInflater.cpp \
- TypeWrappers.cpp \
- ZipFileRO.cpp \
- ZipUtils.cpp
-
-deviceSources := \
- $(commonSources) \
- BackupData.cpp \
- BackupHelpers.cpp \
- CursorWindow.cpp \
- DisplayEventDispatcher.cpp
-
-hostSources := $(commonSources)
-
-# For the host
-# =====================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libandroidfw
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-LOCAL_SRC_FILES:= $(hostSources)
-LOCAL_C_INCLUDES := external/zlib
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
-# For the device
-# =====================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= libandroidfw
-LOCAL_SRC_FILES:= $(deviceSources)
-LOCAL_C_INCLUDES := \
- system/core/include
-LOCAL_SHARED_LIBRARIES := \
- libziparchive \
- libbase \
- libbinder \
- liblog \
- libcutils \
- libgui \
- libutils \
- libz
-
-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)
-
-
# Include subdirectory makefiles
# ============================================================
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/AttributeFinder.h b/libs/androidfw/AttributeFinder.h
new file mode 100644
index 000000000000..f281921824e7
--- /dev/null
+++ b/libs/androidfw/AttributeFinder.h
@@ -0,0 +1,206 @@
+/*
+ * 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_ATTRIBUTE_FINDER_H
+#define ANDROIDFW_ATTRIBUTE_FINDER_H
+
+#include <utils/KeyedVector.h>
+
+#include <stdint.h>
+
+namespace android {
+
+static inline uint32_t get_package(uint32_t attr) { return attr >> 24; }
+
+/**
+ * A helper class to search linearly for the requested
+ * attribute, maintaining it's position and optimizing for
+ * the case that subsequent searches will involve an attribute with
+ * a higher attribute ID.
+ *
+ * In the case that a subsequent attribute has a different package ID,
+ * its resource ID may not be larger than the preceding search, so
+ * back tracking is supported for this case. This
+ * back tracking requirement is mainly for shared library
+ * resources, whose package IDs get assigned at runtime
+ * and thus attributes from a shared library may
+ * be out of order.
+ *
+ * We make two assumptions about the order of attributes:
+ * 1) The input has the same sorting rules applied to it as
+ * the attribute data contained by this class.
+ * 2) Attributes are grouped by package ID.
+ * 3) Among attributes with the same package ID, the attributes are
+ * sorted by increasing resource ID.
+ *
+ * Ex: 02010000, 02010001, 010100f4, 010100f5, 0x7f010001, 07f010003
+ *
+ * The total order of attributes (including package ID) can not be linear
+ * as shared libraries get assigned dynamic package IDs at runtime, which
+ * may break the sort order established at build time.
+ */
+template <typename Derived, typename Iterator>
+class BackTrackingAttributeFinder {
+ public:
+ BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
+
+ Iterator Find(uint32_t attr);
+
+ private:
+ void JumpToClosestAttribute(uint32_t package_id);
+ void MarkCurrentPackageId(uint32_t package_id);
+
+ 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 framework_start_;
+ Iterator app_start_;
+
+ // 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)
+ : 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 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 (current_ == end_) {
+ current_ = largest_;
+ }
+
+ 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 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 (!(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;
+ }
+ 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);
+ }
+ }
+
+ if (current_ > largest_) {
+ // We've moved past the latest attribute we've seen.
+ largest_ = current_;
+ }
+
+ if (attr == prev_attr) {
+ // We found the attribute we were looking for.
+ return current_ - 1;
+ }
+ }
+ return end_;
+}
+
+} // namespace android
+
+#endif // ANDROIDFW_ATTRIBUTE_FINDER_H
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
new file mode 100644
index 000000000000..00f7a42f10e4
--- /dev/null
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -0,0 +1,477 @@
+/*
+ * 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 "AttributeFinder.h"
+
+#include "androidfw/AttributeResolution.h"
+#include "androidfw/ResourceTypes.h"
+
+#include <android/log.h>
+#include <cstdint>
+
+constexpr bool kDebugStyles = false;
+
+namespace android {
+
+class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
+ public:
+ explicit XmlAttributeFinder(const ResXMLParser* parser)
+ : BackTrackingAttributeFinder(0, parser != NULL ? 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 = NULL;
+ 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 != NULL && value.dataType != Res_value::TYPE_NULL) {
+ indices_idx++;
+ out_indices[indices_idx] = ii;
+ }
+
+ out_values += STYLE_NUM_ENTRIES;
+ }
+
+ res.unlock();
+
+ if (out_indices != NULL) {
+ out_indices[0] = indices_idx;
+ }
+ return true;
+}
+
+bool ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_res, 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 != NULL) {
+ 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 = NULL;
+ 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 = NULL;
+ 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 != NULL ? 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 (out_indices != NULL && value.dataType != Res_value::TYPE_NULL) {
+ indices_idx++;
+ out_indices[indices_idx] = ii;
+ }
+
+ out_values += STYLE_NUM_ENTRIES;
+ }
+
+ res.unlock();
+
+ if (out_indices != NULL) {
+ out_indices[0] = indices_idx;
+ }
+ return true;
+}
+
+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 != NULL && value.dataType != Res_value::TYPE_NULL) {
+ indices_idx++;
+ out_indices[indices_idx] = ii;
+ }
+
+ out_values += STYLE_NUM_ENTRIES;
+ }
+
+ res->unlock();
+
+ if (out_indices != NULL) {
+ out_indices[0] = indices_idx;
+ }
+ return true;
+}
+
+} // namespace android
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index e10db05e8557..907d9145f4ea 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -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));
@@ -5881,12 +5919,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/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/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h
deleted file mode 100644
index acf70565c4f9..000000000000
--- a/libs/androidfw/include/androidfw/AttributeFinder.h
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef H_ATTRIBUTE_FINDER
-#define H_ATTRIBUTE_FINDER
-
-#include <stdint.h>
-#include <utils/KeyedVector.h>
-
-namespace android {
-
-static inline uint32_t getPackage(uint32_t attr) {
- return attr >> 24;
-}
-
-/**
- * A helper class to search linearly for the requested
- * attribute, maintaining it's position and optimizing for
- * the case that subsequent searches will involve an attribute with
- * a higher attribute ID.
- *
- * In the case that a subsequent attribute has a different package ID,
- * its resource ID may not be larger than the preceding search, so
- * back tracking is supported for this case. This
- * back tracking requirement is mainly for shared library
- * resources, whose package IDs get assigned at runtime
- * and thus attributes from a shared library may
- * be out of order.
- *
- * We make two assumptions about the order of attributes:
- * 1) The input has the same sorting rules applied to it as
- * the attribute data contained by this class.
- * 2) Attributes are grouped by package ID.
- * 3) Among attributes with the same package ID, the attributes are
- * sorted by increasing resource ID.
- *
- * Ex: 02010000, 02010001, 010100f4, 010100f5, 0x7f010001, 07f010003
- *
- * The total order of attributes (including package ID) can not be linear
- * as shared libraries get assigned dynamic package IDs at runtime, which
- * may break the sort order established at build time.
- */
-template <typename Derived, typename Iterator>
-class BackTrackingAttributeFinder {
-public:
- BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
-
- Iterator find(uint32_t attr);
-
-private:
- void jumpToClosestAttribute(uint32_t packageId);
- void markCurrentPackageId(uint32_t packageId);
-
- bool mFirstTime;
- Iterator mBegin;
- Iterator mEnd;
- Iterator mCurrent;
- Iterator mLargest;
- uint32_t mLastPackageId;
- uint32_t mCurrentAttr;
-
- // Package Offsets (best-case, fast look-up).
- Iterator mFrameworkStart;
- Iterator mAppStart;
-
- // Worst case, we have shared-library resources.
- KeyedVector<uint32_t, Iterator> mPackageOffsets;
-};
-
-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>
-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;
- }
- }
-
- // 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;
- }
-
- if (mCurrent != mEnd) {
- mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent);
- }
-}
-
-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;
- }
-}
-
-template <typename Derived, typename Iterator>
-Iterator BackTrackingAttributeFinder<Derived, Iterator>::find(uint32_t attr) {
- if (!(mBegin < mEnd)) {
- return mEnd;
- }
-
- 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);
- }
-
- // 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;
- }
-
- // 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;
- }
- }
- return mEnd;
-}
-
-} // namespace android
-
-#endif // H_ATTRIBUTE_FINDER
diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h
new file mode 100644
index 000000000000..3ed8bced22ef
--- /dev/null
+++ b/libs/androidfw/include/androidfw/AttributeResolution.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+
+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);
+
+bool ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_res, uint32_t* attrs, size_t attrs_length, uint32_t* out_values,
+ uint32_t* out_indices);
+
+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/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 12a6b0f9a4ec..08d6591e6886 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>
@@ -1227,7 +1226,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 +1851,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/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 1fe1773578fa..d18cb8f472c7 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -28,6 +28,7 @@ testFiles := \
Config_test.cpp \
ConfigLocale_test.cpp \
Idmap_test.cpp \
+ Main.cpp \
ResTable_test.cpp \
Split_test.cpp \
TestHelpers.cpp \
@@ -40,7 +41,7 @@ androidfw_test_cflags := \
-Werror \
-Wunused \
-Wunreachable-code \
- -Wno-missing-field-initializers \
+ -Wno-missing-field-initializers
# gtest is broken.
androidfw_test_cflags += -Wno-unnamed-type-template-args
@@ -52,13 +53,15 @@ include $(CLEAR_VARS)
LOCAL_MODULE := libandroidfw_tests
LOCAL_CFLAGS := $(androidfw_test_cflags)
-LOCAL_SRC_FILES := $(testFiles)
+LOCAL_SRC_FILES := $(testFiles) AttributeResolution_test.cpp
LOCAL_STATIC_LIBRARIES := \
libandroidfw \
+ libbase \
libutils \
libcutils \
liblog \
- libz \
+ libz
+LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
include $(BUILD_HOST_NATIVE_TEST)
@@ -76,9 +79,11 @@ LOCAL_SRC_FILES := $(testFiles) \
LOCAL_SHARED_LIBRARIES := \
libandroidfw \
+ libbase \
libcutils \
libutils \
- libui \
+ libui
+LOCAL_PICKUP_FILES := $(LOCAL_PATH)/data
include $(BUILD_NATIVE_TEST)
endif # Not SDK_ONLY
diff --git a/libs/androidfw/tests/AttributeFinder_test.cpp b/libs/androidfw/tests/AttributeFinder_test.cpp
index 5054624579ea..d9ed48ebe953 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,105 @@
* limitations under the License.
*/
-#include <androidfw/AttributeFinder.h>
+#include "../AttributeFinder.h"
+#include <android-base/macros.h>
#include <gtest/gtest.h>
using android::BackTrackingAttributeFinder;
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);
- }
-
- ~MockAttributeFinder() {
- delete mAttrs;
- }
-
- inline uint32_t getAttribute(const int index) const {
- return mAttrs[index];
- }
-
-private:
- uint32_t* mAttrs;
-};
+ public:
+ MockAttributeFinder(const uint32_t* attrs, int len) : BackTrackingAttributeFinder(0, len) {
+ attrs_ = new uint32_t[len];
+ memcpy(attrs_, attrs, sizeof(*attrs) * len);
+ }
-static const uint32_t sortedAttributes[] = {
- 0x01010000, 0x01010001, 0x01010002, 0x01010004,
- 0x02010001, 0x02010010, 0x7f010001
-};
+ ~MockAttributeFinder() { delete attrs_; }
-static const uint32_t packageUnsortedAttributes[] = {
- 0x02010001, 0x02010010, 0x01010000, 0x01010001,
- 0x01010002, 0x01010004, 0x7f010001
-};
+ inline uint32_t GetAttribute(const int index) const { return attrs_[index]; }
-static const uint32_t singlePackageAttributes[] = {
- 0x7f010007, 0x7f01000a, 0x7f01000d, 0x00000000
+ private:
+ uint32_t* attrs_;
};
+static const uint32_t kSortedAttributes[] = {0x01010000, 0x01010001, 0x01010002, 0x01010004,
+ 0x02010001, 0x02010010, 0x7f010001};
+
+static const uint32_t kPackageUnsortedAttributes[] = {
+ 0x02010001, 0x02010010, 0x01010000, 0x01010001, 0x01010002, 0x01010004, 0x7f010001};
+
+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));
}
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
new file mode 100644
index 000000000000..d6d7890882a8
--- /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 "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 test_source_dir = GetTestDataPath();
+ std::string contents;
+ CHECK(base::ReadFileToString(test_source_dir + "/styles/resources.arsc",
+ &contents));
+ CHECK(table_.add(contents.data(), contents.size(), 1 /*cookie*/,
+ true /*copyData*/) == NO_ERROR);
+ }
+
+ protected:
+ ResTable table_;
+};
+
+class AttributeResolutionXmlTest : public AttributeResolutionTest {
+ public:
+ virtual void SetUp() override {
+ AttributeResolutionTest::SetUp();
+ std::string test_source_dir = GetTestDataPath();
+ std::string contents;
+ CHECK(base::ReadFileToString(test_source_dir + "/styles/layout.xml",
+ &contents));
+ CHECK(xml_parser_.setTo(contents.data(), contents.size(),
+ true /*copyData*/) == NO_ERROR);
+
+ // 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));
+
+ uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+ R::attr::attr_four};
+ std::vector<uint32_t> values;
+ values.resize(arraysize(attrs) * 6);
+
+ ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/,
+ nullptr /*src_values*/, 0 /*src_values_length*/,
+ attrs, arraysize(attrs), 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) {
+ uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+ R::attr::attr_four};
+ std::vector<uint32_t> values;
+ values.resize(arraysize(attrs) * 6);
+
+ ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs, arraysize(attrs),
+ 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));
+
+ uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+ R::attr::attr_four, R::attr::attr_five};
+ std::vector<uint32_t> values;
+ values.resize(arraysize(attrs) * 6);
+
+ ASSERT_TRUE(ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/,
+ 0 /*def_style_res*/, attrs, arraysize(attrs),
+ values.data(), nullptr /*out_indices*/));
+
+ 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/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 2bf9b12b6ce5..6e998159e554 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/Main.cpp b/libs/androidfw/tests/Main.cpp
new file mode 100644
index 000000000000..6a50691e6bb6
--- /dev/null
+++ b/libs/androidfw/tests/Main.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 <libgen.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+
+#include "android-base/file.h"
+#include "android-base/strings.h"
+#include "gtest/gtest.h"
+
+#include "TestHelpers.h"
+
+// Extract the directory of the current executable path.
+static std::string GetExecutableDir() {
+ const std::string path = android::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;
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // Set the default test data path to be the executable path directory.
+ android::SetTestDataPath(GetExecutableDir());
+
+ const char* command = argv[0];
+ ++argv;
+ --argc;
+
+ while (argc > 0) {
+ const std::string arg = *argv;
+ if (android::base::StartsWith(arg, "--testdata=")) {
+ android::SetTestDataPath(arg.substr(strlen("--testdata=")));
+ } 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";
+ return 1;
+ } else {
+ std::cerr << command << ": Unrecognized argument '" << *argv << "'.\n";
+ return 1;
+ }
+
+ --argc;
+ ++argv;
+ }
+
+ std::cerr << "using --testdata=" << android::GetTestDataPath() << "\n";
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp
index 41a19a7f2b24..702ee5c9d4ad 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,45 @@
#include "TestHelpers.h"
-#include <androidfw/ResourceTypes.h>
-#include <utils/String8.h>
-#include <gtest/gtest.h>
+#include <unistd.h>
+
+#include "android-base/logging.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";
- }
-
- 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 = pool->string8ObjectAt(val.data);
- if (String8(expectedStr) != actual) {
- return ::testing::AssertionFailure() << actual.string();
- }
- return ::testing::AssertionSuccess() << actual.string();
+static std::string sTestDataPath;
+
+void SetTestDataPath(const std::string& path) { sTestDataPath = path; }
+
+const std::string& GetTestDataPath() {
+ CHECK(!sTestDataPath.empty()) << "no test data path set.";
+ return sTestDataPath;
+}
+
+::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();
}
-} // namespace android
+} // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index ff9be164dbb0..c1e349fb824f 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -1,35 +1,62 @@
-#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();
+static inline ::std::ostream& operator<<(::std::ostream& out,
+ const android::String8& str) {
+ return out << str.string();
}
-static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) {
- return out << android::String8(str).string();
+static inline ::std::ostream& operator<<(::std::ostream& out,
+ const android::String16& str) {
+ return out << android::String8(str).string();
}
namespace android {
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();
+
+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);
-} // namespace android
+} // namespace android
-#endif // __TEST_HELPERS_H
+#endif // TEST_HELPERS_H_
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/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..6dc6edec64ab
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/R.h
@@ -0,0 +1,35 @@
+#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,
+ };
+ };
+
+ struct string {
+ enum : uint32_t {
+ string_one = 0x7f030000u,
+ };
+ };
+
+ struct style {
+ enum : uint32_t {
+ StyleOne = 0x7f020000u,
+ StyleTwo = 0x7f020001u,
+ };
+ };
+};
+
+} // namespace app
+} // namespace android
+} // namespace com
diff --git a/libs/androidfw/tests/data/styles/build.sh b/libs/androidfw/tests/data/styles/build.sh
new file mode 100755
index 000000000000..e763421626cf
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/build.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -e
+
+aapt package -F package.apk -M AndroidManifest.xml -S res
+unzip -j package.apk resources.arsc res/layout/layout.xml
+rm package.apk
diff --git a/libs/androidfw/tests/data/styles/layout.xml b/libs/androidfw/tests/data/styles/layout.xml
new file mode 100644
index 000000000000..4997e71dce6d
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/layout.xml
Binary files differ
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..70c54f6c4b96
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/res/values/styles.xml
@@ -0,0 +1,52 @@
+<?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>
+ </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>
+
+</resources>
diff --git a/libs/androidfw/tests/data/styles/resources.arsc b/libs/androidfw/tests/data/styles/resources.arsc
new file mode 100644
index 000000000000..8f65c9ad1507
--- /dev/null
+++ b/libs/androidfw/tests/data/styles/resources.arsc
Binary files differ
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index a7cbf5e562d1..cf571e97c1f9 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,8 +38,10 @@ 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 \
@@ -37,7 +50,6 @@ hwui_src_files := \
utils/Blur.cpp \
utils/GLUtils.cpp \
utils/LinearAllocator.cpp \
- utils/NinePatchImpl.cpp \
utils/StringUtils.cpp \
utils/TestWindowContext.cpp \
utils/VectorDrawableUtils.cpp \
@@ -45,20 +57,20 @@ 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 \
@@ -69,23 +81,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 \
@@ -105,10 +118,20 @@ hwui_src_files := \
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 +141,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 +162,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 +180,8 @@ endef
hwui_c_includes += \
external/skia/include/private \
external/skia/src/core \
+ external/skia/src/effects \
+ external/skia/src/image \
external/harfbuzz_ng/src \
external/freetype/include
@@ -172,14 +193,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 +203,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 +225,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 +264,54 @@ 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/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 +323,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 +339,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/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..b463e45fb39b 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 "Layer.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,15 @@ 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.appendFormat(" Layer size %dx%d; texid=%u refs=%d\n",
layer->getWidth(), layer->getHeight(),
- layer->isTextureLayer(), layer->getTextureId(),
- layer->getFbo(), layer->getStrongCount());
+ layer->getTextureId(),
+ layer->getStrongCount());
memused += layer->getWidth() * layer->getHeight() * 4;
}
log.appendFormat(" Layers total %8d (numLayers = %zu)\n",
@@ -242,7 +237,6 @@ void Caches::flush(FlushMode mode) {
gradientCache.clear();
fontRenderer.clear();
fboCache.clear();
- dither.clear();
// fall through
case FlushMode::Moderate:
fontRenderer.flush();
@@ -251,7 +245,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..7e2c28c5eb41 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)
@@ -205,19 +204,16 @@ void CanvasState::concatMatrix(const Matrix4& matrix) {
bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
mSnapshot->clip(Rect(left, top, right, bottom), op);
- mDirtyClip = true;
return !mSnapshot->clipIsEmpty();
}
bool CanvasState::clipPath(const SkPath* path, SkRegion::Op 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();
}
@@ -236,15 +232,6 @@ void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline*
}
}
-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 +250,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 +278,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..22c7e8a96cc8 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_CANVAS_STATE_H
-#define ANDROID_HWUI_CANVAS_STATE_H
+#pragma once
#include "Snapshot.h"
@@ -62,11 +61,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.
*/
@@ -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/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..a7d5f6026d00 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -15,11 +15,9 @@
*/
#include "DeferredLayerUpdater.h"
-#include "OpenGLRenderer.h"
-
-#include "LayerRenderer.h"
#include "renderthread/EglManager.h"
#include "renderthread/RenderTask.h"
+#include "utils/PaintUtils.h"
namespace android {
namespace uirenderer {
@@ -29,10 +27,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,13 +45,12 @@ 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);
@@ -110,8 +106,22 @@ 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::updateLayer(bool forceFilter, GLenum renderTarget,
+ const float* textureTransform) {
+ mLayer->setBlend(mBlend);
+ mLayer->setForceFilter(forceFilter);
+ mLayer->setSize(mWidth, mHeight);
+ mLayer->getTexTransform().load(textureTransform);
+
+ if (renderTarget != mLayer->getRenderTarget()) {
+ mLayer->setRenderTarget(renderTarget);
+ mLayer->bindTexture();
+ mLayer->setFilter(GL_NEAREST, false, true);
+ mLayer->setWrap(GL_CLAMP_TO_EDGE, false, true);
}
}
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 40058223fb48..733500885e67 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>
@@ -92,6 +92,8 @@ public:
void detachSurfaceTexture();
+ void updateLayer(bool forceFilter, GLenum renderTarget, const float* textureTransform);
+
private:
// Generic properties
int mWidth;
@@ -99,20 +101,16 @@ 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();
};
} /* namespace uirenderer */
} /* namespace android */
-
-#endif /* DEFERREDLAYERUPDATE_H_ */
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 4cfbb2a43198..700642ed7334 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -41,6 +41,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..9df73387c36a 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 pre-multiplied linear color
+ // if linear blending is enabled.
+ void setSRGB(uint32_t color) {
+ a = ((color >> 24) & 0xff) / 255.0f;
+ r = a * EOCF_sRGB(((color >> 16) & 0xff) / 255.0f);
+ g = a * EOCF_sRGB(((color >> 8) & 0xff) / 255.0f);
+ b = a * EOCF_sRGB(((color ) & 0xff) / 255.0f);
}
bool isNotBlack() {
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 681cf55066b4..effc65ea967f 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -16,6 +16,9 @@
#include "FontRenderer.h"
+#include "BakedOpDispatcher.h"
+#include "BakedOpRenderer.h"
+#include "BakedOpState.h"
#include "Caches.h"
#include "Debug.h"
#include "Extensions.h"
@@ -27,15 +30,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 +60,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 +293,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..245db1dcec97 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -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/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..34e6d39c9104 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -17,8 +17,10 @@
#include "Caches.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 +167,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 +220,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 +246,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 +262,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 +271,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 +289,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 +308,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 +320,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 +329,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 +351,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 +372,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 +389,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 +399,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 +411,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 +421,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);
@@ -467,7 +445,7 @@ GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) {
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 +459,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 +469,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 +497,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 +520,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 +527,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 +546,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 +599,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 +645,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 +664,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 +685,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..8a8b652c393c 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"
@@ -30,9 +29,13 @@ namespace uirenderer {
class Caches;
class Matrix4;
+class Patch;
class RenderState;
class Texture;
+class UvMapper;
class VertexBuffer;
+struct PathTexture;
+struct ShadowTexture;
namespace TextureFillFlags {
enum {
@@ -53,7 +56,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 +70,12 @@ public:
GlopBuilder& setFillBlack();
GlopBuilder& setFillClear();
GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
- float alpha, SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage);
+ float alpha, SkBlendMode 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
+ // 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 +104,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 +133,3 @@ private:
} /* namespace uirenderer */
} /* namespace android */
-
-#endif // RENDERSTATE_GLOPBUILDER_H
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index c8f5e9435594..0972ac1cafc2 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,56 @@ 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);
+ *dst++ = uint8_t(OECF_sRGB(start.r * oppAmount + end.r * amount) * 255.0f);
+ *dst++ = uint8_t(OECF_sRGB(start.g * oppAmount + end.g * amount) * 255.0f);
+ *dst++ = uint8_t(OECF_sRGB(start.b * oppAmount + end.b * amount) * 255.0f);
+ *dst++ = uint8_t( (start.a * oppAmount + end.a * amount) * 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* d = (float*) dst;
- *d++ = a * (start.r * oppAmount + end.r * amount);
- *d++ = a * (start.g * oppAmount + end.g * amount);
- *d++ = a * (start.b * oppAmount + end.b * amount);
- *d++ = a;
-
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
+ *d++ = start.r * oppAmount + end.r * amount;
+ *d++ = start.g * oppAmount + end.g * amount;
+ *d++ = start.b * oppAmount + end.b * amount;
+#else
+ *d++ = OECF_sRGB(start.r * oppAmount + end.r * amount);
+ *d++ = OECF_sRGB(start.g * oppAmount + end.g * amount);
+ *d++ = OECF_sRGB(start.b * oppAmount + end.b * amount);
+#endif
+ *d++ = start.a * oppAmount + end.a * amount;
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[] = {
- &android::uirenderer::GradientCache::mixBytes,
- &android::uirenderer::GradientCache::mixFloats,
+ &android::uirenderer::GradientCache::mixBytes, // colors are stored gamma-encoded
+ &android::uirenderer::GradientCache::mixFloats, // colors are stored in linear
};
ChannelMixer mix = gMixers[mUseFloatTexture];
- GradientColor start;
- (this->*split)(colors[0], start);
+ FloatColor start;
+ start.setSRGB(colors[0]);
- GradientColor end;
- (this->*split)(colors[1], end);
+ FloatColor end;
+ end.setSRGB(colors[1]);
int currentPos = 1;
float startPos = positions[0];
@@ -255,7 +241,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions,
currentPos++;
- (this->*split)(colors[currentPos], end);
+ end.setSRGB(colors[currentPos]);
distance = positions[currentPos] - startPos;
}
@@ -266,10 +252,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 bddb01b97865..f94a22d51d9f 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -88,6 +88,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..88817efa80ac 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -17,9 +17,6 @@
#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"
@@ -35,17 +32,15 @@
namespace android {
namespace uirenderer {
-Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight)
+Layer::Layer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight)
: GpuMemoryTracker(GpuObjectType::Layer)
, state(State::Uncached)
, caches(Caches::getInstance())
, renderState(renderState)
- , texture(caches)
- , type(layerType) {
+ , texture(caches) {
// 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);
@@ -55,138 +50,22 @@ Layer::~Layer() {
renderState.unregisterLayer(this);
SkSafeUnref(colorFilter);
- if (stencil || fbo || texture.mId) {
- removeFbo();
+ if (texture.mId) {
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);
-}
-
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();
+ caches.textureState().bindTexture(texture.target(), texture.mId);
}
}
@@ -206,86 +85,6 @@ void Layer::clearTexture() {
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);
}
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 1e5498bb3d21..8e71cd11599d 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_LAYER_H
-#define ANDROID_HWUI_LAYER_H
+#pragma once
#include <cutils/compiler.h>
#include <sys/types.h>
@@ -29,7 +28,7 @@
#include <ui/Region.h>
#include <SkPaint.h>
-#include <SkXfermode.h>
+#include <SkBlendMode.h>
#include "Matrix.h"
#include "Rect.h"
@@ -46,22 +45,13 @@ namespace uirenderer {
// 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.
*/
class Layer : public VirtualLightRefBase, GpuMemoryTracker {
public:
- enum class Type {
- Texture,
- DisplayList,
- };
-
// layer lifecycle, controlled from outside
enum class State {
Uncached = 0,
@@ -73,45 +63,9 @@ public:
};
State state; // public for logging/debugging purposes
- Layer(Type type, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight);
+ Layer(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;
}
@@ -120,23 +74,11 @@ public:
return texture.mHeight;
}
- /**
- * 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);
-
void setSize(uint32_t width, uint32_t height) {
- texture.updateSize(width, height, texture.format());
+ texture.updateSize(width, height, texture.internalFormat(), texture.format(),
+ texture.target());
}
- ANDROID_API void setPaint(const SkPaint* paint);
-
inline void setBlend(bool blend) {
texture.blend = blend;
}
@@ -157,7 +99,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,40 +108,10 @@ 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();
}
@@ -209,71 +121,40 @@ public:
}
inline GLenum getRenderTarget() const {
- return renderTarget;
+ return texture.target();
}
inline void setRenderTarget(GLenum renderTarget) {
- this->renderTarget = renderTarget;
+ texture.mTarget = renderTarget;
}
inline bool isRenderable() const {
- return renderTarget != GL_NONE;
+ return texture.target() != GL_NONE;
}
void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) {
- texture.setWrap(wrap, bindTexture, force, renderTarget);
+ texture.setWrap(wrap, bindTexture, force);
}
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;
+ texture.setFilter(filter, bindTexture, force);
}
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 setColorFilter(SkColorFilter* filter);
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 clearTexture();
inline mat4& getTexTransform() {
return texTransform;
@@ -283,11 +164,6 @@ 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.
@@ -300,98 +176,17 @@ public:
*/
void onGlContextLost();
- /**
- * 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;
-
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;
-
- /**
* Color filter used to draw this layer. Optional.
*/
SkColorFilter* colorFilter = nullptr;
@@ -409,7 +204,7 @@ private:
/**
* Blending mode of the layer.
*/
- SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
+ SkBlendMode mode = SkBlendMode::kSrcOver;
/**
* Optional texture coordinates transform.
@@ -421,28 +216,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..408159b8f33a 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 "Layer.h"
#include "renderstate/RenderState.h"
#include "renderthread/EglManager.h"
#include "utils/GLUtils.h"
@@ -30,14 +31,82 @@
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;
+ }
+
+ CopyResult copyResult = copyImageInto(sourceImage, texTransform, graphicBuffer->getWidth(),
+ graphicBuffer->getHeight(), 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);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+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
@@ -98,64 +167,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 +174,21 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
renderState.blend().syncEnabled();
renderState.stencil().disable();
- Rect destRect(destWidth, destHeight);
+ Matrix4 croppedTexTransform(texTransform);
+ if (!srcRect.isEmpty()) {
+ croppedTexTransform.loadTranslate(srcRect.left / sourceTexture.width(),
+ srcRect.top / sourceTexture.height(), 0);
+ croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(),
+ srcRect.getHeight() / sourceTexture.height(), 1);
+ croppedTexTransform.multiply(texTransform);
+ }
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 +202,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,
+ Layer& 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..f4ebabcdebe5
--- /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 Layer;
+
+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, Layer& 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..52c62cc34670 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -43,21 +43,6 @@ 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 +65,7 @@ void PatchCache::clear() {
clearCache();
if (mMeshBuffer) {
- mRenderState.meshState().unbindMeshBuffer();
- glDeleteBuffers(1, &mMeshBuffer);
+ mRenderState.meshState().deleteMeshBuffer(mMeshBuffer);
mMeshBuffer = 0;
mSize = 0;
}
@@ -170,7 +154,8 @@ 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 +167,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 +202,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 +225,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..0624c355332c 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();
@@ -187,5 +184,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 bbef36b72e4f..9536bc807fbc 100644
--- a/libs/hwui/PixelBuffer.h
+++ b/libs/hwui/PixelBuffer.h
@@ -99,12 +99,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.
@@ -199,8 +193,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..9c4cb098b4c3 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"
@@ -85,6 +85,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 +133,7 @@ struct ProgramDescription {
// Shaders
bool hasBitmap;
- bool isBitmapNpot;
+ bool useShaderBasedWrap;
bool hasVertexAlpha;
bool useShadowAlphaInterp;
@@ -140,7 +142,7 @@ struct ProgramDescription {
Gradient gradientType;
bool isSimpleGradient;
- SkXfermode::Mode shadersMode;
+ SkBlendMode shadersMode;
bool isBitmapFirst;
GLenum bitmapWrapS;
@@ -148,16 +150,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 +180,28 @@ struct ProgramDescription {
modulate = false;
hasBitmap = false;
- isBitmapNpot = 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,7 +234,7 @@ 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;
@@ -238,7 +244,7 @@ struct ProgramDescription {
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 +252,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 +268,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..0c2309faf4ea 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,12 +135,11 @@ 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"
};
@@ -172,18 +159,57 @@ 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"
+ "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n"
+ " return pow(mix(a, b, v), vec4(vec3(1.0 / 2.2), 1.0));"
+ "}\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"
+ " return mix(a, b, v);"
+ "}\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 +228,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 +273,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 +307,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 +430,8 @@ const char* gBlendOps[18] = {
///////////////////////////////////////////////////////////////////////////////
ProgramCache::ProgramCache(Extensions& extensions)
- : mHasES3(extensions.getMajorGlVersion() >= 3) {
+ : mHasES3(extensions.getMajorGlVersion() >= 3)
+ , mHasSRGB(extensions.hasSRGB()) {
}
ProgramCache::~ProgramCache() {
@@ -518,6 +564,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,7 +572,7 @@ 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);
}
@@ -570,13 +617,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 +657,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;
}
@@ -649,9 +707,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 +721,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 +734,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 +777,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 +809,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 6f68c2bdff80..848161e44604 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,27 @@ 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;
+}
+
+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..b4a311864938 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,9 +307,12 @@ 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
@@ -306,6 +321,7 @@ public:
private:
static ProfileType sProfileType;
static bool sDisableProfileBars;
+ static RenderPipelineType sRenderPipelineType;
}; // class Caches
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..f9a7c36f2786 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -14,20 +14,18 @@
* 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 "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 +211,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 +226,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 +234,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 +256,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 +288,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;
};
@@ -506,7 +503,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 +517,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 +526,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..9d18484f39f0 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() {
@@ -247,7 +247,7 @@ bool RecordingCanvas::clipRegion(const SkRegion* region, SkRegion::Op 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 +468,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 +492,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 +508,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 +520,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 +532,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 +559,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) {
@@ -660,7 +666,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..f93e8b892e81 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
@@ -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..6786a69c8dd1 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -14,181 +14,32 @@
* 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 +49,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;
}
// ----------------------------------------------------------------------------
@@ -235,11 +103,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 +146,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 +161,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) {
@@ -353,13 +208,62 @@ int SkiaCanvas::saveLayer(float left, float top, float right, float bottom,
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, SkRegion::Op op, const SkMatrix& m)
+ : mType(Type::Rect), mOp(op), mMatrix(m), mRRect(SkRRect::MakeRect(rect)) {}
+ Clip(const SkRRect& rrect, SkRegion::Op op, const SkMatrix& m)
+ : mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {}
+ Clip(const SkPath& path, SkRegion::Op 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;
+ SkRegion::Op 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 +282,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, SkRegion::Op 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);
+ }
}
// ----------------------------------------------------------------------------
@@ -492,12 +399,20 @@ bool SkiaCanvas::quickRejectPath(const SkPath& path) const {
bool SkiaCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op 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);
+ SkRRect roundRect;
+ if (path->isRRect(&roundRect)) {
+ this->recordClip(roundRect, op);
+ mCanvas->clipRRect(roundRect, op);
+ } else {
+ this->recordClip(*path, op);
+ mCanvas->clipPath(*path, op);
+ }
return !mCanvas->isClipEmpty();
}
@@ -507,10 +422,13 @@ bool SkiaCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
// The region is specified in device space.
SkMatrix savedMatrix = mCanvas->getTotalMatrix();
mCanvas->resetMatrix();
+ this->recordClip(rgnPath, op);
mCanvas->clipPath(rgnPath, op);
mCanvas->setMatrix(savedMatrix);
} else {
- mCanvas->clipRect(SkRect::MakeEmpty(), op);
+ const auto emptyClip = SkRect::MakeEmpty();
+ this->recordClip(emptyClip, op);
+ mCanvas->clipRect(emptyClip, op);
}
return !mCanvas->isClipEmpty();
}
@@ -531,7 +449,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 +463,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 +489,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 +551,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 +670,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 +718,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 +791,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..4f1d8572eb2d
--- /dev/null
+++ b/libs/hwui/SkiaCanvas.h
@@ -0,0 +1,193 @@
+/*
+ * 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,
+ 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, 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&, SkRegion::Op);
+ 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 9df32b28bf3b..5978abc9efaa 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -16,6 +16,8 @@
#include "SkiaCanvasProxy.h"
+#include "hwui/Bitmap.h"
+
#include <cutils/log.h>
#include <SkPatchUtils.h>
#include <SkPaint.h>
@@ -23,6 +25,9 @@
#include <SkPixelRef.h>
#include <SkRect.h>
#include <SkRRect.h>
+#include <SkRSXform.h>
+#include <SkSurface.h>
+#include <SkTextBlobRunIterator.h>
#include <memory>
@@ -103,16 +108,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,
@@ -120,15 +121,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,8 +141,9 @@ void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& ce
}
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;
}
@@ -153,7 +156,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;
}
@@ -322,9 +325,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);
@@ -347,18 +350,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;
}
@@ -372,7 +425,7 @@ 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);
}
}
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
index c03c523cd106..01118150a8f0 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;
@@ -67,7 +67,7 @@ protected:
virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
const SkRect& dst, const SkPaint*) override;
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,11 +81,13 @@ 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;
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 6f4a6839be4e..34e6a06c3dc2 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -21,6 +21,7 @@
#include "Layer.h"
#include "Matrix.h"
#include "Texture.h"
+#include "hwui/Bitmap.h"
#include <SkMatrix.h>
#include <utils/Log.h>
@@ -57,7 +58,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);
}
@@ -173,15 +174,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.setSRGB(gradInfo.fColors[0]);
+ outData->endColor.setSRGB(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 +193,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 +207,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 +218,13 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model
const float height = outData->bitmapTexture->height();
description->hasBitmap = true;
- if (!caches.extensions().hasNPot()
+ // 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,10 +312,7 @@ 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;
- }
+ description->shadersMode = rec.fBlendMode;
return true;
}
@@ -388,11 +383,12 @@ void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& mo
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);
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 884196d9fc62..d2f37cda9cb3 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 {
@@ -62,7 +61,6 @@ struct SkiaShaderData {
} bitmapData;
struct GradientShaderData {
Matrix4 screenSpace;
- GLuint ditherSampler;
// simple gradient
FloatColor startColor;
@@ -72,7 +70,6 @@ struct SkiaShaderData {
Texture* gradientTexture;
GLuint gradientSampler;
GLenum wrapST;
-
} gradientData;
struct LayerShaderData {
Layer* layer;
@@ -90,7 +87,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..f61c3e9825f8 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,13 +66,6 @@ Snapshot::Snapshot(Snapshot* s, int saveFlags)
} else {
mClipArea = s->mClipArea;
}
-
- if (s->flags & Snapshot::kFlagFboTarget) {
- flags |= Snapshot::kFlagFboTarget;
- region = s->region;
- } else {
- region = nullptr;
- }
}
///////////////////////////////////////////////////////////////////////////////
@@ -127,58 +115,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 +163,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 +198,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..4ab58302df8f 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>
@@ -63,18 +62,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 +100,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
};
/**
@@ -179,11 +161,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 +184,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 +192,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 +215,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 +224,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 +245,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 +284,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..5b5b74e1c3f3 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,32 +190,39 @@ 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;
default:
@@ -199,9 +231,25 @@ static void colorTypeToGlFormatAndType(SkColorType colorType,
}
}
-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 +272,32 @@ void Texture::upload(const SkBitmap& bitmap) {
setDefaultParams = true;
}
- GLint format, type;
- colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type);
+ sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
+ bool needSRGB = bitmap.info().colorSpace() == sRGB.get();
- if (updateSize(bitmap.width(), bitmap.height(), format)) {
- needsAlloc = true;
- }
+ GLint internalFormat, format, type;
+ colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type);
- blend = !bitmap.isOpaque();
- mCaches.textureState().bindTexture(mId);
-
- 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);
+ GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
+ needsAlloc |= updateSize(bitmap.width(), bitmap.height(), internalFormat, format, target);
- SkCanvas canvas(rgbaBitmap);
- canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
-
- 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 +313,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..c75e88f5dd6e 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,7 +158,6 @@ 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
@@ -148,13 +165,18 @@ private:
friend class Layer;
// 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 aeee66106fb3..97b7dd722e08 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()));
@@ -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 6c1815fe1301..e9a9c719975a 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;
}
@@ -684,11 +692,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.
@@ -701,10 +713,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/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..99ac358163ff
--- /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 <cutils/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..8366387b6d98
--- /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 <cutils/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/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..d3e765dbfbd1
--- /dev/null
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -0,0 +1,499 @@
+/*
+ * 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/RenderThread.h"
+#include "renderthread/RenderProxy.h"
+
+#include <cutils/log.h>
+#include <sys/mman.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_ALPHA:
+ return PIXEL_FORMAT_TRANSPARENT;
+ 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;
+ 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) {
+ ALOGW("unable to create hardware bitmap of configuration");
+ 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(), 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) {
+ return nullptr;
+ }
+ SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ 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..d7839b429648 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;
@@ -170,7 +193,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 +218,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 +243,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 +266,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 a455f576e38d..f172473d1652 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -22,17 +22,14 @@
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);
@@ -43,7 +40,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;
@@ -55,8 +52,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;
@@ -68,23 +65,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;
}
@@ -117,8 +102,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..3ee916c6e8b1 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -19,29 +19,26 @@
#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;
@@ -52,9 +49,10 @@ public:
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 67b775d98356..8dd165c46d21 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -24,17 +24,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
@@ -44,29 +45,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) {
@@ -74,7 +76,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;
@@ -88,7 +90,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..ca43156e88a1 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,66 +49,14 @@ 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) {
@@ -133,16 +85,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 +118,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::FontFamily* family = new minikin::FontFamily();
+ minikin::MinikinFont* font = new MinikinFontSkia(std::move(typeface), data, st.st_size, 0);
+ family->addFont(font);
+ 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..1be630c1e978 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();
@@ -45,9 +45,12 @@ struct ANDROID_API Typeface {
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..fb2134c51e22
--- /dev/null
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -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.
+ */
+
+#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();
+
+ 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..f2af4a891b12
--- /dev/null
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 "LayerDrawable.h"
+#include "SkColorFilter.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);
+ }
+ GrGLTextureInfo externalTexture;
+ externalTexture.fTarget = layer->getRenderTarget();
+ externalTexture.fID = layer->getTextureId();
+ GrBackendTextureDesc textureDescription;
+ textureDescription.fWidth = layer->getWidth();
+ textureDescription.fHeight = layer->getHeight();
+ textureDescription.fConfig = kRGBA_8888_GrPixelConfig;
+ textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin;
+ textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture);
+ sk_sp<SkImage> layerImage = SkImage::MakeFromTexture(context, textureDescription);
+ 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..4b34c7c45c74
--- /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), SkRegion::kIntersect_Op, 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(), SkRegion::kIntersect_Op, 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..7f3474a1bdf3
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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 "LayerDrawable.h"
+#include "renderthread/EglManager.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();
+ Layer* layer = new Layer(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..0f2d09d69bd7
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -0,0 +1,335 @@
+/*
+ * 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 <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->clipRect(layerDamage.toSkRect(), SkRegion::kReplace_Op);
+
+ 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 {
+ return SkImageEncoder::EncodeData(pixmap.info(), pixmap.addr(), pixmap.rowBytes(),
+ SkImageEncoder::kPNG_Type, 100);
+ }
+};
+
+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();
+}
+
+void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
+ const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
+ SkCanvas* canvas) {
+
+ canvas->clipRect(clip, SkRegion::kReplace_Op);
+
+ if (!opaque) {
+ canvas->clear(SK_ColorTRANSPARENT);
+ }
+
+ // If 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 = contentDrawBounds;
+ // 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.
+ int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0;
+
+ for (const sp<RenderNode>& node : nodes) {
+ if (node->nothingToDraw()) continue;
+
+ SkASSERT(node->getDisplayList()->isSkiaDL());
+
+ int count = canvas->save();
+
+ if (layer == 0) {
+ 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;
+ // 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);
+ } else if (layer == 1) {
+ // We shift and clip the content to match its final location in the window.
+ const SkRect clip = SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top,
+ backdropBounds.getWidth(), backdropBounds.getHeight());
+ const float dx = backdropBounds.left - contentDrawBounds.left;
+ const float dy = backdropBounds.top - contentDrawBounds.top;
+ canvas->translate(dx, dy);
+ // It gets cropped against the bounds of the backdrop to stay inside.
+ canvas->clipRect(clip, SkRegion::kIntersect_Op);
+ }
+
+ RenderNodeDrawable root(node.get(), canvas);
+ root.draw(canvas);
+ canvas->restoreToCount(count);
+ layer++;
+ }
+}
+
+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..95db2586f1bc
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -0,0 +1,247 @@
+/*
+ * 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"
+
+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..ca394b2d0b45
--- /dev/null
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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/EglManager.h" // needed for Frame
+#include "Readback.h"
+#include "renderstate/RenderState.h"
+#include "SkiaPipeline.h"
+#include "SkiaProfileRenderer.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);
+ }
+
+ // TODO: support buffer age if Vulkan API can do it
+ Frame frame(backBuffer->width(), backBuffer->height(), 0);
+ 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();
+ Layer* layer = new Layer(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..72af7c98e477 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -52,7 +52,6 @@ void RenderState::onGLContextCreated() {
mCaches = &Caches::createInstance(*this);
}
mCaches->init();
- mCaches->textureCache.setAssetAtlas(&mAssetAtlas);
}
static void layerLostGlContext(Layer* layer) {
@@ -60,43 +59,10 @@ static void layerLostGlContext(Layer* layer) {
}
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();
@@ -179,9 +145,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 +278,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 +314,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..3d119dc9e290 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"
@@ -92,7 +91,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; }
@@ -120,7 +118,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..c561c86460c6 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -19,17 +19,18 @@
#include "AnimationContext.h"
#include "Caches.h"
-#include "DeferredLayerUpdater.h"
#include "EglManager.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 +62,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 +161,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 +168,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 +186,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 +201,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 +226,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 +305,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 +330,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 +382,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 +393,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 +463,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 +497,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 +517,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 +525,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 +534,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 +568,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 +644,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 +656,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 +672,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 ac6a28fe6289..de95bee39763 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -16,6 +16,7 @@
#include "EglManager.h"
+#include "Texture.h"
#include "Caches.h"
#include "DeviceInfo.h"
#include "Properties.h"
@@ -25,8 +26,14 @@
#include <cutils/log.h>
#include <cutils/properties.h>
#include <EGL/eglext.h>
+#include <GrContextOptions.h>
+#include <gl/GrGLInterface.h>
#include <string>
+#ifdef HWUI_GLES_WRAP_ENABLED
+#include "debug/GlesDriver.h"
+#endif
+
#define GLES_VERSION 2
// Android-specific addition that is used to show when frames began in systrace
@@ -58,7 +65,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());
}
@@ -91,9 +98,7 @@ EglManager::EglManager(RenderThread& thread)
, 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() {
@@ -103,11 +108,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);
@@ -128,13 +133,33 @@ 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() {
auto extensions = StringUtils::split(
eglQueryString(mEglDisplay, EGL_EXTENSIONS));
- EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age");
+ // For our purposes we don't care if EGL_BUFFER_AGE is a result of
+ // EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered
+ // under EGL_KHR_partial_update and we don't need the expanded scope
+ // that EGL_EXT_buffer_age provides.
+ EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age")
+ || extensions.has("EGL_KHR_partial_update");
EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update");
LOG_ALWAYS_FATAL_IF(!extensions.has("EGL_KHR_swap_buffers_with_damage"),
"Missing required extension EGL_KHR_swap_buffers_with_damage");
@@ -171,7 +196,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());
}
}
}
@@ -183,33 +208,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() {
@@ -224,15 +223,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;
@@ -243,13 +251,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);
@@ -277,7 +286,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;
@@ -318,7 +327,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
@@ -372,14 +381,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..b12522e6bd41 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -31,6 +31,11 @@ class EglManager;
class Frame {
public:
+ Frame(EGLint width, EGLint height, EGLint bufferAge)
+ : mWidth(width)
+ , mHeight(height)
+ , mBufferAge(bufferAge) { }
+
EGLint width() const { return mWidth; }
EGLint height() const { return mHeight; }
@@ -39,6 +44,7 @@ public:
EGLint bufferAge() const { return mBufferAge; }
private:
+ Frame() {}
friend class EglManager;
EGLSurface mSurface;
@@ -55,6 +61,7 @@ private:
// 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 +86,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 +98,6 @@ private:
void createPBufferSurface();
void loadConfig();
void createContext();
- void initAtlas();
EGLint queryBufferAge(EGLSurface surface);
RenderThread& mRenderThread;
@@ -106,10 +109,6 @@ private:
EGLSurface mCurrentSurface;
- sp<GraphicBuffer> mAtlasBuffer;
- int64_t* mAtlasMap;
- size_t mAtlasMapSize;
-
enum class SwapBehavior {
Discard,
Preserved,
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
new file mode 100644
index 000000000000..0e4000b09e1a
--- /dev/null
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -0,0 +1,85 @@
+/*
+ * 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 "EglManager.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..9dc2b596e3e8
--- /dev/null
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -0,0 +1,263 @@
+/*
+ * 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 "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, *(layer->backingLayer()), bitmap);
+}
+
+DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() {
+ mEglManager.initialize();
+ Layer* layer = new Layer(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..4d239bc616d0
--- /dev/null
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -0,0 +1,675 @@
+/*
+ * 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 "RenderThread.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;
+
+ if (VK_NULL_HANDLE != mCommandPool) {
+ mDestroyCommandPool(mBackendContext->fDevice, mCommandPool, nullptr);
+ mCommandPool = VK_NULL_HANDLE;
+ }
+}
+
+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());
+}
+
+// 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;
+ }
+
+ // 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->mImageLayouts[backbuffer->mImageIndex];
+ 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->mSurfaces[backbuffer->mImageIndex];
+ 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->mSurfaces;
+ surface->mSurfaces = nullptr;
+ delete[] surface->mImageLayouts;
+ surface->mImageLayouts = 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->mImageLayouts = new VkImageLayout[surface->mImageCount];
+ surface->mSurfaces = new sk_sp<SkSurface>[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;
+
+ surface->mSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(),
+ desc, &props);
+ surface->mImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
+ }
+
+ 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;
+ }
+
+ SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR));
+ VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get();
+ res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
+ &surfaceFormatCount, surfaceFormats);
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ uint32_t presentModeCount;
+ res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+ surface->mVkSurface, &presentModeCount, nullptr);
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR));
+ VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get();
+ res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+ surface->mVkSurface, &presentModeCount, presentModes);
+ 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->mSurfaces[backbuffer->mImageIndex].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->mImageLayouts[backbuffer->mImageIndex] = 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();
+}
+
+} /* 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..f0e33206a576
--- /dev/null
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -0,0 +1,171 @@
+/*
+ * 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];
+ };
+
+ sk_sp<SkSurface> mBackbuffer;
+
+ VkSurfaceKHR mVkSurface = VK_NULL_HANDLE;
+ VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
+
+ BackbufferInfo* mBackbuffers;
+ uint32_t mCurrentBackbufferIndex;
+
+ uint32_t mImageCount;
+ VkImage* mImages;
+ VkImageLayout* mImageLayouts;
+ sk_sp<SkSurface>* mSurfaces;
+};
+
+// 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; }
+
+ // 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;
+};
+
+} /* 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..6dadd3e364cf
--- /dev/null
+++ b/libs/hwui/tests/common/BitmapAllocationTestUtils.h
@@ -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.
+ */
+
+#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;
+ sk_sp<Bitmap> heapBitmap(TestUtils::createBitmap(width, height, &skBitmap));
+ 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..9530c793e4e4 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -18,7 +18,10 @@
#include "hwui/Paint.h"
#include "DeferredLayerUpdater.h"
-#include "LayerRenderer.h"
+
+#include <renderthread/EglManager.h>
+#include <renderthread/OpenGLPipeline.h>
+#include <utils/Unicode.h>
namespace android {
namespace uirenderer {
@@ -43,20 +46,14 @@ SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end)
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);
+ renderthread::OpenGLPipeline pipeline(renderThread);
+ sp<DeferredLayerUpdater> layerUpdater = pipeline.createTextureLayer();
+ 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 +65,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 +107,13 @@ 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();
+ renderThread.eglManager().initialize();
+
+ rtCallback(renderThread);
- renderState.onGLContextCreated();
- rtCallback(renderthread::RenderThread::getInstance());
- renderState.flush(Caches::FlushMode::Full);
- renderState.onGLContextDestroyed();
+ renderThread.renderState().flush(Caches::FlushMode::Full);
+ renderThread.eglManager().destroy();
}
std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
@@ -124,5 +125,44 @@ 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;
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 5a4ab99012b9..0ce598d4dba4 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -13,37 +13,28 @@
* 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 <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))
@@ -132,14 +123,16 @@ public:
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(
@@ -155,7 +148,29 @@ 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
+ DeviceInfo::initialize();
+#endif
+
+ sp<RenderNode> node = new RenderNode();
+ RenderProperties& props = node->mutateStagingProperties();
+ props.setLeftTopRightBottom(left, top, right, bottom);
+ if (setup) {
+ 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
@@ -166,7 +181,7 @@ public:
RenderProperties& props = node->mutateStagingProperties();
props.setLeftTopRightBottom(left, top, right, bottom);
if (setup) {
- TestCanvas canvas(props.getWidth(), props.getHeight());
+ RecordingCanvasType canvas(props.getWidth(), props.getHeight());
setup(props, canvas);
node->setStagingDisplayList(canvas.finishRecording(), nullptr);
}
@@ -175,11 +190,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 +279,19 @@ 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);
+
private:
static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
node->syncProperties();
@@ -251,5 +308,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..9b0b9507b503
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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"
+
+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;
+}; \ No newline at end of file
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index a5fd71266314..8f2ba2d2cddc 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -28,10 +28,10 @@ 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);
@@ -39,7 +39,7 @@ public:
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.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
}
canvas.restore();
@@ -48,7 +48,7 @@ public:
SkPath clipCircle;
clipCircle.addCircle(100, 300, 100);
canvas.clipPath(&clipCircle, SkRegion::kIntersect_Op);
- canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
+ 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/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..3630935a809f 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.drawColor(Color::Green_700, SkBlendMode::kSrcOver);
canvas.clipRect(50, 50, 350, 350, SkRegion::kIntersect_Op);
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..5ef8773488b6 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);
}
@@ -83,7 +83,7 @@ public:
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.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 c5af06160b62..f3b7b51dbd31 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 <cutils/log.h>
#include <gui/Surface.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..bbaf267a5798 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, SkRegion::kIntersect_Op);
+
+ 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_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..85a91db58137
--- /dev/null
+++ b/libs/hwui/tests/scripts/stopruntime.sh
@@ -0,0 +1,26 @@
+#!/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
+adb shell daemonize surfaceflinger
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
index 6b7b721eaec6..d44be7dfbc3a 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;
@@ -85,9 +86,7 @@ RENDERTHREAD_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
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)
@@ -159,14 +158,14 @@ RENDERTHREAD_TEST(BakedOpDispatcher, offsetFlags) {
}
RENDERTHREAD_TEST(BakedOpDispatcher, renderTextWithShadow) {
- 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) {
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();
@@ -273,3 +272,17 @@ RENDERTHREAD_TEST(BakedOpDispatcher, layerUpdateProperties) {
}
}
}
+
+RENDERTHREAD_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/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..d3d80a96d5a3
--- /dev/null
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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);
+ ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcess);
+}
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
new file mode 100644
index 000000000000..0326aa91bb18
--- /dev/null
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 "renderthread/OpenGLPipeline.h"
+#include "tests/common/TestUtils.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) {
+ renderthread::OpenGLPipeline pipeline(renderThread);
+ sp<DeferredLayerUpdater> layerUpdater = pipeline.createTextureLayer();
+ layerUpdater->setSize(100, 100);
+ layerUpdater->setBlend(true);
+
+
+ // updates are deferred so the backing layer should still be in its default state
+ EXPECT_EQ((uint32_t)GL_NONE, layerUpdater->backingLayer()->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.
+ EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, layerUpdater->backingLayer()->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/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index e2dc3a0a2c66..a1c225f544dd 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -127,11 +127,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());
@@ -155,7 +155,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);
@@ -171,7 +171,7 @@ RENDERTHREAD_TEST(FrameBuilder, simpleStroke) {
}
RENDERTHREAD_TEST(FrameBuilder, simpleRejection) {
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ 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
@@ -198,10 +198,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 +210,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();
});
@@ -234,7 +235,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());
});
@@ -287,20 +288,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);
}));
@@ -346,7 +347,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
});
@@ -371,7 +372,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());
@@ -393,19 +394,19 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
}
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);
+ 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 +414,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());
@@ -447,25 +448,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.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.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.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.drawBitmap(*bitmap, 40, 70, nullptr);
});
FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
@@ -482,8 +483,8 @@ RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) {
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);
@@ -518,8 +519,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);
@@ -549,7 +550,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);
@@ -605,8 +606,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);
@@ -645,7 +646,7 @@ 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);
@@ -677,7 +678,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);
@@ -699,7 +700,7 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
SkMatrix::MakeTrans(5, 5));
layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
canvas.drawLayer(layerUpdater.get());
});
@@ -722,7 +723,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);
@@ -747,10 +748,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,
@@ -781,14 +782,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);
@@ -820,10 +821,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
@@ -869,7 +870,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());
@@ -945,7 +946,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);
{
@@ -969,7 +970,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
}
RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ 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);
@@ -1003,7 +1004,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 +1021,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());
@@ -1053,7 +1054,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));
@@ -1105,7 +1106,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);
@@ -1137,7 +1138,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 +1153,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));
@@ -1171,7 +1172,7 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
}
RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) {
- auto node = TestUtils::createNode(0, 0, 200, 200,
+ 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));
@@ -1237,7 +1238,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
@@ -1289,7 +1290,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;
@@ -1384,7 +1385,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 +1396,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;
@@ -1459,10 +1460,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 +1487,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 +1504,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
+}
- auto parent = TestUtils::createNode(0, 0, 100, 100,
+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
+
+RENDERTHREAD_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 +1544,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,7 +1559,7 @@ RENDERTHREAD_TEST(FrameBuilder, zReorder) {
ZReorderTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(10, renderer.getIndex());
+ EXPECT_EQ(13, renderer.getIndex());
};
RENDERTHREAD_TEST(FrameBuilder, projectionReorder) {
@@ -1579,7 +1607,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 +1619,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 +1627,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);
@@ -1660,7 +1688,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 +1698,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);
@@ -1735,12 +1763,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,7 +1778,7 @@ 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);
@@ -1758,7 +1786,7 @@ RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) {
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 +1803,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);
@@ -1803,7 +1831,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());
@@ -1844,7 +1872,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);
@@ -1887,7 +1915,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);
@@ -1934,7 +1962,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());
@@ -1961,7 +1989,7 @@ 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.
@@ -1983,7 +2011,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 +2020,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;
@@ -2105,7 +2133,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 +2164,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
@@ -2222,10 +2250,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.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
});
FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
@@ -2237,5 +2265,349 @@ RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
EXPECT_EQ(1, renderer.getIndex());
}
+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());
+}
+
+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());
+}
+
+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());
+}
+
+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());
+}
+
+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());
+}
+
+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());
+}
+
+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());
+}
+
+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());
+}
+
+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());
+}
+
+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());
+}
+
+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..ce1db0585be8 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,7 +112,7 @@ 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;
}
@@ -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/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index 6148b33eceb8..06599dde4957 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -31,7 +31,7 @@ const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
RENDERTHREAD_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();
@@ -51,7 +51,7 @@ RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) {
RENDERTHREAD_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..0881fa246afd
--- /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_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);
+} \ No newline at end of file
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..dda432ea03ed 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -249,7 +249,7 @@ TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
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";
@@ -261,8 +261,7 @@ TEST(RecordingCanvas, drawColor) {
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 +277,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();
@@ -562,7 +561,7 @@ TEST(RecordingCanvas, saveLayer_rejectBegin) {
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);
@@ -577,7 +576,7 @@ TEST(RecordingCanvas, drawRenderNode_rejection) {
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);
@@ -639,7 +638,7 @@ 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.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
canvas.restore();
});
ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
@@ -728,22 +727,27 @@ TEST(RecordingCanvas, refPaint) {
}
TEST(RecordingCanvas, refBitmap) {
- SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ 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);
+ 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));
- paint.setShader(shader);
+ SkShader::TileMode::kClamp_TileMode,
+ nullptr,
+ kNever_SkCopyPixelsMode,
+ nullptr);
+ paint.setShader(std::move(shader));
canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
});
auto& bitmaps = dl->getBitmapResources();
@@ -751,24 +755,29 @@ TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
}
TEST(RecordingCanvas, refBitmapInShader_composeShader) {
- SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ 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();
@@ -782,7 +791,7 @@ TEST(RecordingCanvas, drawText) {
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;
@@ -806,7 +815,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..f4b686d3b1e2
--- /dev/null
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -0,0 +1,789 @@
+/*
+ * 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 <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, mIndex++) << "An op was drawn out of order";
+ }
+ int getIndex() { return mIndex; }
+protected:
+ int mIndex = 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 {
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+ return new AnimationContext(clock);
+ }
+};
+
+inline SkRect getBounds(const SkCanvas* canvas) {
+ SkClipStack::BoundsType boundType;
+ SkRect clipBounds;
+ canvas->getClipStack()->getBounds(&clipBounds, &boundType);
+ return clipBounds;
+}
+inline SkRect getLocalBounds(const SkCanvas* canvas) {
+ SkMatrix invertedTotalMatrix;
+ EXPECT_TRUE(canvas->getTotalMatrix().invert(&invertedTotalMatrix));
+ SkRect outlineInDeviceCoord = getBounds(canvas);
+ SkRect outlineInLocalCoord;
+ invertedTotalMatrix.mapRect(&outlineInLocalCoord, outlineInDeviceCoord);
+ return outlineInLocalCoord;
+}
+} // 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 = mIndex++;
+ 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), getBounds(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), getLocalBounds(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), getBounds(this));
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ EXPECT_EQ(expectedMatrix, getTotalMatrix());
+ }
+
+ int getIndex() { return mIndex; }
+ protected:
+ int mIndex = 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), getBounds(this));
+ }
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ EXPECT_EQ(1, (*mDrawCounter)++);
+ EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(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), getLocalBounds(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), getBounds(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, SkCopyPixelsMode) 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, mIndex++);
+ EXPECT_TRUE(getTotalMatrix().isIdentity());
+ }
+ void onDrawOval(const SkRect&, const SkPaint&) override {
+ EXPECT_EQ(1, mIndex++);
+ EXPECT_EQ(SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), getBounds(this));
+ EXPECT_TRUE(getTotalMatrix().isIdentity());
+ }
+ int mIndex = 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, SkRegion::kIntersect_Op);
+
+ 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->mIndex);
+}
+
+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));
+}
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index cf76a8691dcd..331a6acc1268 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,40 @@ 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();
+ VectorDrawableRoot* vectorDrawable = new VectorDrawableRoot(group);
+ auto rootNode = TestUtils::createNode(0, 0, 200, 400,
+ [&](RenderProperties& props, Canvas& canvas) {
+ canvas.drawVectorDrawable(vectorDrawable);
+ });
+ 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);
+
+ delete vectorDrawable;
+ 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..95f99746f7d3 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -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..899758a9e6fb
--- /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_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..17801aff40ea
--- /dev/null
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -0,0 +1,231 @@
+/*
+ * 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 <SkLiteRecorder.h>
+#include <string.h>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::skiapipeline;
+
+RENDERTHREAD_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_TEST(SkiaPipeline, renderFrameCheckBounds) {
+ auto backdropRedNode = TestUtils::createSkiaNode(1, 1, 4, 4,
+ [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+ redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+ });
+ auto contentGreenNode = TestUtils::createSkiaNode(2, 2, 5, 5,
+ [](RenderProperties& props, SkiaRecordingCanvas& blueCanvas) {
+ blueCanvas.drawColor(SK_ColorGREEN, SkBlendMode::kSrcOver);
+ });
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRect::MakeLargest();
+ std::vector<sp<RenderNode>> renderNodes;
+ renderNodes.push_back(backdropRedNode); //first node is backdrop
+ renderNodes.push_back(contentGreenNode); //second node is content drawn on top of the backdrop
+ android::uirenderer::Rect contentDrawBounds(1, 1, 3, 3);
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ auto surface = SkSurface::MakeRasterN32Premul(5, 5);
+ surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ //backdropBounds is (1, 1, 3, 3), content clip is (1, 1, 3, 3), content translate is (0, 0)
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surface, 2, 2), SK_ColorGREEN);
+ ASSERT_EQ(TestUtils::getColor(surface, 3, 3), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surface, 4, 4), SK_ColorBLUE);
+
+ surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
+ contentDrawBounds.set(0, 0, 5, 5);
+ //backdropBounds is (1, 1, 4, 4), content clip is (0, 0, 3, 3), content translate is (1, 1)
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
+ ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
+ ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surface, 2, 2), SK_ColorRED);
+ ASSERT_EQ(TestUtils::getColor(surface, 3, 3), SK_ColorGREEN);
+ ASSERT_EQ(TestUtils::getColor(surface, 4, 4), SK_ColorBLUE);
+}
+
+RENDERTHREAD_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_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_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_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;
+ android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1);
+ 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);
+ 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);
+}
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 b49c1eb1dc05..94818b2dc44f 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..fa3e13dd71ba 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,14 +86,9 @@ 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() {
@@ -115,12 +110,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 +167,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/input/PointerController.cpp b/libs/input/PointerController.cpp
index abef66f9ecd9..0b22ad5ba470 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 0bc832a8fb37..18ebd47558bc 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/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;
}