diff options
Diffstat (limited to 'libs')
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); } |