summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/hwui/Android.bp390
-rw-r--r--libs/hwui/Android.mk384
-rw-r--r--libs/hwui/AndroidTest.xml36
-rw-r--r--libs/hwui/BakedOpRenderer.cpp6
-rw-r--r--libs/hwui/BakedOpRenderer.h4
-rw-r--r--libs/hwui/Glop.h1
-rw-r--r--libs/hwui/GlopBuilder.cpp1
-rw-r--r--libs/hwui/JankTracker.cpp1
-rw-r--r--libs/hwui/OpenGLReadback.cpp19
-rw-r--r--libs/hwui/OpenGLReadback.h3
-rw-r--r--libs/hwui/ProgramCache.cpp31
-rw-r--r--libs/hwui/Readback.h2
-rw-r--r--libs/hwui/RecordingCanvas.cpp13
-rw-r--r--libs/hwui/RecordingCanvas.h9
-rw-r--r--libs/hwui/SkiaCanvas.cpp29
-rw-r--r--libs/hwui/SkiaCanvas.h9
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp86
-rw-r--r--libs/hwui/Texture.cpp13
-rw-r--r--libs/hwui/Texture.h8
-rw-r--r--libs/hwui/VectorDrawable.cpp30
-rw-r--r--libs/hwui/VectorDrawable.h25
-rw-r--r--libs/hwui/hwui/Bitmap.cpp321
-rw-r--r--libs/hwui/hwui/Bitmap.h65
-rw-r--r--libs/hwui/hwui/Canvas.cpp51
-rw-r--r--libs/hwui/hwui/Canvas.h13
-rw-r--r--libs/hwui/hwui_static_deps.mk33
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp38
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp3
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.h2
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp192
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h10
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp30
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp69
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h26
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp70
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp28
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.h7
-rw-r--r--libs/hwui/renderstate/OffscreenBufferPool.cpp22
-rw-r--r--libs/hwui/renderstate/OffscreenBufferPool.h15
-rw-r--r--libs/hwui/renderstate/RenderState.cpp32
-rw-r--r--libs/hwui/renderstate/RenderState.h2
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp187
-rw-r--r--libs/hwui/renderthread/CacheManager.h92
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp52
-rw-r--r--libs/hwui/renderthread/CanvasContext.h6
-rw-r--r--libs/hwui/renderthread/EglManager.cpp106
-rw-r--r--libs/hwui/renderthread/EglManager.h5
-rw-r--r--libs/hwui/renderthread/IRenderPipeline.h14
-rw-r--r--libs/hwui/renderthread/OpenGLPipeline.cpp185
-rw-r--r--libs/hwui/renderthread/OpenGLPipeline.h10
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp27
-rw-r--r--libs/hwui/renderthread/RenderProxy.h3
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp71
-rw-r--r--libs/hwui/renderthread/RenderThread.h10
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp3
-rw-r--r--libs/hwui/service/GraphicsStatsService.cpp116
-rw-r--r--libs/hwui/tests/common/TestUtils.h11
-rw-r--r--libs/hwui/tests/common/scenes/BitmapShaders.cpp4
-rw-r--r--libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp4
-rw-r--r--libs/hwui/tests/microbench/FrameBuilderBench.cpp4
-rwxr-xr-xlibs/hwui/tests/scripts/process_systrace.py34
-rw-r--r--libs/hwui/tests/scripts/systrace_parser.html89
-rw-r--r--libs/hwui/tests/unit/BakedOpDispatcherTests.cpp4
-rw-r--r--libs/hwui/tests/unit/BakedOpRendererTests.cpp3
-rw-r--r--libs/hwui/tests/unit/BitmapTests.cpp5
-rw-r--r--libs/hwui/tests/unit/CacheManagerTests.cpp75
-rw-r--r--libs/hwui/tests/unit/FontRendererTests.cpp2
-rw-r--r--libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp6
-rw-r--r--libs/hwui/tests/unit/LeakCheckTests.cpp4
-rw-r--r--libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp92
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp2
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp14
-rw-r--r--libs/hwui/tests/unit/SkiaCanvasTests.cpp3
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp38
-rw-r--r--libs/hwui/tests/unit/TextDropShadowCacheTests.cpp2
-rw-r--r--libs/hwui/tests/unit/TextureCacheTests.cpp2
-rw-r--r--libs/hwui/tests/unit/VectorDrawableTests.cpp35
-rw-r--r--libs/input/Android.bp44
-rw-r--r--libs/input/Android.mk54
-rw-r--r--libs/input/SpriteController.cpp14
-rw-r--r--libs/input/SpriteController.h5
81 files changed, 2362 insertions, 1204 deletions
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
new file mode 100644
index 000000000000..303d05f084aa
--- /dev/null
+++ b/libs/hwui/Android.bp
@@ -0,0 +1,390 @@
+cc_defaults {
+ name: "hwui_defaults",
+ defaults: [
+ "hwui_static_deps",
+
+ //"hwui_bugreport_font_cache_usage",
+ //"hwui_compile_for_perf",
+ ],
+
+ cflags: [
+ "-DEGL_EGLEXT_PROTOTYPES",
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DATRACE_TAG=ATRACE_TAG_VIEW",
+ "-DLOG_TAG=\"OpenGLRenderer\"",
+ "-Wall",
+ "-Wno-unused-parameter",
+ "-Wunreachable-code",
+ "-Werror",
+ "-fvisibility=hidden",
+
+ // GCC false-positives on this warning, and since we -Werror that's
+ // a problem
+ "-Wno-free-nonheap-object",
+
+ // clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629
+ "-Wno-missing-braces",
+
+ // TODO: Linear blending should be enabled by default, but we are
+ // TODO: making it an opt-in while it's a work in progress
+ //"-DANDROID_ENABLE_LINEAR_BLENDING",
+ ],
+
+ include_dirs: [
+ "external/skia/include/private",
+ "external/skia/src/core",
+ "external/skia/src/effects",
+ "external/skia/src/image",
+ "external/skia/src/utils",
+ ],
+
+ product_variables: {
+ device_uses_hwc2: {
+ cflags: ["-DUSE_HWC2"],
+ },
+ },
+}
+
+cc_defaults {
+ name: "hwui_static_deps",
+ shared_libs: [
+ "liblog",
+ "libcutils",
+ "libutils",
+ "libEGL",
+ "libGLESv2",
+ "libvulkan",
+ "libskia",
+ "libui",
+ "libgui",
+ "libprotobuf-cpp-lite",
+ "libharfbuzz_ng",
+ "libft2",
+ "libminikin",
+ "libandroidfw",
+ "libRScpp",
+ ],
+ static_libs: [
+ "libplatformprotos",
+ ],
+}
+
+cc_defaults {
+ name: "hwui_bugreport_font_cache_usage",
+ srcs: ["font/FontCacheHistoryTracker.cpp"],
+ cflags: ["-DBUGREPORT_FONT_CACHE_USAGE"],
+}
+
+cc_defaults {
+ name: "hwui_compile_for_perf",
+ // TODO: Non-arm?
+ cflags: [
+ "-fno-omit-frame-pointer",
+ "-marm",
+ "-mapcs",
+ ],
+}
+
+cc_defaults {
+ name: "hwui_debug",
+ cflags: ["-include debug/wrap_gles.h"],
+ srcs: [
+ "debug/wrap_gles.cpp",
+ "debug/DefaultGlesDriver.cpp",
+ "debug/GlesErrorCheckWrapper.cpp",
+ "debug/GlesDriver.cpp",
+ "debug/FatalBaseDriver.cpp",
+ "debug/NullGlesDriver.cpp",
+ ],
+ include_dirs: ["frameworks/native/opengl/libs/GLES2"],
+}
+
+cc_defaults {
+ name: "hwui_enable_opengl_validation",
+ defaults: ["hwui_debug"],
+ cflags: ["-DDEBUG_OPENGL=3"],
+ include_dirs: ["frameworks/native/opengl/libs/GLES2"],
+}
+
+// ------------------------
+// library
+// ------------------------
+
+cc_defaults {
+ name: "libhwui_defaults",
+ defaults: ["hwui_defaults"],
+ srcs: [
+ "hwui/Bitmap.cpp",
+ "font/CacheTexture.cpp",
+ "font/Font.cpp",
+ "hwui/Canvas.cpp",
+ "hwui/MinikinSkia.cpp",
+ "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",
+ "renderstate/PixelBufferState.cpp",
+ "renderstate/RenderState.cpp",
+ "renderstate/Scissor.cpp",
+ "renderstate/Stencil.cpp",
+ "renderstate/TextureState.cpp",
+ "renderthread/CacheManager.cpp",
+ "renderthread/CanvasContext.cpp",
+ "renderthread/OpenGLPipeline.cpp",
+ "renderthread/DrawFrameTask.cpp",
+ "renderthread/EglManager.cpp",
+ "renderthread/VulkanManager.cpp",
+ "renderthread/RenderProxy.cpp",
+ "renderthread/RenderTask.cpp",
+ "renderthread/RenderThread.cpp",
+ "renderthread/TimeLord.cpp",
+ "renderthread/Frame.cpp",
+ "service/GraphicsStatsService.cpp",
+ "thread/TaskManager.cpp",
+ "utils/Blur.cpp",
+ "utils/Color.cpp",
+ "utils/GLUtils.cpp",
+ "utils/LinearAllocator.cpp",
+ "utils/StringUtils.cpp",
+ "utils/TestWindowContext.cpp",
+ "utils/VectorDrawableUtils.cpp",
+ "AmbientShadow.cpp",
+ "AnimationContext.cpp",
+ "Animator.cpp",
+ "AnimatorManager.cpp",
+ "BakedOpDispatcher.cpp",
+ "BakedOpRenderer.cpp",
+ "BakedOpState.cpp",
+ "Caches.cpp",
+ "CanvasState.cpp",
+ "ClipArea.cpp",
+ "DamageAccumulator.cpp",
+ "DeferredLayerUpdater.cpp",
+ "DeviceInfo.cpp",
+ "DisplayList.cpp",
+ "Extensions.cpp",
+ "FboCache.cpp",
+ "FontRenderer.cpp",
+ "FrameBuilder.cpp",
+ "FrameInfo.cpp",
+ "FrameInfoVisualizer.cpp",
+ "GammaFontRenderer.cpp",
+ "GlLayer.cpp",
+ "GlopBuilder.cpp",
+ "GpuMemoryTracker.cpp",
+ "GradientCache.cpp",
+ "Image.cpp",
+ "Interpolator.cpp",
+ "JankTracker.cpp",
+ "Layer.cpp",
+ "LayerBuilder.cpp",
+ "LayerUpdateQueue.cpp",
+ "Matrix.cpp",
+ "OpDumper.cpp",
+ "OpenGLReadback.cpp",
+ "Patch.cpp",
+ "PatchCache.cpp",
+ "PathCache.cpp",
+ "PathParser.cpp",
+ "PathTessellator.cpp",
+ "PixelBuffer.cpp",
+ "ProfileRenderer.cpp",
+ "Program.cpp",
+ "ProgramCache.cpp",
+ "Properties.cpp",
+ "PropertyValuesAnimatorSet.cpp",
+ "PropertyValuesHolder.cpp",
+ "RecordingCanvas.cpp",
+ "RenderBufferCache.cpp",
+ "RenderNode.cpp",
+ "RenderProperties.cpp",
+ "ResourceCache.cpp",
+ "ShadowTessellator.cpp",
+ "SkiaCanvas.cpp",
+ "SkiaCanvasProxy.cpp",
+ "SkiaShader.cpp",
+ "Snapshot.cpp",
+ "SpotShadow.cpp",
+ "TessellationCache.cpp",
+ "TextDropShadowCache.cpp",
+ "Texture.cpp",
+ "TextureCache.cpp",
+ "VectorDrawable.cpp",
+ "VkLayer.cpp",
+ "protos/hwui.proto",
+ ],
+
+ proto: {
+ export_proto_headers: true,
+ },
+
+ export_include_dirs: ["."],
+ export_shared_lib_headers: ["libRScpp"],
+}
+
+cc_library {
+ name: "libhwui",
+ defaults: [
+ "libhwui_defaults",
+
+ // Enables fine-grained GLES error checking
+ // If enabled, every GLES call is wrapped & error checked
+ // Has moderate overhead
+ "hwui_enable_opengl_validation",
+],
+}
+
+// ------------------------
+// static library null gpu
+// ------------------------
+
+cc_library_static {
+ name: "libhwui_static_debug",
+ defaults: [
+ "libhwui_defaults",
+ "hwui_debug",
+ ],
+ cflags: ["-DHWUI_NULL_GPU"],
+ srcs: [
+ "debug/nullegl.cpp",
+ ],
+}
+
+cc_defaults {
+ name: "hwui_test_defaults",
+ defaults: ["hwui_defaults"],
+ test_suites: ["device-tests"],
+ srcs: [
+ "tests/common/scenes/*.cpp",
+ "tests/common/LeakChecker.cpp",
+ "tests/common/TestListViewSceneBase.cpp",
+ "tests/common/TestContext.cpp",
+ "tests/common/TestScene.cpp",
+ "tests/common/TestUtils.cpp",
+ ],
+}
+
+// ------------------------
+// unit tests
+// ------------------------
+
+cc_test {
+ name: "hwui_unit_tests",
+ defaults: ["hwui_test_defaults"],
+
+ static_libs: [
+ "libgmock",
+ "libhwui_static_debug",
+ ],
+ shared_libs: ["libmemunreachable"],
+ cflags: [
+ "-include debug/wrap_gles.h",
+ "-DHWUI_NULL_GPU",
+ ],
+
+ srcs: [
+ "tests/unit/main.cpp",
+ "tests/unit/BakedOpDispatcherTests.cpp",
+ "tests/unit/BakedOpRendererTests.cpp",
+ "tests/unit/BakedOpStateTests.cpp",
+ "tests/unit/BitmapTests.cpp",
+ "tests/unit/CacheManagerTests.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/GraphicsStatsServiceTests.cpp",
+ "tests/unit/LayerUpdateQueueTests.cpp",
+ "tests/unit/LeakCheckTests.cpp",
+ "tests/unit/LinearAllocatorTests.cpp",
+ "tests/unit/MatrixTests.cpp",
+ "tests/unit/MeshStateTests.cpp",
+ "tests/unit/OffscreenBufferPoolTests.cpp",
+ "tests/unit/OpDumperTests.cpp",
+ "tests/unit/PathInterpolatorTests.cpp",
+ "tests/unit/RenderNodeDrawableTests.cpp",
+ "tests/unit/RecordingCanvasTests.cpp",
+ "tests/unit/RenderNodeTests.cpp",
+ "tests/unit/RenderPropertiesTests.cpp",
+ "tests/unit/SkiaBehaviorTests.cpp",
+ "tests/unit/SkiaDisplayListTests.cpp",
+ "tests/unit/SkiaPipelineTests.cpp",
+ "tests/unit/SkiaRenderPropertiesTests.cpp",
+ "tests/unit/SkiaCanvasTests.cpp",
+ "tests/unit/SnapshotTests.cpp",
+ "tests/unit/StringUtilsTests.cpp",
+ "tests/unit/TestUtilsTests.cpp",
+ "tests/unit/TextDropShadowCacheTests.cpp",
+ "tests/unit/TextureCacheTests.cpp",
+ "tests/unit/TypefaceTests.cpp",
+ "tests/unit/VectorDrawableTests.cpp",
+ ],
+}
+
+// ------------------------
+// Macro-bench app
+// ------------------------
+
+cc_benchmark {
+ name: "hwuimacro",
+ defaults: ["hwui_test_defaults"],
+
+ // set to libhwui_static_debug to skip actual GL commands
+ whole_static_libs: ["libhwui"],
+ shared_libs: ["libmemunreachable"],
+
+ srcs: [
+ "tests/macrobench/TestSceneRunner.cpp",
+ "tests/macrobench/main.cpp",
+ ],
+}
+
+// ------------------------
+// Micro-bench app
+// ---------------------
+
+cc_benchmark {
+ name: "hwuimicro",
+ defaults: ["hwui_test_defaults"],
+
+ cflags: [
+ "-include debug/wrap_gles.h",
+ "-DHWUI_NULL_GPU",
+ ],
+
+ whole_static_libs: ["libhwui_static_debug"],
+ shared_libs: ["libmemunreachable"],
+
+ srcs: [
+ "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",
+ ],
+}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
deleted file mode 100644
index 030e8456f01c..000000000000
--- a/libs/hwui/Android.mk
+++ /dev/null
@@ -1,384 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-BUGREPORT_FONT_CACHE_USAGE := false
-
-# Enables fine-grained GLES error checking
-# If set to true, every GLES call is wrapped & error checked
-# Has moderate overhead
-HWUI_ENABLE_OPENGL_VALIDATION := false
-
-hwui_src_files := \
- hwui/Bitmap.cpp \
- font/CacheTexture.cpp \
- font/Font.cpp \
- hwui/Canvas.cpp \
- hwui/MinikinSkia.cpp \
- 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 \
- renderstate/PixelBufferState.cpp \
- renderstate/RenderState.cpp \
- renderstate/Scissor.cpp \
- renderstate/Stencil.cpp \
- renderstate/TextureState.cpp \
- renderthread/CanvasContext.cpp \
- renderthread/OpenGLPipeline.cpp \
- renderthread/DrawFrameTask.cpp \
- renderthread/EglManager.cpp \
- renderthread/VulkanManager.cpp \
- renderthread/RenderProxy.cpp \
- renderthread/RenderTask.cpp \
- renderthread/RenderThread.cpp \
- renderthread/TimeLord.cpp \
- renderthread/Frame.cpp \
- service/GraphicsStatsService.cpp \
- thread/TaskManager.cpp \
- utils/Blur.cpp \
- utils/Color.cpp \
- utils/GLUtils.cpp \
- utils/LinearAllocator.cpp \
- utils/StringUtils.cpp \
- utils/TestWindowContext.cpp \
- utils/VectorDrawableUtils.cpp \
- AmbientShadow.cpp \
- AnimationContext.cpp \
- Animator.cpp \
- AnimatorManager.cpp \
- BakedOpDispatcher.cpp \
- BakedOpRenderer.cpp \
- BakedOpState.cpp \
- Caches.cpp \
- CanvasState.cpp \
- ClipArea.cpp \
- DamageAccumulator.cpp \
- DeferredLayerUpdater.cpp \
- DeviceInfo.cpp \
- DisplayList.cpp \
- Extensions.cpp \
- FboCache.cpp \
- FontRenderer.cpp \
- FrameBuilder.cpp \
- FrameInfo.cpp \
- FrameInfoVisualizer.cpp \
- GammaFontRenderer.cpp \
- GlLayer.cpp \
- GlopBuilder.cpp \
- GpuMemoryTracker.cpp \
- GradientCache.cpp \
- Image.cpp \
- Interpolator.cpp \
- JankTracker.cpp \
- Layer.cpp \
- LayerBuilder.cpp \
- LayerUpdateQueue.cpp \
- Matrix.cpp \
- OpDumper.cpp \
- OpenGLReadback.cpp \
- Patch.cpp \
- PatchCache.cpp \
- PathCache.cpp \
- PathParser.cpp \
- PathTessellator.cpp \
- PixelBuffer.cpp \
- ProfileRenderer.cpp \
- Program.cpp \
- ProgramCache.cpp \
- Properties.cpp \
- PropertyValuesAnimatorSet.cpp \
- PropertyValuesHolder.cpp \
- RecordingCanvas.cpp \
- RenderBufferCache.cpp \
- RenderNode.cpp \
- RenderProperties.cpp \
- ResourceCache.cpp \
- ShadowTessellator.cpp \
- SkiaCanvas.cpp \
- SkiaCanvasProxy.cpp \
- SkiaShader.cpp \
- Snapshot.cpp \
- SpotShadow.cpp \
- TessellationCache.cpp \
- TextDropShadowCache.cpp \
- Texture.cpp \
- TextureCache.cpp \
- VectorDrawable.cpp \
- VkLayer.cpp \
- protos/hwui.proto
-
-hwui_test_common_src_files := \
- $(call all-cpp-files-under, tests/common/scenes) \
- tests/common/LeakChecker.cpp \
- tests/common/TestListViewSceneBase.cpp \
- tests/common/TestContext.cpp \
- tests/common/TestScene.cpp \
- tests/common/TestUtils.cpp
-
-hwui_debug_common_src_files := \
- debug/wrap_gles.cpp \
- debug/DefaultGlesDriver.cpp \
- debug/GlesErrorCheckWrapper.cpp \
- debug/GlesDriver.cpp \
- debug/FatalBaseDriver.cpp \
- debug/NullGlesDriver.cpp
-
-hwui_cflags := \
- -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES \
- -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" \
- -Wall -Wno-unused-parameter -Wunreachable-code -Werror
-
-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
-
-# 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 += \
- font/FontCacheHistoryTracker.cpp
- hwui_cflags += -DBUGREPORT_FONT_CACHE_USAGE
-endif
-
-ifndef HWUI_COMPILE_SYMBOLS
- hwui_cflags += -fvisibility=hidden
-endif
-
-ifdef HWUI_COMPILE_FOR_PERF
- # TODO: Non-arm?
- hwui_cflags += -fno-omit-frame-pointer -marm -mapcs
-endif
-
-# This has to be lazy-resolved because it depends on the LOCAL_MODULE_CLASS
-# which varies depending on what is being built
-define hwui_proto_include
-$(call local-generated-sources-dir)/proto/$(LOCAL_PATH)
-endef
-
-hwui_c_includes += \
- external/skia/include/private \
- external/skia/src/core \
- external/skia/src/effects \
- external/skia/src/image \
- external/skia/src/utils \
- external/icu/icu4c/source/common \
- external/harfbuzz_ng/src \
- external/freetype/include
-
-# enable RENDERSCRIPT
-hwui_c_includes += \
- $(call intermediates-dir-for,STATIC_LIBRARIES,TARGET,) \
- frameworks/rs/cpp \
- frameworks/rs
-
-# ------------------------
-# static library
-# ------------------------
-
-include $(CLEAR_VARS)
-
-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) \
- $(call hwui_proto_include)
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_STATIC_LIBRARY)
-
-# ------------------------
-# static library null gpu
-# ------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_CLASS := STATIC_LIBRARIES
-LOCAL_MODULE := libhwui_static_debug
-LOCAL_CFLAGS := \
- $(hwui_cflags) \
- -include debug/wrap_gles.h \
- -DHWUI_NULL_GPU
-LOCAL_SRC_FILES := \
- $(hwui_src_files) \
- $(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) \
- $(call hwui_proto_include)
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_STATIC_LIBRARY)
-
-# ------------------------
-# shared library
-# ------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-LOCAL_MODULE := libhwui
-LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_SHARED_LIBRARY)
-
-# ------------------------
-# unit tests
-# ------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := hwui_unit_tests
-LOCAL_MODULE_TAGS := tests
-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/GraphicsStatsServiceTests.cpp \
- tests/unit/LayerUpdateQueueTests.cpp \
- tests/unit/LeakCheckTests.cpp \
- tests/unit/LinearAllocatorTests.cpp \
- tests/unit/MatrixTests.cpp \
- tests/unit/MeshStateTests.cpp \
- tests/unit/OffscreenBufferPoolTests.cpp \
- tests/unit/OpDumperTests.cpp \
- tests/unit/PathInterpolatorTests.cpp \
- tests/unit/RenderNodeDrawableTests.cpp \
- tests/unit/RecordingCanvasTests.cpp \
- tests/unit/RenderNodeTests.cpp \
- tests/unit/RenderPropertiesTests.cpp \
- tests/unit/SkiaBehaviorTests.cpp \
- tests/unit/SkiaDisplayListTests.cpp \
- tests/unit/SkiaPipelineTests.cpp \
- tests/unit/SkiaRenderPropertiesTests.cpp \
- tests/unit/SkiaCanvasTests.cpp \
- tests/unit/SnapshotTests.cpp \
- tests/unit/StringUtilsTests.cpp \
- tests/unit/TestUtilsTests.cpp \
- tests/unit/TextDropShadowCacheTests.cpp \
- tests/unit/TextureCacheTests.cpp \
- tests/unit/TypefaceTests.cpp \
- tests/unit/VectorDrawableTests.cpp \
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_NATIVE_TEST)
-
-# ------------------------
-# Macro-bench app
-# ------------------------
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
-LOCAL_MODULE:= hwuimacro
-LOCAL_MODULE_TAGS := tests
-LOCAL_MULTILIB := both
-LOCAL_CFLAGS := $(hwui_cflags)
-LOCAL_C_INCLUDES := $(hwui_c_includes)
-
-# 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) \
- tests/macrobench/TestSceneRunner.cpp \
- tests/macrobench/main.cpp
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_NATIVE_BENCHMARK)
-
-# ------------------------
-# Micro-bench app
-# ---------------------
-include $(CLEAR_VARS)
-
-LOCAL_MODULE:= hwuimicro
-LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS := \
- $(hwui_cflags) \
- -include debug/wrap_gles.h \
- -DHWUI_NULL_GPU
-
-LOCAL_C_INCLUDES := $(hwui_c_includes)
-
-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
-
-
-include $(LOCAL_PATH)/hwui_static_deps.mk
-include $(BUILD_NATIVE_BENCHMARK)
diff --git a/libs/hwui/AndroidTest.xml b/libs/hwui/AndroidTest.xml
new file mode 100644
index 000000000000..eab32c5a67ce
--- /dev/null
+++ b/libs/hwui/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for hwuimicro">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="hwui_unit_tests->/data/nativetest/hwui_unit_tests" />
+ <option name="push" value="hwuimicro->/data/benchmarktest/hwuimicro" />
+ <option name="push" value="hwuimacro->/data/benchmarktest/hwuimacro" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/nativetest" />
+ <option name="module-name" value="hwui_unit_tests" />
+ </test>
+ <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
+ <option name="native-benchmark-device-path" value="/data/benchmarktest" />
+ <option name="benchmark-module-name" value="hwuimicro" />
+ </test>
+ <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
+ <option name="native-benchmark-device-path" value="/data/benchmarktest" />
+ <option name="benchmark-module-name" value="hwuimacro" />
+ </test>
+</configuration>
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index d154730058ee..df2b35b39aa8 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -32,7 +32,8 @@ namespace uirenderer {
OffscreenBuffer* BakedOpRenderer::startTemporaryLayer(uint32_t width, uint32_t height) {
LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
- OffscreenBuffer* buffer = mRenderState.layerPool().get(mRenderState, width, height);
+ OffscreenBuffer* buffer = mRenderState.layerPool().get(
+ mRenderState, width, height, mWideColorGamut);
startRepaintLayer(buffer, Rect(width, height));
return buffer;
}
@@ -103,7 +104,8 @@ void BakedOpRenderer::endLayer() {
OffscreenBuffer* BakedOpRenderer::copyToLayer(const Rect& area) {
const uint32_t width = area.getWidth();
const uint32_t height = area.getHeight();
- OffscreenBuffer* buffer = mRenderState.layerPool().get(mRenderState, width, height);
+ OffscreenBuffer* buffer = mRenderState.layerPool().get(
+ mRenderState, width, height, mWideColorGamut);
if (!area.isEmpty() && width != 0 && height != 0) {
mCaches.textureState().activateTexture(0);
mCaches.textureState().bindTexture(buffer->texture.id());
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 4d76a3df7a62..01ca36742d24 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -54,12 +54,13 @@ public:
uint8_t spotShadowAlpha;
};
- BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque,
+ BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque, bool wideColorGamut,
const LightInfo& lightInfo)
: mGlopReceiver(DefaultGlopReceiver)
, mRenderState(renderState)
, mCaches(caches)
, mOpaque(opaque)
+ , mWideColorGamut(wideColorGamut)
, mLightInfo(lightInfo) {
}
@@ -118,6 +119,7 @@ private:
RenderState& mRenderState;
Caches& mCaches;
bool mOpaque;
+ bool mWideColorGamut;
bool mHasDrawn = false;
// render target state - setup by start/end layer/frame
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index 34c7934db198..e91c08d5a351 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -110,6 +110,7 @@ public:
} vertices;
int elementCount;
+ int vertexCount; // only used for meshes (for glDrawRangeElements)
TextureVertex mappedVertices[4];
} mesh;
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 248e92f7c97b..c19c1a11e3e2 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -199,6 +199,7 @@ GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer)
alphaVertex ? kAlphaVertexStride : kVertexStride };
mOutGlop->mesh.elementCount = indices
? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount();
+ mOutGlop->mesh.vertexCount = vertexBuffer.getVertexCount(); // used for glDrawRangeElements()
return *this;
}
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 8126d57a3a79..028d9f756fb7 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -291,7 +291,6 @@ void JankTracker::addFrame(const FrameInfo& frame) {
/ kSlowFrameBucketIntervalMs;
framebucket = std::min(framebucket,
static_cast<uint32_t>(mData->slowFrameCounts.size() - 1));
- framebucket = std::max(framebucket, 0u);
mData->slowFrameCounts[framebucket]++;
}
diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
index 51927d5a2c61..19d5d9d2250e 100644
--- a/libs/hwui/OpenGLReadback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -27,6 +27,7 @@
#include <GLES2/gl2.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
+#include <gui/Surface.h>
namespace android {
namespace uirenderer {
@@ -132,8 +133,7 @@ inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
return CopyResult::DestinationInvalid;
}
- // TODO: Add support for RGBA_F16 destinations
- if (bitmap->colorType() == kRGBA_F16_SkColorType) {
+ if (bitmap->colorType() == kRGBA_F16_SkColorType && !caches.extensions().hasFloatTextures()) {
ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
return CopyResult::DestinationInvalid;
}
@@ -144,29 +144,37 @@ inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
return CopyResult::UnknownError;
}
- SkAutoLockPixels alp(*bitmap);
-
GLuint texture;
GLenum format;
+ GLenum internalFormat;
GLenum type;
switch (bitmap->colorType()) {
case kAlpha_8_SkColorType:
format = GL_ALPHA;
+ internalFormat = GL_ALPHA;
type = GL_UNSIGNED_BYTE;
break;
case kRGB_565_SkColorType:
format = GL_RGB;
+ internalFormat = GL_RGB;
type = GL_UNSIGNED_SHORT_5_6_5;
break;
case kARGB_4444_SkColorType:
format = GL_RGBA;
+ internalFormat = GL_RGBA;
type = GL_UNSIGNED_SHORT_4_4_4_4;
break;
+ case kRGBA_F16_SkColorType:
+ format = GL_RGBA;
+ internalFormat = GL_RGBA16F;
+ type = GL_HALF_FLOAT;
+ break;
case kN32_SkColorType:
default:
format = GL_RGBA;
+ internalFormat = GL_RGBA;
type = GL_UNSIGNED_BYTE;
break;
}
@@ -185,7 +193,7 @@ inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
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, destWidth, destHeight,
+ glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, destWidth, destHeight,
0, format, type, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texture, 0);
@@ -227,6 +235,7 @@ inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
ortho.loadOrtho(destWidth, destHeight);
renderState.render(glop, ortho);
+ // TODO: We should convert to linear space when the target is RGBA16F
glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
type, bitmap->getPixels());
}
diff --git a/libs/hwui/OpenGLReadback.h b/libs/hwui/OpenGLReadback.h
index c9222cff51da..403f2e34d515 100644
--- a/libs/hwui/OpenGLReadback.h
+++ b/libs/hwui/OpenGLReadback.h
@@ -18,6 +18,9 @@
#include "Readback.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index d0f0949d5e78..8cc0aa7b414c 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -58,7 +58,9 @@ const char* gVS_Header_Uniforms_HasBitmap =
"uniform mat4 textureTransform;\n"
"uniform mediump vec2 textureDimension;\n";
const char* gVS_Header_Uniforms_HasRoundRectClip =
- "uniform mat4 roundRectInvTransform;\n";
+ "uniform mat4 roundRectInvTransform;\n"
+ "uniform mediump vec4 roundRectInnerRectLTWH;\n"
+ "uniform mediump float roundRectRadius;\n";
const char* gVS_Header_Varyings_HasTexture =
"varying vec2 outTexCoords;\n";
const char* gVS_Header_Varyings_HasColors =
@@ -81,7 +83,7 @@ const char* gVS_Header_Varyings_HasGradient[6] = {
"varying highp vec2 sweep;\n",
};
const char* gVS_Header_Varyings_HasRoundRectClip =
- "varying highp vec2 roundRectPos;\n";
+ "varying mediump vec2 roundRectPos;\n";
const char* gVS_Main =
"\nvoid main(void) {\n";
const char* gVS_Main_OutTexCoords =
@@ -113,7 +115,7 @@ const char* gVS_Main_VertexAlpha =
" alpha = vtxAlpha;\n";
const char* gVS_Main_HasRoundRectClip =
- " roundRectPos = (roundRectInvTransform * transformedPosition).xy;\n";
+ " roundRectPos = ((roundRectInvTransform * transformedPosition).xy / roundRectRadius) - roundRectInnerRectLTWH.xy;\n";
const char* gVS_Footer =
"}\n\n";
@@ -158,8 +160,8 @@ const char* gFS_Uniforms_ColorOp[3] = {
};
const char* gFS_Uniforms_HasRoundRectClip =
- "uniform vec4 roundRectInnerRectLTRB;\n"
- "uniform float roundRectRadius;\n";
+ "uniform mediump vec4 roundRectInnerRectLTWH;\n"
+ "uniform mediump float roundRectRadius;\n";
const char* gFS_Uniforms_ColorSpaceConversion =
// TODO: Should we use a 3D LUT to combine the matrix and transfer functions?
@@ -431,15 +433,18 @@ const char* gFS_Main_ApplyColorOp[3] = {
" fragColor = blendColors(colorBlend, fragColor);\n"
};
-// Note: LTRB -> xyzw
+// Note: LTWH (left top width height) -> xyzw
+// roundRectPos is now divided by roundRectRadius in vertex shader
+// after we also subtract roundRectInnerRectLTWH.xy from roundRectPos
const char* gFS_Main_FragColor_HasRoundRectClip =
- " mediump vec2 fragToLT = roundRectInnerRectLTRB.xy - roundRectPos;\n"
- " mediump vec2 fragFromRB = roundRectPos - roundRectInnerRectLTRB.zw;\n"
-
- // divide + multiply by 128 to avoid falling out of range in length() function
- " mediump vec2 dist = max(max(fragToLT, fragFromRB), vec2(0.0, 0.0)) / 128.0;\n"
- " mediump float linearDist = roundRectRadius - (length(dist) * 128.0);\n"
- " gl_FragColor *= clamp(linearDist, 0.0, 1.0);\n";
+ " mediump vec2 fragToLT = -roundRectPos;\n"
+ " mediump vec2 fragFromRB = roundRectPos - roundRectInnerRectLTWH.zw;\n"
+
+ // since distance is divided by radius, it's in [0;1] so precision is not an issue
+ // this also lets us clamp(0.0, 1.0) instead of max() which is cheaper on GPUs
+ " mediump vec2 dist = clamp(max(fragToLT, fragFromRB), 0.0, 1.0);\n"
+ " mediump float linearDist = clamp(roundRectRadius - (length(dist) * roundRectRadius), 0.0, 1.0);\n"
+ " gl_FragColor *= linearDist;\n";
const char* gFS_Main_DebugHighlight =
" gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n";
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index b76395301a21..7cf426228af6 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -20,10 +20,10 @@
#include "Rect.h"
#include <SkBitmap.h>
-#include <gui/Surface.h>
namespace android {
class GraphicBuffer;
+class Surface;
namespace uirenderer {
// Keep in sync with PixelCopy.java codes
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 374c1b174640..d966372a7699 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -540,12 +540,13 @@ void RecordingCanvas::drawNinePatch(Bitmap& bitmap, const android::Res_png_9patc
}
// Text
-void RecordingCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions, int glyphCount,
- const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
- float boundsRight, float boundsBottom, float totalAdvance) {
- if (!glyphs || !positions || glyphCount <= 0 || paint.nothingToDraw()) return;
- glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
- positions = refBuffer<float>(positions, glyphCount * 2);
+void RecordingCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int glyphCount, const SkPaint& paint,
+ float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) {
+ if (glyphCount <= 0 || paint.nothingToDraw()) return;
+ uint16_t* glyphs = (glyph_t*)alloc().alloc<glyph_t>(glyphCount * sizeof(glyph_t));
+ float* positions = (float*)alloc().alloc<float>(2 * glyphCount * sizeof(float));
+ glyphFunc(glyphs, positions);
// TODO: either must account for text shadow in bounds, or record separate ops for text shadows
addOp(alloc().create_trivial<TextOp>(
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 44181bd22397..ccdb4b0c1d8e 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -168,9 +168,7 @@ public:
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 drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) override
{ /* RecordingCanvas does not support drawVertices(); ignore */ }
virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
@@ -191,9 +189,8 @@ public:
virtual bool drawTextAbsolutePos() const override { return false; }
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,
+ virtual void drawGlyphs(ReadGlyphFunc glyphFunc, 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;
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 3e5e3bfc3bf2..623b496f5f09 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -482,7 +482,7 @@ void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint)
void SkiaCanvas::drawRect(float left, float top, float right, float bottom,
const SkPaint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
- mCanvas->drawRectCoords(left, top, right, bottom, paint);
+ mCanvas->drawRect({left, top, right, bottom}, paint);
}
@@ -521,17 +521,8 @@ void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
mCanvas->drawPath(path, paint);
}
-void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
- const float* verts, const float* texs, const int* colors,
- const uint16_t* indices, int indexCount, const SkPaint& paint) {
-#ifndef SK_SCALAR_IS_FLOAT
- SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
-#endif
- const int ptCount = vertexCount >> 1;
- mCanvas->drawVertices(SkVertices::MakeCopy(vertexMode, ptCount, (SkPoint*)verts,
- (SkPoint*)texs, (SkColor*)colors,
- indexCount, indices),
- SkBlendMode::kModulate, paint);
+void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
+ mCanvas->drawVertices(vertices, mode, paint);
}
// ----------------------------------------------------------------------------
@@ -572,7 +563,7 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeigh
if (colors) {
flags |= SkVertices::kHasColors_BuilderFlag;
}
- SkVertices::Builder builder(SkCanvas::kTriangles_VertexMode, ptCount, indexCount, flags);
+ SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, ptCount, indexCount, flags);
memcpy(builder.positions(), vertices, ptCount * sizeof(SkPoint));
if (colors) {
memcpy(builder.colors(), colors, ptCount * sizeof(SkColor));
@@ -685,11 +676,10 @@ void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
// Canvas draw operations: Text
// ----------------------------------------------------------------------------
-void SkiaCanvas::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,
+void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x,
+ float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
float totalAdvance) {
- if (!text || !positions || count <= 0 || paint.nothingToDraw()) return;
+ if (count <= 0 || paint.nothingToDraw()) 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.
@@ -701,10 +691,7 @@ void SkiaCanvas::drawGlyphs(const uint16_t* text, const float* positions, int co
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));
+ glyphFunc(buffer.glyphs, buffer.pos);
sk_sp<SkTextBlob> textBlob(builder.make());
mCanvas->drawTextBlob(textBlob, 0, 0, paintCopy);
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index aeecafafbae2..af2c23e4a2b7 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -123,9 +123,7 @@ public:
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 drawVertices(const SkVertices*, SkBlendMode, 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;
@@ -159,9 +157,8 @@ protected:
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,
+ virtual void drawGlyphs(ReadGlyphFunc glyphFunc, 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;
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index f6e92dca2bb9..34dddd169e96 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -183,18 +183,10 @@ void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& la
void SkiaCanvasProxy::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
const SkPaint& paint) {
- // TODO: should we pass through blendmode
if (mFilterHwuiCalls) {
return;
}
- // convert the SkPoints into floats
- static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
- const int floatCount = vertices->vertexCount() << 1;
- const float* vArray = (const float*)vertices->positions();
- const float* tArray = (const float*)vertices->texCoords();
- const int* cArray = (const int*)vertices->colors();
- mCanvas->drawVertices(vertices->mode(), floatCount, vArray, tArray, cArray,
- vertices->indices(), vertices->indexCount(), paint);
+ mCanvas->drawVertices(vertices, bmode, paint);
}
sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
@@ -286,7 +278,6 @@ void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x
GlyphIDConverter glyphs(text, byteLength, origPaint);
// compute the glyph positions
- std::unique_ptr<SkPoint[]> pointStorage(new SkPoint[glyphs.count]);
std::unique_ptr<SkScalar[]> glyphWidths(new SkScalar[glyphs.count]);
glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
@@ -320,22 +311,33 @@ void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x
xBaseline = x;
yBaseline = y;
}
- pointStorage[0].set(xBaseline, yBaseline);
-
- // setup the remaining glyph positions
- if (glyphs.paint.isVerticalText()) {
- for (int i = 1; i < glyphs.count; i++) {
- pointStorage[i].set(xBaseline, glyphWidths[i-1] + pointStorage[i-1].fY);
- }
- } else {
- for (int i = 1; i < glyphs.count; i++) {
- pointStorage[i].set(glyphWidths[i-1] + pointStorage[i-1].fX, yBaseline);
- }
- }
static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
- mCanvas->drawGlyphs(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint,
- x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+ auto glyphFunc = [&] (uint16_t* text, float* positions) {
+ memcpy(text, glyphs.glyphIDs, glyphs.count*sizeof(uint16_t));
+ size_t posIndex = 0;
+ // setup the first glyph position
+ positions[posIndex++] = xBaseline;
+ positions[posIndex++] = yBaseline;
+ // setup the remaining glyph positions
+ if (glyphs.paint.isVerticalText()) {
+ float yPosition = yBaseline;
+ for (int i = 1; i < glyphs.count; i++) {
+ positions[posIndex++] = xBaseline;
+ yPosition += glyphWidths[i-1];
+ positions[posIndex++] = yPosition;
+ }
+ } else {
+ float xPosition = xBaseline;
+ for (int i = 1; i < glyphs.count; i++) {
+ xPosition += glyphWidths[i-1];
+ positions[posIndex++] = xPosition;
+ positions[posIndex++] = yBaseline;
+ }
+ }
+ };
+ mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop,
+ bounds.fRight, bounds.fBottom, 0);
}
void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
@@ -345,21 +347,12 @@ void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const S
// convert to relative positions if necessary
int x, y;
- const SkPoint* posArray;
- std::unique_ptr<SkPoint[]> pointStorage;
if (mCanvas->drawTextAbsolutePos()) {
x = 0;
y = 0;
- posArray = pos;
} else {
x = pos[0].fX;
y = pos[0].fY;
- pointStorage.reset(new SkPoint[glyphs.count]);
- for (int i = 0; i < glyphs.count; i++) {
- pointStorage[i].fX = pos[i].fX - x;
- pointStorage[i].fY = pos[i].fY - y;
- }
- posArray = pointStorage.get();
}
// Compute conservative bounds. If the content has already been processed
@@ -375,8 +368,19 @@ void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const S
}
static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
- mCanvas->drawGlyphs(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y,
- bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+ auto glyphFunc = [&] (uint16_t* text, float* positions) {
+ memcpy(text, glyphs.glyphIDs, glyphs.count*sizeof(uint16_t));
+ if (mCanvas->drawTextAbsolutePos()) {
+ memcpy(positions, pos, 2*glyphs.count*sizeof(float));
+ } else {
+ for (int i = 0, posIndex = 0; i < glyphs.count; i++) {
+ positions[posIndex++] = pos[i].fX - x;
+ positions[posIndex++] = pos[i].fY - y;
+ }
+ }
+ };
+ mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop,
+ bounds.fRight, bounds.fBottom, 0);
}
void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
@@ -456,19 +460,13 @@ void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors
if (mFilterHwuiCalls) {
return;
}
- SkPatchUtils::VertexData data;
-
SkMatrix matrix;
mCanvas->getMatrix(&matrix);
SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
- // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
- // 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, bmode, data.fIndices, data.fIndexCount,
- paint);
- }
+ mCanvas->drawVertices(SkPatchUtils::MakeVertices(cubics, colors, texCoords,
+ lod.width(), lod.height()).get(),
+ bmode, paint);
}
void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle) {
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 959059fede0c..4ef31d59271e 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -120,6 +120,10 @@ void Texture::resetCachedParams() {
void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height,
GLenum format, GLenum type, const void* pixels) {
GL_CHECKPOINT(MODERATE);
+
+ // We don't have color space information, we assume the data is gamma encoded
+ mIsLinear = false;
+
bool needsAlloc = updateLayout(width, height, internalFormat, format, GL_TEXTURE_2D);
if (!mId) {
glGenTextures(1, &mId);
@@ -309,11 +313,16 @@ void Texture::upload(Bitmap& bitmap) {
bool rgba16fNeedsConversion = bitmap.colorType() == kRGBA_F16_SkColorType
&& internalFormat != GL_RGBA16F;
+ // RGBA16F is always linear extended sRGB
+ if (internalFormat == GL_RGBA16F) {
+ mIsLinear = true;
+ }
+
mConnector.reset();
- // RGBA16F is always extended sRGB, alpha masks don't have color profiles
+ // Alpha masks don't have color profiles
// If an RGBA16F bitmap needs conversion, we know the target will be sRGB
- if (internalFormat != GL_RGBA16F && internalFormat != GL_ALPHA && !rgba16fNeedsConversion) {
+ if (!mIsLinear && internalFormat != GL_ALPHA && !rgba16fNeedsConversion) {
SkColorSpace* colorSpace = bitmap.info().colorSpace();
// If the bitmap is sRGB we don't need conversion
if (colorSpace != nullptr && !colorSpace->isSRGB()) {
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index 55b74ed9e234..7f742e604838 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -88,7 +88,8 @@ public:
* The image data is undefined after calling this.
*/
void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) {
- upload(internalFormat, width, height, format, GL_UNSIGNED_BYTE, nullptr);
+ upload(internalFormat, width, height, format,
+ internalFormat == GL_RGBA16F ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr);
}
/**
@@ -155,7 +156,7 @@ public:
* Returns true if this texture uses a linear encoding format.
*/
constexpr bool isLinear() const {
- return mInternalFormat == GL_RGBA16F;
+ return mIsLinear;
}
/**
@@ -219,6 +220,9 @@ private:
GLenum mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
GLenum mMagFilter = GL_LINEAR;
+ // Indicates whether the content of the texture is in linear space
+ bool mIsLinear = false;
+
Caches& mCaches;
std::unique_ptr<ColorSpaceConnector> mConnector;
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 8823a9212958..f6b2912a6254 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -491,6 +491,36 @@ Bitmap& Tree::getBitmapUpdateIfDirty() {
return *mCache.bitmap;
}
+void Tree::updateCache(sk_sp<SkSurface> surface) {
+ if (surface.get()) {
+ mCache.surface = surface;
+ }
+ if (surface.get() || mCache.dirty) {
+ SkSurface* vdSurface = mCache.surface.get();
+ SkCanvas* canvas = vdSurface->getCanvas();
+ float scaleX = vdSurface->width() / mProperties.getViewportWidth();
+ float scaleY = vdSurface->height() / mProperties.getViewportHeight();
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->clear(SK_ColorTRANSPARENT);
+ canvas->scale(scaleX, scaleY);
+ mRootNode->draw(canvas, false);
+ mCache.dirty = false;
+ canvas->flush();
+ }
+}
+
+void Tree::draw(SkCanvas* canvas) {
+ /*
+ * TODO address the following...
+ *
+ * 1) figure out how to set path's as volatile during animation
+ * 2) if mRoot->getPaint() != null either promote to layer (during
+ * animation) or cache in SkSurface (for static content)
+ */
+ canvas->drawImageRect(mCache.surface->makeImageSnapshot().get(),
+ mutateProperties()->getBounds(), getPaint());
+}
+
void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
SkBitmap outCache;
bitmap.getSkBitmap(&outCache);
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 729a4dd4ba76..22cfe29d2aa5 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -31,6 +31,7 @@
#include <SkPathMeasure.h>
#include <SkRect.h>
#include <SkShader.h>
+#include <SkSurface.h>
#include <cutils/compiler.h>
#include <stddef.h>
@@ -677,15 +678,37 @@ public:
// This should only be called from animations on RT
TreeProperties* mutateProperties() { return &mProperties; }
+ // called from RT only
+ const TreeProperties& properties() const { return mProperties; }
+
// This should always be called from RT.
void markDirty() { mCache.dirty = true; }
bool isDirty() const { return mCache.dirty; }
bool getPropertyChangeWillBeConsumed() const { return mWillBeConsumed; }
void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; }
+ // Returns true if VD cache surface is big enough. This should always be called from RT and it
+ // works with Skia pipelines only.
+ bool canReuseSurface() {
+ SkSurface* surface = mCache.surface.get();
+ return surface && surface->width() >= mProperties.getScaledWidth()
+ && surface->height() >= mProperties.getScaledHeight();
+ }
+
+ // Draws VD cache into a canvas. This should always be called from RT and it works with Skia
+ // pipelines only.
+ void draw(SkCanvas* canvas);
+
+ // Draws VD into a GPU backed surface. If canReuseSurface returns false, then "surface" must
+ // contain a new surface. This should always be called from RT and it works with Skia pipelines
+ // only.
+ void updateCache(sk_sp<SkSurface> surface);
+
private:
struct Cache {
- sk_sp<Bitmap> bitmap;
+ sk_sp<Bitmap> bitmap; //used by HWUI pipeline and software
+ //TODO: use surface instead of bitmap when drawing in software canvas
+ sk_sp<SkSurface> surface; //used only by Skia pipelines
bool dirty = true;
};
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index bb1e674d4861..6dde005c2fc4 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -26,16 +26,12 @@
#include <log/log.h>
#include <cutils/ashmem.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
#include <private/gui/ComposerService.h>
#include <binder/IServiceManager.h>
#include <ui/PixelFormat.h>
#include <SkCanvas.h>
+#include <SkImagePriv.h>
namespace android {
@@ -51,9 +47,9 @@ static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) {
}
typedef sk_sp<Bitmap> (*AllocPixeRef)(size_t allocSize, const SkImageInfo& info, size_t rowBytes,
- SkColorTable* ctable);
+ sk_sp<SkColorTable> ctable);
-static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, SkColorTable* ctable, AllocPixeRef alloc) {
+static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, sk_sp<SkColorTable> ctable, AllocPixeRef alloc) {
const SkImageInfo& info = bitmap->info();
if (info.colorType() == kUnknown_SkColorType) {
LOG_ALWAYS_FATAL("unknown bitmap configuration");
@@ -69,201 +65,32 @@ static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, SkColorTable* ctable, Allo
return nullptr;
}
- auto wrapper = alloc(size, info, rowBytes, ctable);
+ auto wrapper = alloc(size, info, rowBytes, std::move(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);
+sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap, sk_sp<SkColorTable> ctable) {
+ return allocateBitmap(bitmap, std::move(ctable), &Bitmap::allocateAshmemBitmap);
}
static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes,
- SkColorTable* ctable) {
+ sk_sp<SkColorTable> ctable) {
void* addr = calloc(size, 1);
if (!addr) {
return nullptr;
}
- return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes, ctable));
-}
-
-#define FENCE_TIMEOUT 2000000000
-
-// TODO: handle SRGB sanely
-static PixelFormat internalFormatToPixelFormat(GLint internalFormat) {
- switch (internalFormat) {
- case GL_LUMINANCE:
- return PIXEL_FORMAT_RGBA_8888;
- case GL_SRGB8_ALPHA8:
- return PIXEL_FORMAT_RGBA_8888;
- case GL_RGBA:
- return PIXEL_FORMAT_RGBA_8888;
- case GL_RGB:
- return PIXEL_FORMAT_RGB_565;
- case GL_RGBA16F:
- return PIXEL_FORMAT_RGBA_FP16;
- default:
- LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat);
- return PIXEL_FORMAT_UNKNOWN;
- }
-}
-
-class AutoEglFence {
-public:
- AutoEglFence(EGLDisplay display)
- : mDisplay(display) {
- fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL);
- }
-
- ~AutoEglFence() {
- if (fence != EGL_NO_SYNC_KHR) {
- eglDestroySyncKHR(mDisplay, fence);
- }
- }
-
- EGLSyncKHR fence = EGL_NO_SYNC_KHR;
-private:
- EGLDisplay mDisplay = EGL_NO_DISPLAY;
-};
-
-class AutoEglImage {
-public:
- AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer)
- : mDisplay(display) {
- EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
- image = eglCreateImageKHR(display, EGL_NO_CONTEXT,
- EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
- }
-
- ~AutoEglImage() {
- if (image != EGL_NO_IMAGE_KHR) {
- eglDestroyImageKHR(mDisplay, image);
- }
- }
-
- EGLImageKHR image = EGL_NO_IMAGE_KHR;
-private:
- EGLDisplay mDisplay = EGL_NO_DISPLAY;
-};
-
-class AutoGlTexture {
-public:
- AutoGlTexture(uirenderer::Caches& caches)
- : mCaches(caches) {
- glGenTextures(1, &mTexture);
- caches.textureState().bindTexture(mTexture);
- }
-
- ~AutoGlTexture() {
- mCaches.textureState().deleteTexture(mTexture);
- }
-
-private:
- uirenderer::Caches& mCaches;
- GLuint mTexture = 0;
-};
-
-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());
- // 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;
- }
- AutoGlTexture glTexture(caches);
- 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();
-
- const SkImageInfo& info = skBitmap.info();
- if (info.colorType() == kUnknown_SkColorType || info.colorType() == kAlpha_8_SkColorType) {
- ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType());
- return nullptr;
- }
-
- bool needSRGB = uirenderer::transferFunctionCloseToSRGB(skBitmap.info().colorSpace());
- bool hasLinearBlending = caches.extensions().hasLinearBlending();
- GLint format, type, internalFormat;
- uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(),
- needSRGB && hasLinearBlending, &internalFormat, &format, &type);
-
- PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat);
- sp<GraphicBuffer> buffer = new GraphicBuffer(info.width(), info.height(), pixelFormat,
- GraphicBuffer::USAGE_HW_TEXTURE |
- GraphicBuffer::USAGE_SW_WRITE_NEVER |
- GraphicBuffer::USAGE_SW_READ_NEVER,
- std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) + "]");
-
- status_t error = buffer->initCheck();
- if (error < 0) {
- ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
- return nullptr;
- }
-
- SkBitmap bitmap;
- if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(),
- hasLinearBlending))) {
- sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
- bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB));
- } else {
- bitmap = skBitmap;
- }
-
- if (!uploadBitmapToGraphicBuffer(caches, bitmap, *buffer, format, type)) {
- return nullptr;
- }
- return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
+ return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes, std::move(ctable)));
}
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(SkBitmap* bitmap, sk_sp<SkColorTable> ctable) {
+ return allocateBitmap(bitmap, std::move(ctable), &android::allocateHeapBitmap);
}
sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
@@ -276,7 +103,7 @@ sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
}
sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info,
- size_t rowBytes, SkColorTable* ctable) {
+ size_t rowBytes, sk_sp<SkColorTable> ctable) {
// Create new ashmem region with read/write priv
int fd = ashmem_create_region("bitmap", size);
if (fd < 0) {
@@ -294,20 +121,18 @@ sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info,
close(fd);
return nullptr;
}
- return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes, ctable));
+ return sk_sp<Bitmap>(new Bitmap(addr, fd, size, info, rowBytes, std::move(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()));
+ info, pixelRef.rowBytes(), sk_ref_sp(pixelRef.colorTable())));
}
sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
@@ -323,76 +148,87 @@ sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
}
void Bitmap::setColorSpace(sk_sp<SkColorSpace> colorSpace) {
- // TODO: See todo in reconfigure() below
- SkImageInfo* myInfo = const_cast<SkImageInfo*>(&this->info());
- *myInfo = info().makeColorSpace(std::move(colorSpace));
+ mInfo = mInfo.makeColorSpace(std::move(colorSpace));
}
-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));
- }
-
+static SkImageInfo validateAlpha(const SkImageInfo& info) {
// 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),
+ info.colorType(), info.alphaType(), &alphaType),
"Failed to validate alpha type!");
+ return info.makeAlphaType(alphaType);
+}
+
+void Bitmap::reconfigure(const SkImageInfo& newInfo, size_t rowBytes, sk_sp<SkColorTable> ctable) {
+ if (kIndex_8_SkColorType != newInfo.colorType()) {
+ ctable = nullptr;
+ }
+
+ mInfo = validateAlpha(newInfo);
// 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());
+ this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes, std::move(ctable));
}
-Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
- : SkPixelRef(info)
+static sk_sp<SkColorTable> sanitize(const SkImageInfo& info, sk_sp<SkColorTable> ctable) {
+ if (info.colorType() == kIndex_8_SkColorType) {
+ SkASSERT(ctable);
+ return ctable;
+ }
+ return nullptr; // drop the ctable if we're not indexed
+}
+Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes,
+ sk_sp<SkColorTable> ctable)
+ : SkPixelRef(info.width(), info.height(), address, rowBytes,
+ sanitize(info, std::move(ctable)))
+ , mInfo(validateAlpha(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)
+ const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable)
+ : SkPixelRef(info.width(), info.height(), address, rowBytes,
+ sanitize(info, std::move(ctable)))
+ , mInfo(validateAlpha(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)
+ const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable)
+ : SkPixelRef(info.width(), info.height(), address, rowBytes,
+ sanitize(info, std::move(ctable)))
+ , mInfo(validateAlpha(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)
+ : SkPixelRef(info.width(), info.height(), nullptr,
+ bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride(),
+ nullptr)
+ , mInfo(validateAlpha(info))
, mPixelStorageType(PixelStorageType::Hardware) {
mPixelStorage.hardware.buffer = buffer;
buffer->incStrong(buffer);
- mRowBytes = bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride();
+ setImmutable(); // HW bitmaps are always immutable
+ if (uirenderer::Properties::isSkiaEnabled()) {
+ // TODO: add color correctness for Skia pipeline - pass null color space for now
+ mImage = SkImage::MakeFromAHardwareBuffer(reinterpret_cast<AHardwareBuffer*>(buffer),
+ mInfo.alphaType(), nullptr);
+ }
}
Bitmap::~Bitmap() {
@@ -440,17 +276,6 @@ void* Bitmap::getStorage() const {
}
}
-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:
@@ -478,7 +303,7 @@ void Bitmap::setAlphaType(SkAlphaType alphaType) {
return;
}
- changeAlphaType(alphaType);
+ mInfo = mInfo.makeAlphaType(alphaType);
}
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
@@ -494,23 +319,13 @@ void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap);
return;
}
- outBitmap->setInfo(info(), rowBytes());
- outBitmap->setPixelRef(this);
-}
-
-void Bitmap::getSkBitmapForShaders(SkBitmap* outBitmap) {
- if (isHardware() && uirenderer::Properties::isSkiaEnabled()) {
- getSkBitmap(outBitmap);
- } else {
- outBitmap->setInfo(info(), rowBytes());
- outBitmap->setPixelRef(this);
- outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
- }
+ outBitmap->setInfo(mInfo, rowBytes());
+ outBitmap->setPixelRef(sk_ref_sp(this), 0, 0);
}
void Bitmap::getBounds(SkRect* bounds) const {
SkASSERT(bounds);
- bounds->set(0, 0, SkIntToScalar(info().width()), SkIntToScalar(info().height()));
+ bounds->set(0, 0, SkIntToScalar(width()), SkIntToScalar(height()));
}
GraphicBuffer* Bitmap::graphicBuffer() {
@@ -520,4 +335,20 @@ GraphicBuffer* Bitmap::graphicBuffer() {
return nullptr;
}
+sk_sp<SkImage> Bitmap::makeImage() {
+ sk_sp<SkImage> image = mImage;
+ if (!image) {
+ SkASSERT(!(isHardware() && uirenderer::Properties::isSkiaEnabled()));
+ SkBitmap skiaBitmap;
+ skiaBitmap.setInfo(info(), rowBytes());
+ skiaBitmap.setPixelRef(sk_ref_sp(this), 0, 0);
+ skiaBitmap.setHasHardwareMipMap(mHasHardwareMipMap);
+ // Note we don't cache in this case, because the raster image holds a pointer to this Bitmap
+ // internally and ~Bitmap won't be invoked.
+ // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here.
+ image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
+ }
+ return image;
+}
+
} // namespace android
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index da45f7697f56..3642406709f9 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -18,10 +18,12 @@
#include <SkBitmap.h>
#include <SkColorSpace.h>
#include <SkColorTable.h>
+#include <SkImage.h>
#include <SkImageInfo.h>
#include <SkPixelRef.h>
#include <cutils/compiler.h>
#include <ui/GraphicBuffer.h>
+#include <SkImage.h>
namespace android {
@@ -44,66 +46,54 @@ 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(SkBitmap* bitmap, sk_sp<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(SkBitmap* bitmap, sk_sp<SkColorTable> ctable);
static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info,
- size_t rowBytes, SkColorTable* ctable);
+ size_t rowBytes, sk_sp<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);
+ sk_sp<SkColorTable> ctable);
Bitmap(void* address, void* context, FreeFunc freeFunc,
- const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
+ const SkImageInfo& info, size_t rowBytes, sk_sp<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; }
+ size_t rowBytes, sk_sp<SkColorTable> ctable);
+ Bitmap(GraphicBuffer* buffer, const SkImageInfo& info);
int rowBytesAsPixels() const {
- return mRowBytes >> info().shiftPerPixel();
+ return rowBytes() >> SkColorTypeShiftPerPixel(mInfo.colorType());
}
- void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
+ void reconfigure(const SkImageInfo& info, size_t rowBytes, sk_sp<SkColorTable> ctable);
void reconfigure(const SkImageInfo& info);
void setColorSpace(sk_sp<SkColorSpace> colorSpace);
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(); }
+ bool isOpaque() const { return mInfo.isOpaque(); }
+ SkColorType colorType() const { return mInfo.colorType(); }
+ const SkImageInfo& info() const {
+ return mInfo;
+ }
+
void getBounds(SkRect* bounds) const;
bool readyToDraw() const {
- return this->colorType() != kIndex_8_SkColorType || mColorTable;
+ return this->colorType() != kIndex_8_SkColorType || this->colorTable();
}
bool isHardware() const {
@@ -111,19 +101,18 @@ public:
}
GraphicBuffer* graphicBuffer();
-protected:
- virtual bool onNewLockPixels(LockRec* rec) override;
- virtual void onUnlockPixels() override { };
- virtual size_t getAllocatedSizeInBytes() const override;
+
+ // makeImage creates or returns a cached SkImage. Can be invoked from UI or render thread.
+ // Caching is supported only for HW Bitmaps with skia pipeline.
+ sk_sp<SkImage> makeImage();
private:
- Bitmap(GraphicBuffer* buffer, const SkImageInfo& info);
virtual ~Bitmap();
void* getStorage() const;
- PixelStorageType mPixelStorageType;
+ SkImageInfo mInfo;
+
+ const PixelStorageType mPixelStorageType;
- size_t mRowBytes = 0;
- sk_sp<SkColorTable> mColorTable;
bool mHasHardwareMipMap = false;
union {
@@ -145,6 +134,8 @@ private:
GraphicBuffer* buffer;
} hardware;
} mPixelStorage;
+
+ sk_sp<SkImage> mImage; // Cache is used only for HW Bitmaps with Skia pipeline.
};
-} //namespace android \ No newline at end of file
+} //namespace android
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index c64a89de7296..679cb5021e27 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -80,13 +80,11 @@ static void simplifyPaint(int color, SkPaint* paint) {
class DrawTextFunctor {
public:
- DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
+ DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas,
const SkPaint& paint, float x, float y, minikin::MinikinRect& bounds,
float totalAdvance)
: layout(layout)
, canvas(canvas)
- , glyphs(glyphs)
- , pos(pos)
, paint(paint)
, x(x)
, y(y)
@@ -95,19 +93,21 @@ public:
}
void operator()(size_t start, size_t end) {
- if (canvas->drawTextAbsolutePos()) {
- for (size_t i = start; i < end; i++) {
- glyphs[i] = layout.getGlyphId(i);
- pos[2 * i] = x + layout.getX(i);
- pos[2 * i + 1] = y + layout.getY(i);
+ auto glyphFunc = [&] (uint16_t* text, float* positions) {
+ if (canvas->drawTextAbsolutePos()) {
+ for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
+ text[textIndex++] = layout.getGlyphId(i);
+ positions[posIndex++] = x + layout.getX(i);
+ positions[posIndex++] = y + layout.getY(i);
+ }
+ } else {
+ for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
+ text[textIndex++] = layout.getGlyphId(i);
+ positions[posIndex++] = layout.getX(i);
+ positions[posIndex++] = layout.getY(i);
+ }
}
- } else {
- for (size_t i = start; i < end; i++) {
- glyphs[i] = layout.getGlyphId(i);
- pos[2 * i] = layout.getX(i);
- pos[2 * i + 1] = layout.getY(i);
- }
- }
+ };
size_t glyphCount = end - start;
@@ -121,26 +121,24 @@ public:
SkPaint outlinePaint(paint);
simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
- canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, outlinePaint, x, y,
- bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
+ canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, bounds.mLeft, bounds.mTop,
+ bounds.mRight, bounds.mBottom, totalAdvance);
// inner
SkPaint innerPaint(paint);
simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
innerPaint.setStyle(SkPaint::kFill_Style);
- canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, innerPaint, x, y,
- bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
+ canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, bounds.mLeft, bounds.mTop,
+ bounds.mRight, bounds.mBottom, totalAdvance);
} else {
// standard draw path
- canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, paint, x, y,
- bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance);
+ canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, bounds.mLeft, bounds.mTop,
+ bounds.mRight, bounds.mBottom, totalAdvance);
}
}
private:
const minikin::Layout& layout;
Canvas* canvas;
- uint16_t* glyphs;
- float* pos;
const SkPaint& paint;
float x;
float y;
@@ -156,10 +154,6 @@ void Canvas::drawText(const uint16_t* text, int start, int count, int contextCou
minikin::Layout layout = MinikinUtils::doLayout(
&paint, bidiFlags, typeface, text, start, count, contextCount);
- size_t nGlyphs = layout.nGlyphs();
- std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]);
- std::unique_ptr<float[]> pos(new float[nGlyphs * 2]);
-
x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
minikin::MinikinRect bounds;
@@ -173,8 +167,7 @@ void Canvas::drawText(const uint16_t* text, int start, int count, int contextCou
// care of all alignment.
paint.setTextAlign(Paint::kLeft_Align);
- DrawTextFunctor f(layout, this, glyphs.get(), pos.get(),
- paint, x, y, bounds, layout.getAdvance());
+ DrawTextFunctor f(layout, this, paint, x, y, bounds, layout.getAdvance());
MinikinUtils::forFontRun(layout, &paint, f);
}
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 86af67837dcd..ac8a08169997 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -28,6 +28,7 @@
#include <SkMatrix.h>
class SkCanvasState;
+class SkVertices;
namespace minikin {
class Layout;
@@ -67,6 +68,8 @@ class Tree;
};
typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
+typedef std::function<void(uint16_t* text, float* positions)> ReadGlyphFunc;
+
class Bitmap;
class Paint;
struct Typeface;
@@ -228,9 +231,7 @@ public:
virtual void drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) = 0;
virtual void drawPath(const SkPath& path, const SkPaint& paint) = 0;
- 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) = 0;
+ virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) = 0;
// Bitmap-based
virtual void drawBitmap(Bitmap& bitmap, float left, float top,
@@ -274,12 +275,12 @@ protected:
void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
/**
+ * glyphFunc: valid only for the duration of the call and should not be cached.
* drawText: count is of glyphs
* totalAdvance: used to define width of text decorations (underlines, strikethroughs).
*/
- 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,
+ virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x,
+ float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
float totalAdvance) = 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;
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
deleted file mode 100644
index 8826cfcc3100..000000000000
--- a/libs/hwui/hwui_static_deps.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-###############################################################################
-#
-#
-# This file contains the shared and static dependencies needed by any target
-# that attempts to statically link HWUI (i.e. libhwui_static build target). This
-# file should be included by any target that lists libhwui_static as a
-# dependency.
-#
-# This is a workaround for the fact that the build system does not add these
-# transitive dependencies when it attempts to link libhwui_static into another
-# library.
-#
-###############################################################################
-
-LOCAL_SHARED_LIBRARIES += \
- liblog \
- libcutils \
- libutils \
- libEGL \
- libGLESv2 \
- libvulkan \
- libskia \
- libui \
- libgui \
- libprotobuf-cpp-full \
- libharfbuzz_ng \
- libft2 \
- libminikin \
- libandroidfw \
- libRScpp
-
-LOCAL_STATIC_LIBRARIES += \
- libplatformprotos
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 68a08693249c..975f849c7071 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -21,7 +21,6 @@
#include <SkBlurMask.h>
#include <SkBlurMaskFilter.h>
-#include <SkGaussianEdgeShader.h>
#include <SkPathOps.h>
#include <SkRRectsGaussianEdgeMaskFilter.h>
#include <SkShadowUtils.h>
@@ -137,7 +136,6 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable*
float ambientAlpha = (SkiaPipeline::getAmbientShadowAlpha()/255.f)*casterAlpha;
float spotAlpha = (SkiaPipeline::getSpotShadowAlpha()/255.f)*casterAlpha;
- const float casterZValue = casterProperties.getZ();
const RevealClip& revealClip = casterProperties.getRevealClip();
const SkPath* revealClipPath = revealClip.getPath();
@@ -178,6 +176,7 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable*
// intersect the shadow-casting path with the reveal, if present
if (revealClipPath) {
Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, &tmpPath);
+ tmpPath.setIsVolatile(true);
casterPath = &tmpPath;
}
@@ -186,36 +185,23 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable*
SkPath clipBoundsPath;
clipBoundsPath.addRect(casterClipRect);
Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, &tmpPath);
+ tmpPath.setIsVolatile(true);
casterPath = &tmpPath;
}
const Vector3 lightPos = SkiaPipeline::getLightCenter();
SkPoint3 skiaLightPos = SkPoint3::Make(lightPos.x, lightPos.y, lightPos.z);
- if (shadowMatrix.hasPerspective() || revealClipPath || clippedToBounds) {
- std::function<SkScalar(SkScalar, SkScalar)> casterHeightFunc;
- if (shadowMatrix.hasPerspective()) {
- // get the matrix with the full 3D transform
- mat4 zMatrix;
- caster->getRenderNode()->applyViewPropertyTransforms(zMatrix, true);
- SkScalar A = zMatrix[2];
- SkScalar B = zMatrix[6];
- SkScalar C = zMatrix[mat4::kTranslateZ];
- casterHeightFunc = [A, B, C](SkScalar x, SkScalar y) {
- return A*x + B*y + C; // casterZValue already baked into C
- };
- } else {
- casterHeightFunc = [casterZValue] (SkScalar, SkScalar) {
- return casterZValue;
- };
- }
-
- SkShadowUtils::DrawUncachedShadow(canvas, *casterPath, casterHeightFunc, skiaLightPos,
- SkiaPipeline::getLightRadius(), ambientAlpha, spotAlpha, SK_ColorBLACK,
- casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
+ SkPoint3 zParams;
+ if (shadowMatrix.hasPerspective()) {
+ // get the matrix with the full 3D transform
+ mat4 zMatrix;
+ caster->getRenderNode()->applyViewPropertyTransforms(zMatrix, true);
+ zParams = SkPoint3::Make(zMatrix[2], zMatrix[6], zMatrix[mat4::kTranslateZ]);
} else {
- SkShadowUtils::DrawShadow(canvas, *casterPath, casterZValue, skiaLightPos,
- SkiaPipeline::getLightRadius(), ambientAlpha, spotAlpha, SK_ColorBLACK,
- casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
+ zParams = SkPoint3::Make(0, 0, casterProperties.getZ());
}
+ SkShadowUtils::DrawShadow(canvas, *casterPath, zParams, skiaLightPos,
+ SkiaPipeline::getLightRadius(), ambientAlpha, spotAlpha, SK_ColorBLACK,
+ casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
}
}; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 496f7babd3cc..3ddc09fbeca1 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -19,6 +19,7 @@
#include "renderthread/CanvasContext.h"
#include "VectorDrawable.h"
#include "DumpOpsCanvas.h"
+#include "SkiaPipeline.h"
#include <SkImagePriv.h>
@@ -92,6 +93,8 @@ bool SkiaDisplayList::prepareListAndChildren(TreeObserver& observer, TreeInfo& i
// If any vector drawable in the display list needs update, damage the node.
if (vectorDrawable->isDirty()) {
isDirty = true;
+ static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
+ ->getVectorDrawables()->push_back(vectorDrawable);
}
vectorDrawable->setPropertyChangeWillBeConsumed(true);
}
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 6ee5922f9cd6..66375d13826c 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -60,7 +60,7 @@ public:
* 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.
+ * that creates them. Allocator dtor invokes all SkDrawable dtors.
*/
template<class T, typename... Params>
SkDrawable* allocateDrawable(Params&&... params) {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index ae1313101f3c..6d5ef1d4ff1f 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -16,6 +16,7 @@
#include "SkiaOpenGLPipeline.h"
+#include "hwui/Bitmap.h"
#include "DeferredLayerUpdater.h"
#include "GlLayer.h"
#include "LayerDrawable.h"
@@ -60,7 +61,7 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty,
const SkRect& dirty,
const FrameBuilder::LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque,
+ const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
const BakedOpRenderer::LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) {
@@ -84,7 +85,8 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty,
mRenderThread.getGrContext(), renderTargetDesc, &props));
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
- renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut,
+ contentDrawBounds, surface);
layerUpdateQueue->clear();
// Draw visual debugging features
@@ -155,7 +157,8 @@ void SkiaOpenGLPipeline::onStop() {
}
}
-bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
+bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
+ ColorMode colorMode) {
if (mEglSurface != EGL_NO_SURFACE) {
mEglManager.destroySurface(mEglSurface);
@@ -163,7 +166,8 @@ bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior)
}
if (surface) {
- mEglSurface = mEglManager.createSurface(surface);
+ const bool wideColorGamut = colorMode == ColorMode::WideColorGamut;
+ mEglSurface = mEglManager.createSurface(surface, wideColorGamut);
}
if (mEglSurface != EGL_NO_SURFACE) {
@@ -197,6 +201,186 @@ void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* func
}
}
+#define FENCE_TIMEOUT 2000000000
+
+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;
+};
+
+class AutoSkiaGlTexture {
+public:
+ AutoSkiaGlTexture() {
+ glGenTextures(1, &mTexture);
+ glBindTexture(GL_TEXTURE_2D, mTexture);
+ }
+
+ ~AutoSkiaGlTexture() {
+ glDeleteTextures(1, &mTexture);
+ }
+
+private:
+ GLuint mTexture = 0;
+};
+
+sk_sp<Bitmap> SkiaOpenGLPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
+ SkBitmap& skBitmap) {
+ renderThread.eglManager().initialize();
+
+ sk_sp<GrContext> grContext = sk_ref_sp(renderThread.getGrContext());
+ const SkImageInfo& info = skBitmap.info();
+ PixelFormat pixelFormat;
+ GLint format, type;
+ bool isSupported = false;
+
+ //TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined)
+ switch (info.colorType()) {
+ case kRGBA_8888_SkColorType:
+ isSupported = true;
+ // ARGB_4444 and Index_8 are both upconverted to RGBA_8888
+ case kIndex_8_SkColorType:
+ case kARGB_4444_SkColorType:
+ pixelFormat = PIXEL_FORMAT_RGBA_8888;
+ format = GL_RGBA;
+ type = GL_UNSIGNED_BYTE;
+ break;
+ case kRGBA_F16_SkColorType:
+ isSupported = grContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig);
+ if (isSupported) {
+ type = GL_HALF_FLOAT;
+ pixelFormat = PIXEL_FORMAT_RGBA_FP16;
+ } else {
+ type = GL_UNSIGNED_BYTE;
+ pixelFormat = PIXEL_FORMAT_RGBA_8888;
+ }
+ format = GL_RGBA;
+ break;
+ case kRGB_565_SkColorType:
+ isSupported = true;
+ pixelFormat = PIXEL_FORMAT_RGB_565;
+ format = GL_RGB;
+ type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case kGray_8_SkColorType:
+ isSupported = true;
+ pixelFormat = PIXEL_FORMAT_RGBA_8888;
+ format = GL_LUMINANCE;
+ type = GL_UNSIGNED_BYTE;
+ break;
+ default:
+ ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType());
+ return nullptr;
+ }
+
+ SkBitmap bitmap;
+ if (isSupported) {
+ bitmap = skBitmap;
+ } else {
+ bitmap.allocPixels(SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(),
+ nullptr));
+ bitmap.eraseColor(0);
+ if (info.colorType() == kRGBA_F16_SkColorType) {
+ // Drawing RGBA_F16 onto ARGB_8888 is not supported
+ skBitmap.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
+ bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
+ } else {
+ SkCanvas canvas(bitmap);
+ canvas.drawBitmap(skBitmap, 0.0f, 0.0f, nullptr);
+ }
+ }
+
+ sp<GraphicBuffer> buffer = new GraphicBuffer(info.width(), info.height(), pixelFormat,
+ GraphicBuffer::USAGE_HW_TEXTURE |
+ GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER,
+ std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) + "]");
+
+ status_t error = buffer->initCheck();
+ if (error < 0) {
+ ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
+ return nullptr;
+ }
+
+ //upload the bitmap into a texture
+ EGLDisplay display = eglGetCurrentDisplay();
+ LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
+ "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+ uirenderer::renderthread::EglManager::eglErrorString());
+ // 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 nullptr;
+ }
+ AutoSkiaGlTexture glTexture;
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+ GL_CHECKPOINT(MODERATE);
+
+ // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we provide.
+ // But asynchronous in sense that driver may upload texture onto hardware buffer when we first
+ // use it in drawing
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, info.width(), info.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 nullptr;
+ }
+ // 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 nullptr;
+ }
+
+ grContext->resetContext(kTextureBinding_GrGLBackendState);
+
+ return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
+}
+
} /* namespace skiapipeline */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 36685ddb17a7..aa29c8e3babc 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -19,6 +19,9 @@
#include "SkiaPipeline.h"
namespace android {
+
+class Bitmap;
+
namespace uirenderer {
namespace skiapipeline {
@@ -32,7 +35,7 @@ public:
bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const FrameBuilder::LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque,
+ const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
const BakedOpRenderer::LightInfo& lightInfo,
const std::vector< sp<RenderNode> >& renderNodes,
FrameInfoVisualizer* profiler) override;
@@ -40,12 +43,15 @@ public:
FrameInfo* currentFrameInfo, bool* requireSwap) override;
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override;
+ bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
+ renderthread::ColorMode colorMode) override;
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
+ static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread,
+ SkBitmap& skBitmap);
private:
renderthread::EglManager& mEglManager;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index a18d26471a29..89697d7445c6 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -63,8 +63,6 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4
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();
@@ -88,23 +86,17 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4
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)) {
+ // we render in an offscreen buffer to scale and to avoid an issue b/62262733
+ // with reading incorrect data from EGLImage backed SkImage (likely a driver bug)
+ 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();
+
+ if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
copyResult = CopyResult::Success;
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 10c1865ac50c..0bab7932432c 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -25,6 +25,7 @@
#include <SkPictureRecorder.h>
#include <SkPixelSerializer.h>
#include <SkStream.h>
+#include "VectorDrawable.h"
#include <unistd.h>
@@ -40,15 +41,16 @@ uint8_t SkiaPipeline::mSpotShadowAlpha = 0;
Vector3 SkiaPipeline::mLightCenter = {FLT_MIN, FLT_MIN, FLT_MIN};
-SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { }
+SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) {
+ mVectorDrawables.reserve(30);
+}
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.
+ mRenderThread.cacheManager().trimStaleResources();
}
bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) {
@@ -70,15 +72,18 @@ void SkiaPipeline::unpinImages() {
}
void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut,
const BakedOpRenderer::LightInfo& lightInfo) {
updateLighting(lightGeometry, lightInfo);
ATRACE_NAME("draw layers");
- renderLayersImpl(*layerUpdateQueue, opaque);
+ renderVectorDrawableCache();
+ renderLayersImpl(*layerUpdateQueue, opaque, wideColorGamut);
layerUpdateQueue->clear();
}
-void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
+void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers,
+ bool opaque, bool wideColorGamut) {
+ // TODO: Handle wide color gamut
// 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.get();
@@ -126,12 +131,13 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque)
}
bool SkiaPipeline::createOrUpdateLayer(RenderNode* node,
- const DamageAccumulator& damageAccumulator) {
+ const DamageAccumulator& damageAccumulator, bool wideColorGamut) {
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);
+ // TODO: Handle wide color gamut requests
node->setLayerSurface(
SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
info, 0, &props));
@@ -155,11 +161,11 @@ 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);
+ auto image = bitmap->makeImage();
+ if (image.get() && !bitmap->isHardware()) {
+ SkImage_pinAsTexture(image.get(), context);
+ SkImage_unpinAsTexture(image.get(), context);
+ }
}
}
@@ -176,12 +182,37 @@ public:
}
};
+void SkiaPipeline::renderVectorDrawableCache() {
+ //render VectorDrawables into offscreen buffers
+ for (auto vd : mVectorDrawables) {
+ sk_sp<SkSurface> surface;
+ if (!vd->canReuseSurface()) {
+#ifndef ANDROID_ENABLE_LINEAR_BLENDING
+ sk_sp<SkColorSpace> colorSpace = nullptr;
+#else
+ sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
+#endif
+ int scaledWidth = SkScalarCeilToInt(vd->properties().getScaledWidth());
+ int scaledHeight = SkScalarCeilToInt(vd->properties().getScaledHeight());
+ SkImageInfo info = SkImageInfo::MakeN32(scaledWidth, scaledHeight,
+ kPremul_SkAlphaType, colorSpace);
+ SkASSERT(mRenderThread.getGrContext() != nullptr);
+ surface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
+ info);
+ }
+ vd->updateCache(surface);
+ }
+ mVectorDrawables.clear();
+}
+
void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
- sk_sp<SkSurface> surface) {
+ const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
+ const Rect &contentDrawBounds, sk_sp<SkSurface> surface) {
+
+ renderVectorDrawableCache();
// draw all layers up front
- renderLayersImpl(layers, opaque);
+ renderLayersImpl(layers, opaque, wideColorGamut);
// initialize the canvas for the current frame
SkCanvas* canvas = surface->getCanvas();
@@ -199,7 +230,7 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli
}
}
- renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas);
+ renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas);
if (skpCaptureEnabled() && recordingPicture) {
sk_sp<SkPicture> picture = recorder->finishRecordingAsPicture();
@@ -232,8 +263,8 @@ static Rect nodeBounds(RenderNode& node) {
}
void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
- SkCanvas* canvas) {
+ const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
+ const Rect &contentDrawBounds, SkCanvas* canvas) {
SkAutoCanvasRestore saver(canvas, true);
canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
@@ -360,7 +391,7 @@ void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect&
// 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);
+ renderFrameImpl(layers, clip, nodes, true, false, contentDrawBounds, &overdrawCanvas);
sk_sp<SkImage> counts = offscreen->makeImageSnapshot();
// Draw overdraw colors to the canvas. The color filter will convert counts to colors.
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index c58fedf834ff..19ffc463c121 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -39,21 +39,23 @@ public:
void unpinImages() override;
void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut,
const BakedOpRenderer::LightInfo& lightInfo) override;
bool createOrUpdateLayer(RenderNode* node,
- const DamageAccumulator& damageAccumulator) override;
+ const DamageAccumulator& damageAccumulator, bool wideColorGamut) override;
void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector< sp<RenderNode> >& nodes, bool opaque, const Rect &contentDrawBounds,
- sk_sp<SkSurface> surface);
+ const std::vector< sp<RenderNode> >& nodes, bool opaque, bool wideColorGamut,
+ const Rect &contentDrawBounds, sk_sp<SkSurface> surface);
+
+ std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; }
static void destroyLayer(RenderNode* node);
static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
- static void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque);
+ static void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut);
static bool skpCaptureEnabled() { return false; }
@@ -108,8 +110,8 @@ protected:
private:
void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector< sp<RenderNode> >& nodes, bool opaque, const Rect &contentDrawBounds,
- SkCanvas* canvas);
+ const std::vector< sp<RenderNode> >& nodes, bool opaque, bool wideColorGamut,
+ const Rect &contentDrawBounds, SkCanvas* canvas);
/**
* Debugging feature. Draws a semi-transparent overlay on each pixel, indicating
@@ -119,8 +121,18 @@ private:
const std::vector< sp<RenderNode> >& nodes, const Rect &contentDrawBounds,
sk_sp<SkSurface>);
+ /**
+ * Render mVectorDrawables into offscreen buffers.
+ */
+ void renderVectorDrawableCache();
+
TaskManager mTaskManager;
std::vector<sk_sp<SkImage>> mPinnedImages;
+
+ /**
+ * populated by prepareTree with dirty VDs
+ */
+ std::vector<VectorDrawableRoot*> mVectorDrawables;
static float mLightRadius;
static uint8_t mAmbientShadowAlpha;
static uint8_t mSpotShadowAlpha;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 559d268b71f7..a0cce98c8d57 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -62,6 +62,7 @@ void SkiaRecordingCanvas::drawRoundRect(uirenderer::CanvasPropertyPrimitive* lef
uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
uirenderer::CanvasPropertyPrimitive* bottom, uirenderer::CanvasPropertyPrimitive* rx,
uirenderer::CanvasPropertyPrimitive* ry, uirenderer::CanvasPropertyPaint* paint) {
+ // Destructor of drawables created with allocateDrawable, will be invoked by ~LinearAllocator.
drawDrawable(mDisplayList->allocateDrawable<AnimatedRoundRect>(left, top, right, bottom,
rx, ry, paint));
}
@@ -92,13 +93,14 @@ void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
void SkiaRecordingCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layerUpdater) {
if (layerUpdater != nullptr && layerUpdater->backingLayer() != nullptr) {
uirenderer::Layer* layer = layerUpdater->backingLayer();
+ // Create a ref-counted drawable, which is kept alive by sk_sp in SkLiteDL.
sk_sp<SkDrawable> drawable(new LayerDrawable(layer));
drawDrawable(drawable.get());
}
}
void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
- // record the child node
+ // Record the child node. Drawable dtor will be invoked when mChildNodes deque is cleared.
mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
auto& renderNodeDrawable = mDisplayList->mChildNodes.back();
drawDrawable(&renderNodeDrawable);
@@ -113,6 +115,7 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
uirenderer::GlFunctorLifecycleListener* listener) {
+ // Drawable dtor will be invoked when mChildFunctors deque is cleared.
mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
drawDrawable(&mDisplayList->mChildFunctors.back());
}
@@ -126,22 +129,7 @@ class VectorDrawable : public SkDrawable {
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)
- *
- */
+ mRoot->draw(canvas);
}
private:
@@ -168,53 +156,47 @@ inline static const SkPaint* nonAAPaint(const SkPaint* origPaint, SkPaint* tmpPa
}
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());
- }
+ sk_sp<SkImage> image = bitmap.makeImage();
SkPaint tmpPaint;
mRecorder.drawImage(image, left, top, nonAAPaint(paint, &tmpPaint));
+ // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
+ // it is not safe to store a raw SkImage pointer, because the image object will be destroyed
+ // when this function ends.
+ if (!bitmap.isImmutable() && image.get() && !image->unique()) {
+ mDisplayList->mMutableImages.push_back(image.get());
+ }
}
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());
- }
+ sk_sp<SkImage> image = hwuiBitmap.makeImage();
SkPaint tmpPaint;
mRecorder.drawImage(image, 0, 0, nonAAPaint(paint, &tmpPaint));
+ if (!hwuiBitmap.isImmutable() && image.get() && !image->unique()) {
+ mDisplayList->mMutableImages.push_back(image.get());
+ }
}
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());
- }
+ sk_sp<SkImage> image = hwuiBitmap.makeImage();
SkPaint tmpPaint;
mRecorder.drawImageRect(image, srcRect, dstRect, nonAAPaint(paint, &tmpPaint));
+ if (!hwuiBitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty()
+ && !dstRect.isEmpty()) {
+ mDisplayList->mMutableImages.push_back(image.get());
+ }
}
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());
+ NinePatchUtils::SetLatticeDivs(&lattice, chunk, hwuiBitmap.width(), hwuiBitmap.height());
lattice.fFlags = nullptr;
int numFlags = 0;
@@ -231,13 +213,13 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch
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());
- }
+ sk_sp<SkImage> image = hwuiBitmap.makeImage();
SkPaint tmpPaint;
mRecorder.drawImageLattice(image.get(), lattice, dst, nonAAPaint(paint, &tmpPaint));
+ if (!hwuiBitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
+ mDisplayList->mMutableImages.push_back(image.get());
+ }
}
}; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index d28e605a051f..e1ef71f7d3ab 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -66,7 +66,7 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty,
const SkRect& dirty,
const FrameBuilder::LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque,
+ const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
const BakedOpRenderer::LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) {
@@ -76,7 +76,8 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty,
return false;
}
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
- renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer);
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut,
+ contentDrawBounds, backBuffer);
layerUpdateQueue->clear();
// Draw visual debugging features
@@ -131,13 +132,15 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
void SkiaVulkanPipeline::onStop() {
}
-bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
+bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
+ ColorMode colorMode) {
if (mVkSurface) {
mVkManager.destroySurface(mVkSurface);
mVkSurface = nullptr;
}
if (surface) {
+ // TODO: handle color mode
mVkSurface = mVkManager.createSurface(surface);
}
@@ -158,6 +161,25 @@ void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* func
(*functor)(mode, nullptr);
}
+sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
+ SkBitmap& skBitmap) {
+ //TODO: implement this function for Vulkan pipeline
+ //code below is a hack to avoid crashing because of missing HW Bitmap support
+ sp<GraphicBuffer> buffer = new GraphicBuffer(skBitmap.info().width(), skBitmap.info().height(),
+ PIXEL_FORMAT_RGBA_8888,
+ GraphicBuffer::USAGE_HW_TEXTURE |
+ GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER,
+ std::string("SkiaVulkanPipeline::allocateHardwareBitmap pid [")
+ + std::to_string(getpid()) + "]");
+ status_t error = buffer->initCheck();
+ if (error < 0) {
+ ALOGW("SkiaVulkanPipeline::allocateHardwareBitmap() failed in GraphicBuffer.create()");
+ return nullptr;
+ }
+ return sk_sp<Bitmap>(new Bitmap(buffer.get(), skBitmap.info()));
+}
+
} /* namespace skiapipeline */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index aab1d7a547c0..263206d97571 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -33,7 +33,7 @@ public:
bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const FrameBuilder::LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque,
+ const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
const BakedOpRenderer::LightInfo& lightInfo,
const std::vector< sp<RenderNode> >& renderNodes,
FrameInfoVisualizer* profiler) override;
@@ -41,12 +41,15 @@ public:
FrameInfo* currentFrameInfo, bool* requireSwap) override;
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override;
+ bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
+ renderthread::ColorMode colorMode) override;
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
+ static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread,
+ SkBitmap& skBitmap);
private:
renderthread::VulkanManager& mVkManager;
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp
index a9bbb273dbb5..90b27c8d8fb0 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.cpp
+++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp
@@ -35,17 +35,19 @@ namespace uirenderer {
////////////////////////////////////////////////////////////////////////////////
OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
- uint32_t viewportWidth, uint32_t viewportHeight)
+ uint32_t viewportWidth, uint32_t viewportHeight, bool wideColorGamut)
: GpuMemoryTracker(GpuObjectType::OffscreenBuffer)
, renderState(renderState)
, viewportWidth(viewportWidth)
, viewportHeight(viewportHeight)
- , texture(caches) {
+ , texture(caches)
+ , wideColorGamut(wideColorGamut) {
uint32_t width = computeIdealDimension(viewportWidth);
uint32_t height = computeIdealDimension(viewportHeight);
ATRACE_FORMAT("Allocate %ux%u HW Layer", width, height);
caches.textureState().activateTexture(0);
- texture.resize(width, height, caches.rgbaInternalFormat(), GL_RGBA);
+ texture.resize(width, height,
+ wideColorGamut ? GL_RGBA16F : 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
@@ -127,7 +129,10 @@ int OffscreenBufferPool::Entry::compare(const Entry& lhs, const Entry& rhs) {
int deltaInt = int(lhs.width) - int(rhs.width);
if (deltaInt != 0) return deltaInt;
- return int(lhs.height) - int(rhs.height);
+ deltaInt = int(lhs.height) - int(rhs.height);
+ if (deltaInt != 0) return deltaInt;
+
+ return int(lhs.wideColorGamut) - int(rhs.wideColorGamut);
}
void OffscreenBufferPool::clear() {
@@ -139,10 +144,10 @@ void OffscreenBufferPool::clear() {
}
OffscreenBuffer* OffscreenBufferPool::get(RenderState& renderState,
- const uint32_t width, const uint32_t height) {
+ const uint32_t width, const uint32_t height, bool wideColorGamut) {
OffscreenBuffer* layer = nullptr;
- Entry entry(width, height);
+ Entry entry(width, height, wideColorGamut);
auto iter = mPool.find(entry);
if (iter != mPool.end()) {
@@ -154,7 +159,8 @@ OffscreenBuffer* OffscreenBufferPool::get(RenderState& renderState,
layer->viewportHeight = height;
mSize -= layer->getSizeInBytes();
} else {
- layer = new OffscreenBuffer(renderState, Caches::getInstance(), width, height);
+ layer = new OffscreenBuffer(renderState, Caches::getInstance(),
+ width, height, wideColorGamut);
}
return layer;
@@ -174,7 +180,7 @@ OffscreenBuffer* OffscreenBufferPool::resize(OffscreenBuffer* layer,
return layer;
}
putOrDelete(layer);
- return get(renderState, width, height);
+ return get(renderState, width, height, layer->wideColorGamut);
}
void OffscreenBufferPool::dump() {
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h
index 26d4e3654a48..d9422c9edd69 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.h
+++ b/libs/hwui/renderstate/OffscreenBufferPool.h
@@ -43,7 +43,7 @@ class RenderState;
class OffscreenBuffer : GpuMemoryTracker {
public:
OffscreenBuffer(RenderState& renderState, Caches& caches,
- uint32_t viewportWidth, uint32_t viewportHeight);
+ uint32_t viewportWidth, uint32_t viewportHeight, bool wideColorGamut = false);
~OffscreenBuffer();
Rect getTextureCoordinates();
@@ -68,6 +68,8 @@ public:
uint32_t viewportHeight;
Texture texture;
+ bool wideColorGamut = false;
+
// Portion of layer that has been drawn to. Used to minimize drawing area when
// drawing back to screen / parent FBO.
Region region;
@@ -90,7 +92,7 @@ public:
~OffscreenBufferPool();
WARN_UNUSED_RESULT OffscreenBuffer* get(RenderState& renderState,
- const uint32_t width, const uint32_t height);
+ const uint32_t width, const uint32_t height, bool wideColorGamut = false);
WARN_UNUSED_RESULT OffscreenBuffer* resize(OffscreenBuffer* layer,
const uint32_t width, const uint32_t height);
@@ -122,14 +124,16 @@ private:
struct Entry {
Entry() {}
- Entry(const uint32_t layerWidth, const uint32_t layerHeight)
+ Entry(const uint32_t layerWidth, const uint32_t layerHeight, bool wideColorGamut)
: width(OffscreenBuffer::computeIdealDimension(layerWidth))
- , height(OffscreenBuffer::computeIdealDimension(layerHeight)) {}
+ , height(OffscreenBuffer::computeIdealDimension(layerHeight))
+ , wideColorGamut(wideColorGamut) {}
explicit Entry(OffscreenBuffer* layer)
: layer(layer)
, width(layer->texture.width())
- , height(layer->texture.height()) {
+ , height(layer->texture.height())
+ , wideColorGamut(layer->wideColorGamut) {
}
static int compare(const Entry& lhs, const Entry& rhs);
@@ -149,6 +153,7 @@ private:
OffscreenBuffer* layer = nullptr;
uint32_t width = 0;
uint32_t height = 0;
+ bool wideColorGamut = false;
}; // struct Entry
std::multiset<Entry> mPool;
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index ed96d49bbc15..2c92924cc12c 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -296,14 +296,20 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
// TODO: avoid query, and cache values (or RRCS ptr) in program
const RoundRectClipState* state = glop.roundRectClipState;
const Rect& innerRect = state->innerRect;
- glUniform4f(fill.program->getUniform("roundRectInnerRectLTRB"),
- innerRect.left, innerRect.top,
- innerRect.right, innerRect.bottom);
- glUniformMatrix4fv(fill.program->getUniform("roundRectInvTransform"),
- 1, GL_FALSE, &state->matrix.data[0]);
// add half pixel to round out integer rect space to cover pixel centers
float roundedOutRadius = state->radius + 0.5f;
+
+ // Divide by the radius to simplify the calculations in the fragment shader
+ // roundRectPos is also passed from vertex shader relative to top/left & radius
+ glUniform4f(fill.program->getUniform("roundRectInnerRectLTWH"),
+ innerRect.left / roundedOutRadius, innerRect.top / roundedOutRadius,
+ (innerRect.right - innerRect.left) / roundedOutRadius,
+ (innerRect.bottom - innerRect.top) / roundedOutRadius);
+
+ glUniformMatrix4fv(fill.program->getUniform("roundRectInvTransform"),
+ 1, GL_FALSE, &state->matrix.data[0]);
+
glUniform1f(fill.program->getUniform("roundRectRadius"),
roundedOutRadius);
}
@@ -420,18 +426,28 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
const GLbyte* vertexData = static_cast<const GLbyte*>(vertices.position);
while (elementsCount > 0) {
GLsizei drawCount = std::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
+ GLsizei vertexCount = (drawCount / 6) * 4;
meshState().bindPositionVertexPointer(vertexData, vertices.stride);
if (vertices.attribFlags & VertexAttribFlags::TextureCoord) {
meshState().bindTexCoordsVertexPointer(
vertexData + kMeshTextureOffset, vertices.stride);
}
- glDrawElements(mesh.primitiveMode, drawCount, GL_UNSIGNED_SHORT, nullptr);
+ if (mCaches->extensions().getMajorGlVersion() >= 3) {
+ glDrawRangeElements(mesh.primitiveMode, 0, vertexCount-1, drawCount, GL_UNSIGNED_SHORT, nullptr);
+ } else {
+ glDrawElements(mesh.primitiveMode, drawCount, GL_UNSIGNED_SHORT, nullptr);
+ }
elementsCount -= drawCount;
- vertexData += (drawCount / 6) * 4 * vertices.stride;
+ vertexData += vertexCount * vertices.stride;
}
} else if (indices.bufferObject || indices.indices) {
- glDrawElements(mesh.primitiveMode, mesh.elementCount, GL_UNSIGNED_SHORT, indices.indices);
+ if (mCaches->extensions().getMajorGlVersion() >= 3) {
+ // use glDrawRangeElements to reduce CPU overhead (otherwise the driver has to determine the min/max index values)
+ glDrawRangeElements(mesh.primitiveMode, 0, mesh.vertexCount-1, mesh.elementCount, GL_UNSIGNED_SHORT, indices.indices);
+ } else {
+ glDrawElements(mesh.primitiveMode, mesh.elementCount, GL_UNSIGNED_SHORT, indices.indices);
+ }
} else {
glDrawArrays(mesh.primitiveMode, 0, mesh.elementCount);
}
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 787946f79f6b..4b7a86580621 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -45,6 +45,7 @@ class Layer;
class DeferredLayerUpdater;
namespace renderthread {
+class CacheManager;
class CanvasContext;
class RenderThread;
}
@@ -55,6 +56,7 @@ class RenderState {
PREVENT_COPY_AND_ASSIGN(RenderState);
friend class renderthread::RenderThread;
friend class Caches;
+ friend class renderthread::CacheManager;
public:
void onGLContextCreated();
void onGLContextDestroyed();
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
new file mode 100644
index 000000000000..f0d6b3860938
--- /dev/null
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CacheManager.h"
+
+#include "Layer.h"
+#include "RenderThread.h"
+#include "renderstate/RenderState.h"
+
+#include <gui/Surface.h>
+#include <GrContextOptions.h>
+#include <math.h>
+#include <set>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+// This multiplier was selected based on historical review of cache sizes relative
+// to the screen resolution. This is meant to be a conservative default based on
+// that analysis. The 4.0f is used because the default pixel format is assumed to
+// be ARGB_8888.
+#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f)
+#define BACKGROUND_RETENTION_PERCENTAGE (0.5f)
+
+// for super large fonts we will draw them as paths so no need to keep linearly
+// increasing the font cache size.
+#define FONT_CACHE_MIN_MB (0.5f)
+#define FONT_CACHE_MAX_MB (4.0f)
+
+CacheManager::CacheManager(const DisplayInfo& display)
+ : mMaxSurfaceArea(display.w * display.h) {
+ mVectorDrawableAtlas.reset(new VectorDrawableAtlas);
+}
+
+void CacheManager::reset(GrContext* context) {
+ if (context != mGrContext.get()) {
+ destroy();
+ }
+
+ if (context) {
+ mGrContext = sk_ref_sp(context);
+ mGrContext->getResourceCacheLimits(&mMaxResources, nullptr);
+ updateContextCacheSizes();
+ }
+}
+
+void CacheManager::destroy() {
+ // cleanup any caches here as the GrContext is about to go away...
+ mGrContext.reset(nullptr);
+ mVectorDrawableAtlas.reset(new VectorDrawableAtlas);
+}
+
+void CacheManager::updateContextCacheSizes() {
+ mMaxResourceBytes = mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER;
+ mBackgroundResourceBytes = mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE;
+
+ mGrContext->setResourceCacheLimits(mMaxResources, mMaxResourceBytes);
+}
+
+void CacheManager::configureContext(GrContextOptions* contextOptions) {
+ contextOptions->fAllowPathMaskCaching = true;
+
+ float screenMP = mMaxSurfaceArea / 1024.0f / 1024.0f;
+ float fontCacheMB = 0;
+ float decimalVal = std::modf(screenMP, &fontCacheMB);
+
+ // This is a basic heuristic to size the cache to a multiple of 512 KB
+ if (decimalVal > 0.8f) {
+ fontCacheMB += 1.0f;
+ } else if (decimalVal > 0.5f) {
+ fontCacheMB += 0.5f;
+ }
+
+ // set limits on min/max size of the cache
+ fontCacheMB = std::max(FONT_CACHE_MIN_MB, std::min(FONT_CACHE_MAX_MB, fontCacheMB));
+
+ // We must currently set the size of the text cache based on the size of the
+ // display even though we like to be dynamicallysizing it to the size of the window.
+ // Skia's implementation doesn't provide a mechanism to resize the font cache due to
+ // the potential cost of recreating the glyphs.
+ contextOptions->fGlyphCacheTextureMaximumBytes = fontCacheMB * 1024 * 1024;
+}
+
+void CacheManager::trimMemory(TrimMemoryMode mode) {
+ if (!mGrContext) {
+ return;
+ }
+
+ mGrContext->flush();
+
+ switch (mode) {
+ case TrimMemoryMode::Complete:
+ mVectorDrawableAtlas.reset(new VectorDrawableAtlas);
+ mGrContext->freeGpuResources();
+ break;
+ case TrimMemoryMode::UiHidden:
+ mGrContext->purgeUnlockedResources(mMaxResourceBytes - mBackgroundResourceBytes, true);
+ break;
+ }
+}
+
+void CacheManager::trimStaleResources() {
+ if (!mGrContext) {
+ return;
+ }
+ mGrContext->flush();
+ mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
+}
+
+VectorDrawableAtlas* CacheManager::acquireVectorDrawableAtlas() {
+ LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() == nullptr);
+ LOG_ALWAYS_FATAL_IF(mGrContext == nullptr);
+
+ /**
+ * TODO LIST:
+ * 1) compute the atlas based on the surfaceArea surface
+ * 2) identify a way to reuse cache entries
+ * 3) add ability to repack the cache?
+ * 4) define memory conditions where we clear the cache (e.g. surface->reset())
+ */
+
+ return mVectorDrawableAtlas.release();
+}
+void CacheManager::releaseVectorDrawableAtlas(VectorDrawableAtlas* atlas) {
+ LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() != nullptr);
+ mVectorDrawableAtlas.reset(atlas);
+ mVectorDrawableAtlas->isNewAtlas = false;
+}
+
+void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
+ if (!mGrContext) {
+ log.appendFormat("No valid cache instance.\n");
+ return;
+ }
+
+ size_t bytesCached;
+ mGrContext->getResourceCacheUsage(nullptr, &bytesCached);
+
+ log.appendFormat("Caches:\n");
+ log.appendFormat(" Current / Maximum\n");
+ log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f kB (entries = %zu)\n",
+ 0.0f, 0.0f, (size_t)0);
+
+ if (renderState) {
+ if (renderState->mActiveLayers.size() > 0) {
+ log.appendFormat(" Layer Info:\n");
+ }
+
+ size_t layerMemoryTotal = 0;
+ for (std::set<Layer*>::iterator it = renderState->mActiveLayers.begin();
+ it != renderState->mActiveLayers.end(); it++) {
+ const Layer* layer = *it;
+ const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer";
+ log.appendFormat(" %s size %dx%d\n", layerType,
+ layer->getWidth(), layer->getHeight());
+ layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4;
+ }
+ log.appendFormat(" Layers Total %6.2f kB (numLayers = %zu)\n",
+ layerMemoryTotal / 1024.0f, renderState->mActiveLayers.size());
+ }
+
+
+ log.appendFormat("Total memory usage:\n");
+ log.appendFormat(" %zu bytes, %.2f MB (%.2f MB is purgeable)\n",
+ bytesCached, bytesCached / 1024.0f / 1024.0f,
+ mGrContext->getResourceCachePurgeableBytes() / 1024.0f / 1024.0f);
+
+
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
new file mode 100644
index 000000000000..43d58f2d58a8
--- /dev/null
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CACHEMANAGER_H
+#define CACHEMANAGER_H
+
+#include <GrContext.h>
+#include <SkSurface.h>
+#include <ui/DisplayInfo.h>
+#include <utils/String8.h>
+#include <vector>
+
+namespace android {
+
+class Surface;
+
+namespace uirenderer {
+
+class RenderState;
+
+namespace renderthread {
+
+class IRenderPipeline;
+class RenderThread;
+
+struct VectorDrawableAtlas {
+ sk_sp<SkSurface> surface;
+ bool isNewAtlas = true;
+};
+
+class CacheManager {
+public:
+ enum class TrimMemoryMode {
+ Complete,
+ UiHidden
+ };
+
+ void configureContext(GrContextOptions* context);
+ void trimMemory(TrimMemoryMode mode);
+ void trimStaleResources();
+ void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
+
+ VectorDrawableAtlas* acquireVectorDrawableAtlas();
+ void releaseVectorDrawableAtlas(VectorDrawableAtlas*);
+
+ size_t getCacheSize() const { return mMaxResourceBytes; }
+ size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
+
+private:
+ friend class RenderThread;
+
+ CacheManager(const DisplayInfo& display);
+
+
+ void reset(GrContext* grContext);
+ void destroy();
+ void updateContextCacheSizes();
+
+ const size_t mMaxSurfaceArea;
+ sk_sp<GrContext> mGrContext;
+
+ int mMaxResources = 0;
+ size_t mMaxResourceBytes = 0;
+ size_t mBackgroundResourceBytes = 0;
+
+ struct PipelineProps {
+ const void* pipelineKey = nullptr;
+ size_t surfaceArea = 0;
+ };
+
+ std::unique_ptr<VectorDrawableAtlas> mVectorDrawableAtlas;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* CACHEMANAGER_H */
+
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9c80ab304b80..779924883016 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -186,7 +186,8 @@ void CanvasContext::setSurface(Surface* surface) {
mNativeSurface = surface;
- bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior);
+ ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Srgb;
+ bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior, colorMode);
mFrameNumber = -1;
@@ -241,6 +242,10 @@ void CanvasContext::setOpaque(bool opaque) {
mOpaque = opaque;
}
+void CanvasContext::setWideGamut(bool wideGamut) {
+ mWideColorGamut = wideGamut;
+}
+
bool CanvasContext::makeCurrent() {
if (mStopped) return false;
@@ -416,7 +421,7 @@ void CanvasContext::draw() {
SkRect windowDirty = computeDirtyRect(frame, &dirty);
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
- mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler()));
+ mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo, mRenderNodes, &(profiler()));
waitOnFences();
@@ -558,7 +563,8 @@ void CanvasContext::buildLayer(RenderNode* node) {
// purposes when the frame is actually drawn
node->setPropertyFieldsDirty(RenderNode::GENERIC);
- mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo);
+ mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue,
+ mOpaque, mWideColorGamut, mLightInfo);
node->incStrong(nullptr);
mPrefetchedLayers.insert(node);
@@ -580,15 +586,37 @@ void CanvasContext::destroyHardwareResources() {
}
void CanvasContext::trimMemory(RenderThread& thread, int level) {
- // No context means nothing to free
- if (!thread.eglManager().hasEglContext()) return;
-
- ATRACE_CALL();
- if (level >= TRIM_MEMORY_COMPLETE) {
- thread.renderState().flush(Caches::FlushMode::Full);
- thread.eglManager().destroy();
- } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
- thread.renderState().flush(Caches::FlushMode::Moderate);
+ auto renderType = Properties::getRenderPipelineType();
+ switch (renderType) {
+ case RenderPipelineType::OpenGL: {
+ // No context means nothing to free
+ if (!thread.eglManager().hasEglContext()) return;
+ ATRACE_CALL();
+ if (level >= TRIM_MEMORY_COMPLETE) {
+ thread.renderState().flush(Caches::FlushMode::Full);
+ thread.eglManager().destroy();
+ } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
+ thread.renderState().flush(Caches::FlushMode::Moderate);
+ }
+ break;
+ }
+ case RenderPipelineType::SkiaGL:
+ case RenderPipelineType::SkiaVulkan: {
+ // No context means nothing to free
+ if (!thread.getGrContext()) return;
+ ATRACE_CALL();
+ if (level >= TRIM_MEMORY_COMPLETE) {
+ thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ thread.eglManager().destroy();
+ thread.vulkanManager().destroy();
+ } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
+ thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
+ }
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
}
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 738c09141a7a..b1f405040a59 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -76,7 +76,7 @@ public:
* @return true if the layer has been created or updated
*/
bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator) {
- return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator);
+ return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, mWideColorGamut);
}
/**
@@ -128,6 +128,7 @@ public:
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
void setLightCenter(const Vector3& lightCenter);
void setOpaque(bool opaque);
+ void setWideGamut(bool wideGamut);
bool makeCurrent();
void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
int64_t syncQueued, RenderNode* target);
@@ -194,6 +195,8 @@ public:
void waitOnFences();
+ IRenderPipeline* getRenderPipeline() { return mRenderPipeline.get(); }
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
@@ -238,6 +241,7 @@ private:
nsecs_t mLastDropVsync = 0;
bool mOpaque;
+ bool mWideColorGamut = false;
BakedOpRenderer::LightInfo mLightInfo;
FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 };
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index ed3070887b8b..d6240e7064cf 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -76,12 +76,16 @@ const char* EglManager::eglErrorString() {
static struct {
bool bufferAge = false;
bool setDamage = false;
+ bool noConfigContext = false;
+ bool pixelFormatFloat = false;
+ bool glColorSpace = false;
} EglExtensions;
EglManager::EglManager(RenderThread& thread)
: mRenderThread(thread)
, mEglDisplay(EGL_NO_DISPLAY)
, mEglConfig(nullptr)
+ , mEglConfigWideGamut(nullptr)
, mEglContext(EGL_NO_CONTEXT)
, mPBufferSurface(EGL_NO_SURFACE)
, mCurrentSurface(EGL_NO_SURFACE) {
@@ -116,7 +120,7 @@ void EglManager::initialize() {
}
}
- loadConfig();
+ loadConfigs();
createContext();
createPBufferSurface();
makeCurrent(mPBufferSurface);
@@ -134,7 +138,7 @@ void EglManager::initialize() {
GrContextOptions options;
options.fGpuPathRenderers &= ~GrContextOptions::GpuPathRenderers::kDistanceField;
- options.fAllowPathMaskCaching = true;
+ mRenderThread.cacheManager().configureContext(&options);
mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend,
(GrBackendContext)glInterface.get(), options));
}
@@ -143,6 +147,7 @@ void EglManager::initialize() {
void EglManager::initExtensions() {
auto extensions = StringUtils::split(
eglQueryString(mEglDisplay, EGL_EXTENSIONS));
+
// 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
@@ -152,13 +157,17 @@ void EglManager::initExtensions() {
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");
+
+ EglExtensions.glColorSpace = extensions.has("EGL_KHR_gl_colorspace");
+ EglExtensions.noConfigContext = extensions.has("EGL_KHR_no_config_context");
+ EglExtensions.pixelFormatFloat = extensions.has("EGL_EXT_pixel_format_float");
}
bool EglManager::hasEglContext() {
return mEglDisplay != EGL_NO_DISPLAY;
}
-void EglManager::loadConfig() {
+void EglManager::loadConfigs() {
ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
EGLint swapBehavior = (mSwapBehavior == SwapBehavior::Preserved)
? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
@@ -175,19 +184,44 @@ void EglManager::loadConfig() {
EGL_NONE
};
- EGLint num_configs = 1;
- if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
- || num_configs != 1) {
+ EGLint numConfigs = 1;
+ if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, numConfigs, &numConfigs)
+ || numConfigs != 1) {
if (mSwapBehavior == SwapBehavior::Preserved) {
// Try again without dirty regions enabled
ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
mSwapBehavior = SwapBehavior::Discard;
- loadConfig();
+ loadConfigs();
+ return; // the call to loadConfigs() we just made picks the wide gamut config
} else {
// Failed to get a valid config
LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString());
}
}
+
+ if (EglExtensions.pixelFormatFloat) {
+ // If we reached this point, we have a valid swap behavior
+ EGLint attribs16F[] = {
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT,
+ EGL_RED_SIZE, 16,
+ EGL_GREEN_SIZE, 16,
+ EGL_BLUE_SIZE, 16,
+ EGL_ALPHA_SIZE, 16,
+ EGL_DEPTH_SIZE, 0,
+ EGL_STENCIL_SIZE, Stencil::getStencilSize(),
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
+ EGL_NONE
+ };
+
+ numConfigs = 1;
+ if (!eglChooseConfig(mEglDisplay, attribs16F, &mEglConfigWideGamut, numConfigs, &numConfigs)
+ || numConfigs != 1) {
+ LOG_ALWAYS_FATAL(
+ "Device claims wide gamut support, cannot find matching config, error = %s",
+ eglErrorString());
+ }
+ }
}
void EglManager::createContext() {
@@ -195,7 +229,9 @@ void EglManager::createContext() {
EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
EGL_NONE
};
- mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
+ mEglContext = eglCreateContext(mEglDisplay,
+ EglExtensions.noConfigContext ? ((EGLConfig) nullptr) : mEglConfig,
+ EGL_NO_CONTEXT, attribs);
LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
"Failed to create context, error = %s", eglErrorString());
}
@@ -210,18 +246,60 @@ void EglManager::createPBufferSurface() {
}
}
-EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
+EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorGamut) {
initialize();
+ wideColorGamut = wideColorGamut && EglExtensions.glColorSpace
+ && EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext;
+
+ // The color space we want to use depends on whether linear blending is turned
+ // on and whether the app has requested wide color gamut rendering. When wide
+ // color gamut rendering is off, the app simply renders in the display's native
+ // color gamut.
+ //
+ // When wide gamut rendering is off:
+ // - Blending is done by default in gamma space, which requires using a
+ // linear EGL color space (the GPU uses the color values as is)
+ // - If linear blending is on, we must use the sRGB EGL color space (the
+ // GPU will perform sRGB to linear and linear to SRGB conversions before
+ // and after blending)
+ //
+ // When wide gamut rendering is on we cannot rely on the GPU performing
+ // linear blending for us. We use two different color spaces to tag the
+ // surface appropriately for SurfaceFlinger:
+ // - Gamma blending (default) requires the use of the scRGB-nl color space
+ // - Linear blending requires the use of the scRGB color space
+
+ // Not all Android targets support the EGL_GL_COLOR_SPACE_KHR extension
+ // We insert to placeholders to set EGL_GL_COLORSPACE_KHR and its value.
+ // According to section 3.4.1 of the EGL specification, the attributes
+ // list is considered empty if the first entry is EGL_NONE
EGLint attribs[] = {
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
- EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
- EGL_COLORSPACE, EGL_COLORSPACE_sRGB,
-#endif
+ EGL_NONE, EGL_NONE,
EGL_NONE
};
- EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, attribs);
+ if (EglExtensions.glColorSpace) {
+ attribs[0] = EGL_GL_COLORSPACE_KHR;
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
+ if (wideColorGamut) {
+ attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT;
+ } else {
+ attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR;
+ }
+#else
+ if (wideColorGamut) {
+ // TODO: this should be using scRGB-nl, not scRGB, we need an extension for this
+ // TODO: in the meantime SurfaceFlinger just assumes that scRGB is scRGB-nl
+ attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT;
+ } else {
+ attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
+ }
+#endif
+ }
+
+ EGLSurface surface = eglCreateWindowSurface(mEglDisplay,
+ wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs);
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
"Failed to create EGLSurface for window %p, eglErr = %s",
(void*) window, eglErrorString());
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 025192511cd9..2982c23552c9 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -39,7 +39,7 @@ public:
bool hasEglContext();
- EGLSurface createSurface(EGLNativeWindowType window);
+ EGLSurface createSurface(EGLNativeWindowType window, bool wideColorGamut);
void destroySurface(EGLSurface surface);
void destroy();
@@ -68,7 +68,7 @@ private:
void initExtensions();
void createPBufferSurface();
- void loadConfig();
+ void loadConfigs();
void createContext();
EGLint queryBufferAge(EGLSurface surface);
@@ -76,6 +76,7 @@ private:
EGLDisplay mEglDisplay;
EGLConfig mEglConfig;
+ EGLConfig mEglConfigWideGamut;
EGLContext mEglContext;
EGLSurface mPBufferSurface;
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 45f6718a68fb..f9b6e384d211 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -44,6 +44,12 @@ enum class MakeCurrentResult {
Succeeded
};
+enum class ColorMode {
+ Srgb,
+ WideColorGamut,
+ // Hdr
+};
+
class Frame;
class IRenderPipeline {
@@ -53,7 +59,7 @@ public:
virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const FrameBuilder::LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque,
+ const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
const BakedOpRenderer::LightInfo& lightInfo,
const std::vector< sp<RenderNode> >& renderNodes,
FrameInfoVisualizer* profiler) = 0;
@@ -61,17 +67,17 @@ public:
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 bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 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,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut,
const BakedOpRenderer::LightInfo& lightInfo) = 0;
virtual TaskManager* getTaskManager() = 0;
virtual bool createOrUpdateLayer(RenderNode* node,
- const DamageAccumulator& damageAccumulator) = 0;
+ const DamageAccumulator& damageAccumulator, bool wideColorGamut) = 0;
virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0;
virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0;
virtual void unpinImages() = 0;
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index e1ae58532a02..7283eb123d6a 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -58,7 +58,7 @@ Frame OpenGLPipeline::getFrame() {
bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const FrameBuilder::LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque,
+ const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
const BakedOpRenderer::LightInfo& lightInfo,
const std::vector< sp<RenderNode> >& renderNodes,
FrameInfoVisualizer* profiler) {
@@ -77,7 +77,7 @@ bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const S
frameBuilder.deferRenderNodeScene(renderNodes, contentDrawBounds);
BakedOpRenderer renderer(caches, mRenderThread.renderState(),
- opaque, lightInfo);
+ opaque, wideColorGamut, lightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
ProfileRenderer profileRenderer(renderer);
profiler->draw(profileRenderer);
@@ -146,7 +146,7 @@ void OpenGLPipeline::onStop() {
}
}
-bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
+bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, ColorMode colorMode) {
if (mEglSurface != EGL_NO_SURFACE) {
mEglManager.destroySurface(mEglSurface);
@@ -154,7 +154,8 @@ bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
}
if (surface) {
- mEglSurface = mEglManager.createSurface(surface);
+ const bool wideColorGamut = colorMode == ColorMode::WideColorGamut;
+ mEglSurface = mEglManager.createSurface(surface, wideColorGamut);
}
if (mEglSurface != EGL_NO_SURFACE) {
@@ -183,14 +184,14 @@ void OpenGLPipeline::onDestroyHardwareResources() {
}
void OpenGLPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut,
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);
+ // TODO: Handle wide color gamut contexts
+ BakedOpRenderer renderer(caches, mRenderThread.renderState(), opaque, wideColorGamut, lightInfo);
LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case");
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
}
@@ -204,12 +205,13 @@ static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) {
}
bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
- const DamageAccumulator& damageAccumulator) {
+ const DamageAccumulator& damageAccumulator, bool wideColorGamut) {
RenderState& renderState = mRenderThread.renderState();
OffscreenBufferPool& layerPool = renderState.layerPool();
bool transformUpdateNeeded = false;
if (node->getLayer() == nullptr) {
- node->setLayer(layerPool.get(renderState, node->getWidth(), node->getHeight()));
+ node->setLayer(layerPool.get(renderState,
+ node->getWidth(), node->getHeight(), wideColorGamut));
transformUpdateNeeded = true;
} else if (!layerMatchesWH(node->getLayer(), node->getWidth(), node->getHeight())) {
// TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
@@ -267,6 +269,171 @@ void OpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor)
thread.renderState().invokeFunctor(functor, mode, nullptr);
}
+#define FENCE_TIMEOUT 2000000000
+
+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;
+};
+
+class AutoGlTexture {
+public:
+ AutoGlTexture(uirenderer::Caches& caches)
+ : mCaches(caches) {
+ glGenTextures(1, &mTexture);
+ caches.textureState().bindTexture(mTexture);
+ }
+
+ ~AutoGlTexture() {
+ mCaches.textureState().deleteTexture(mTexture);
+ }
+
+private:
+ uirenderer::Caches& mCaches;
+ GLuint mTexture = 0;
+};
+
+static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bitmap,
+ GraphicBuffer& buffer, GLint format, GLint type) {
+ EGLDisplay display = eglGetCurrentDisplay();
+ LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
+ "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
+ uirenderer::renderthread::EglManager::eglErrorString());
+ // 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;
+ }
+ AutoGlTexture glTexture(caches);
+ 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;
+}
+
+// TODO: handle SRGB sanely
+static PixelFormat internalFormatToPixelFormat(GLint internalFormat) {
+ switch (internalFormat) {
+ case GL_LUMINANCE:
+ return PIXEL_FORMAT_RGBA_8888;
+ case GL_SRGB8_ALPHA8:
+ return PIXEL_FORMAT_RGBA_8888;
+ case GL_RGBA:
+ return PIXEL_FORMAT_RGBA_8888;
+ case GL_RGB:
+ return PIXEL_FORMAT_RGB_565;
+ case GL_RGBA16F:
+ return PIXEL_FORMAT_RGBA_FP16;
+ default:
+ LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat);
+ return PIXEL_FORMAT_UNKNOWN;
+ }
+}
+
+sk_sp<Bitmap> OpenGLPipeline::allocateHardwareBitmap(RenderThread& renderThread,
+ SkBitmap& skBitmap) {
+ renderThread.eglManager().initialize();
+ uirenderer::Caches& caches = uirenderer::Caches::getInstance();
+
+ const SkImageInfo& info = skBitmap.info();
+ if (info.colorType() == kUnknown_SkColorType || info.colorType() == kAlpha_8_SkColorType) {
+ ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType());
+ return nullptr;
+ }
+
+ bool needSRGB = uirenderer::transferFunctionCloseToSRGB(skBitmap.info().colorSpace());
+ bool hasLinearBlending = caches.extensions().hasLinearBlending();
+ GLint format, type, internalFormat;
+ uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(),
+ needSRGB && hasLinearBlending, &internalFormat, &format, &type);
+
+ PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat);
+ sp<GraphicBuffer> buffer = new GraphicBuffer(info.width(), info.height(), pixelFormat,
+ GraphicBuffer::USAGE_HW_TEXTURE |
+ GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER,
+ std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) + "]");
+
+ status_t error = buffer->initCheck();
+ if (error < 0) {
+ ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
+ return nullptr;
+ }
+
+ SkBitmap bitmap;
+ if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(),
+ hasLinearBlending))) {
+ sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
+ bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB));
+ } else {
+ bitmap = skBitmap;
+ }
+
+ if (!uploadBitmapToGraphicBuffer(caches, bitmap, *buffer, format, type)) {
+ return nullptr;
+ }
+ return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h
index 6df8be477e9c..4ca19fb6245c 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.h
+++ b/libs/hwui/renderthread/OpenGLPipeline.h
@@ -36,7 +36,7 @@ public:
bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const FrameBuilder::LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque,
+ const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
const BakedOpRenderer::LightInfo& lightInfo,
const std::vector< sp<RenderNode> >& renderNodes,
FrameInfoVisualizer* profiler) override;
@@ -44,23 +44,25 @@ public:
FrameInfo* currentFrameInfo, bool* requireSwap) override;
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(Surface* window, SwapBehavior swapBehavior) override;
+ bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) override;
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
void onDestroyHardwareResources() override;
void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut,
const BakedOpRenderer::LightInfo& lightInfo) override;
TaskManager* getTaskManager() override;
bool createOrUpdateLayer(RenderNode* node,
- const DamageAccumulator& damageAccumulator) override;
+ const DamageAccumulator& damageAccumulator, bool wideColorGamut) 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);
+ static sk_sp<Bitmap> allocateHardwareBitmap(RenderThread& thread,
+ SkBitmap& skBitmap);
private:
EglManager& mEglManager;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index d842be9e7d6e..80c2955400d8 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -228,6 +228,18 @@ void RenderProxy::setOpaque(bool opaque) {
post(task);
}
+CREATE_BRIDGE2(setWideGamut, CanvasContext* context, bool wideGamut) {
+ args->context->setWideGamut(args->wideGamut);
+ return nullptr;
+}
+
+void RenderProxy::setWideGamut(bool wideGamut) {
+ SETUP_TASK(setWideGamut);
+ args->context = mContext;
+ args->wideGamut = wideGamut;
+ post(task);
+}
+
int64_t* RenderProxy::frameInfo() {
return mDrawFrameTask.frameInfo();
}
@@ -458,18 +470,7 @@ uint32_t RenderProxy::frameTimePercentile(int p) {
}
CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) {
- args->thread->jankTracker().dump(args->fd);
-
- FILE *file = fdopen(args->fd, "a");
- if (Caches::hasInstance()) {
- String8 cachesLog;
- Caches::getInstance().dumpMemoryUsage(cachesLog);
- fprintf(file, "\nCaches:\n%s\n", cachesLog.string());
- } else {
- fprintf(file, "\nNo caches instance.\n");
- }
- fprintf(file, "\nPipeline=FrameBuilder\n");
- fflush(file);
+ args->thread->dumpGraphicsMemory(args->fd);
return nullptr;
}
@@ -665,7 +666,7 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) {
}
CREATE_BRIDGE2(allocateHardwareBitmap, RenderThread* thread, SkBitmap* bitmap) {
- sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(*args->thread, *args->bitmap);
+ sk_sp<Bitmap> hardwareBitmap = args->thread->allocateHardwareBitmap(*args->bitmap);
return hardwareBitmap.release();
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 6f4e8cef4502..31f0ce67e1a7 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -69,7 +69,7 @@ namespace DumpFlags {
*/
class ANDROID_API RenderProxy {
public:
- ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode, IContextFactory* contextFactory);
+ ANDROID_API RenderProxy(bool opaque, RenderNode* rootNode, IContextFactory* contextFactory);
ANDROID_API virtual ~RenderProxy();
// Won't take effect until next EGLSurface creation
@@ -85,6 +85,7 @@ public:
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
ANDROID_API void setLightCenter(const Vector3& lightCenter);
ANDROID_API void setOpaque(bool opaque);
+ ANDROID_API void setWideGamut(bool wideGamut);
ANDROID_API int64_t* frameInfo();
ANDROID_API int syncAndDrawFrame();
ANDROID_API void destroy();
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 1450ec98dabf..13af2c4d15e8 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,8 +16,12 @@
#include "RenderThread.h"
-#include "../renderstate/RenderState.h"
-#include "../pipeline/skia/SkiaOpenGLReadback.h"
+#include "hwui/Bitmap.h"
+#include "renderstate/RenderState.h"
+#include "renderthread/OpenGLPipeline.h"
+#include "pipeline/skia/SkiaOpenGLReadback.h"
+#include "pipeline/skia/SkiaOpenGLPipeline.h"
+#include "pipeline/skia/SkiaVulkanPipeline.h"
#include "CanvasContext.h"
#include "EglManager.h"
#include "OpenGLReadback.h"
@@ -198,6 +202,45 @@ void RenderThread::initThreadLocals() {
mRenderState = new RenderState(*this);
mJankTracker = new JankTracker(mDisplayInfo);
mVkManager = new VulkanManager(*this);
+ mCacheManager = new CacheManager(mDisplayInfo);
+}
+
+void RenderThread::dumpGraphicsMemory(int fd) {
+ jankTracker().dump(fd);
+
+ String8 cachesOutput;
+ String8 pipeline;
+ auto renderType = Properties::getRenderPipelineType();
+ switch (renderType) {
+ case RenderPipelineType::OpenGL: {
+ if (Caches::hasInstance()) {
+ cachesOutput.appendFormat("Caches:\n");
+ Caches::getInstance().dumpMemoryUsage(cachesOutput);
+ } else {
+ cachesOutput.appendFormat("No caches instance.");
+ }
+ pipeline.appendFormat("FrameBuilder");
+ break;
+ }
+ case RenderPipelineType::SkiaGL: {
+ mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState);
+ pipeline.appendFormat("Skia (OpenGL)");
+ break;
+ }
+ case RenderPipelineType::SkiaVulkan: {
+ mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState);
+ pipeline.appendFormat("Skia (Vulkan)");
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
+ }
+
+ FILE *file = fdopen(fd, "a");
+ fprintf(file, "\n%s\n", cachesOutput.string());
+ fprintf(file, "\nPipeline=%s\n", pipeline.string());
+ fflush(file);
}
Readback& RenderThread::readback() {
@@ -224,6 +267,14 @@ Readback& RenderThread::readback() {
return *mReadback;
}
+void RenderThread::setGrContext(GrContext* context) {
+ mCacheManager->reset(context);
+ if (mGrContext.get()) {
+ mGrContext->releaseResourcesAndAbandonContext();
+ }
+ mGrContext.reset(context);
+}
+
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
ALOGE("Display event receiver pipe was closed or an error occurred. "
@@ -433,6 +484,22 @@ RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
return next;
}
+sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) {
+ auto renderType = Properties::getRenderPipelineType();
+ switch (renderType) {
+ case RenderPipelineType::OpenGL:
+ return OpenGLPipeline::allocateHardwareBitmap(*this, skBitmap);
+ case RenderPipelineType::SkiaGL:
+ return skiapipeline::SkiaOpenGLPipeline::allocateHardwareBitmap(*this, skBitmap);
+ case RenderPipelineType::SkiaVulkan:
+ return skiapipeline::SkiaVulkanPipeline::allocateHardwareBitmap(*this, skBitmap);
+ default:
+ LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+ break;
+ }
+ return nullptr;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 9bc5985e5b16..d9842572d7cd 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -20,10 +20,12 @@
#include "RenderTask.h"
#include "../JankTracker.h"
+#include "CacheManager.h"
#include "TimeLord.h"
#include <GrContext.h>
#include <cutils/compiler.h>
+#include <SkBitmap.h>
#include <ui/DisplayInfo.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
@@ -33,6 +35,7 @@
namespace android {
+class Bitmap;
class DisplayEventReceiver;
namespace uirenderer {
@@ -100,10 +103,14 @@ public:
const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; }
GrContext* getGrContext() const { return mGrContext.get(); }
- void setGrContext(GrContext* cxt) { mGrContext.reset(cxt); }
+ void setGrContext(GrContext* cxt);
+ CacheManager& cacheManager() { return *mCacheManager; }
VulkanManager& vulkanManager() { return *mVkManager; }
+ sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap);
+ void dumpGraphicsMemory(int fd);
+
protected:
virtual bool threadLoop() override;
@@ -157,6 +164,7 @@ private:
Readback* mReadback = nullptr;
sk_sp<GrContext> mGrContext;
+ CacheManager* mCacheManager;
VulkanManager* mVkManager;
};
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index c2c2f2239c7f..a745320ca884 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -54,7 +54,8 @@ void VulkanManager::initialize() {
auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; };
- mBackendContext.reset(GrVkBackendContext::Create(&mPresentQueueIndex, canPresent));
+ mBackendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, vkGetDeviceProcAddr,
+ &mPresentQueueIndex, canPresent));
// Get all the addresses of needed vulkan functions
VkInstance instance = mBackendContext->fInstance;
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index ab6420e990f9..87eaa6add459 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -19,7 +19,7 @@
#include "JankTracker.h"
#include <frameworks/base/core/proto/android/service/graphicsstats.pb.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <log/log.h>
#include <inttypes.h>
@@ -27,6 +27,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
+#include <sys/mman.h>
namespace android {
namespace uirenderer {
@@ -46,10 +47,74 @@ static void mergeProfileDataIntoProto(service::GraphicsStatsProto* proto,
const ProfileData* data);
static void dumpAsTextToFd(service::GraphicsStatsProto* proto, int outFd);
+class FileDescriptor {
+public:
+ FileDescriptor(int fd) : mFd(fd) {}
+ ~FileDescriptor() {
+ if (mFd != -1) {
+ close(mFd);
+ mFd = -1;
+ }
+ }
+ bool valid() { return mFd != -1; }
+ operator int() { return mFd; }
+private:
+ int mFd;
+};
+
+class FileOutputStreamLite : public io::ZeroCopyOutputStream {
+public:
+ FileOutputStreamLite(int fd) : mCopyAdapter(fd), mImpl(&mCopyAdapter) {}
+ virtual ~FileOutputStreamLite() {}
+
+ int GetErrno() { return mCopyAdapter.mErrno; }
+
+ virtual bool Next(void** data, int* size) override {
+ return mImpl.Next(data, size);
+ }
+
+ virtual void BackUp(int count) override {
+ mImpl.BackUp(count);
+ }
+
+ virtual int64 ByteCount() const override {
+ return mImpl.ByteCount();
+ }
+
+ bool Flush() {
+ return mImpl.Flush();
+ }
+
+private:
+ struct FDAdapter : public io::CopyingOutputStream {
+ int mFd;
+ int mErrno = 0;
+
+ FDAdapter(int fd) : mFd(fd) {}
+ virtual ~FDAdapter() {}
+
+ virtual bool Write(const void* buffer, int size) override {
+ int ret;
+ while (size) {
+ ret = TEMP_FAILURE_RETRY( write(mFd, buffer, size) );
+ if (ret <= 0) {
+ mErrno = errno;
+ return false;
+ }
+ size -= ret;
+ }
+ return true;
+ }
+ };
+
+ FileOutputStreamLite::FDAdapter mCopyAdapter;
+ io::CopyingOutputStreamAdaptor mImpl;
+};
+
bool GraphicsStatsService::parseFromFile(const std::string& path, service::GraphicsStatsProto* output) {
- int fd = open(path.c_str(), O_RDONLY);
- if (fd == -1) {
+ FileDescriptor fd{open(path.c_str(), O_RDONLY)};
+ if (!fd.valid()) {
int err = errno;
// The file not existing is normal for addToDump(), so only log if
// we get an unexpected error
@@ -58,26 +123,41 @@ bool GraphicsStatsService::parseFromFile(const std::string& path, service::Graph
}
return false;
}
- uint32_t file_version;
- ssize_t bytesRead = read(fd, &file_version, sHeaderSize);
- if (bytesRead != sHeaderSize || file_version != sCurrentFileVersion) {
- ALOGW("Failed to read '%s', bytesRead=%zd file_version=%d", path.c_str(), bytesRead,
- file_version);
- close(fd);
+ struct stat sb;
+ if (fstat(fd, &sb) || sb.st_size < sHeaderSize) {
+ int err = errno;
+ // The file not existing is normal for addToDump(), so only log if
+ // we get an unexpected error
+ if (err != ENOENT) {
+ ALOGW("Failed to fstat '%s', errno=%d (%s) (st_size %d)", path.c_str(), err,
+ strerror(err), (int) sb.st_size);
+ }
+ return false;
+ }
+ void* addr = mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (!addr) {
+ int err = errno;
+ // The file not existing is normal for addToDump(), so only log if
+ // we get an unexpected error
+ if (err != ENOENT) {
+ ALOGW("Failed to mmap '%s', errno=%d (%s)", path.c_str(), err, strerror(err));
+ }
+ return false;
+ }
+ uint32_t file_version = *reinterpret_cast<uint32_t*>(addr);
+ if (file_version != sCurrentFileVersion) {
+ ALOGW("file_version mismatch! expected %d got %d", sCurrentFileVersion, file_version);
return false;
}
- io::FileInputStream input(fd);
+ void* data = reinterpret_cast<uint8_t*>(addr) + sHeaderSize;
+ int dataSize = sb.st_size - sHeaderSize;
+ io::ArrayInputStream input{data, dataSize};
bool success = output->ParseFromZeroCopyStream(&input);
- if (input.GetErrno() != 0) {
- ALOGW("Error reading from fd=%d, path='%s' err=%d (%s)",
- fd, path.c_str(), input.GetErrno(), strerror(input.GetErrno()));
- success = false;
- } else if (!success) {
+ if (!success) {
ALOGW("Parse failed on '%s' error='%s'",
path.c_str(), output->InitializationErrorString().c_str());
}
- close(fd);
return success;
}
@@ -212,7 +292,7 @@ void GraphicsStatsService::saveBuffer(const std::string& path, const std::string
return;
}
{
- io::FileOutputStream output(outFd);
+ FileOutputStreamLite output(outFd);
bool success = statsProto.SerializeToZeroCopyStream(&output) && output.Flush();
if (output.GetErrno() != 0) {
ALOGW("Error writing to fd=%d, path='%s' err=%d (%s)",
@@ -277,7 +357,7 @@ void GraphicsStatsService::addToDump(Dump* dump, const std::string& path) {
void GraphicsStatsService::finishDump(Dump* dump) {
if (dump->type() == DumpType::Protobuf) {
- io::FileOutputStream stream(dump->fd());
+ FileOutputStreamLite stream(dump->fd());
dump->proto().SerializeToZeroCopyStream(&stream);
}
delete dump;
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index dd457867ea42..98d5fb3af09a 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -365,8 +365,15 @@ private:
}
auto displayList = node->getDisplayList();
if (displayList) {
- for (auto&& childOp : displayList->getChildren()) {
- syncHierarchyPropertiesAndDisplayListImpl(childOp->renderNode);
+ if (displayList->isSkiaDL()) {
+ for (auto&& childDr : static_cast<skiapipeline::SkiaDisplayList*>(
+ const_cast<DisplayList*>(displayList))->mChildNodes) {
+ syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode());
+ }
+ } else {
+ for (auto&& childOp : displayList->getChildren()) {
+ syncHierarchyPropertiesAndDisplayListImpl(childOp->renderNode);
+ }
}
}
}
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index a7ebb68d9d1f..4797dec8e89e 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -45,10 +45,8 @@ public:
skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint);
});
- SkBitmap bitmap;
SkPaint paint;
- hwuiBitmap->getSkBitmapForShaders(&bitmap);
- sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+ sk_sp<SkImage> image = hwuiBitmap->makeImage();
sk_sp<SkShader> repeatShader = image->makeShader(
SkShader::TileMode::kRepeat_TileMode,
SkShader::TileMode::kRepeat_TileMode,
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index a46142642ed4..c246ebaddcad 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -75,9 +75,7 @@ public:
void doFrame(int frameNr) override { }
sk_sp<SkShader> createBitmapShader(Bitmap& bitmap) {
- SkBitmap skBitmap;
- bitmap.getSkBitmapForShaders(&skBitmap);
- sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
+ sk_sp<SkImage> image = bitmap.makeImage();
return image->makeShader(SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode);
}
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 398e7a89be1e..a5e85df22c8e 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -83,7 +83,7 @@ void BM_FrameBuilder_deferAndRender(benchmark::State& state) {
sLightGeometry, caches);
frameBuilder.deferRenderNode(*node);
- BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
+ BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
benchmark::DoNotOptimize(&renderer);
}
@@ -142,7 +142,7 @@ void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) {
sLightGeometry, Caches::getInstance());
frameBuilder.deferRenderNode(*node);
- BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
+ BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
benchmark::DoNotOptimize(&renderer);
}
diff --git a/libs/hwui/tests/scripts/process_systrace.py b/libs/hwui/tests/scripts/process_systrace.py
new file mode 100755
index 000000000000..f497bf57e099
--- /dev/null
+++ b/libs/hwui/tests/scripts/process_systrace.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+import codecs, httplib, json, os, urllib, shutil, subprocess, sys, argparse
+
+upstream_git = 'https://github.com/catapult-project/catapult.git'
+
+script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
+catapult_src_dir = os.path.join(script_dir, 'catapult-upstream')
+
+parser = argparse.ArgumentParser()
+parser.add_argument('trace_file_or_dir',
+ help='Path to trace file or directory of trace files.')
+parser.add_argument('--output_file', dest='outfile', default=os.path.join(os.getcwd(), 'mapper_output.json'),
+ help='Path to output file to store results.')
+parser.add_argument('--mapper_func', dest='func', default='AvgDrawFrame',
+ help='Name of javascript mapper function in systrace_parser.html.')
+args = parser.parse_args()
+
+# Update the source if needed.
+if not os.path.exists(catapult_src_dir):
+ # Pull the latest source from the upstream git.
+ git_args = ['git', 'clone', upstream_git, catapult_src_dir]
+ p = subprocess.Popen(git_args, stdout=subprocess.PIPE, cwd=script_dir)
+ p.communicate()
+ if p.wait() != 0:
+ print 'Failed to checkout source from upstream git.'
+ sys.exit(1)
+
+mapper_func_file = os.path.join(script_dir, 'systrace_parser.html')
+path_to_process_traces = os.path.join(catapult_src_dir, 'trace_processor/bin/process_traces')
+run_command = path_to_process_traces + " --mapper_handle " + mapper_func_file + ":" + args.func + " --output_file " + args.outfile + " " + args.trace_file_or_dir
+print run_command
+sys.exit(os.system(run_command))
+
diff --git a/libs/hwui/tests/scripts/systrace_parser.html b/libs/hwui/tests/scripts/systrace_parser.html
new file mode 100644
index 000000000000..4c66ae251f8e
--- /dev/null
+++ b/libs/hwui/tests/scripts/systrace_parser.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+
+<script>
+'use strict';
+
+const RENDER_THREAD_NAME = "RenderThread";
+const UI_THREAD_NAME = "UI Thread";
+const DRAW_FRAME_SLICE_TITLE = "DrawFrame";
+const BINDER_SLICE_TITLE = "binder transaction";
+const RECORD_SLICE_TITLE = "Record View#draw()";
+const DEQUEUE_BUFFER_SLICE_TITLE = "dequeueBuffer";
+
+function getTimeInBinder(slice) {
+ if (slice.title === BINDER_SLICE_TITLE) {
+ return slice.duration;
+ }
+ let result = 0.0;
+ for (let subslice of slice.subSlices) {
+ result += getTimeInBinder(subslice);
+ }
+ return result;
+}
+
+function getTimeInDequeueBuffer(slice) {
+ if (slice.title === DEQUEUE_BUFFER_SLICE_TITLE) {
+ return slice.duration;
+ }
+ let result = 0.0;
+ for (let subslice of slice.subSlices) {
+ result += getTimeInDequeueBuffer(subslice);
+ }
+ return result;
+}
+
+tr.mre.FunctionRegistry.register(
+ function AvgDrawFrame(result, model) {
+ let canonicalUrl = model.canonicalUrl;
+
+ for (let p of model.getAllProcesses()) {
+ //calc stats for processes that have UI and render threads and at least 10 frames
+ let renderThread = p.findAtMostOneThreadNamed(RENDER_THREAD_NAME);
+ let UIThread = p.findAtMostOneThreadNamed(UI_THREAD_NAME);
+ if (renderThread && UIThread)
+ {
+ let numDrawFrames = 0;
+ let drawFramesWallDuration = 0.0;
+ let binderDuration = 0.0;
+ let dequeueBufferDuration = 0.0;
+
+ let numRecordViewDraw = 0;
+ let recordViewDrawWallDuration = 0.0;
+
+ renderThread.sliceGroup.slices.forEach(function(slice) {
+ if (slice.title === DRAW_FRAME_SLICE_TITLE) {
+ drawFramesWallDuration += slice.duration;
+ numDrawFrames++;
+ binderDuration += getTimeInBinder(slice);
+ dequeueBufferDuration += getTimeInDequeueBuffer(slice);
+ }
+ });
+ if (numDrawFrames < 10) continue;
+ UIThread.sliceGroup.slices.forEach(function(slice) {
+ if (slice.title === RECORD_SLICE_TITLE) {
+ recordViewDrawWallDuration += slice.duration;
+ numRecordViewDraw++;
+ }
+ });
+
+ let avgDrawFrameDuration = undefined;
+ if (numDrawFrames > 0) {
+ avgDrawFrameDuration = (drawFramesWallDuration-dequeueBufferDuration)/numDrawFrames;
+ }
+ let avgRecordViewDrawDuration = undefined;
+ if (numRecordViewDraw > 0) {
+ avgRecordViewDrawDuration = recordViewDrawWallDuration/numRecordViewDraw;
+ }
+
+ result.addPair('result', {
+ canonicalUrl: canonicalUrl,
+ processName: p.name,
+ avgDrawFrameDuration: Number(avgDrawFrameDuration).toFixed(3),
+ avgRecordViewDrawDuration: Number(avgRecordViewDrawDuration).toFixed(3),
+ avgRecordAndPlay: Number(avgDrawFrameDuration+avgRecordViewDrawDuration).toFixed(3)
+ });
+ }
+ }
+ });
+
+</script>
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
index 9a3b81cc0138..b0ef11f26bdd 100644
--- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -37,7 +37,7 @@ const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
class ValidatingBakedOpRenderer : public BakedOpRenderer {
public:
ValidatingBakedOpRenderer(RenderState& renderState, std::function<void(const Glop& glop)> validator)
- : BakedOpRenderer(Caches::getInstance(), renderState, true, sLightInfo)
+ : BakedOpRenderer(Caches::getInstance(), renderState, true, false, sLightInfo)
, mValidator(validator) {
mGlopReceiver = ValidatingGlopReceiver;
}
@@ -118,7 +118,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
layerPaint.setAlpha(128);
OffscreenBuffer* buffer = nullptr; // no providing a buffer, should hit rect fallback case
LayerOp op(Rect(10, 10), Matrix4::identity(), nullptr, &layerPaint, &buffer);
- testUnmergedGlopDispatch(renderThread, &op, [&renderThread] (const Glop& glop) {
+ testUnmergedGlopDispatch(renderThread, &op, [] (const Glop& glop) {
ADD_FAILURE() << "Nothing should happen";
}, 0);
}
diff --git a/libs/hwui/tests/unit/BakedOpRendererTests.cpp b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
index 380062a36d45..603599ceb88a 100644
--- a/libs/hwui/tests/unit/BakedOpRendererTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
@@ -24,7 +24,8 @@ using namespace android::uirenderer;
const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, startRepaintLayer_clear) {
- BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(), true, sLightInfo);
+ BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(),
+ true, false, sLightInfo);
OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200u, 200u);
layer.dirty(Rect(200, 200));
diff --git a/libs/hwui/tests/unit/BitmapTests.cpp b/libs/hwui/tests/unit/BitmapTests.cpp
index 8c7e08183a1e..ed689bd6f174 100644
--- a/libs/hwui/tests/unit/BitmapTests.cpp
+++ b/libs/hwui/tests/unit/BitmapTests.cpp
@@ -29,16 +29,15 @@ using namespace android::uirenderer;
TEST(Bitmap, colorTableRefCounting) {
const SkPMColor c[] = { SkPackARGB32(0x80, 0x80, 0, 0) };
- SkColorTable* ctable = new SkColorTable(c, SK_ARRAY_COUNT(c));
+ sk_sp<SkColorTable> ctable = SkColorTable::Make(c, SK_ARRAY_COUNT(c));
SkBitmap* bm = new SkBitmap();
bm->allocPixels(SkImageInfo::Make(1, 1, kIndex_8_SkColorType, kPremul_SkAlphaType),
- nullptr, ctable);
+ 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/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
new file mode 100644
index 000000000000..6115162c8f81
--- /dev/null
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "renderthread/CacheManager.h"
+#include "renderthread/EglManager.h"
+#include "tests/common/TestUtils.h"
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+
+static size_t getCacheUsage(GrContext* grContext) {
+ size_t cacheUsage;
+ grContext->getResourceCacheUsage(nullptr, &cacheUsage);
+ return cacheUsage;
+}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) {
+ DisplayInfo displayInfo = renderThread.mainDisplayInfo();
+ GrContext* grContext = renderThread.getGrContext();
+ ASSERT_TRUE(grContext != nullptr);
+
+ // create pairs of offscreen render targets and images until we exceed the backgroundCacheSizeLimit
+ std::vector<sk_sp<SkSurface>> surfaces;
+
+ while (getCacheUsage(grContext) <= renderThread.cacheManager().getBackgroundCacheSize()) {
+ SkImageInfo info = SkImageInfo::MakeA8(displayInfo.w, displayInfo.h);
+ sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(grContext, SkBudgeted::kYes, info);
+ surface->getCanvas()->drawColor(SK_AlphaTRANSPARENT);
+
+ grContext->flush();
+
+ surfaces.push_back(surface);
+ }
+
+ ASSERT_TRUE(1 < surfaces.size());
+
+ // attempt to trim all memory while we still hold strong refs
+ renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
+
+ // free the surfaces
+ for (size_t i = 0; i < surfaces.size(); i++) {
+ ASSERT_TRUE(surfaces[i]->unique());
+ surfaces[i].reset();
+ }
+
+ // verify that we have enough purgeable bytes
+ const size_t purgeableBytes = grContext->getResourceCachePurgeableBytes();
+ ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() < purgeableBytes);
+
+ // UI hidden and make sure only some got purged
+ renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
+ ASSERT_TRUE(0 < grContext->getResourceCachePurgeableBytes());
+ ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() > getCacheUsage(grContext));
+
+ // complete and make sure all get purged
+ renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
+}
diff --git a/libs/hwui/tests/unit/FontRendererTests.cpp b/libs/hwui/tests/unit/FontRendererTests.cpp
index 773a7ea54a52..ee202367d73e 100644
--- a/libs/hwui/tests/unit/FontRendererTests.cpp
+++ b/libs/hwui/tests/unit/FontRendererTests.cpp
@@ -28,7 +28,7 @@ static bool isZero(uint8_t* data, int size) {
return true;
}
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, DISABLED_renderDropShadow) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, renderDropShadow) {
SkPaint paint;
paint.setTextSize(10);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
diff --git a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
index cfe11347ff35..f6f73377e147 100644
--- a/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
+++ b/libs/hwui/tests/unit/GraphicsStatsServiceTests.cpp
@@ -50,7 +50,11 @@ std::string findRootPath() {
// No code left untested
TEST(GraphicsStats, findRootPath) {
+#ifdef __LP64__
+ std::string expected = "/data/nativetest64/hwui_unit_tests";
+#else
std::string expected = "/data/nativetest/hwui_unit_tests";
+#endif
EXPECT_EQ(expected, findRootPath());
}
@@ -156,4 +160,4 @@ TEST(GraphicsStats, merge) {
EXPECT_EQ(expectedCount, loadedProto.histogram().Get(i).frame_count());
EXPECT_EQ(expectedBucket, loadedProto.histogram().Get(i).render_millis());
}
-} \ No newline at end of file
+}
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index 6c42ca1f2c2e..19d7ef59e397 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -45,7 +45,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayer_overdrawRejection) {
FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
sLightGeometery, Caches::getInstance());
frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
- BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
+ BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
}
@@ -62,6 +62,6 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayerUnclipped_simple) {
FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
sLightGeometery, Caches::getInstance());
frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
- BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
+ BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
}
diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
index 6cd595af6d2f..919852f6b2d7 100644
--- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
@@ -41,6 +41,19 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, construct) {
EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
}
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, constructWideColorGamut) {
+ OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 49u, 149u, true);
+ EXPECT_EQ(49u, layer.viewportWidth);
+ EXPECT_EQ(149u, layer.viewportHeight);
+
+ EXPECT_EQ(64u, layer.texture.width());
+ EXPECT_EQ(192u, layer.texture.height());
+
+ EXPECT_TRUE(layer.wideColorGamut);
+
+ EXPECT_EQ(64u * 192u * 8u, layer.getSizeInBytes());
+}
+
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, getTextureCoordinates) {
OffscreenBuffer layerAligned(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
EXPECT_EQ(Rect(0, 1, 1, 0),
@@ -88,6 +101,47 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, getPutClear) {
EXPECT_EQ(0u, pool.getCount());
}
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, getPutClearWideColorGamut) {
+ OffscreenBufferPool pool;
+
+ auto layer = pool.get(renderThread.renderState(), 100u, 200u, true);
+ EXPECT_EQ(100u, layer->viewportWidth);
+ EXPECT_EQ(200u, layer->viewportHeight);
+ EXPECT_TRUE(layer->wideColorGamut);
+
+ ASSERT_LT(layer->getSizeInBytes(), pool.getMaxSize());
+
+ pool.putOrDelete(layer);
+ ASSERT_EQ(layer->getSizeInBytes(), pool.getSize());
+
+ auto layer2 = pool.get(renderThread.renderState(), 102u, 202u, true);
+ EXPECT_EQ(layer, layer2) << "layer should be recycled";
+ ASSERT_EQ(0u, pool.getSize()) << "pool should have been emptied by removing only layer";
+
+ pool.putOrDelete(layer2);
+ EXPECT_EQ(1u, pool.getCount());
+ pool.clear();
+ EXPECT_EQ(0u, pool.getSize());
+ EXPECT_EQ(0u, pool.getCount());
+
+ // add non wide gamut layer
+ auto layer3 = pool.get(renderThread.renderState(), 100u, 200u);
+ EXPECT_FALSE(layer3->wideColorGamut);
+ pool.putOrDelete(layer3);
+ EXPECT_EQ(1u, pool.getCount());
+
+ auto layer4 = pool.get(renderThread.renderState(), 100u, 200u, true);
+ EXPECT_TRUE(layer4->wideColorGamut);
+ EXPECT_EQ(1u, pool.getCount());
+ ASSERT_NE(layer3, layer4);
+
+ pool.putOrDelete(layer4);
+
+ pool.clear();
+ EXPECT_EQ(0u, pool.getSize());
+ EXPECT_EQ(0u, pool.getCount());
+}
+
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, resize) {
OffscreenBufferPool pool;
@@ -123,6 +177,43 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, resize) {
pool.putOrDelete(layer2);
}
+RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, resizeWideColorGamut) {
+ OffscreenBufferPool pool;
+
+ auto layer = pool.get(renderThread.renderState(), 64u, 64u, true);
+
+ // resize in place
+ ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
+ EXPECT_EQ(60u, layer->viewportWidth);
+ EXPECT_EQ(55u, layer->viewportHeight);
+ EXPECT_EQ(64u, layer->texture.width());
+ EXPECT_EQ(64u, layer->texture.height());
+
+ EXPECT_TRUE(layer->wideColorGamut);
+ EXPECT_EQ(64u * 64u * 8u, layer->getSizeInBytes());
+
+ // resized to use different object in pool
+ auto layer2 = pool.get(renderThread.renderState(), 128u, 128u, true);
+ pool.putOrDelete(layer2);
+ ASSERT_EQ(1u, pool.getCount());
+
+ // add a non-wide gamut layer
+ auto layer3 = pool.get(renderThread.renderState(), 128u, 128u);
+ pool.putOrDelete(layer3);
+ ASSERT_EQ(2u, pool.getCount());
+
+ ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
+ EXPECT_EQ(120u, layer2->viewportWidth);
+ EXPECT_EQ(125u, layer2->viewportHeight);
+ EXPECT_EQ(128u, layer2->texture.width());
+ EXPECT_EQ(128u, layer2->texture.height());
+
+ EXPECT_TRUE(layer2->wideColorGamut);
+ EXPECT_EQ(128u * 128u * 8u, layer2->getSizeInBytes());
+
+ pool.putOrDelete(layer2);
+}
+
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, putAndDestroy) {
OffscreenBufferPool pool;
// layer too big to return to the pool
@@ -153,3 +244,4 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, clear) {
EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
}
+
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 686d06f9cd85..4c3e182ced2f 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -450,7 +450,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
LayerUpdateQueue layerUpdateQueue;
layerUpdateQueue.enqueueLayerWithDamage(child.get(),
android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
- SkiaPipeline::renderLayersImpl(layerUpdateQueue, true);
+ SkiaPipeline::renderLayersImpl(layerUpdateQueue, true, false);
EXPECT_EQ(1, drawCounter); //assert index 0 is drawn on the layer
RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index dafa0745ad67..a3d5079c6ce9 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -17,6 +17,7 @@
#include "tests/common/TestUtils.h"
#include <gtest/gtest.h>
+#include <SkBlurDrawLooper.h>
#include <SkColorMatrixFilter.h>
#include <SkColorSpace.h>
#include <SkImagePriv.h>
@@ -106,3 +107,16 @@ TEST(SkiaBehavior, srgbColorSpaceIsSingleton) {
sk_sp<SkColorSpace> sRGB2 = SkColorSpace::MakeSRGB();
ASSERT_EQ(sRGB1.get(), sRGB2.get());
}
+
+TEST(SkiaBehavior, blurDrawLooper) {
+ sk_sp<SkDrawLooper> looper = SkBlurDrawLooper::Make(SK_ColorRED, 5.0f, 3.0f, 4.0f);
+
+ SkDrawLooper::BlurShadowRec blur;
+ bool success = looper->asABlurShadow(&blur);
+ ASSERT_TRUE(success);
+
+ ASSERT_EQ(SK_ColorRED, blur.fColor);
+ ASSERT_EQ(5.0f, blur.fSigma);
+ ASSERT_EQ(3.0f, blur.fOffset.fX);
+ ASSERT_EQ(4.0f, blur.fOffset.fY);
+}
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 0aecb8540e4e..c048dda4a2e9 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -89,7 +89,6 @@ TEST(SkiaCanvas, colorSpaceXform) {
sk_sp<Bitmap> adobeBitmap = Bitmap::allocateHeapBitmap(adobeInfo);
SkBitmap adobeSkBitmap;
adobeBitmap->getSkBitmap(&adobeSkBitmap);
- adobeSkBitmap.lockPixels();
*adobeSkBitmap.getAddr32(0, 0) = 0xFF0000F0; // Opaque, almost fully-red
SkImageInfo info = adobeInfo.makeColorSpace(nullptr);
@@ -101,7 +100,6 @@ TEST(SkiaCanvas, colorSpaceXform) {
SkiaCanvas canvas(skBitmap);
canvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
// The result should be fully red, since we convert to sRGB at draw time.
- skBitmap.lockPixels();
ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
// Create a software canvas with an Adobe color space.
@@ -116,7 +114,6 @@ TEST(SkiaCanvas, colorSpaceXform) {
SkiaCanvas deferCanvas(&skCanvas, Canvas::XformToSRGB::kDefer);
deferCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
// The result should be as before, since we deferred the conversion to sRGB.
- skBitmap.lockPixels();
ASSERT_EQ(0xFF0000DC, *skBitmap.getAddr32(0, 0));
// Test picture recording. We will kDefer the xform at recording time, but handle it when
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index a895cbad838e..b397b151ad76 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -51,7 +51,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
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);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes,
+ opaque, false, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
@@ -72,10 +73,12 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
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);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes,
+ true, false, 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);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes,
+ false, false, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
}
@@ -94,7 +97,8 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
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);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes,
+ true, false, 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);
@@ -135,7 +139,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
lightGeometry.center = { 0.0f, 0.0f, 0.0f };
BakedOpRenderer::LightInfo lightInfo;
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
- pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, lightInfo);
+ pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, false, 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);
@@ -166,32 +170,38 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
// Single draw, should be white.
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque,
+ false, 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);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque,
+ false, 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);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque,
+ false, 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);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque,
+ false, 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);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque,
+ false, 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);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque,
+ false, contentDrawBounds, surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned) 0xffff8080);
}
@@ -278,7 +288,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) {
SkRect dirty = SkRect::MakeWH(800, 600);
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
sk_sp<DeferLayer<DeferTestCanvas>> surface(new DeferLayer<DeferTestCanvas>());
- pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false, contentDrawBounds, surface);
EXPECT_EQ(4, surface->canvas()->mDrawCounter);
}
@@ -308,7 +318,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) {
SkRect dirty = SkRect::MakeLTRB(10, 20, 30, 40);
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
sk_sp<DeferLayer<ClippedTestCanvas>> surface(new DeferLayer<ClippedTestCanvas>());
- pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false,
SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
@@ -339,7 +349,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
SkRect dirty = SkRect::MakeLTRB(10, 10, 40, 40);
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
sk_sp<DeferLayer<ClipReplaceTestCanvas>> surface(new DeferLayer<ClipReplaceTestCanvas>());
- pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false,
SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
index 5383e5756b93..8312bda8d67d 100644
--- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
+++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
@@ -26,7 +26,7 @@
using namespace android;
using namespace android::uirenderer;
-RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, DISABLED_addRemove) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) {
SkPaint paint;
paint.setTextSize(20);
diff --git a/libs/hwui/tests/unit/TextureCacheTests.cpp b/libs/hwui/tests/unit/TextureCacheTests.cpp
index 72384bfa87d8..ab740dd8330e 100644
--- a/libs/hwui/tests/unit/TextureCacheTests.cpp
+++ b/libs/hwui/tests/unit/TextureCacheTests.cpp
@@ -31,7 +31,7 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(TextureCache, clear) {
SkBitmap skBitmap;
SkImageInfo info = SkImageInfo::Make(100, 100, kN32_SkColorType, kPremul_SkAlphaType);
skBitmap.setInfo(info);
- sk_sp<Bitmap> hwBitmap(Bitmap::allocateHardwareBitmap(renderThread, skBitmap));
+ sk_sp<Bitmap> hwBitmap(renderThread.allocateHardwareBitmap(skBitmap));
cache.get(hwBitmap.get());
ASSERT_EQ(GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture), initialCount + 1);
cache.clear();
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 6f264e1ebf62..1c2156765fe6 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -382,47 +382,24 @@ TEST(VectorDrawable, groupProperties) {
}
-static SkShader* createShader(bool* isDestroyed) {
- class TestShader : public SkShader {
- public:
- TestShader(bool* isDestroyed) : SkShader(), mDestroyed(isDestroyed) {
- }
- ~TestShader() {
- *mDestroyed = true;
- }
-
- Factory getFactory() const override { return nullptr; }
- private:
- bool* mDestroyed;
- };
- return new TestShader(isDestroyed);
-}
-
TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) {
VectorDrawable::FullPath path("m1 1", 4);
SkBitmap bitmap;
- SkImageInfo info = SkImageInfo::Make(5, 5, kN32_SkColorType, kPremul_SkAlphaType);
- bitmap.setInfo(info);
- bitmap.allocPixels(info);
+ bitmap.allocN32Pixels(5, 5, false);
SkCanvas canvas(bitmap);
- bool shaderIsDestroyed = false;
-
+ sk_sp<SkShader> shader = SkShader::MakeColorShader(SK_ColorBLACK);
// Initial ref count is 1
- SkShader* shader = createShader(&shaderIsDestroyed);
+ EXPECT_TRUE(shader->unique());
// Setting the fill gradient increments the ref count of the shader by 1
- path.mutateStagingProperties()->setFillGradient(shader);
+ path.mutateStagingProperties()->setFillGradient(shader.get());
+ EXPECT_FALSE(shader->unique());
path.draw(&canvas, true);
// Resetting the fill gradient decrements the ref count of the shader by 1
path.mutateStagingProperties()->setFillGradient(nullptr);
- // Expect ref count to be 1 again, i.e. nothing else to have a ref to the shader now. Unref()
- // again should bring the ref count to zero and consequently trigger detor.
- shader->unref();
-
- // Verify that detor is called.
- EXPECT_TRUE(shaderIsDestroyed);
+ EXPECT_TRUE(shader->unique());
}
}; // namespace uirenderer
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
new file mode 100644
index 000000000000..43107064332e
--- /dev/null
+++ b/libs/input/Android.bp
@@ -0,0 +1,44 @@
+// 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.
+
+cc_library_shared {
+ name: "libinputservice",
+
+ srcs: [
+ "PointerController.cpp",
+ "SpriteController.cpp",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libutils",
+ "libskia",
+ "libgui",
+ "libui",
+ "libinput",
+ "libinputflinger",
+ "libnativewindow",
+ ],
+
+ include_dirs: ["frameworks/native/services"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+}
diff --git a/libs/input/Android.mk b/libs/input/Android.mk
deleted file mode 100644
index e8242751ba5c..000000000000
--- a/libs/input/Android.mk
+++ /dev/null
@@ -1,54 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- PointerController.cpp \
- SpriteController.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog \
- libutils \
- libskia \
- libgui \
- libui \
- libinput \
- libinputflinger \
- libnativewindow
-
-LOCAL_C_INCLUDES := \
- frameworks/native/services
-
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-LOCAL_MODULE:= libinputservice
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
-
-
-# Include subdirectory makefiles
-# ============================================================
-
-# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
-# team really wants is to build the stuff defined by this makefile.
-ifeq (,$(ONE_SHOT_MAKEFILE))
-include $(call first-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 4991f0434bc2..ed31b1202863 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -220,13 +220,13 @@ void SpriteController::doUpdateSprites() {
if (outBuffer.width > update.state.icon.bitmap.width()) {
paint.setColor(0); // transparent fill color
- surfaceCanvas.drawRectCoords(update.state.icon.bitmap.width(), 0,
- outBuffer.width, update.state.icon.bitmap.height(), paint);
+ surfaceCanvas.drawRect(SkRect::MakeLTRB(update.state.icon.bitmap.width(), 0,
+ outBuffer.width, update.state.icon.bitmap.height()), paint);
}
if (outBuffer.height > update.state.icon.bitmap.height()) {
paint.setColor(0); // transparent fill color
- surfaceCanvas.drawRectCoords(0, update.state.icon.bitmap.height(),
- outBuffer.width, outBuffer.height, paint);
+ surfaceCanvas.drawRect(SkRect::MakeLTRB(0, update.state.icon.bitmap.height(),
+ outBuffer.width, outBuffer.height), paint);
}
status = surface->unlockAndPost();
@@ -405,7 +405,11 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
uint32_t dirty;
if (icon.isValid()) {
- icon.bitmap.copyTo(&mLocked.state.icon.bitmap, kN32_SkColorType);
+ SkBitmap* bitmapCopy = &mLocked.state.icon.bitmap;
+ if (bitmapCopy->tryAllocPixels(icon.bitmap.info().makeColorType(kN32_SkColorType))) {
+ icon.bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
+ bitmapCopy->rowBytes(), 0, 0);
+ }
if (!mLocked.state.icon.isValid()
|| mLocked.state.icon.hotSpotX != icon.hotSpotX
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 7fc8d6fd5197..31e43e9b99e5 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -65,7 +65,10 @@ struct SpriteIcon {
inline SpriteIcon copy() const {
SkBitmap bitmapCopy;
- bitmap.copyTo(&bitmapCopy, kN32_SkColorType);
+ if (bitmapCopy.tryAllocPixels(bitmap.info().makeColorType(kN32_SkColorType))) {
+ bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(),
+ 0, 0);
+ }
return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
}