summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/Android.bp3
-rw-r--r--libs/androidfw/AssetManager.cpp1
-rw-r--r--libs/androidfw/AssetManager2.cpp250
-rw-r--r--libs/androidfw/Locale.cpp2
-rw-r--r--libs/androidfw/ResourceTypes.cpp65
-rw-r--r--libs/androidfw/include/androidfw/AssetManager.h1
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h12
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h10
-rw-r--r--libs/androidfw/include/androidfw/StringPiece.h8
-rw-r--r--libs/androidfw/tests/DynamicRefTable_test.cpp116
-rw-r--r--libs/androidfw/tests/Idmap_test.cpp6
-rw-r--r--libs/androidfw/tests/Theme_test.cpp39
-rw-r--r--libs/androidfw/tests/data/lib_two/R.h12
-rw-r--r--libs/androidfw/tests/data/lib_two/lib_two.apkbin1106 -> 1426 bytes
-rw-r--r--libs/androidfw/tests/data/lib_two/res/values/values.xml8
-rw-r--r--libs/hwui/AmbientShadow.cpp329
-rw-r--r--libs/hwui/AmbientShadow.h42
-rw-r--r--libs/hwui/Android.bp92
-rw-r--r--libs/hwui/Animator.h4
-rw-r--r--libs/hwui/BakedOpDispatcher.cpp879
-rw-r--r--libs/hwui/BakedOpDispatcher.h52
-rw-r--r--libs/hwui/BakedOpRenderer.cpp383
-rw-r--r--libs/hwui/BakedOpRenderer.h149
-rw-r--r--libs/hwui/BakedOpState.cpp173
-rw-r--r--libs/hwui/BakedOpState.h171
-rw-r--r--libs/hwui/Caches.cpp274
-rw-r--r--libs/hwui/Caches.h201
-rw-r--r--libs/hwui/CanvasState.cpp284
-rw-r--r--libs/hwui/CanvasState.h195
-rw-r--r--libs/hwui/CanvasTransform.cpp149
-rw-r--r--libs/hwui/CanvasTransform.h45
-rw-r--r--libs/hwui/ClipArea.cpp534
-rw-r--r--libs/hwui/ClipArea.h212
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp133
-rw-r--r--libs/hwui/DeferredLayerUpdater.h38
-rw-r--r--libs/hwui/DeviceInfo.cpp53
-rw-r--r--libs/hwui/DeviceInfo.h32
-rw-r--r--libs/hwui/DisplayList.cpp145
-rw-r--r--libs/hwui/DisplayList.h132
-rw-r--r--libs/hwui/DisplayListOps.in53
-rw-r--r--libs/hwui/Extensions.cpp83
-rw-r--r--libs/hwui/Extensions.h71
-rw-r--r--libs/hwui/FboCache.cpp82
-rw-r--r--libs/hwui/FboCache.h79
-rw-r--r--libs/hwui/FloatColor.h71
-rw-r--r--libs/hwui/FontRenderer.cpp802
-rw-r--r--libs/hwui/FontRenderer.h208
-rw-r--r--libs/hwui/FrameBuilder.cpp976
-rw-r--r--libs/hwui/FrameBuilder.h251
-rw-r--r--libs/hwui/FrameInfoVisualizer.cpp2
-rw-r--r--libs/hwui/GammaFontRenderer.cpp43
-rw-r--r--libs/hwui/GammaFontRenderer.h70
-rw-r--r--libs/hwui/GlLayer.cpp84
-rw-r--r--libs/hwui/GlLayer.h81
-rw-r--r--libs/hwui/Glop.h179
-rw-r--r--libs/hwui/GlopBuilder.cpp691
-rw-r--r--libs/hwui/GlopBuilder.h142
-rw-r--r--libs/hwui/GpuMemoryTracker.cpp17
-rw-r--r--libs/hwui/GradientCache.cpp272
-rw-r--r--libs/hwui/GradientCache.h177
-rw-r--r--libs/hwui/HardwareBitmapUploader.cpp259
-rw-r--r--libs/hwui/HardwareBitmapUploader.h (renamed from libs/hwui/tests/unit/DeviceInfoTests.cpp)23
-rw-r--r--libs/hwui/Image.cpp61
-rw-r--r--libs/hwui/Image.h63
-rw-r--r--libs/hwui/JankTracker.cpp2
-rw-r--r--libs/hwui/Layer.cpp23
-rw-r--r--libs/hwui/Layer.h81
-rw-r--r--libs/hwui/LayerBuilder.cpp378
-rw-r--r--libs/hwui/LayerBuilder.h137
-rw-r--r--libs/hwui/LayerUpdateQueue.h1
-rw-r--r--libs/hwui/Lighting.h (renamed from libs/hwui/OpDumper.h)17
-rw-r--r--libs/hwui/NinePatchUtils.h5
-rw-r--r--libs/hwui/OpDumper.cpp53
-rw-r--r--libs/hwui/OpenGLReadback.cpp287
-rw-r--r--libs/hwui/OpenGLReadback.h67
-rw-r--r--libs/hwui/Outline.h3
-rw-r--r--libs/hwui/Patch.cpp229
-rw-r--r--libs/hwui/Patch.h73
-rw-r--r--libs/hwui/PatchCache.cpp264
-rw-r--r--libs/hwui/PatchCache.h171
-rw-r--r--libs/hwui/PathCache.cpp559
-rw-r--r--libs/hwui/PathCache.h267
-rw-r--r--libs/hwui/PathParser.cpp13
-rw-r--r--libs/hwui/PathTessellator.cpp1069
-rw-r--r--libs/hwui/PathTessellator.h148
-rw-r--r--libs/hwui/PixelBuffer.cpp156
-rw-r--r--libs/hwui/PixelBuffer.h198
-rw-r--r--libs/hwui/ProfileData.cpp4
-rw-r--r--libs/hwui/ProfileRenderer.cpp40
-rw-r--r--libs/hwui/ProfileRenderer.h40
-rw-r--r--libs/hwui/Program.cpp199
-rw-r--r--libs/hwui/Program.h446
-rw-r--r--libs/hwui/ProgramCache.cpp865
-rw-r--r--libs/hwui/ProgramCache.h68
-rw-r--r--libs/hwui/Properties.cpp26
-rw-r--r--libs/hwui/Properties.h14
-rw-r--r--libs/hwui/Readback.cpp287
-rw-r--r--libs/hwui/Readback.h22
-rw-r--r--libs/hwui/RecordedOp.h500
-rw-r--r--libs/hwui/RecordingCanvas.cpp1435
-rw-r--r--libs/hwui/RecordingCanvas.h476
-rw-r--r--libs/hwui/RenderBuffer.h181
-rw-r--r--libs/hwui/RenderBufferCache.cpp161
-rw-r--r--libs/hwui/RenderBufferCache.h115
-rw-r--r--libs/hwui/RenderNode.cpp208
-rw-r--r--libs/hwui/RenderNode.h26
-rw-r--r--libs/hwui/RenderProperties.cpp6
-rw-r--r--libs/hwui/RenderProperties.h29
-rw-r--r--libs/hwui/ResourceCache.cpp154
-rw-r--r--libs/hwui/ResourceCache.h103
-rw-r--r--libs/hwui/ShadowTessellator.cpp191
-rw-r--r--libs/hwui/ShadowTessellator.h94
-rw-r--r--libs/hwui/SkiaCanvas.cpp138
-rw-r--r--libs/hwui/SkiaCanvas.h64
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp486
-rw-r--r--libs/hwui/SkiaCanvasProxy.h112
-rw-r--r--libs/hwui/SkiaShader.cpp360
-rw-r--r--libs/hwui/SkiaShader.h86
-rw-r--r--libs/hwui/Snapshot.cpp209
-rw-r--r--libs/hwui/Snapshot.h279
-rw-r--r--libs/hwui/SpotShadow.cpp1120
-rw-r--r--libs/hwui/SpotShadow.h79
-rw-r--r--libs/hwui/TessellationCache.cpp444
-rw-r--r--libs/hwui/TessellationCache.h225
-rw-r--r--libs/hwui/TextDropShadowCache.cpp209
-rw-r--r--libs/hwui/TextDropShadowCache.h164
-rw-r--r--libs/hwui/Texture.cpp413
-rw-r--r--libs/hwui/Texture.h228
-rw-r--r--libs/hwui/TextureCache.cpp227
-rw-r--r--libs/hwui/TextureCache.h144
-rw-r--r--libs/hwui/TreeInfo.cpp29
-rw-r--r--libs/hwui/TreeInfo.h10
-rw-r--r--libs/hwui/VectorDrawable.cpp114
-rw-r--r--libs/hwui/VectorDrawable.h68
-rw-r--r--libs/hwui/Vertex.h41
-rw-r--r--libs/hwui/VkLayer.cpp40
-rw-r--r--libs/hwui/VkLayer.h70
-rw-r--r--libs/hwui/debug/wrap_gles.h3
-rw-r--r--libs/hwui/font/CacheTexture.cpp355
-rw-r--r--libs/hwui/font/CacheTexture.h173
-rw-r--r--libs/hwui/font/CachedGlyphInfo.h56
-rw-r--r--libs/hwui/font/Font.cpp490
-rw-r--r--libs/hwui/font/Font.h161
-rw-r--r--libs/hwui/font/FontCacheHistoryTracker.cpp100
-rw-r--r--libs/hwui/font/FontCacheHistoryTracker.h65
-rw-r--r--libs/hwui/font/FontUtil.h46
-rw-r--r--libs/hwui/hwui/AnimatedImageDrawable.cpp19
-rw-r--r--libs/hwui/hwui/AnimatedImageDrawable.h4
-rw-r--r--libs/hwui/hwui/Bitmap.cpp152
-rw-r--r--libs/hwui/hwui/Bitmap.h34
-rw-r--r--libs/hwui/hwui/Canvas.cpp65
-rw-r--r--libs/hwui/hwui/Canvas.h35
-rw-r--r--libs/hwui/hwui/MinikinSkia.cpp27
-rw-r--r--libs/hwui/hwui/MinikinSkia.h7
-rw-r--r--libs/hwui/hwui/MinikinUtils.cpp17
-rw-r--r--libs/hwui/hwui/MinikinUtils.h5
-rw-r--r--libs/hwui/hwui/Paint.h16
-rw-r--r--libs/hwui/hwui/PaintFilter.h19
-rw-r--r--libs/hwui/hwui/PaintImpl.cpp10
-rw-r--r--libs/hwui/hwui/Typeface.cpp13
-rw-r--r--libs/hwui/pipeline/skia/DumpOpsCanvas.h13
-rw-r--r--libs/hwui/pipeline/skia/FunctorDrawable.h53
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.cpp21
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.h18
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp76
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.h2
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp30
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.h12
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp20
-rw-r--r--libs/hwui/pipeline/skia/ReorderBarrierDrawables.h5
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.cpp56
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.h46
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp3
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.h66
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp325
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h17
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp146
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLReadback.h37
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp38
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h26
-rw-r--r--libs/hwui/pipeline/skia/SkiaProfileRenderer.h2
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp115
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h43
-rw-r--r--libs/hwui/pipeline/skia/SkiaUtils.h (renamed from libs/hwui/renderstate/PixelBufferState.h)24
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp36
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanPipeline.h6
-rw-r--r--libs/hwui/pipeline/skia/SkiaVulkanReadback.h44
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp224
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h56
-rw-r--r--libs/hwui/protos/ProtoHelpers.h41
-rw-r--r--libs/hwui/protos/hwui.proto99
-rw-r--r--libs/hwui/renderstate/Blend.cpp140
-rw-r--r--libs/hwui/renderstate/Blend.h63
-rw-r--r--libs/hwui/renderstate/MeshState.cpp179
-rw-r--r--libs/hwui/renderstate/MeshState.h133
-rw-r--r--libs/hwui/renderstate/OffscreenBufferPool.cpp217
-rw-r--r--libs/hwui/renderstate/OffscreenBufferPool.h162
-rw-r--r--libs/hwui/renderstate/PixelBufferState.cpp42
-rw-r--r--libs/hwui/renderstate/RenderState.cpp435
-rw-r--r--libs/hwui/renderstate/RenderState.h105
-rw-r--r--libs/hwui/renderstate/Scissor.cpp103
-rw-r--r--libs/hwui/renderstate/Scissor.h51
-rw-r--r--libs/hwui/renderstate/Stencil.cpp140
-rw-r--r--libs/hwui/renderstate/Stencil.h103
-rw-r--r--libs/hwui/renderstate/TextureState.cpp147
-rw-r--r--libs/hwui/renderstate/TextureState.h98
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp14
-rw-r--r--libs/hwui/renderthread/CacheManager.h2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp149
-rw-r--r--libs/hwui/renderthread/CanvasContext.h43
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h1
-rw-r--r--libs/hwui/renderthread/EglManager.cpp192
-rw-r--r--libs/hwui/renderthread/EglManager.h31
-rw-r--r--libs/hwui/renderthread/IRenderPipeline.h26
-rw-r--r--libs/hwui/renderthread/OpenGLPipeline.cpp452
-rw-r--r--libs/hwui/renderthread/OpenGLPipeline.h75
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp71
-rw-r--r--libs/hwui/renderthread/RenderProxy.h23
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp100
-rw-r--r--libs/hwui/renderthread/RenderThread.h5
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp717
-rw-r--r--libs/hwui/renderthread/VulkanManager.h60
-rw-r--r--libs/hwui/surfacetexture/EGLConsumer.cpp675
-rw-r--r--libs/hwui/surfacetexture/EGLConsumer.h311
-rw-r--r--libs/hwui/surfacetexture/ImageConsumer.cpp152
-rw-r--r--libs/hwui/surfacetexture/ImageConsumer.h97
-rw-r--r--libs/hwui/surfacetexture/SurfaceTexture.cpp496
-rw-r--r--libs/hwui/surfacetexture/SurfaceTexture.h452
-rw-r--r--libs/hwui/tests/common/LeakChecker.cpp4
-rw-r--r--libs/hwui/tests/common/TestUtils.cpp67
-rw-r--r--libs/hwui/tests/common/TestUtils.h75
-rw-r--r--libs/hwui/tests/common/scenes/ListViewAnimation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/TestSceneBase.h2
-rw-r--r--libs/hwui/tests/macrobench/TestSceneRunner.cpp6
-rw-r--r--libs/hwui/tests/macrobench/main.cpp7
-rw-r--r--libs/hwui/tests/microbench/DisplayListCanvasBench.cpp53
-rw-r--r--libs/hwui/tests/microbench/FontBench.cpp50
-rw-r--r--libs/hwui/tests/microbench/FrameBuilderBench.cpp149
-rw-r--r--libs/hwui/tests/microbench/ShadowBench.cpp101
-rw-r--r--libs/hwui/tests/unit/BakedOpDispatcherTests.cpp289
-rw-r--r--libs/hwui/tests/unit/BakedOpRendererTests.cpp106
-rw-r--r--libs/hwui/tests/unit/BakedOpStateTests.cpp266
-rw-r--r--libs/hwui/tests/unit/CanvasStateTests.cpp160
-rw-r--r--libs/hwui/tests/unit/ClipAreaTests.cpp353
-rw-r--r--libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp23
-rw-r--r--libs/hwui/tests/unit/FatalTestCanvas.h4
-rw-r--r--libs/hwui/tests/unit/FontRendererTests.cpp55
-rw-r--r--libs/hwui/tests/unit/FrameBuilderTests.cpp2705
-rw-r--r--libs/hwui/tests/unit/GlopBuilderTests.cpp145
-rw-r--r--libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp2
-rw-r--r--libs/hwui/tests/unit/GradientCacheTests.cpp40
-rw-r--r--libs/hwui/tests/unit/LeakCheckTests.cpp65
-rw-r--r--libs/hwui/tests/unit/MeshStateTests.cpp37
-rw-r--r--libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp244
-rw-r--r--libs/hwui/tests/unit/OpDumperTests.cpp43
-rw-r--r--libs/hwui/tests/unit/RecordingCanvasTests.cpp847
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp42
-rw-r--r--libs/hwui/tests/unit/RenderPropertiesTests.cpp2
-rw-r--r--libs/hwui/tests/unit/ShaderCacheTests.cpp143
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp17
-rw-r--r--libs/hwui/tests/unit/SkiaCanvasTests.cpp36
-rw-r--r--libs/hwui/tests/unit/SkiaDisplayListTests.cpp55
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp54
-rw-r--r--libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp1
-rw-r--r--libs/hwui/tests/unit/SnapshotTests.cpp74
-rw-r--r--libs/hwui/tests/unit/TextDropShadowCacheTests.cpp56
-rw-r--r--libs/hwui/tests/unit/TextureCacheTests.cpp39
-rw-r--r--libs/hwui/tests/unit/TypefaceTests.cpp3
-rw-r--r--libs/hwui/tests/unit/main.cpp3
-rw-r--r--libs/hwui/thread/ThreadBase.h2
-rw-r--r--libs/hwui/utils/Color.cpp116
-rw-r--r--libs/hwui/utils/Color.h14
-rw-r--r--libs/hwui/utils/GLUtils.cpp17
-rw-r--r--libs/hwui/utils/GLUtils.h51
-rw-r--r--libs/hwui/utils/LinearAllocator.cpp10
-rw-r--r--libs/hwui/utils/Macros.h9
-rw-r--r--libs/hwui/utils/PaintUtils.h2
-rw-r--r--libs/hwui/utils/TestWindowContext.cpp197
-rw-r--r--libs/hwui/utils/TestWindowContext.h64
-rw-r--r--libs/hwui/utils/TypeLogic.h40
-rw-r--r--libs/incident/proto/android/section.proto3
-rw-r--r--libs/input/Android.bp3
-rw-r--r--libs/input/PointerController.h1
-rw-r--r--libs/input/SpriteController.cpp4
-rw-r--r--libs/protoutil/Android.bp31
-rw-r--r--libs/protoutil/include/android/util/ProtoOutputStream.h22
-rw-r--r--libs/protoutil/src/ProtoOutputStream.cpp174
-rw-r--r--libs/protoutil/tests/EncodedBuffer_test.cpp133
-rw-r--r--libs/protoutil/tests/ProtoOutputStream_test.cpp228
-rw-r--r--libs/protoutil/tests/protobuf_test.cpp51
-rw-r--r--libs/protoutil/tests/test.proto42
-rw-r--r--libs/services/include/android/os/StatsLogEventWrapper.h75
-rw-r--r--libs/services/src/os/DropBoxManager.cpp2
-rw-r--r--libs/services/src/os/StatsLogEventWrapper.cpp68
-rw-r--r--libs/storage/Android.bp1
-rw-r--r--libs/storage/IMountService.cpp3
-rw-r--r--libs/storage/ObbInfo.cpp47
-rw-r--r--libs/storage/include/storage/IMountService.h3
-rw-r--r--libs/storage/include/storage/ObbInfo.h48
299 files changed, 8491 insertions, 34552 deletions
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 0c32fdf5baf5..98af3eb05391 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -59,8 +59,6 @@ cc_library {
"ZipFileRO.cpp",
"ZipUtils.cpp",
],
- // Allow implicit fallthroughs in Locale.cpp and ResourceTypes.cpp until they are fixed.
- cflags: ["-Wno-implicit-fallthrough"],
export_include_dirs: ["include"],
export_shared_lib_headers: ["libz"],
target: {
@@ -142,6 +140,7 @@ cc_test {
"tests/Config_test.cpp",
"tests/ConfigDescription_test.cpp",
"tests/ConfigLocale_test.cpp",
+ "tests/DynamicRefTable_test.cpp",
"tests/Idmap_test.cpp",
"tests/LoadedArsc_test.cpp",
"tests/Locale_test.cpp",
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index fc625bbaf72d..843c1461e21b 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -74,6 +74,7 @@ const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay";
+const char* AssetManager::PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay";
const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme";
const char* AssetManager::TARGET_PACKAGE_NAME = "android";
const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 04cc5bb30ade..9e6948878b1d 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <iterator>
#include <set>
+#include <map>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
@@ -161,6 +162,13 @@ void AssetManager2::DumpToLog() const {
LOG(INFO) << base::StringPrintf("PG (%02x): ",
package_group.dynamic_ref_table.mAssignedPackageId)
<< list;
+
+ for (size_t i = 0; i < 256; i++) {
+ if (package_group.dynamic_ref_table.mLookupTable[i] != 0) {
+ LOG(INFO) << base::StringPrintf(" e[0x%02x] -> 0x%02x", (uint8_t) i,
+ package_group.dynamic_ref_table.mLookupTable[i]);
+ }
+ }
}
}
@@ -869,6 +877,17 @@ void AssetManager2::InvalidateCaches(uint32_t diff) {
}
}
+uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) {
+ for (auto& package_group : package_groups_) {
+ for (auto& package2 : package_group.packages_) {
+ if (package2.loaded_package_ == package) {
+ return package_group.dynamic_ref_table.mAssignedPackageId;
+ }
+ }
+ }
+ return 0;
+}
+
std::unique_ptr<Theme> AssetManager2::NewTheme() {
return std::unique_ptr<Theme>(new Theme(this));
}
@@ -1054,44 +1073,231 @@ void Theme::Clear() {
}
}
-bool Theme::SetTo(const Theme& o) {
+void Theme::SetTo(const Theme& o) {
if (this == &o) {
- return true;
+ return;
}
type_spec_flags_ = o.type_spec_flags_;
- const bool copy_only_system = asset_manager_ != o.asset_manager_;
+ if (asset_manager_ == o.asset_manager_) {
+ // The theme comes from the same asset manager so all theme data can be copied exactly
+ for (size_t p = 0; p < packages_.size(); p++) {
+ const Package *package = o.packages_[p].get();
+ if (package == nullptr) {
+ // The other theme doesn't have this package, clear ours.
+ packages_[p].reset();
+ continue;
+ }
- for (size_t p = 0; p < packages_.size(); p++) {
- const Package* package = o.packages_[p].get();
- if (package == nullptr || (copy_only_system && p != 0x01)) {
- // The other theme doesn't have this package, clear ours.
- packages_[p].reset();
- continue;
+ if (packages_[p] == nullptr) {
+ // The other theme has this package, but we don't. Make one.
+ packages_[p].reset(new Package());
+ }
+
+ for (size_t t = 0; t < package->types.size(); t++) {
+ const ThemeType *type = package->types[t].get();
+ if (type == nullptr) {
+ // The other theme doesn't have this type, clear ours.
+ packages_[p]->types[t].reset();
+ continue;
+ }
+
+ // Create a new type and update it to theirs.
+ const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry));
+ void *copied_data = malloc(type_alloc_size);
+ memcpy(copied_data, type, type_alloc_size);
+ packages_[p]->types[t].reset(reinterpret_cast<ThemeType *>(copied_data));
+ }
}
+ } else {
+ std::map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies;
+ typedef std::map<int, int> SourceToDestinationRuntimePackageMap;
+ std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
+
+ // Determine which ApkAssets are loaded in both theme AssetManagers
+ std::vector<const ApkAssets*> src_assets = o.asset_manager_->GetApkAssets();
+ for (size_t i = 0; i < src_assets.size(); i++) {
+ const ApkAssets* src_asset = src_assets[i];
+
+ std::vector<const ApkAssets*> dest_assets = asset_manager_->GetApkAssets();
+ for (size_t j = 0; j < dest_assets.size(); j++) {
+ const ApkAssets* dest_asset = dest_assets[j];
+
+ // Map the runtime package of the source apk asset to the destination apk asset
+ if (src_asset->GetPath() == dest_asset->GetPath()) {
+ const std::vector<std::unique_ptr<const LoadedPackage>>& src_packages =
+ src_asset->GetLoadedArsc()->GetPackages();
+ const std::vector<std::unique_ptr<const LoadedPackage>>& dest_packages =
+ dest_asset->GetLoadedArsc()->GetPackages();
+
+ SourceToDestinationRuntimePackageMap package_map;
+
+ // The source and destination package should have the same number of packages loaded in
+ // the same order.
+ const size_t N = src_packages.size();
+ CHECK(N == dest_packages.size())
+ << " LoadedArsc " << src_asset->GetPath() << " differs number of packages.";
+ for (size_t p = 0; p < N; p++) {
+ auto& src_package = src_packages[p];
+ auto& dest_package = dest_packages[p];
+ CHECK(src_package->GetPackageName() == dest_package->GetPackageName())
+ << " Package " << src_package->GetPackageName() << " differs in load order.";
+
+ int src_package_id = o.asset_manager_->GetAssignedPackageId(src_package.get());
+ int dest_package_id = asset_manager_->GetAssignedPackageId(dest_package.get());
+ package_map[src_package_id] = dest_package_id;
+ }
- if (packages_[p] == nullptr) {
- // The other theme has this package, but we don't. Make one.
- packages_[p].reset(new Package());
+ src_to_dest_asset_cookies.insert(std::pair<ApkAssetsCookie, ApkAssetsCookie>(i, j));
+ src_asset_cookie_id_map.insert(
+ std::pair<ApkAssetsCookie, SourceToDestinationRuntimePackageMap>(i, package_map));
+ break;
+ }
+ }
+ }
+
+ // Reset the data in the destination theme
+ for (size_t p = 0; p < packages_.size(); p++) {
+ if (packages_[p] != nullptr) {
+ packages_[p].reset();
+ }
}
- for (size_t t = 0; t < package->types.size(); t++) {
- const ThemeType* type = package->types[t].get();
+ for (size_t p = 0; p < packages_.size(); p++) {
+ const Package *package = o.packages_[p].get();
+ if (package == nullptr) {
+ continue;
+ }
+
+ for (size_t t = 0; t < package->types.size(); t++) {
+ const ThemeType *type = package->types[t].get();
+ if (type == nullptr) {
+ continue;
+ }
+
+ for (size_t e = 0; e < type->entry_count; e++) {
+ const ThemeEntry &entry = type->entries[e];
+ if (entry.value.dataType == Res_value::TYPE_NULL &&
+ entry.value.data != Res_value::DATA_NULL_EMPTY) {
+ continue;
+ }
+
+ // The package id of the attribute needs to be rewritten to the package id of the value in
+ // the destination
+ int attribute_dest_package_id = p;
+ if (attribute_dest_package_id != 0x01) {
+ // Find the cookie of the attribute resource id
+ FindEntryResult attribute_entry_result;
+ ApkAssetsCookie attribute_cookie =
+ o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , false,
+ &attribute_entry_result);
+
+ // Determine the package id of the attribute in the destination AssetManager
+ auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie);
+ if (attribute_package_map == src_asset_cookie_id_map.end()) {
+ continue;
+ }
+ auto attribute_dest_package = attribute_package_map->second.find(
+ attribute_dest_package_id);
+ if (attribute_dest_package == attribute_package_map->second.end()) {
+ continue;
+ }
+ attribute_dest_package_id = attribute_dest_package->second;
+ }
+
+ // If the attribute value represents an attribute or reference, the package id of the
+ // value needs to be rewritten to the package id of the value in the destination
+ uint32_t attribue_data = entry.value.data;
+ if (entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE
+ || entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE
+ || entry.value.dataType == Res_value::TYPE_ATTRIBUTE
+ || entry.value.dataType == Res_value::TYPE_REFERENCE) {
+
+ // Determine the package id of the reference in the destination AssetManager
+ auto value_package_map = src_asset_cookie_id_map.find(entry.cookie);
+ if (value_package_map == src_asset_cookie_id_map.end()) {
+ continue;
+ }
+
+ auto value_dest_package = value_package_map->second.find(
+ get_package_id(entry.value.data));
+ if (value_dest_package == value_package_map->second.end()) {
+ continue;
+ }
+
+ attribue_data = fix_package_id(entry.value.data, value_dest_package->second);
+ }
+
+ // Lazily instantiate the destination package
+ std::unique_ptr<Package>& dest_package = packages_[attribute_dest_package_id];
+ if (dest_package == nullptr) {
+ dest_package.reset(new Package());
+ }
+
+ // Lazily instantiate and resize the destination type
+ util::unique_cptr<ThemeType>& dest_type = dest_package->types[t];
+ if (dest_type == nullptr || dest_type->entry_count < type->entry_count) {
+ const size_t type_alloc_size = sizeof(ThemeType)
+ + (type->entry_count * sizeof(ThemeEntry));
+ void* dest_data = malloc(type_alloc_size);
+ memset(dest_data, 0, type->entry_count * sizeof(ThemeEntry));
+
+ // Copy the existing destination type values if the type is resized
+ if (dest_type != nullptr) {
+ memcpy(dest_data, type, sizeof(ThemeType)
+ + (dest_type->entry_count * sizeof(ThemeEntry)));
+ }
+
+ dest_type.reset(reinterpret_cast<ThemeType *>(dest_data));
+ dest_type->entry_count = type->entry_count;
+ }
+
+ // Find the cookie of the value in the destination
+ auto value_dest_cookie = src_to_dest_asset_cookies.find(entry.cookie);
+ if (value_dest_cookie == src_to_dest_asset_cookies.end()) {
+ continue;
+ }
+
+ dest_type->entries[e].cookie = value_dest_cookie->second;
+ dest_type->entries[e].value.dataType = entry.value.dataType;
+ dest_type->entries[e].value.data = attribue_data;
+ dest_type->entries[e].type_spec_flags = entry.type_spec_flags;
+ }
+ }
+ }
+ }
+}
+
+void Theme::Dump() const {
+ base::ScopedLogSeverity _log(base::INFO);
+ LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
+
+ for (int p = 0; p < packages_.size(); p++) {
+ auto& package = packages_[p];
+ if (package == nullptr) {
+ continue;
+ }
+
+ for (int t = 0; t < package->types.size(); t++) {
+ auto& type = package->types[t];
if (type == nullptr) {
- // The other theme doesn't have this type, clear ours.
- packages_[p]->types[t].reset();
continue;
}
- // Create a new type and update it to theirs.
- const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry));
- void* copied_data = malloc(type_alloc_size);
- memcpy(copied_data, type, type_alloc_size);
- packages_[p]->types[t].reset(reinterpret_cast<ThemeType*>(copied_data));
+ for (int e = 0; e < type->entry_count; e++) {
+ auto& entry = type->entries[e];
+ if (entry.value.dataType == Res_value::TYPE_NULL &&
+ entry.value.data != Res_value::DATA_NULL_EMPTY) {
+ continue;
+ }
+
+ LOG(INFO) << base::StringPrintf(" entry(0x%08x)=(0x%08x) type=(0x%02x), cookie(%d)",
+ make_resid(p, t, e), entry.value.data,
+ entry.value.dataType, entry.cookie);
+ }
}
}
- return true;
}
} // namespace android
diff --git a/libs/androidfw/Locale.cpp b/libs/androidfw/Locale.cpp
index 2870066ccbba..3eedda88fdce 100644
--- a/libs/androidfw/Locale.cpp
+++ b/libs/androidfw/Locale.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "android-base/macros.h"
#include "androidfw/Locale.h"
#include "androidfw/Util.h"
@@ -162,6 +163,7 @@ bool LocaleValue::InitFromBcp47TagImpl(const StringPiece& bcp47tag, const char s
set_script(subtags[1].c_str());
break;
}
+ FALLTHROUGH_INTENDED;
case 5:
case 6:
case 7:
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 861dc0f3879c..76db18de6122 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -29,6 +29,7 @@
#include <memory>
#include <type_traits>
+#include <android-base/macros.h>
#include <androidfw/ByteBucketArray.h>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
@@ -3073,6 +3074,7 @@ struct LocaleParserState {
}
break;
}
+ FALLTHROUGH_INTENDED;
case 5:
case 6:
case 7:
@@ -3205,20 +3207,6 @@ String8 ResTable_config::toString() const {
break;
}
}
- if ((colorMode&MASK_HDR) != 0) {
- if (res.size() > 0) res.append("-");
- switch (colorMode&MASK_HDR) {
- case ResTable_config::HDR_NO:
- res.append("lowdr");
- break;
- case ResTable_config::HDR_YES:
- res.append("highdr");
- break;
- default:
- res.appendFormat("hdr=%d", dtohs(colorMode&MASK_HDR));
- break;
- }
- }
if ((colorMode&MASK_WIDE_COLOR_GAMUT) != 0) {
if (res.size() > 0) res.append("-");
switch (colorMode&MASK_WIDE_COLOR_GAMUT) {
@@ -3233,6 +3221,20 @@ String8 ResTable_config::toString() const {
break;
}
}
+ if ((colorMode&MASK_HDR) != 0) {
+ if (res.size() > 0) res.append("-");
+ switch (colorMode&MASK_HDR) {
+ case ResTable_config::HDR_NO:
+ res.append("lowdr");
+ break;
+ case ResTable_config::HDR_YES:
+ res.append("highdr");
+ break;
+ default:
+ res.appendFormat("hdr=%d", dtohs(colorMode&MASK_HDR));
+ break;
+ }
+ }
if (orientation != ORIENTATION_ANY) {
if (res.size() > 0) res.append("-");
switch (orientation) {
@@ -6960,6 +6962,11 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
uint32_t res = *resId;
size_t packageId = Res_GETPACKAGE(res) + 1;
+ if (!Res_VALIDID(res)) {
+ // Cannot look up a null or invalid id, so no lookup needs to be done.
+ return NO_ERROR;
+ }
+
if (packageId == APP_PACKAGE_ID && !mAppAsLib) {
// No lookup needs to be done, app package IDs are absolute.
return NO_ERROR;
@@ -6995,24 +7002,26 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
uint8_t resolvedType = Res_value::TYPE_REFERENCE;
switch (value->dataType) {
- case Res_value::TYPE_ATTRIBUTE:
- resolvedType = Res_value::TYPE_ATTRIBUTE;
- // fallthrough
- case Res_value::TYPE_REFERENCE:
- if (!mAppAsLib) {
- return NO_ERROR;
- }
+ case Res_value::TYPE_ATTRIBUTE:
+ resolvedType = Res_value::TYPE_ATTRIBUTE;
+ FALLTHROUGH_INTENDED;
+ case Res_value::TYPE_REFERENCE:
+ // Only resolve non-dynamic references and attributes if the package is loaded as a
+ // library or if a shared library is attempting to retrieve its own resource
+ if (!(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
+ return NO_ERROR;
+ }
// If the package is loaded as shared library, the resource reference
// also need to be fixed.
break;
- case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
- resolvedType = Res_value::TYPE_ATTRIBUTE;
- // fallthrough
- case Res_value::TYPE_DYNAMIC_REFERENCE:
- break;
- default:
- return NO_ERROR;
+ case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
+ resolvedType = Res_value::TYPE_ATTRIBUTE;
+ FALLTHROUGH_INTENDED;
+ case Res_value::TYPE_DYNAMIC_REFERENCE:
+ break;
+ default:
+ return NO_ERROR;
}
status_t err = lookupResourceId(&value->data);
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index 08da7319de85..cdb87bcb8e11 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -61,6 +61,7 @@ public:
static const char* IDMAP_BIN;
static const char* OVERLAY_DIR;
static const char* PRODUCT_OVERLAY_DIR;
+ static const char* PRODUCT_SERVICES_OVERLAY_DIR;
/*
* If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay
* APKs in OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 2f0ee01639fe..5312b062473a 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -74,6 +74,8 @@ struct FindEntryResult;
// AssetManager2 is the main entry point for accessing assets and resources.
// AssetManager2 provides caching of resources retrieved via the underlying ApkAssets.
class AssetManager2 {
+ friend Theme;
+
public:
struct ResourceName {
const char* package = nullptr;
@@ -285,6 +287,9 @@ class AssetManager2 {
// been seen while traversing bag parents.
const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
+ // Retrieve the assigned package id of the package if loaded into this AssetManager
+ uint8_t GetAssignedPackageId(const LoadedPackage* package);
+
// The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
// have a longer lifetime.
std::vector<const ApkAssets*> apk_assets_;
@@ -355,11 +360,14 @@ class Theme {
bool ApplyStyle(uint32_t resid, bool force = false);
// Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
- // Returns false if the AssetManagers of the Themes were not compatible.
- bool SetTo(const Theme& o);
+ // If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded
+ // into both AssetManagers will be copied to this theme.
+ void SetTo(const Theme& o);
void Clear();
+ void Dump() const;
+
inline const AssetManager2* GetAssetManager() const {
return asset_manager_;
}
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index a02851502c9b..59abad45edbb 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1709,13 +1709,13 @@ public:
struct resource_name
{
- const char16_t* package;
+ const char16_t* package = NULL;
size_t packageLen;
- const char16_t* type;
- const char* type8;
+ const char16_t* type = NULL;
+ const char* type8 = NULL;
size_t typeLen;
- const char16_t* name;
- const char* name8;
+ const char16_t* name = NULL;
+ const char* name8 = NULL;
size_t nameLen;
};
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
index 99b424568a1f..a33865f4d34f 100644
--- a/libs/androidfw/include/androidfw/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -32,6 +32,14 @@ namespace android {
// WARNING: When creating from std::basic_string<>, moving the original
// std::basic_string<> will invalidate the data held in a BasicStringPiece<>.
// BasicStringPiece<> should only be used transitively.
+//
+// NOTE: When creating an std::pair<StringPiece, T> using std::make_pair(),
+// passing an std::string will first copy the string, then create a StringPiece
+// on the copy, which is then immediately destroyed.
+// Instead, create a StringPiece explicitly:
+//
+// std::string my_string = "foo";
+// std::make_pair<StringPiece, T>(StringPiece(my_string), ...);
template <typename TChar>
class BasicStringPiece {
public:
diff --git a/libs/androidfw/tests/DynamicRefTable_test.cpp b/libs/androidfw/tests/DynamicRefTable_test.cpp
new file mode 100644
index 000000000000..5acc46a3c0d9
--- /dev/null
+++ b/libs/androidfw/tests/DynamicRefTable_test.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/ResourceTypes.h"
+#include "utils/String8.h"
+
+#include "gtest/gtest.h"
+namespace android {
+
+TEST(DynamicRefTableTest, LookupSharedLibSelfReferences) {
+ // Shared library
+ DynamicRefTable shared_table(0x02, /* appAsLib */ false);
+ shared_table.addMapping(0x00, 0x02);
+ Res_value value;
+ value.dataType = Res_value::TYPE_REFERENCE;
+ value.data = 0x00010000;
+ ASSERT_EQ(shared_table.lookupResourceValue(&value), NO_ERROR);
+ EXPECT_EQ(value.data, 0x02010000);
+
+ // App loaded as a shared library
+ DynamicRefTable shared_app_table(0x02, /* appAsLib */ true);
+ shared_app_table.addMapping(0x7f, 0x02);
+ Res_value value2;
+ value2.dataType = Res_value::TYPE_REFERENCE;
+ value2.data = 0x7f010000;
+ ASSERT_EQ(shared_app_table.lookupResourceValue(&value2), NO_ERROR);
+ EXPECT_EQ(value2.data, 0x02010000);
+};
+
+TEST(DynamicRefTableTest, LookupSharedLibSelfAttributes) {
+ // Shared library
+ DynamicRefTable shared_table(0x03, /* appAsLib */ false);
+ shared_table.addMapping(0x00, 0x03);
+ Res_value value;
+ value.dataType = Res_value::TYPE_ATTRIBUTE;
+ value.data = 0x00010000;
+ ASSERT_EQ(shared_table.lookupResourceValue(&value), NO_ERROR);
+ EXPECT_EQ(value.data, 0x03010000);
+
+ // App loaded as a shared library
+ DynamicRefTable shared_app_table(0x04, /* appAsLib */ true);
+ shared_app_table.addMapping(0x7f, 0x04);
+ Res_value value2;
+ value2.dataType = Res_value::TYPE_ATTRIBUTE;
+ value2.data = 0x7f010000;
+ ASSERT_EQ(shared_app_table.lookupResourceValue(&value2), NO_ERROR);
+ EXPECT_EQ(value2.data, 0x04010000);
+};
+
+TEST(DynamicRefTableTest, LookupDynamicReferences) {
+ // Shared library
+ DynamicRefTable shared_table(0x2, /* appAsLib */ false);
+ shared_table.addMapping(0x00, 0x02);
+ shared_table.addMapping(0x03, 0x05);
+ Res_value value;
+ value.dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
+ value.data = 0x03010000;
+ ASSERT_EQ(shared_table.lookupResourceValue(&value), NO_ERROR);
+ EXPECT_EQ(value.data, 0x05010000);
+
+ // Regular application
+ DynamicRefTable app_table(0x7f, /* appAsLib */ false);
+ app_table.addMapping(0x03, 0x05);
+ Res_value value3;
+ value3.dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
+ value3.data = 0x03010000;
+ ASSERT_EQ(app_table.lookupResourceValue(&value3), NO_ERROR);
+ EXPECT_EQ(value3.data, 0x05010000);
+};
+
+TEST(DynamicRefTableTest, LookupDynamicAttributes) {
+// App loaded as a shared library
+ DynamicRefTable shared_app_table(0x2, /* appAsLib */ true);
+ shared_app_table.addMapping(0x03, 0x05);
+ shared_app_table.addMapping(0x7f, 0x2);
+ Res_value value2;
+ value2.dataType = Res_value::TYPE_DYNAMIC_ATTRIBUTE;
+ value2.data = 0x03010000;
+ ASSERT_EQ(shared_app_table.lookupResourceValue(&value2), NO_ERROR);
+ EXPECT_EQ(value2.data, 0x05010000);
+}
+
+TEST(DynamicRefTableTest, DoNotLookupNonDynamicReferences) {
+ // Regular application
+ DynamicRefTable app_table(0x7f, /* appAsLib */ false);
+ Res_value value;
+ value.dataType = Res_value::TYPE_REFERENCE;
+ value.data = 0x03010000;
+ ASSERT_EQ(app_table.lookupResourceValue(&value), NO_ERROR);
+ EXPECT_EQ(value.data, 0x03010000);
+};
+
+TEST(DynamicRefTableTest, DoNotLookupNonDynamicAttributes) {
+ // App with custom package id
+ DynamicRefTable custom_app_table(0x8f, /* appAsLib */ false);
+ Res_value value2;
+ value2.dataType = Res_value::TYPE_ATTRIBUTE;
+ value2.data = 0x03010000;
+ ASSERT_EQ(custom_app_table.lookupResourceValue(&value2), NO_ERROR);
+ EXPECT_EQ(value2.data, 0x03010000);
+};
+
+} // namespace android \ No newline at end of file
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 9eb4a13f34d1..26d28965d459 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -94,15 +94,15 @@ TEST_F(IdmapTest, OverlaidResourceHasSameName) {
target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
ResTable::resource_name res_name;
- ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, false, &res_name));
+ ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, true, &res_name));
ASSERT_TRUE(res_name.package != NULL);
ASSERT_TRUE(res_name.type != NULL);
- ASSERT_TRUE(res_name.name != NULL);
+ ASSERT_TRUE(res_name.name8 != NULL);
EXPECT_EQ(String16("com.android.basic"), String16(res_name.package, res_name.packageLen));
EXPECT_EQ(String16("array"), String16(res_name.type, res_name.typeLen));
- EXPECT_EQ(String16("integerArray1"), String16(res_name.name, res_name.nameLen));
+ EXPECT_EQ(String8("integerArray1"), String8(res_name.name8, res_name.nameLen));
}
constexpr const uint32_t kNonOverlaidResourceId = 0x7fff0000u;
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 55d53edf6a2b..2c39ceead123 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -21,12 +21,14 @@
#include "TestHelpers.h"
#include "androidfw/ResourceUtils.h"
#include "data/lib_one/R.h"
+#include "data/lib_two/R.h"
#include "data/libclient/R.h"
#include "data/styles/R.h"
#include "data/system/R.h"
namespace app = com::android::app;
namespace lib_one = com::android::lib_one;
+namespace lib_two = com::android::lib_two;
namespace libclient = com::android::libclient;
namespace android {
@@ -263,7 +265,7 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) {
ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree));
// Copy the theme to theme_one.
- ASSERT_TRUE(theme_one->SetTo(*theme_two));
+ theme_one->SetTo(*theme_two);
// Clear theme_two to make sure we test that there WAS a copy.
theme_two->Clear();
@@ -279,12 +281,14 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) {
EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
}
-TEST_F(ThemeTest, OnlyCopySystemThemeWhenAssetManagersDiffer) {
+TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) {
AssetManager2 assetmanager_one;
- assetmanager_one.SetApkAssets({system_assets_.get(), style_assets_.get()});
+ assetmanager_one.SetApkAssets({system_assets_.get(), lib_one_assets_.get(), style_assets_.get(),
+ libclient_assets_.get()});
AssetManager2 assetmanager_two;
- assetmanager_two.SetApkAssets({system_assets_.get(), style_assets_.get()});
+ assetmanager_two.SetApkAssets({system_assets_.get(), lib_two_assets_.get(), lib_one_assets_.get(),
+ style_assets_.get()});
auto theme_one = assetmanager_one.NewTheme();
ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
@@ -292,17 +296,34 @@ TEST_F(ThemeTest, OnlyCopySystemThemeWhenAssetManagersDiffer) {
auto theme_two = assetmanager_two.NewTheme();
ASSERT_TRUE(theme_two->ApplyStyle(R::style::Theme_One));
ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleTwo));
+ ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03),
+ false /*force*/));
+ ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02),
+ false /*force*/));
- EXPECT_TRUE(theme_one->SetTo(*theme_two));
+ theme_one->SetTo(*theme_two);
Res_value value;
uint32_t flags;
- // No app resources.
- EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+ // System resources (present in destination asset manager)
+ EXPECT_EQ(0, theme_one->GetAttribute(R::attr::foreground, &value, &flags));
+
+ // The cookie of the style asset is 3 in the source and 2 in the destination.
+ // Check that the cookie has been rewritten to the destination values
+ EXPECT_EQ(2, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+
+ // The cookie of the lib_one asset is 2 in the source and 1 in the destination.
+ // The package id of the lib_one package is 0x03 in the source and 0x02 in the destination
+ // Check that the cookie and packages have been rewritten to the destination values
+ EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value,
+ &flags));
+ EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value,
+ &flags));
- // Only system.
- EXPECT_NE(kInvalidCookie, theme_one->GetAttribute(R::attr::foreground, &value, &flags));
+ // attr2 references an attribute in lib_one. Check that the resolution of the attribute value is
+ // correct after the value of attr2 had its package id rewritten to the destination package id
+ EXPECT_EQ(700, value.data);
}
} // namespace android
diff --git a/libs/androidfw/tests/data/lib_two/R.h b/libs/androidfw/tests/data/lib_two/R.h
index c04a9d3b4de0..92b9cc10e7a8 100644
--- a/libs/androidfw/tests/data/lib_two/R.h
+++ b/libs/androidfw/tests/data/lib_two/R.h
@@ -24,12 +24,24 @@ namespace android {
namespace lib_two {
struct R {
+ struct attr {
+ enum : uint32_t {
+ attr3 = 0x02010000, // default
+ };
+ };
+
struct string {
enum : uint32_t {
LibraryString = 0x02020000, // default
foo = 0x02020001, // default
};
};
+
+ struct style {
+ enum : uint32_t {
+ Theme = 0x02030000, // default
+ };
+ };
};
} // namespace lib_two
diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk
index ad44f9c21e31..486c23000276 100644
--- a/libs/androidfw/tests/data/lib_two/lib_two.apk
+++ b/libs/androidfw/tests/data/lib_two/lib_two.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/lib_two/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml
index f4eea2610cab..340d14c34c5d 100644
--- a/libs/androidfw/tests/data/lib_two/res/values/values.xml
+++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml
@@ -15,9 +15,17 @@
-->
<resources>
+ <public type="attr" name="attr3" id="0x00010000" />
+ <attr name="attr3" format="integer" />
+
<public type="string" name="LibraryString" id="0x00020000" />
<string name="LibraryString">Hi from library two</string>
<public type="string" name="foo" id="0x00020001" />
<string name="foo">Foo from lib_two</string>
+
+ <public type="style" name="Theme" id="0x02030000" />
+ <style name="Theme">
+ <item name="com.android.lib_two:attr3">800</item>
+ </style>
</resources>
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
deleted file mode 100644
index aa96698c1e53..000000000000
--- a/libs/hwui/AmbientShadow.cpp
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Extra vertices for the corner for smoother corner.
- * Only for outer vertices.
- * Note that we use such extra memory to avoid an extra loop.
- */
-// For half circle, we could add EXTRA_VERTEX_PER_PI vertices.
-// Set to 1 if we don't want to have any.
-#define EXTRA_CORNER_VERTEX_PER_PI 12
-
-// For the whole polygon, the sum of all the deltas b/t normals is 2 * M_PI,
-// therefore, the maximum number of extra vertices will be twice bigger.
-#define MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * EXTRA_CORNER_VERTEX_PER_PI)
-
-// For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals.
-#define CORNER_RADIANS_DIVISOR (M_PI / EXTRA_CORNER_VERTEX_PER_PI)
-
-/**
- * Extra vertices for the Edge for interpolation artifacts.
- * Same value for both inner and outer vertices.
- */
-#define EXTRA_EDGE_VERTEX_PER_PI 50
-
-#define MAX_EXTRA_EDGE_VERTEX_NUMBER (2 * EXTRA_EDGE_VERTEX_PER_PI)
-
-#define EDGE_RADIANS_DIVISOR (M_PI / EXTRA_EDGE_VERTEX_PER_PI)
-
-/**
- * Other constants:
- */
-#define OUTER_ALPHA (0.0f)
-
-// Once the alpha difference is greater than this threshold, we will allocate extra
-// edge vertices.
-// If this is set to negative value, then all the edge will be tessellated.
-#define ALPHA_THRESHOLD (0.1f / 255.0f)
-
-#include "AmbientShadow.h"
-
-#include "ShadowTessellator.h"
-#include "Vertex.h"
-#include "VertexBuffer.h"
-
-#include <utils/Log.h>
-#include <algorithm>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Local utility functions.
- */
-inline Vector2 getNormalFromVertices(const Vector3* vertices, int current, int next) {
- // Convert from Vector3 to Vector2 first.
- Vector2 currentVertex = {vertices[current].x, vertices[current].y};
- Vector2 nextVertex = {vertices[next].x, vertices[next].y};
-
- return ShadowTessellator::calculateNormal(currentVertex, nextVertex);
-}
-
-// The input z value will be converted to be non-negative inside.
-// The output must be ranged from 0 to 1.
-inline float getAlphaFromFactoredZ(float factoredZ) {
- return 1.0 / (1 + std::max(factoredZ, 0.0f));
-}
-
-inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike, const Vector3& secondVertex,
- const Vector3& centroid) {
- Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y};
- secondSpike.normalize();
-
- int result = ShadowTessellator::getExtraVertexNumber(secondSpike, *currentSpike,
- EDGE_RADIANS_DIVISOR);
- *currentSpike = secondSpike;
- return result;
-}
-
-// Given the caster's vertex count, compute all the buffers size depending on
-// whether or not the caster is opaque.
-inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount, int* totalUmbraCount,
- int casterVertexCount, bool isCasterOpaque) {
- // Compute the size of the vertex buffer.
- int outerVertexCount =
- casterVertexCount * 2 + MAX_EXTRA_CORNER_VERTEX_NUMBER + MAX_EXTRA_EDGE_VERTEX_NUMBER;
- int innerVertexCount = casterVertexCount + MAX_EXTRA_EDGE_VERTEX_NUMBER;
- *totalVertexCount = outerVertexCount + innerVertexCount;
-
- // Compute the size of the index buffer.
- *totalIndexCount = 2 * outerVertexCount + 2;
-
- // Compute the size of the umber buffer.
- // For translucent object, keep track of the umbra(inner) vertex in order to draw
- // inside. We only need to store the index information.
- *totalUmbraCount = 0;
- if (!isCasterOpaque) {
- // Add the centroid if occluder is translucent.
- (*totalVertexCount)++;
- *totalIndexCount += 2 * innerVertexCount + 1;
- *totalUmbraCount = innerVertexCount;
- }
-}
-
-inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) {
- return fabsf(firstAlpha - secondAlpha) > ALPHA_THRESHOLD;
-}
-
-/**
- * Calculate the shadows as a triangle strips while alpha value as the
- * shadow values.
- *
- * @param isCasterOpaque Whether the caster is opaque.
- * @param vertices The shadow caster's polygon, which is represented in a Vector3
- * array.
- * @param vertexCount The length of caster's polygon in terms of number of
- * vertices.
- * @param centroid3d The centroid of the shadow caster.
- * @param heightFactor The factor showing the higher the object, the lighter the
- * shadow.
- * @param geomFactor The factor scaling the geometry expansion along the normal.
- *
- * @param shadowVertexBuffer Return an floating point array of (x, y, a)
- * triangle strips mode.
- *
- * An simple illustration:
- * For now let's mark the outer vertex as Pi, the inner as Vi, the centroid as C.
- *
- * First project the occluder to the Z=0 surface.
- * Then we got all the inner vertices. And we compute the normal for each edge.
- * According to the normal, we generate outer vertices. E.g: We generate P1 / P4
- * as extra corner vertices to make the corner looks round and smoother.
- *
- * Due to the fact that the alpha is not linear interpolated along the inner
- * edge, when the alpha is different, we may add extra vertices such as P2.1, P2.2,
- * V0.1, V0.2 to avoid the visual artifacts.
- *
- * (P3)
- * (P2) (P2.1) (P2.2) | ' (P4)
- * (P1)' | | | | '
- * ' | | | | '
- * (P0) ------------------------------------------------(P5)
- * | (V0) (V0.1) (V0.2) |(V1)
- * | |
- * | |
- * | (C) |
- * | |
- * | |
- * | |
- * | |
- * (V3)-----------------------------------(V2)
- */
-void AmbientShadow::createAmbientShadow(bool isCasterOpaque, const Vector3* casterVertices,
- int casterVertexCount, const Vector3& centroid3d,
- float heightFactor, float geomFactor,
- VertexBuffer& shadowVertexBuffer) {
- shadowVertexBuffer.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices);
-
- // In order to computer the outer vertices in one loop, we need pre-compute
- // the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value
- // for vertex 0.
- Vector2 previousNormal = getNormalFromVertices(casterVertices, casterVertexCount - 1, 0);
- Vector2 currentSpike = {casterVertices[0].x - centroid3d.x, casterVertices[0].y - centroid3d.y};
- currentSpike.normalize();
- float currentAlpha = getAlphaFromFactoredZ(casterVertices[0].z * heightFactor);
-
- // Preparing all the output data.
- int totalVertexCount, totalIndexCount, totalUmbraCount;
- computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount, casterVertexCount,
- isCasterOpaque);
- AlphaVertex* shadowVertices = shadowVertexBuffer.alloc<AlphaVertex>(totalVertexCount);
- int vertexBufferIndex = 0;
- uint16_t* indexBuffer = shadowVertexBuffer.allocIndices<uint16_t>(totalIndexCount);
- int indexBufferIndex = 0;
- uint16_t umbraVertices[totalUmbraCount];
- int umbraIndex = 0;
-
- for (int i = 0; i < casterVertexCount; i++) {
- // Corner: first figure out the extra vertices we need for the corner.
- const Vector3& innerVertex = casterVertices[i];
- Vector2 currentNormal =
- getNormalFromVertices(casterVertices, i, (i + 1) % casterVertexCount);
-
- int extraVerticesNumber = ShadowTessellator::getExtraVertexNumber(
- currentNormal, previousNormal, CORNER_RADIANS_DIVISOR);
-
- float expansionDist = innerVertex.z * heightFactor * geomFactor;
- const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1.
-#if DEBUG_SHADOW
- ALOGD("cornerSlicesNumber is %d", cornerSlicesNumber);
-#endif
-
- // Corner: fill the corner Vertex Buffer(VB) and Index Buffer(IB).
- // We fill the inner vertex first, such that we can fill the index buffer
- // inside the loop.
- int currentInnerVertexIndex = vertexBufferIndex;
- if (!isCasterOpaque) {
- umbraVertices[umbraIndex++] = vertexBufferIndex;
- }
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x,
- casterVertices[i].y, currentAlpha);
-
- const Vector3& innerStart = casterVertices[i];
-
- // outerStart is the first outer vertex for this inner vertex.
- // outerLast is the last outer vertex for this inner vertex.
- Vector2 outerStart = {0, 0};
- Vector2 outerLast = {0, 0};
- // This will create vertices from [0, cornerSlicesNumber] inclusively,
- // which means minimally 2 vertices even without the extra ones.
- for (int j = 0; j <= cornerSlicesNumber; j++) {
- Vector2 averageNormal = previousNormal * (cornerSlicesNumber - j) + currentNormal * j;
- averageNormal /= cornerSlicesNumber;
- averageNormal.normalize();
- Vector2 outerVertex;
- outerVertex.x = innerVertex.x + averageNormal.x * expansionDist;
- outerVertex.y = innerVertex.y + averageNormal.y * expansionDist;
-
- indexBuffer[indexBufferIndex++] = vertexBufferIndex;
- indexBuffer[indexBufferIndex++] = currentInnerVertexIndex;
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x, outerVertex.y,
- OUTER_ALPHA);
-
- if (j == 0) {
- outerStart = outerVertex;
- } else if (j == cornerSlicesNumber) {
- outerLast = outerVertex;
- }
- }
- previousNormal = currentNormal;
-
- // Edge: first figure out the extra vertices needed for the edge.
- const Vector3& innerNext = casterVertices[(i + 1) % casterVertexCount];
- float nextAlpha = getAlphaFromFactoredZ(innerNext.z * heightFactor);
- if (needsExtraForEdge(currentAlpha, nextAlpha)) {
- // TODO: See if we can / should cache this outer vertex across the loop.
- Vector2 outerNext;
- float expansionDist = innerNext.z * heightFactor * geomFactor;
- outerNext.x = innerNext.x + currentNormal.x * expansionDist;
- outerNext.y = innerNext.y + currentNormal.y * expansionDist;
-
- // Compute the angle and see how many extra points we need.
- int extraVerticesNumber =
- getEdgeExtraAndUpdateSpike(&currentSpike, innerNext, centroid3d);
-#if DEBUG_SHADOW
- ALOGD("extraVerticesNumber %d for edge %d", extraVerticesNumber, i);
-#endif
- // Edge: fill the edge's VB and IB.
- // This will create vertices pair from [1, extraVerticesNumber - 1].
- // If there is no extra vertices created here, the edge will be drawn
- // as just 2 triangles.
- for (int k = 1; k < extraVerticesNumber; k++) {
- int startWeight = extraVerticesNumber - k;
- Vector2 currentOuter =
- (outerLast * startWeight + outerNext * k) / extraVerticesNumber;
- indexBuffer[indexBufferIndex++] = vertexBufferIndex;
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x,
- currentOuter.y, OUTER_ALPHA);
-
- if (!isCasterOpaque) {
- umbraVertices[umbraIndex++] = vertexBufferIndex;
- }
- Vector3 currentInner =
- (innerStart * startWeight + innerNext * k) / extraVerticesNumber;
- indexBuffer[indexBufferIndex++] = vertexBufferIndex;
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x,
- currentInner.y,
- getAlphaFromFactoredZ(currentInner.z * heightFactor));
- }
- }
- currentAlpha = nextAlpha;
- }
-
- indexBuffer[indexBufferIndex++] = 1;
- indexBuffer[indexBufferIndex++] = 0;
-
- if (!isCasterOpaque) {
- // Add the centroid as the last one in the vertex buffer.
- float centroidOpacity = getAlphaFromFactoredZ(centroid3d.z * heightFactor);
- int centroidIndex = vertexBufferIndex;
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x, centroid3d.y,
- centroidOpacity);
-
- for (int i = 0; i < umbraIndex; i++) {
- // Note that umbraVertices[0] is always 0.
- // So the start and the end of the umbra are using the "0".
- // And penumbra ended with 0, so a degenerated triangle is formed b/t
- // the umbra and penumbra.
- indexBuffer[indexBufferIndex++] = umbraVertices[i];
- indexBuffer[indexBufferIndex++] = centroidIndex;
- }
- indexBuffer[indexBufferIndex++] = 0;
- }
-
- // At the end, update the real index and vertex buffer size.
- shadowVertexBuffer.updateVertexCount(vertexBufferIndex);
- shadowVertexBuffer.updateIndexCount(indexBufferIndex);
- shadowVertexBuffer.computeBounds<AlphaVertex>();
-
- ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Ambient Vertex Buffer");
- ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Ambient Index Buffer");
- ShadowTessellator::checkOverflow(umbraIndex, totalUmbraCount, "Ambient Umbra Buffer");
-
-#if DEBUG_SHADOW
- for (int i = 0; i < vertexBufferIndex; i++) {
- ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y,
- shadowVertices[i].alpha);
- }
- for (int i = 0; i < indexBufferIndex; i++) {
- ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]);
- }
-#endif
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h
deleted file mode 100644
index cb1d915f2540..000000000000
--- a/libs/hwui/AmbientShadow.h
+++ /dev/null
@@ -1,42 +0,0 @@
-
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_AMBIENT_SHADOW_H
-#define ANDROID_HWUI_AMBIENT_SHADOW_H
-
-#include "Debug.h"
-#include "Vector.h"
-
-namespace android {
-namespace uirenderer {
-
-class VertexBuffer;
-
-/**
- * AmbientShadow is used to calculate the ambient shadow value around a polygon.
- */
-class AmbientShadow {
-public:
- static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly, int polyLength,
- const Vector3& centroid3d, float heightFactor, float geomFactor,
- VertexBuffer& shadowVertexBuffer);
-}; // AmbientShadow
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_AMBIENT_SHADOW_H
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index fb8274426b16..17d2db71ab58 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -61,6 +61,7 @@ cc_defaults {
shared_libs: [
"liblog",
"libcutils",
+ "libstatslog",
"libutils",
"libEGL",
"libGLESv2",
@@ -72,7 +73,7 @@ cc_defaults {
"libft2",
"libminikin",
"libandroidfw",
- "libRScpp",
+ "libcrypto",
],
static_libs: [
"libEGL_blobCache",
@@ -81,7 +82,6 @@ cc_defaults {
cc_defaults {
name: "hwui_bugreport_font_cache_usage",
- srcs: ["font/FontCacheHistoryTracker.cpp"],
cflags: ["-DBUGREPORT_FONT_CACHE_USAGE"],
}
@@ -158,8 +158,6 @@ cc_defaults {
"hwui/AnimatedImageDrawable.cpp",
"hwui/AnimatedImageThread.cpp",
"hwui/Bitmap.cpp",
- "font/CacheTexture.cpp",
- "font/Font.cpp",
"hwui/Canvas.cpp",
"hwui/MinikinSkia.cpp",
"hwui/MinikinUtils.cpp",
@@ -173,23 +171,15 @@ cc_defaults {
"pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaMemoryTracer.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",
"pipeline/skia/VectorDrawableAtlas.cpp",
- "renderstate/Blend.cpp",
- "renderstate/MeshState.cpp",
- "renderstate/OffscreenBufferPool.cpp",
- "renderstate/PixelBufferState.cpp",
+ "pipeline/skia/VkInteropFunctorDrawable.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",
@@ -199,89 +189,56 @@ cc_defaults {
"renderthread/TimeLord.cpp",
"renderthread/Frame.cpp",
"service/GraphicsStatsService.cpp",
+ "surfacetexture/EGLConsumer.cpp",
+ "surfacetexture/ImageConsumer.cpp",
+ "surfacetexture/SurfaceTexture.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",
+ "CanvasTransform.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",
+ "HardwareBitmapUploader.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",
"ProfileData.cpp",
"ProfileDataContainer.cpp",
- "ProfileRenderer.cpp",
- "Program.cpp",
- "ProgramCache.cpp",
"Properties.cpp",
"PropertyValuesAnimatorSet.cpp",
"PropertyValuesHolder.cpp",
+ "Readback.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",
+ "TreeInfo.cpp",
"VectorDrawable.cpp",
- "VkLayer.cpp",
"protos/graphicsstats.proto",
- "protos/hwui.proto",
],
+ // Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed.
+ cflags: ["-Wno-implicit-fallthrough"],
+
proto: {
export_proto_headers: true,
},
export_include_dirs: ["."],
- export_shared_lib_headers: ["libRScpp"],
}
cc_library {
@@ -292,7 +249,7 @@ cc_library {
// Enables fine-grained GLES error checking
// If enabled, every GLES call is wrapped & error checked
// Has moderate overhead
- "hwui_enable_opengl_validation",
+ //"hwui_enable_opengl_validation",
],
}
@@ -348,33 +305,18 @@ cc_test {
srcs: [
"tests/unit/main.cpp",
- "tests/unit/BakedOpDispatcherTests.cpp",
- "tests/unit/BakedOpRendererTests.cpp",
- "tests/unit/BakedOpStateTests.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/ShaderCacheTests.cpp",
@@ -383,11 +325,8 @@ cc_test {
"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/ThreadBaseTests.cpp",
"tests/unit/TypefaceTests.cpp",
"tests/unit/VectorDrawableTests.cpp",
@@ -436,12 +375,9 @@ cc_benchmark {
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/Animator.h b/libs/hwui/Animator.h
index 42f4cf8828bd..ed7b6eb1cf4a 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -16,6 +16,8 @@
#ifndef ANIMATOR_H
#define ANIMATOR_H
+#include "CanvasProperty.h"
+
#include <cutils/compiler.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
@@ -31,8 +33,6 @@ namespace uirenderer {
class AnimationContext;
class BaseRenderNodeAnimator;
-class CanvasPropertyPrimitive;
-class CanvasPropertyPaint;
class Interpolator;
class RenderNode;
class RenderProperties;
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
deleted file mode 100644
index f78cb418c5cd..000000000000
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ /dev/null
@@ -1,879 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BakedOpDispatcher.h"
-
-#include "BakedOpRenderer.h"
-#include "Caches.h"
-#include "DeferredLayerUpdater.h"
-#include "Glop.h"
-#include "GlopBuilder.h"
-#include "Patch.h"
-#include "PathTessellator.h"
-#include "VertexBuffer.h"
-#include "renderstate/OffscreenBufferPool.h"
-#include "renderstate/RenderState.h"
-#include "utils/GLUtils.h"
-
-#include <SkPaintDefaults.h>
-#include <SkPathOps.h>
-#include <math.h>
-#include <algorithm>
-
-namespace android {
-namespace uirenderer {
-
-static void storeTexturedRect(TextureVertex* vertices, const Rect& bounds) {
- vertices[0] = {bounds.left, bounds.top, 0, 0};
- vertices[1] = {bounds.right, bounds.top, 1, 0};
- vertices[2] = {bounds.left, bounds.bottom, 0, 1};
- vertices[3] = {bounds.right, bounds.bottom, 1, 1};
-}
-
-void BakedOpDispatcher::onMergedBitmapOps(BakedOpRenderer& renderer,
- const MergedBakedOpList& opList) {
- const BakedOpState& firstState = *(opList.states[0]);
- Bitmap* bitmap = (static_cast<const BitmapOp*>(opList.states[0]->op))->bitmap;
-
- Texture* texture = renderer.caches().textureCache.get(bitmap);
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
-
- TextureVertex vertices[opList.count * 4];
- for (size_t i = 0; i < opList.count; i++) {
- const BakedOpState& state = *(opList.states[i]);
- TextureVertex* rectVerts = &vertices[i * 4];
-
- // calculate unclipped bounds, since they'll determine texture coordinates
- Rect opBounds = state.op->unmappedBounds;
- state.computedState.transform.mapRect(opBounds);
- if (CC_LIKELY(state.computedState.transform.isPureTranslate())) {
- // pure translate, so snap (same behavior as onBitmapOp)
- opBounds.snapToPixelBoundaries();
- }
- storeTexturedRect(rectVerts, opBounds);
- renderer.dirtyRenderTarget(opBounds);
- }
-
- const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
- ? TextureFillFlags::IsAlphaMaskTexture
- : TextureFillFlags::None;
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(firstState.roundRectClipState)
- .setMeshTexturedIndexedQuads(vertices, opList.count * 6)
- .setFillTexturePaint(*texture, textureFillFlags, firstState.op->paint, firstState.alpha)
- .setTransform(Matrix4::identity(), TransformFlags::None)
- .setModelViewIdentityEmptyBounds()
- .build();
- ClipRect renderTargetClip(opList.clip);
- const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
- renderer.renderGlop(nullptr, clip, glop);
-}
-
-void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
- const MergedBakedOpList& opList) {
- const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op));
- const BakedOpState& firstState = *(opList.states[0]);
-
- // Batches will usually contain a small number of items so it's
- // worth performing a first iteration to count the exact number
- // of vertices we need in the new mesh
- uint32_t totalVertices = 0;
-
- for (size_t i = 0; i < opList.count; i++) {
- const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
-
- // TODO: cache mesh lookups
- const Patch* opMesh = renderer.caches().patchCache.get(
- op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(),
- op.unmappedBounds.getHeight(), op.patch);
- totalVertices += opMesh->verticesCount;
- }
-
- const bool dirtyRenderTarget = renderer.offscreenRenderTarget();
-
- uint32_t indexCount = 0;
-
- TextureVertex vertices[totalVertices];
- TextureVertex* vertex = &vertices[0];
- // Create a mesh that contains the transformed vertices for all the
- // 9-patch objects that are part of the batch. Note that onDefer()
- // enforces ops drawn by this function to have a pure translate or
- // identity matrix
- for (size_t i = 0; i < opList.count; i++) {
- const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
- const BakedOpState& state = *opList.states[i];
-
- // TODO: cache mesh lookups
- const Patch* opMesh = renderer.caches().patchCache.get(
- op.bitmap->width(), op.bitmap->height(), op.unmappedBounds.getWidth(),
- op.unmappedBounds.getHeight(), op.patch);
-
- uint32_t vertexCount = opMesh->verticesCount;
- if (vertexCount == 0) continue;
-
- // We use the bounds to know where to translate our vertices
- // Using patchOp->state.mBounds wouldn't work because these
- // bounds are clipped
- const float tx = floorf(state.computedState.transform.getTranslateX() +
- op.unmappedBounds.left + 0.5f);
- const float ty = floorf(state.computedState.transform.getTranslateY() +
- op.unmappedBounds.top + 0.5f);
-
- // Copy & transform all the vertices for the current operation
- TextureVertex* opVertices = opMesh->vertices.get();
- for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
- TextureVertex::set(vertex++, opVertices->x + tx, opVertices->y + ty, opVertices->u,
- opVertices->v);
- }
-
- // Dirty the current layer if possible. When the 9-patch does not
- // contain empty quads we can take a shortcut and simply set the
- // dirty rect to the object's bounds.
- if (dirtyRenderTarget) {
- if (!opMesh->hasEmptyQuads) {
- renderer.dirtyRenderTarget(Rect(tx, ty, tx + op.unmappedBounds.getWidth(),
- ty + op.unmappedBounds.getHeight()));
- } else {
- const size_t count = opMesh->quads.size();
- for (size_t i = 0; i < count; i++) {
- const Rect& quadBounds = opMesh->quads[i];
- const float x = tx + quadBounds.left;
- const float y = ty + quadBounds.top;
- renderer.dirtyRenderTarget(
- Rect(x, y, x + quadBounds.getWidth(), y + quadBounds.getHeight()));
- }
- }
- }
-
- indexCount += opMesh->indexCount;
- }
-
- Texture* texture = renderer.caches().textureCache.get(firstOp.bitmap);
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
-
- // 9 patches are built for stretching - always filter
- int textureFillFlags = TextureFillFlags::ForceFilter;
- if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) {
- textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
- }
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(firstState.roundRectClipState)
- .setMeshTexturedIndexedQuads(vertices, indexCount)
- .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha)
- .setTransform(Matrix4::identity(), TransformFlags::None)
- .setModelViewIdentityEmptyBounds()
- .build();
- ClipRect renderTargetClip(opList.clip);
- const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
- renderer.renderGlop(nullptr, clip, glop);
-}
-
-static void renderTextShadow(BakedOpRenderer& renderer, const TextOp& op,
- const BakedOpState& textOpState) {
- if (CC_LIKELY(!PaintUtils::hasTextShadow(op.paint))) return;
-
- FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
- fontRenderer.setFont(op.paint, SkMatrix::I());
- renderer.caches().textureState().activateTexture(0);
-
- PaintUtils::TextShadow textShadow;
- if (!PaintUtils::getTextShadow(op.paint, &textShadow)) {
- LOG_ALWAYS_FATAL("failed to query shadow attributes");
- }
-
- renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
- ShadowTexture* texture = renderer.caches().dropShadowCache.get(
- op.paint, op.glyphs, op.glyphCount, textShadow.radius, op.positions);
- // If the drop shadow exceeds the max texture size or couldn't be
- // allocated, skip drawing
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
-
- const float sx = op.x - texture->left + textShadow.dx;
- const float sy = op.y - texture->top + textShadow.dy;
-
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(textOpState.roundRectClipState)
- .setMeshTexturedUnitQuad(nullptr)
- .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, textOpState.alpha)
- .setTransform(textOpState.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
- .build();
-
- // Compute damage bounds and clip (since may differ from those in textOpState).
- // Bounds should be same as text op, but with dx/dy offset and radius outset
- // applied in local space.
- auto& transform = textOpState.computedState.transform;
- Rect shadowBounds = op.unmappedBounds; // STROKE
- const bool expandForStroke = op.paint->getStyle() != SkPaint::kFill_Style;
- if (expandForStroke) {
- shadowBounds.outset(op.paint->getStrokeWidth() * 0.5f);
- }
- shadowBounds.translate(textShadow.dx, textShadow.dy);
- shadowBounds.outset(textShadow.radius, textShadow.radius);
- transform.mapRect(shadowBounds);
- if (CC_UNLIKELY(expandForStroke &&
- (!transform.isPureTranslate() || op.paint->getStrokeWidth() < 1.0f))) {
- shadowBounds.outset(0.5f);
- }
-
- auto clipState = textOpState.computedState.clipState;
- if (clipState->mode != ClipMode::Rectangle || !clipState->rect.contains(shadowBounds)) {
- // need clip, so pass it and clip bounds
- shadowBounds.doIntersect(clipState->rect);
- } else {
- // don't need clip, ignore
- clipState = nullptr;
- }
-
- renderer.renderGlop(&shadowBounds, clipState, glop);
-}
-
-enum class TextRenderType { Defer, Flush };
-
-static void renderText(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state,
- const ClipBase* renderClip, TextRenderType renderType) {
- FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
- float x = op.x;
- float y = op.y;
- const Matrix4& transform = state.computedState.transform;
- const bool pureTranslate = transform.isPureTranslate();
- if (CC_LIKELY(pureTranslate)) {
- x = floorf(x + transform.getTranslateX() + 0.5f);
- y = floorf(y + transform.getTranslateY() + 0.5f);
- fontRenderer.setFont(op.paint, SkMatrix::I());
- fontRenderer.setTextureFiltering(false);
- } else if (CC_UNLIKELY(transform.isPerspective())) {
- fontRenderer.setFont(op.paint, SkMatrix::I());
- fontRenderer.setTextureFiltering(true);
- } else {
- // We only pass a partial transform to the font renderer. That partial
- // matrix defines how glyphs are rasterized. Typically we want glyphs
- // to be rasterized at their final size on screen, which means the partial
- // matrix needs to take the scale factor into account.
- // When a partial matrix is used to transform glyphs during rasterization,
- // the mesh is generated with the inverse transform (in the case of scale,
- // the mesh is generated at 1.0 / scale for instance.) This allows us to
- // apply the full transform matrix at draw time in the vertex shader.
- // Applying the full matrix in the shader is the easiest way to handle
- // rotation and perspective and allows us to always generated quads in the
- // font renderer which greatly simplifies the code, clipping in particular.
- float sx, sy;
- transform.decomposeScale(sx, sy);
- fontRenderer.setFont(op.paint, SkMatrix::MakeScale(roundf(std::max(1.0f, sx)),
- roundf(std::max(1.0f, sy))));
- fontRenderer.setTextureFiltering(true);
- }
- Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
-
- int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
- SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint);
- TextDrawFunctor functor(&renderer, &state, renderClip, x, y, pureTranslate, alpha, mode,
- op.paint);
-
- bool forceFinish = (renderType == TextRenderType::Flush);
- bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
- const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr;
- fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y, op.positions,
- mustDirtyRenderTarget ? &layerBounds : nullptr, &functor,
- forceFinish);
-
- if (mustDirtyRenderTarget) {
- if (!pureTranslate) {
- transform.mapRect(layerBounds);
- }
- renderer.dirtyRenderTarget(layerBounds);
- }
-}
-
-void BakedOpDispatcher::onMergedTextOps(BakedOpRenderer& renderer,
- const MergedBakedOpList& opList) {
- for (size_t i = 0; i < opList.count; i++) {
- const BakedOpState& state = *(opList.states[i]);
- const TextOp& op = *(static_cast<const TextOp*>(state.op));
- renderTextShadow(renderer, op, state);
- }
-
- ClipRect renderTargetClip(opList.clip);
- const ClipBase* clip = opList.clipSideFlags ? &renderTargetClip : nullptr;
- for (size_t i = 0; i < opList.count; i++) {
- const BakedOpState& state = *(opList.states[i]);
- const TextOp& op = *(static_cast<const TextOp*>(state.op));
- TextRenderType renderType =
- (i + 1 == opList.count) ? TextRenderType::Flush : TextRenderType::Defer;
- renderText(renderer, op, state, clip, renderType);
- }
-}
-
-namespace VertexBufferRenderFlags {
-enum {
- Offset = 0x1,
- ShadowInterp = 0x2,
-};
-}
-
-static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
- const VertexBuffer& vertexBuffer, float translateX, float translateY,
- const SkPaint& paint, int vertexBufferRenderFlags) {
- if (CC_LIKELY(vertexBuffer.getVertexCount())) {
- bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
- const int transformFlags = vertexBufferRenderFlags & VertexBufferRenderFlags::Offset
- ? TransformFlags::OffsetByFudgeFactor
- : 0;
-
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshVertexBuffer(vertexBuffer)
- .setFillPaint(paint, state.alpha, shadowInterp)
- .setTransform(state.computedState.transform, transformFlags)
- .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
- .build();
- renderer.renderGlop(state, glop);
- }
-}
-
-static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
- const SkPath& path, const SkPaint& paint) {
- VertexBuffer vertexBuffer;
- // TODO: try clipping large paths to viewport
- PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
- renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
-}
-
-static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state, float xOffset,
- float yOffset, PathTexture& texture, const SkPaint& paint) {
- Rect dest(texture.width(), texture.height());
- dest.translate(xOffset + texture.left - texture.offset, yOffset + texture.top - texture.offset);
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshTexturedUnitQuad(nullptr)
- .setFillPathTexturePaint(texture, paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRect(dest)
- .build();
- renderer.renderGlop(state, glop);
-}
-
-SkRect getBoundsOfFill(const RecordedOp& op) {
- SkRect bounds = op.unmappedBounds.toSkRect();
- if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
- float outsetDistance = op.paint->getStrokeWidth() / 2;
- bounds.outset(outsetDistance, outsetDistance);
- }
- return bounds;
-}
-
-void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op,
- const BakedOpState& state) {
- // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
- if (op.paint->getStyle() != SkPaint::kStroke_Style || op.paint->getPathEffect() != nullptr ||
- op.useCenter) {
- PathTexture* texture = renderer.caches().pathCache.getArc(
- op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.startAngle,
- op.sweepAngle, op.useCenter, op.paint);
- const AutoTexture holder(texture);
- if (CC_LIKELY(holder.texture)) {
- renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
- *texture, *(op.paint));
- }
- } else {
- SkRect rect = getBoundsOfFill(op);
- SkPath path;
- if (op.useCenter) {
- path.moveTo(rect.centerX(), rect.centerY());
- }
- path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
- if (op.useCenter) {
- path.close();
- }
- renderConvexPath(renderer, state, path, *(op.paint));
- }
-}
-
-void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op,
- const BakedOpState& state) {
- Texture* texture = renderer.getTexture(op.bitmap);
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
-
- const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
- ? TextureFillFlags::IsAlphaMaskTexture
- : TextureFillFlags::None;
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshTexturedUnitQuad(texture->uvMapper)
- .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height()))
- .build();
- renderer.renderGlop(state, glop);
-}
-
-void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op,
- const BakedOpState& state) {
- Texture* texture = renderer.caches().textureCache.get(op.bitmap);
- if (!texture) {
- return;
- }
- const AutoTexture autoCleanup(texture);
-
- const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;
-
- std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
- ColorTextureVertex* vertex = &mesh[0];
-
- const int* colors = op.colors;
- std::unique_ptr<int[]> tempColors;
- if (!colors) {
- uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1);
- tempColors.reset(new int[colorsCount]);
- memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
- colors = tempColors.get();
- }
-
- for (int32_t y = 0; y < op.meshHeight; y++) {
- for (int32_t x = 0; x < op.meshWidth; x++) {
- uint32_t i = (y * (op.meshWidth + 1) + x) * 2;
-
- float u1 = float(x) / op.meshWidth;
- float u2 = float(x + 1) / op.meshWidth;
- float v1 = float(y) / op.meshHeight;
- float v2 = float(y + 1) / op.meshHeight;
-
- int ax = i + (op.meshWidth + 1) * 2;
- int ay = ax + 1;
- int bx = i;
- int by = bx + 1;
- int cx = i + 2;
- int cy = cx + 1;
- int dx = i + (op.meshWidth + 1) * 2 + 2;
- int dy = dx + 1;
-
- const float* vertices = op.vertices;
- ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
- ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
- ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
-
- ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
- ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
- ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
- }
- }
-
- /*
- * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
- * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
- */
- const int textureFillFlags = TextureFillFlags::None;
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshColoredTexturedMesh(mesh.get(), elementCount)
- .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewOffsetRect(0, 0, op.unmappedBounds)
- .build();
- renderer.renderGlop(state, glop);
-}
-
-void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op,
- const BakedOpState& state) {
- Texture* texture = renderer.getTexture(op.bitmap);
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
-
- Rect uv(std::max(0.0f, op.src.left / texture->width()),
- std::max(0.0f, op.src.top / texture->height()),
- std::min(1.0f, op.src.right / texture->width()),
- std::min(1.0f, op.src.bottom / texture->height()));
-
- const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
- ? TextureFillFlags::IsAlphaMaskTexture
- : TextureFillFlags::None;
- const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth()) &&
- MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight());
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshTexturedUvQuad(texture->uvMapper, uv)
- .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
- .build();
- renderer.renderGlop(state, glop);
-}
-
-void BakedOpDispatcher::onColorOp(BakedOpRenderer& renderer, const ColorOp& op,
- const BakedOpState& state) {
- SkPaint paint;
- paint.setColor(op.color);
- paint.setBlendMode(op.mode);
-
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshUnitQuad()
- .setFillPaint(paint, state.alpha)
- .setTransform(Matrix4::identity(), TransformFlags::None)
- .setModelViewMapUnitToRect(state.computedState.clipState->rect)
- .build();
- renderer.renderGlop(state, glop);
-}
-
-void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op,
- const BakedOpState& state) {
- renderer.renderFunctor(op, state);
-}
-
-void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op,
- const BakedOpState& state) {
- VertexBuffer buffer;
- PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
- state.computedState.transform, buffer);
- int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
- renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
-}
-
-void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op,
- const BakedOpState& state) {
- if (op.paint->getPathEffect() != nullptr) {
- PathTexture* texture = renderer.caches().pathCache.getOval(
- op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
- const AutoTexture holder(texture);
- if (CC_LIKELY(holder.texture)) {
- renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
- *texture, *(op.paint));
- }
- } else {
- SkPath path;
- SkRect rect = getBoundsOfFill(op);
- path.addOval(rect);
-
- if (state.computedState.localProjectionPathMask != nullptr) {
- // Mask the ripple path by the local space projection mask in local space.
- // Note that this can create CCW paths.
- Op(path, *state.computedState.localProjectionPathMask, kIntersect_SkPathOp, &path);
- }
- renderConvexPath(renderer, state, path, *(op.paint));
- }
-}
-
-void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op,
- const BakedOpState& state) {
- // 9 patches are built for stretching - always filter
- int textureFillFlags = TextureFillFlags::ForceFilter;
- if (op.bitmap->colorType() == kAlpha_8_SkColorType) {
- textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
- }
-
- // TODO: avoid redoing the below work each frame:
- const Patch* mesh = renderer.caches().patchCache.get(op.bitmap->width(), op.bitmap->height(),
- op.unmappedBounds.getWidth(),
- op.unmappedBounds.getHeight(), op.patch);
-
- Texture* texture = renderer.caches().textureCache.get(op.bitmap);
- if (CC_LIKELY(texture)) {
- const AutoTexture autoCleanup(texture);
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshPatchQuads(*mesh)
- .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewOffsetRectSnap(
- op.unmappedBounds.left, op.unmappedBounds.top,
- Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
- .build();
- renderer.renderGlop(state, glop);
- }
-}
-
-void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op,
- const BakedOpState& state) {
- PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
- const AutoTexture holder(texture);
- if (CC_LIKELY(holder.texture)) {
- // Unlike other callers to renderPathTexture, no offsets are used because PathOp doesn't
- // have any translate built in, other than what's in the SkPath itself
- renderPathTexture(renderer, state, 0, 0, *texture, *(op.paint));
- }
-}
-
-void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op,
- const BakedOpState& state) {
- VertexBuffer buffer;
- PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
- state.computedState.transform, buffer);
- int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
- renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
-}
-
-// See SkPaintDefaults.h
-#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
-
-void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op,
- const BakedOpState& state) {
- if (op.paint->getStyle() != SkPaint::kFill_Style) {
- // only fill + default miter is supported by drawConvexPath, since others must handle joins
- static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
- if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr ||
- op.paint->getStrokeJoin() != SkPaint::kMiter_Join ||
- op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
- PathTexture* texture = renderer.caches().pathCache.getRect(
- op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
- const AutoTexture holder(texture);
- if (CC_LIKELY(holder.texture)) {
- renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
- *texture, *(op.paint));
- }
- } else {
- SkPath path;
- path.addRect(getBoundsOfFill(op));
- renderConvexPath(renderer, state, path, *(op.paint));
- }
- } else {
- if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
- SkPath path;
- path.addRect(op.unmappedBounds.toSkRect());
- renderConvexPath(renderer, state, path, *(op.paint));
- } else {
- // render simple unit quad, no tessellation required
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshUnitQuad()
- .setFillPaint(*op.paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRect(op.unmappedBounds)
- .build();
- renderer.renderGlop(state, glop);
- }
- }
-}
-
-void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op,
- const BakedOpState& state) {
- if (op.paint->getPathEffect() != nullptr) {
- PathTexture* texture = renderer.caches().pathCache.getRoundRect(
- op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry,
- op.paint);
- const AutoTexture holder(texture);
- if (CC_LIKELY(holder.texture)) {
- renderPathTexture(renderer, state, op.unmappedBounds.left, op.unmappedBounds.top,
- *texture, *(op.paint));
- }
- } else {
- const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
- state.computedState.transform, *(op.paint), op.unmappedBounds.getWidth(),
- op.unmappedBounds.getHeight(), op.rx, op.ry);
- renderVertexBuffer(renderer, state, *buffer, op.unmappedBounds.left, op.unmappedBounds.top,
- *(op.paint), 0);
- }
-}
-
-static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
- const VertexBuffer* ambientShadowVertexBuffer,
- const VertexBuffer* spotShadowVertexBuffer) {
- SkPaint paint;
- paint.setAntiAlias(true); // want to use AlphaVertex
-
- // The caller has made sure casterAlpha > 0.
- uint8_t ambientShadowAlpha = renderer.getLightInfo().ambientShadowAlpha;
- if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
- ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
- }
- if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
- paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
- renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0, paint,
- VertexBufferRenderFlags::ShadowInterp);
- }
-
- uint8_t spotShadowAlpha = renderer.getLightInfo().spotShadowAlpha;
- if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
- spotShadowAlpha = Properties::overrideSpotShadowStrength;
- }
- if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
- paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
- renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0, paint,
- VertexBufferRenderFlags::ShadowInterp);
- }
-}
-
-void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op,
- const BakedOpState& state) {
- TessellationCache::vertexBuffer_pair_t buffers = op.shadowTask->getResult();
- renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
-}
-
-void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op,
- const BakedOpState& state) {
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
- .setFillPaint(*op.paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewOffsetRect(0, 0, op.unmappedBounds)
- .build();
- renderer.renderGlop(state, glop);
-}
-
-void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op,
- const BakedOpState& state) {
- renderTextShadow(renderer, op, state);
- renderText(renderer, op, state, state.computedState.getClipIfNeeded(), TextRenderType::Flush);
-}
-
-void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPathOp& op,
- const BakedOpState& state) {
- // Note: can't trust clipSideFlags since we record with unmappedBounds == clip.
- // TODO: respect clipSideFlags, once we record with bounds
- auto renderTargetClip = state.computedState.clipState;
-
- FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer();
- fontRenderer.setFont(op.paint, SkMatrix::I());
- fontRenderer.setTextureFiltering(true);
-
- Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
-
- int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha;
- SkBlendMode mode = PaintUtils::getBlendModeDirect(op.paint);
- TextDrawFunctor functor(&renderer, &state, renderTargetClip, 0.0f, 0.0f, false, alpha, mode,
- op.paint);
-
- bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
- const Rect localSpaceClip = state.computedState.computeLocalSpaceClip();
- if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount, op.path,
- op.hOffset, op.vOffset,
- mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) {
- if (mustDirtyRenderTarget) {
- // manually dirty render target, since TextDrawFunctor won't
- state.computedState.transform.mapRect(layerBounds);
- renderer.dirtyRenderTarget(layerBounds);
- }
- }
-}
-
-void BakedOpDispatcher::onTextureLayerOp(BakedOpRenderer& renderer, const TextureLayerOp& op,
- const BakedOpState& state) {
- GlLayer* layer = static_cast<GlLayer*>(op.layerHandle->backingLayer());
- if (!layer) {
- return;
- }
- const bool tryToSnap = layer->getForceFilter();
- float alpha = (layer->getAlpha() / 255.0f) * state.alpha;
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
- .setFillTextureLayer(*(layer), alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRectOptionalSnap(tryToSnap,
- Rect(layer->getWidth(), layer->getHeight()))
- .build();
- renderer.renderGlop(state, glop);
-}
-
-void renderRectForLayer(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state,
- int color, SkBlendMode mode, SkColorFilter* colorFilter) {
- SkPaint paint;
- paint.setColor(color);
- paint.setBlendMode(mode);
- paint.setColorFilter(sk_ref_sp(colorFilter));
- RectOp rectOp(op.unmappedBounds, op.localMatrix, op.localClip, &paint);
- BakedOpDispatcher::onRectOp(renderer, rectOp, state);
-}
-
-void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op,
- const BakedOpState& state) {
- // Note that we don't use op->paint in this function - it's never set on a LayerOp
- OffscreenBuffer* buffer = *op.layerHandle;
-
- if (CC_UNLIKELY(!buffer)) return;
-
- float layerAlpha = op.alpha * state.alpha;
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
- .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode,
- Blend::ModeOrderSwap::NoSwap)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewOffsetRectSnap(
- op.unmappedBounds.left, op.unmappedBounds.top,
- Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
- .build();
- renderer.renderGlop(state, glop);
-
- if (!buffer->hasRenderedSinceRepaint) {
- buffer->hasRenderedSinceRepaint = true;
- if (CC_UNLIKELY(Properties::debugLayersUpdates)) {
- // render debug layer highlight
- renderRectForLayer(renderer, op, state, 0x7f00ff00, SkBlendMode::kSrcOver, nullptr);
- } else if (CC_UNLIKELY(Properties::debugOverdraw)) {
- // render transparent to increment overdraw for repaint area
- renderRectForLayer(renderer, op, state, SK_ColorTRANSPARENT, SkBlendMode::kSrcOver,
- nullptr);
- }
- }
-}
-
-void BakedOpDispatcher::onCopyToLayerOp(BakedOpRenderer& renderer, const CopyToLayerOp& op,
- const BakedOpState& state) {
- LOG_ALWAYS_FATAL_IF(*(op.layerHandle) != nullptr, "layer already exists!");
- *(op.layerHandle) = renderer.copyToLayer(state.computedState.clippedBounds);
- LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "layer copy failed");
-}
-
-void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyFromLayerOp& op,
- const BakedOpState& state) {
- LOG_ALWAYS_FATAL_IF(*op.layerHandle == nullptr, "no layer to draw underneath!");
- if (!state.computedState.clippedBounds.isEmpty()) {
- if (op.paint && op.paint->getAlpha() < 255) {
- SkPaint layerPaint;
- layerPaint.setAlpha(op.paint->getAlpha());
- layerPaint.setBlendMode(SkBlendMode::kDstIn);
- layerPaint.setColorFilter(sk_ref_sp(op.paint->getColorFilter()));
- RectOp rectOp(state.computedState.clippedBounds, Matrix4::identity(), nullptr,
- &layerPaint);
- BakedOpDispatcher::onRectOp(renderer, rectOp, state);
- }
-
- OffscreenBuffer& layer = **(op.layerHandle);
- auto mode = PaintUtils::getBlendModeDirect(op.paint);
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshTexturedUvQuad(nullptr, layer.getTextureCoordinates())
- .setFillLayer(layer.texture, nullptr, 1.0f, mode, Blend::ModeOrderSwap::Swap)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRect(state.computedState.clippedBounds)
- .build();
- renderer.renderGlop(state, glop);
- }
- renderer.renderState().layerPool().putOrDelete(*op.layerHandle);
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/BakedOpDispatcher.h b/libs/hwui/BakedOpDispatcher.h
deleted file mode 100644
index e3708685afc9..000000000000
--- a/libs/hwui/BakedOpDispatcher.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_BAKED_OP_DISPATCHER_H
-#define ANDROID_HWUI_BAKED_OP_DISPATCHER_H
-
-#include "BakedOpState.h"
-#include "RecordedOp.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Provides all "onBitmapOp(...)" style static methods for every op type, which convert the
- * RecordedOps and their state to Glops, and renders them with the provided BakedOpRenderer.
- *
- * onXXXOp methods must either render directly with the renderer, or call a static renderYYY
- * method to render content. There should never be draw content rejection in BakedOpDispatcher -
- * it must happen at a higher level (except in error-ish cases, like texture-too-big).
- */
-class BakedOpDispatcher {
-public:
-// Declares all "onMergedBitmapOps(...)" style methods for mergeable op types
-#define X(Type) \
- static void onMerged##Type##s(BakedOpRenderer& renderer, const MergedBakedOpList& opList);
- MAP_MERGEABLE_OPS(X)
-#undef X
-
-// Declares all "onBitmapOp(...)" style methods for every op type
-#define X(Type) \
- static void on##Type(BakedOpRenderer& renderer, const Type& op, const BakedOpState& state);
- MAP_RENDERABLE_OPS(X)
-#undef X
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_BAKED_OP_DISPATCHER_H
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
deleted file mode 100644
index 6b64b94d291e..000000000000
--- a/libs/hwui/BakedOpRenderer.cpp
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BakedOpRenderer.h"
-
-#include "Caches.h"
-#include "Glop.h"
-#include "GlopBuilder.h"
-#include "VertexBuffer.h"
-#include "renderstate/OffscreenBufferPool.h"
-#include "renderstate/RenderState.h"
-#include "utils/GLUtils.h"
-
-#include <algorithm>
-
-namespace android {
-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, mWideColorGamut);
- startRepaintLayer(buffer, Rect(width, height));
- return buffer;
-}
-
-void BakedOpRenderer::recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) {
- mRenderState.layerPool().putOrDelete(offscreenBuffer);
-}
-
-void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) {
- LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
-
- // subtract repaintRect from region, since it will be regenerated
- if (repaintRect.contains(0, 0, offscreenBuffer->viewportWidth,
- offscreenBuffer->viewportHeight)) {
- // repaint full layer, so throw away entire region
- offscreenBuffer->region.clear();
- } else {
- offscreenBuffer->region.subtractSelf(android::Rect(repaintRect.left, repaintRect.top,
- repaintRect.right, repaintRect.bottom));
- }
-
- mRenderTarget.offscreenBuffer = offscreenBuffer;
- mRenderTarget.offscreenBuffer->hasRenderedSinceRepaint = false;
-
- // create and bind framebuffer
- mRenderTarget.frameBufferId = mRenderState.createFramebuffer();
- mRenderState.bindFramebuffer(mRenderTarget.frameBufferId);
-
- // attach the texture to the FBO
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- offscreenBuffer->texture.id(), 0);
- GL_CHECKPOINT(LOW);
-
- int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- LOG_ALWAYS_FATAL_IF(status != GL_FRAMEBUFFER_COMPLETE,
- "framebuffer incomplete, status %d, textureId %d, size %dx%d", status,
- offscreenBuffer->texture.id(), offscreenBuffer->texture.width(),
- offscreenBuffer->texture.height());
-
- // Change the viewport & ortho projection
- setViewport(offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight);
-
- clearColorBuffer(repaintRect);
-}
-
-void BakedOpRenderer::endLayer() {
- if (mRenderTarget.stencil) {
- // if stencil was used for clipping, detach it and return it to pool
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
- GL_CHECKPOINT(MODERATE);
- mCaches.renderBufferCache.put(mRenderTarget.stencil);
- mRenderTarget.stencil = nullptr;
- }
- mRenderTarget.lastStencilClip = nullptr;
-
- mRenderTarget.offscreenBuffer->updateMeshFromRegion();
- mRenderTarget.offscreenBuffer = nullptr; // It's in drawLayerOp's hands now.
-
- // Detach the texture from the FBO
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
- GL_CHECKPOINT(LOW);
- mRenderState.deleteFramebuffer(mRenderTarget.frameBufferId);
- mRenderTarget.frameBufferId = 0;
-}
-
-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, mWideColorGamut);
- if (!area.isEmpty() && width != 0 && height != 0) {
- mCaches.textureState().activateTexture(0);
- mCaches.textureState().bindTexture(buffer->texture.id());
-
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, area.left,
- mRenderTarget.viewportHeight - area.bottom, width, height);
- }
- return buffer;
-}
-
-void BakedOpRenderer::startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {
- LOG_ALWAYS_FATAL_IF(mRenderTarget.frameBufferId != 0, "primary framebufferId must be 0");
- mRenderState.bindFramebuffer(0);
- setViewport(width, height);
-
- if (!mOpaque) {
- clearColorBuffer(repaintRect);
- }
-
- mRenderState.debugOverdraw(true, true);
-}
-
-void BakedOpRenderer::endFrame(const Rect& repaintRect) {
- if (CC_UNLIKELY(Properties::debugOverdraw)) {
- ClipRect overdrawClip(repaintRect);
- Rect viewportRect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight);
- // overdraw visualization
- for (int i = 1; i <= 4; i++) {
- if (i < 4) {
- // nth level of overdraw tests for n+1 draws per pixel
- mRenderState.stencil().enableDebugTest(i + 1, false);
- } else {
- // 4th level tests for 4 or higher draws per pixel
- mRenderState.stencil().enableDebugTest(4, true);
- }
-
- SkPaint paint;
- paint.setColor(mCaches.getOverdrawColor(i));
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(nullptr)
- .setMeshUnitQuad()
- .setFillPaint(paint, 1.0f)
- .setTransform(Matrix4::identity(), TransformFlags::None)
- .setModelViewMapUnitToRect(viewportRect)
- .build();
- renderGlop(nullptr, &overdrawClip, glop);
- }
- mRenderState.stencil().disable();
- }
-
- // Note: we leave FBO 0 renderable here, for post-frame-content decoration
-}
-
-void BakedOpRenderer::setViewport(uint32_t width, uint32_t height) {
- mRenderTarget.viewportWidth = width;
- mRenderTarget.viewportHeight = height;
- mRenderTarget.orthoMatrix.loadOrtho(width, height);
-
- mRenderState.setViewport(width, height);
- mRenderState.blend().syncEnabled();
-}
-
-void BakedOpRenderer::clearColorBuffer(const Rect& rect) {
- if (rect.contains(Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight))) {
- // Full viewport is being cleared - disable scissor
- mRenderState.scissor().setEnabled(false);
- } else {
- // Requested rect is subset of viewport - scissor to it to avoid over-clearing
- mRenderState.scissor().setEnabled(true);
- mRenderState.scissor().set(rect.left, mRenderTarget.viewportHeight - rect.bottom,
- rect.getWidth(), rect.getHeight());
- }
- glClear(GL_COLOR_BUFFER_BIT);
- if (!mRenderTarget.frameBufferId) mHasDrawn = true;
-}
-
-Texture* BakedOpRenderer::getTexture(Bitmap* bitmap) {
- return mCaches.textureCache.get(bitmap);
-}
-
-void BakedOpRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
- std::vector<Vertex> vertices;
- vertices.reserve(count);
- Vertex* vertex = vertices.data();
-
- for (int index = 0; index < count; index += 4) {
- float l = rects[index + 0];
- float t = rects[index + 1];
- float r = rects[index + 2];
- float b = rects[index + 3];
-
- Vertex::set(vertex++, l, t);
- Vertex::set(vertex++, r, t);
- Vertex::set(vertex++, l, b);
- Vertex::set(vertex++, r, b);
- }
-
- LOG_ALWAYS_FATAL_IF(mRenderTarget.frameBufferId != 0, "decoration only supported for FBO 0");
- // TODO: Currently assume full FBO damage, due to FrameInfoVisualizer::unionDirty.
- // Should should scissor/set mHasDrawn safely.
- mRenderState.scissor().setEnabled(false);
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(nullptr)
- .setMeshIndexedQuads(vertices.data(), count / 4)
- .setFillPaint(*paint, 1.0f)
- .setTransform(Matrix4::identity(), TransformFlags::None)
- .setModelViewIdentityEmptyBounds()
- .build();
- mRenderState.render(glop, mRenderTarget.orthoMatrix, false);
- mHasDrawn = true;
-}
-
-// clears and re-fills stencil with provided rendertarget space quads,
-// and then put stencil into test mode
-void BakedOpRenderer::setupStencilQuads(std::vector<Vertex>& quadVertices, int incrementThreshold) {
- mRenderState.stencil().enableWrite(incrementThreshold);
- mRenderState.stencil().clear();
- Glop glop;
- GlopBuilder(mRenderState, mCaches, &glop)
- .setRoundRectClipState(nullptr)
- .setMeshIndexedQuads(quadVertices.data(), quadVertices.size() / 4)
- .setFillBlack()
- .setTransform(Matrix4::identity(), TransformFlags::None)
- .setModelViewIdentityEmptyBounds()
- .build();
- mRenderState.render(glop, mRenderTarget.orthoMatrix, false);
- mRenderState.stencil().enableTest(incrementThreshold);
-}
-
-void BakedOpRenderer::setupStencilRectList(const ClipBase* clip) {
- LOG_ALWAYS_FATAL_IF(clip->mode != ClipMode::RectangleList,
- "can't rectlist clip without rectlist");
- auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList;
- int quadCount = rectList.getTransformedRectanglesCount();
- std::vector<Vertex> rectangleVertices;
- rectangleVertices.reserve(quadCount * 4);
- for (int i = 0; i < quadCount; i++) {
- const TransformedRectangle& tr(rectList.getTransformedRectangle(i));
- const Matrix4& transform = tr.getTransform();
- Rect bounds = tr.getBounds();
- if (transform.rectToRect()) {
- // If rectToRect, can simply map bounds before storing verts
- transform.mapRect(bounds);
- bounds.doIntersect(clip->rect);
- if (bounds.isEmpty()) {
- continue; // will be outside of scissor, skip
- }
- }
-
- rectangleVertices.push_back(Vertex{bounds.left, bounds.top});
- rectangleVertices.push_back(Vertex{bounds.right, bounds.top});
- rectangleVertices.push_back(Vertex{bounds.left, bounds.bottom});
- rectangleVertices.push_back(Vertex{bounds.right, bounds.bottom});
-
- if (!transform.rectToRect()) {
- // If not rectToRect, must map each point individually
- for (auto cur = rectangleVertices.end() - 4; cur < rectangleVertices.end(); cur++) {
- transform.mapPoint(cur->x, cur->y);
- }
- }
- }
- setupStencilQuads(rectangleVertices, rectList.getTransformedRectanglesCount());
-}
-
-void BakedOpRenderer::setupStencilRegion(const ClipBase* clip) {
- LOG_ALWAYS_FATAL_IF(clip->mode != ClipMode::Region, "can't region clip without region");
- auto&& region = reinterpret_cast<const ClipRegion*>(clip)->region;
-
- std::vector<Vertex> regionVertices;
- SkRegion::Cliperator it(region, clip->rect.toSkIRect());
- while (!it.done()) {
- const SkIRect& r = it.rect();
- regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fTop});
- regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fTop});
- regionVertices.push_back(Vertex{(float)r.fLeft, (float)r.fBottom});
- regionVertices.push_back(Vertex{(float)r.fRight, (float)r.fBottom});
- it.next();
- }
- setupStencilQuads(regionVertices, 0);
-}
-
-void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* clip) {
- // Prepare scissor (done before stencil, to simplify filling stencil)
- mRenderState.scissor().setEnabled(clip != nullptr);
- if (clip) {
- mRenderState.scissor().set(mRenderTarget.viewportHeight, clip->rect);
- }
-
- // If stencil may be used for clipping, enable it, fill it, or disable it as appropriate
- if (CC_LIKELY(!Properties::debugOverdraw)) {
- // only modify stencil mode and content when it's not used for overdraw visualization
- if (CC_UNLIKELY(clip && clip->mode != ClipMode::Rectangle)) {
- // NOTE: this pointer check is only safe for non-rect clips,
- // since rect clips may be created on the stack
- if (mRenderTarget.lastStencilClip != clip) {
- // Stencil needed, but current stencil isn't up to date
- mRenderTarget.lastStencilClip = clip;
-
- if (mRenderTarget.frameBufferId != 0 && !mRenderTarget.stencil) {
- OffscreenBuffer* layer = mRenderTarget.offscreenBuffer;
- mRenderTarget.stencil = mCaches.renderBufferCache.get(
- Stencil::getLayerStencilFormat(), layer->texture.width(),
- layer->texture.height());
- // stencil is bound + allocated - associate it with current FBO
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, mRenderTarget.stencil->getName());
- }
-
- if (clip->mode == ClipMode::RectangleList) {
- setupStencilRectList(clip);
- } else {
- setupStencilRegion(clip);
- }
- } else {
- // stencil is up to date - just need to ensure it's enabled (since an unclipped
- // or scissor-only clipped op may have been drawn, disabling the stencil)
- int incrementThreshold = 0;
- if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
- auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList;
- incrementThreshold = rectList.getTransformedRectanglesCount();
- }
- mRenderState.stencil().enableTest(incrementThreshold);
- }
- } else {
- // either scissor or no clip, so disable stencil test
- mRenderState.stencil().disable();
- }
- }
-
- if (dirtyBounds) {
- // dirty offscreenbuffer if present
- dirtyRenderTarget(*dirtyBounds);
- }
-}
-
-void BakedOpRenderer::renderGlopImpl(const Rect* dirtyBounds, const ClipBase* clip,
- const Glop& glop) {
- prepareRender(dirtyBounds, clip);
- // Disable blending if this is the first draw to the main framebuffer, in case app has defined
- // transparency where it doesn't make sense - as first draw in opaque window. Note that we only
- // apply this improvement when the blend mode is SRC_OVER - other modes (e.g. CLEAR) can be
- // valid draws that affect other content (e.g. draw CLEAR, then draw DST_OVER)
- bool overrideDisableBlending = !mHasDrawn && mOpaque && !mRenderTarget.frameBufferId &&
- glop.blend.src == GL_ONE &&
- glop.blend.dst == GL_ONE_MINUS_SRC_ALPHA;
- mRenderState.render(glop, mRenderTarget.orthoMatrix, overrideDisableBlending);
- if (!mRenderTarget.frameBufferId) mHasDrawn = true;
-}
-
-void BakedOpRenderer::renderFunctor(const FunctorOp& op, const BakedOpState& state) {
- prepareRender(&state.computedState.clippedBounds, state.computedState.getClipIfNeeded());
-
- DrawGlInfo info;
- auto&& clip = state.computedState.clipRect();
- info.clipLeft = clip.left;
- info.clipTop = clip.top;
- info.clipRight = clip.right;
- info.clipBottom = clip.bottom;
- info.isLayer = offscreenRenderTarget();
- info.width = mRenderTarget.viewportWidth;
- info.height = mRenderTarget.viewportHeight;
- state.computedState.transform.copyTo(&info.transform[0]);
-
- mRenderState.invokeFunctor(op.functor, DrawGlInfo::kModeDraw, &info);
- if (!mRenderTarget.frameBufferId) mHasDrawn = true;
-}
-
-void BakedOpRenderer::dirtyRenderTarget(const Rect& uiDirty) {
- if (mRenderTarget.offscreenBuffer) {
- mRenderTarget.offscreenBuffer->dirty(uiDirty);
- }
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
deleted file mode 100644
index 7c0590d7ab48..000000000000
--- a/libs/hwui/BakedOpRenderer.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "BakedOpState.h"
-#include "Matrix.h"
-#include "utils/Macros.h"
-
-namespace android {
-namespace uirenderer {
-
-class Caches;
-struct Glop;
-class Layer;
-class RenderState;
-struct ClipBase;
-
-/**
- * Main rendering manager for a collection of work - one frame + any contained FBOs.
- *
- * Manages frame and FBO lifecycle, binding the GL framebuffer as appropriate. This is the only
- * place where FBOs are bound, created, and destroyed.
- *
- * All rendering operations will be sent by the Dispatcher, a collection of static methods,
- * which has intentionally limited access to the renderer functionality.
- */
-class BakedOpRenderer {
-public:
- typedef void (*GlopReceiver)(BakedOpRenderer&, const Rect*, const ClipBase*, const Glop&);
- /**
- * Position agnostic shadow lighting info. Used with all shadow ops in scene.
- */
- struct LightInfo {
- LightInfo() : LightInfo(0, 0) {}
- LightInfo(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha)
- : ambientShadowAlpha(ambientShadowAlpha), spotShadowAlpha(spotShadowAlpha) {}
- uint8_t ambientShadowAlpha;
- uint8_t spotShadowAlpha;
- };
-
- BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque, bool wideColorGamut,
- const LightInfo& lightInfo)
- : mGlopReceiver(DefaultGlopReceiver)
- , mRenderState(renderState)
- , mCaches(caches)
- , mOpaque(opaque)
- , mWideColorGamut(wideColorGamut)
- , mLightInfo(lightInfo) {}
-
- RenderState& renderState() { return mRenderState; }
- Caches& caches() { return mCaches; }
-
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect);
- void endFrame(const Rect& repaintRect);
- WARN_UNUSED_RESULT OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height);
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer);
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect);
- void endLayer();
- WARN_UNUSED_RESULT OffscreenBuffer* copyToLayer(const Rect& area);
-
- Texture* getTexture(Bitmap* bitmap);
- const LightInfo& getLightInfo() const { return mLightInfo; }
-
- void renderGlop(const BakedOpState& state, const Glop& glop) {
- renderGlop(&state.computedState.clippedBounds, state.computedState.getClipIfNeeded(), glop);
- }
- void renderFunctor(const FunctorOp& op, const BakedOpState& state);
-
- void renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop) {
- mGlopReceiver(*this, dirtyBounds, clip, glop);
- }
- bool offscreenRenderTarget() { return mRenderTarget.offscreenBuffer != nullptr; }
- void dirtyRenderTarget(const Rect& dirtyRect);
- bool didDraw() const { return mHasDrawn; }
-
- uint32_t getViewportWidth() const { return mRenderTarget.viewportWidth; }
- uint32_t getViewportHeight() const { return mRenderTarget.viewportHeight; }
-
- // simple draw methods, to be used for end frame decoration
- void drawRect(float left, float top, float right, float bottom, const SkPaint* paint) {
- float ltrb[4] = {left, top, right, bottom};
- drawRects(ltrb, 4, paint);
- }
- void drawRects(const float* rects, int count, const SkPaint* paint);
-
-protected:
- GlopReceiver mGlopReceiver;
-
-private:
- static void DefaultGlopReceiver(BakedOpRenderer& renderer, const Rect* dirtyBounds,
- const ClipBase* clip, const Glop& glop) {
- renderer.renderGlopImpl(dirtyBounds, clip, glop);
- }
- void renderGlopImpl(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop);
- void setViewport(uint32_t width, uint32_t height);
- void clearColorBuffer(const Rect& clearRect);
- void prepareRender(const Rect* dirtyBounds, const ClipBase* clip);
- void setupStencilRectList(const ClipBase* clip);
- void setupStencilRegion(const ClipBase* clip);
- void setupStencilQuads(std::vector<Vertex>& quadVertices, int incrementThreshold);
-
- RenderState& mRenderState;
- Caches& mCaches;
- bool mOpaque;
- bool mWideColorGamut;
- bool mHasDrawn = false;
-
- // render target state - setup by start/end layer/frame
- // only valid to use in between start/end pairs.
- struct {
- // If not drawing to a layer: fbo = 0, offscreenBuffer = null,
- // Otherwise these refer to currently painting layer's state
- GLuint frameBufferId = 0;
- OffscreenBuffer* offscreenBuffer = nullptr;
-
- // Used when drawing to a layer and using stencil clipping. otherwise null.
- RenderBuffer* stencil = nullptr;
-
- // value representing the ClipRectList* or ClipRegion* currently stored in
- // the stencil of the current render target
- const ClipBase* lastStencilClip = nullptr;
-
- // Size of renderable region in current render target - for layers, may not match actual
- // bounds of FBO texture. offscreenBuffer->texture has this information.
- uint32_t viewportWidth = 0;
- uint32_t viewportHeight = 0;
-
- Matrix4 orthoMatrix;
- } mRenderTarget;
-
- const LightInfo mLightInfo;
-};
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
deleted file mode 100644
index 63edf77279e3..000000000000
--- a/libs/hwui/BakedOpState.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BakedOpState.h"
-
-#include "ClipArea.h"
-
-namespace android {
-namespace uirenderer {
-
-static int computeClipSideFlags(const Rect& clip, const Rect& bounds) {
- int clipSideFlags = 0;
- if (clip.left > bounds.left) clipSideFlags |= OpClipSideFlags::Left;
- if (clip.top > bounds.top) clipSideFlags |= OpClipSideFlags::Top;
- if (clip.right < bounds.right) clipSideFlags |= OpClipSideFlags::Right;
- if (clip.bottom < bounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
- return clipSideFlags;
-}
-
-ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
- const RecordedOp& recordedOp, bool expandForStroke,
- bool expandForPathTexture) {
- // resolvedMatrix = parentMatrix * localMatrix
- transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix);
-
- // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
- clippedBounds = recordedOp.unmappedBounds;
- if (CC_UNLIKELY(expandForStroke)) {
- // account for non-hairline stroke
- clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f);
- } else if (CC_UNLIKELY(expandForPathTexture)) {
- clippedBounds.outset(1);
- }
- transform.mapRect(clippedBounds);
- if (CC_UNLIKELY(expandForStroke &&
- (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) {
- // account for hairline stroke when stroke may be < 1 scaled pixel
- // Non translate || strokeWidth < 1 is conservative, but will cover all cases
- clippedBounds.outset(0.5f);
- }
-
- // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
- clipState = snapshot.serializeIntersectedClip(allocator, recordedOp.localClip,
- *(snapshot.transform));
- LOG_ALWAYS_FATAL_IF(!clipState, "must clip!");
-
- const Rect& clipRect = clipState->rect;
- if (CC_UNLIKELY(clipRect.isEmpty() || !clippedBounds.intersects(clipRect))) {
- // Rejected based on either empty clip, or bounds not intersecting with clip
-
- // Note: we could rewind the clipState object in situations where the clipRect is empty,
- // but *only* if the caching logic within ClipArea was aware of the rewind.
- clipState = nullptr;
- clippedBounds.setEmpty();
- } else {
- // Not rejected! compute true clippedBounds, clipSideFlags, and path mask
- clipSideFlags = computeClipSideFlags(clipRect, clippedBounds);
- clippedBounds.doIntersect(clipRect);
-
- if (CC_UNLIKELY(snapshot.projectionPathMask)) {
- // map projection path mask from render target space into op space,
- // so intersection with op geometry is possible
- Matrix4 inverseTransform;
- inverseTransform.loadInverse(transform);
- SkMatrix skInverseTransform;
- inverseTransform.copyTo(skInverseTransform);
-
- auto localMask = allocator.create<SkPath>();
- snapshot.projectionPathMask->transform(skInverseTransform, localMask);
- localProjectionPathMask = localMask;
- }
- }
-}
-
-ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
- const Matrix4& localTransform, const ClipBase* localClip) {
- transform.loadMultiply(*snapshot.transform, localTransform);
- clipState = snapshot.serializeIntersectedClip(allocator, localClip, *(snapshot.transform));
- clippedBounds = clipState->rect;
- clipSideFlags = OpClipSideFlags::Full;
- localProjectionPathMask = nullptr;
-}
-
-ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot)
- : transform(*snapshot.transform)
- , clipState(snapshot.mutateClipArea().serializeClip(allocator))
- , clippedBounds(clipState->rect)
- , clipSideFlags(OpClipSideFlags::Full)
- , localProjectionPathMask(nullptr) {}
-
-ResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& dstRect)
- : transform(Matrix4::identity())
- , clipState(clipRect)
- , clippedBounds(dstRect)
- , clipSideFlags(computeClipSideFlags(clipRect->rect, dstRect))
- , localProjectionPathMask(nullptr) {
- clippedBounds.doIntersect(clipRect->rect);
-}
-
-BakedOpState* BakedOpState::tryConstruct(LinearAllocator& allocator, Snapshot& snapshot,
- const RecordedOp& recordedOp) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- BakedOpState* bakedState =
- allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp, false, false);
- if (bakedState->computedState.clippedBounds.isEmpty()) {
- // bounds are empty, so op is rejected
- allocator.rewindIfLastAlloc(bakedState);
- return nullptr;
- }
- return bakedState;
-}
-
-BakedOpState* BakedOpState::tryConstructUnbounded(LinearAllocator& allocator, Snapshot& snapshot,
- const RecordedOp& recordedOp) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp);
-}
-
-BakedOpState* BakedOpState::tryStrokeableOpConstruct(LinearAllocator& allocator, Snapshot& snapshot,
- const RecordedOp& recordedOp,
- StrokeBehavior strokeBehavior,
- bool expandForPathTexture) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- bool expandForStroke =
- (strokeBehavior == StrokeBehavior::Forced ||
- (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style));
-
- BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
- allocator, snapshot, recordedOp, expandForStroke, expandForPathTexture);
- if (bakedState->computedState.clippedBounds.isEmpty()) {
- // bounds are empty, so op is rejected
- // NOTE: this won't succeed if a clip was allocated
- allocator.rewindIfLastAlloc(bakedState);
- return nullptr;
- }
- return bakedState;
-}
-
-BakedOpState* BakedOpState::tryShadowOpConstruct(LinearAllocator& allocator, Snapshot& snapshot,
- const ShadowOp* shadowOpPtr) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
-
- // clip isn't empty, so construct the op
- return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr);
-}
-
-BakedOpState* BakedOpState::directConstruct(LinearAllocator& allocator, const ClipRect* clip,
- const Rect& dstRect, const RecordedOp& recordedOp) {
- return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp);
-}
-
-void BakedOpState::setupOpacity(const SkPaint* paint) {
- computedState.opaqueOverClippedBounds = computedState.transform.isSimple() &&
- computedState.clipState->mode == ClipMode::Rectangle &&
- MathUtils::areEqual(alpha, 1.0f) &&
- !roundRectClipState && PaintUtils::isOpaquePaint(paint);
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
deleted file mode 100644
index c74475516cdc..000000000000
--- a/libs/hwui/BakedOpState.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_BAKED_OP_STATE_H
-#define ANDROID_HWUI_BAKED_OP_STATE_H
-
-#include "Matrix.h"
-#include "RecordedOp.h"
-#include "Rect.h"
-#include "Snapshot.h"
-
-namespace android {
-namespace uirenderer {
-
-namespace OpClipSideFlags {
-enum {
- None = 0x0,
- Left = 0x1,
- Top = 0x2,
- Right = 0x4,
- Bottom = 0x8,
- Full = 0xF,
- // ConservativeFull = 0x1F needed?
-};
-}
-
-/**
- * Holds a list of BakedOpStates of ops that can be drawn together
- */
-struct MergedBakedOpList {
- const BakedOpState* const* states;
- size_t count;
- int clipSideFlags;
- Rect clip;
-};
-
-/**
- * Holds the resolved clip, transform, and bounds of a recordedOp, when replayed with a snapshot
- */
-class ResolvedRenderState {
-public:
- ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
- const RecordedOp& recordedOp, bool expandForStroke,
- bool expandForPathTexture);
-
- // Constructor for unbounded ops *with* transform/clip
- ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
- const Matrix4& localTransform, const ClipBase* localClip);
-
- // Constructor for unbounded ops without transform/clip (namely shadows)
- ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot);
-
- // Constructor for primitive ops provided clip, and no transform
- ResolvedRenderState(const ClipRect* viewportRect, const Rect& dstRect);
-
- Rect computeLocalSpaceClip() const {
- Matrix4 inverse;
- inverse.loadInverse(transform);
-
- Rect outClip(clipRect());
- inverse.mapRect(outClip);
- return outClip;
- }
-
- const Rect& clipRect() const { return clipState->rect; }
-
- bool requiresClip() const {
- return clipSideFlags != OpClipSideFlags::None ||
- CC_UNLIKELY(clipState->mode != ClipMode::Rectangle);
- }
-
- // returns the clip if it's needed to draw the operation, otherwise nullptr
- const ClipBase* getClipIfNeeded() const { return requiresClip() ? clipState : nullptr; }
-
- Matrix4 transform;
- const ClipBase* clipState = nullptr;
- Rect clippedBounds;
- int clipSideFlags = 0;
- const SkPath* localProjectionPathMask = nullptr;
- bool opaqueOverClippedBounds = false;
-};
-
-/**
- * Self-contained op wrapper, containing all resolved state required to draw the op.
- *
- * Stashed pointers within all point to longer lived objects, with no ownership implied.
- */
-class BakedOpState {
-public:
- static BakedOpState* tryConstruct(LinearAllocator& allocator, Snapshot& snapshot,
- const RecordedOp& recordedOp);
-
- static BakedOpState* tryConstructUnbounded(LinearAllocator& allocator, Snapshot& snapshot,
- const RecordedOp& recordedOp);
-
- enum class StrokeBehavior {
- // stroking is forced, regardless of style on paint (such as for lines)
- Forced,
- // stroking is defined by style on paint
- StyleDefined,
- };
-
- static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator, Snapshot& snapshot,
- const RecordedOp& recordedOp,
- StrokeBehavior strokeBehavior,
- bool expandForPathTexture);
-
- static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator, Snapshot& snapshot,
- const ShadowOp* shadowOpPtr);
-
- static BakedOpState* directConstruct(LinearAllocator& allocator, const ClipRect* clip,
- const Rect& dstRect, const RecordedOp& recordedOp);
-
- // Set opaqueOverClippedBounds. If this method isn't called, the op is assumed translucent.
- void setupOpacity(const SkPaint* paint);
-
- // computed state:
- ResolvedRenderState computedState;
-
- // simple state (straight pointer/value storage):
- const float alpha;
- const RoundRectClipState* roundRectClipState;
- const RecordedOp* op;
-
-private:
- friend class LinearAllocator;
-
- BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp,
- bool expandForStroke, bool expandForPathTexture)
- : computedState(allocator, snapshot, recordedOp, expandForStroke, expandForPathTexture)
- , alpha(snapshot.alpha)
- , roundRectClipState(snapshot.roundRectClipState)
- , op(&recordedOp) {}
-
- // TODO: fix this brittleness
- BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp)
- : computedState(allocator, snapshot, recordedOp.localMatrix, recordedOp.localClip)
- , alpha(snapshot.alpha)
- , roundRectClipState(snapshot.roundRectClipState)
- , op(&recordedOp) {}
-
- BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr)
- : computedState(allocator, snapshot)
- , alpha(snapshot.alpha)
- , roundRectClipState(snapshot.roundRectClipState)
- , op(shadowOpPtr) {}
-
- BakedOpState(const ClipRect* clipRect, const Rect& dstRect, const RecordedOp& recordedOp)
- : computedState(clipRect, dstRect)
- , alpha(1.0f)
- , roundRectClipState(nullptr)
- , op(&recordedOp) {}
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_BAKED_OP_STATE_H
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
deleted file mode 100644
index 3c774a3313d2..000000000000
--- a/libs/hwui/Caches.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Caches.h"
-
-#include "GammaFontRenderer.h"
-#include "GlLayer.h"
-#include "Properties.h"
-#include "ShadowTessellator.h"
-#include "renderstate/RenderState.h"
-#ifdef BUGREPORT_FONT_CACHE_USAGE
-#include "font/FontCacheHistoryTracker.h"
-#endif
-#include "utils/GLUtils.h"
-
-#include <cutils/properties.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-namespace android {
-namespace uirenderer {
-
-Caches* Caches::sInstance = nullptr;
-
-///////////////////////////////////////////////////////////////////////////////
-// Macros
-///////////////////////////////////////////////////////////////////////////////
-
-#if DEBUG_CACHE_FLUSH
-#define FLUSH_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-#define FLUSH_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-Caches::Caches(RenderState& renderState)
- : gradientCache(extensions())
- , patchCache(renderState)
- , programCache(extensions())
- , mRenderState(&renderState)
- , mInitialized(false) {
- INIT_LOGD("Creating OpenGL renderer caches");
- init();
- initConstraints();
- initStaticProperties();
- initExtensions();
-}
-
-bool Caches::init() {
- if (mInitialized) return false;
-
- ATRACE_NAME("Caches::init");
-
- mRegionMesh = nullptr;
- mProgram = nullptr;
-
- mInitialized = true;
-
- mPixelBufferState = new PixelBufferState();
- mTextureState = new TextureState();
- mTextureState->constructTexture(*this);
-
- return true;
-}
-
-void Caches::initExtensions() {
- if (extensions().hasDebugMarker()) {
- eventMark = glInsertEventMarkerEXT;
-
- startMark = glPushGroupMarkerEXT;
- endMark = glPopGroupMarkerEXT;
- } else {
- eventMark = eventMarkNull;
- startMark = startMarkNull;
- endMark = endMarkNull;
- }
-}
-
-void Caches::initConstraints() {
- maxTextureSize = DeviceInfo::get()->maxTextureSize();
-}
-
-void Caches::initStaticProperties() {
- // OpenGL ES 3.0+ specific features
- gpuPixelBuffersEnabled = extensions().hasPixelBufferObjects() &&
- property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true);
-}
-
-void Caches::terminate() {
- if (!mInitialized) return;
- mRegionMesh.reset(nullptr);
-
- fboCache.clear();
-
- programCache.clear();
- mProgram = nullptr;
-
- patchCache.clear();
-
- clearGarbage();
-
- delete mPixelBufferState;
- mPixelBufferState = nullptr;
- delete mTextureState;
- mTextureState = nullptr;
- mInitialized = false;
-}
-
-void Caches::setProgram(const ProgramDescription& description) {
- setProgram(programCache.get(description));
-}
-
-void Caches::setProgram(Program* program) {
- if (!program || !program->isInUse()) {
- if (mProgram) {
- mProgram->remove();
- }
- if (program) {
- program->use();
- }
- mProgram = program;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Debug
-///////////////////////////////////////////////////////////////////////////////
-
-uint32_t Caches::getOverdrawColor(uint32_t amount) const {
- static uint32_t sOverdrawColors[2][4] = {{0x2f0000ff, 0x2f00ff00, 0x3fff0000, 0x7fff0000},
- {0x2f0000ff, 0x4fffff00, 0x5fff8ad8, 0x7fff0000}};
- if (amount < 1) amount = 1;
- if (amount > 4) amount = 4;
-
- int overdrawColorIndex = static_cast<int>(Properties::overdrawColorSet);
- return sOverdrawColors[overdrawColorIndex][amount - 1];
-}
-
-void Caches::dumpMemoryUsage() {
- String8 stringLog;
- dumpMemoryUsage(stringLog);
- ALOGD("%s", stringLog.string());
-}
-
-void Caches::dumpMemoryUsage(String8& log) {
- uint32_t total = 0;
- log.appendFormat("Current memory usage / total memory usage (bytes):\n");
- log.appendFormat(" TextureCache %8d / %8d\n", textureCache.getSize(),
- textureCache.getMaxSize());
- if (mRenderState) {
- int memused = 0;
- for (std::set<Layer*>::iterator it = mRenderState->mActiveLayers.begin();
- it != mRenderState->mActiveLayers.end(); it++) {
- const Layer* layer = *it;
- LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL);
- const GlLayer* glLayer = static_cast<const GlLayer*>(layer);
- log.appendFormat(" GlLayer size %dx%d; texid=%u refs=%d\n", layer->getWidth(),
- layer->getHeight(), glLayer->getTextureId(), layer->getStrongCount());
- memused += layer->getWidth() * layer->getHeight() * 4;
- }
- log.appendFormat(" Layers total %8d (numLayers = %zu)\n", memused,
- mRenderState->mActiveLayers.size());
- total += memused;
- }
- log.appendFormat(" RenderBufferCache %8d / %8d\n", renderBufferCache.getSize(),
- renderBufferCache.getMaxSize());
- log.appendFormat(" GradientCache %8d / %8d\n", gradientCache.getSize(),
- gradientCache.getMaxSize());
- log.appendFormat(" PathCache %8d / %8d\n", pathCache.getSize(),
- pathCache.getMaxSize());
- log.appendFormat(" TessellationCache %8d / %8d\n", tessellationCache.getSize(),
- tessellationCache.getMaxSize());
- log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
- dropShadowCache.getMaxSize());
- log.appendFormat(" PatchCache %8d / %8d\n", patchCache.getSize(),
- patchCache.getMaxSize());
-
- fontRenderer.dumpMemoryUsage(log);
-
- log.appendFormat("Other:\n");
- log.appendFormat(" FboCache %8d / %8d\n", fboCache.getSize(),
- fboCache.getMaxSize());
-
- total += textureCache.getSize();
- total += renderBufferCache.getSize();
- total += gradientCache.getSize();
- total += pathCache.getSize();
- total += tessellationCache.getSize();
- total += dropShadowCache.getSize();
- total += patchCache.getSize();
- total += fontRenderer.getSize();
-
- log.appendFormat("Total memory usage:\n");
- log.appendFormat(" %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
-
-#ifdef BUGREPORT_FONT_CACHE_USAGE
- fontRenderer.getFontRenderer().historyTracker().dump(log);
-#endif
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Memory management
-///////////////////////////////////////////////////////////////////////////////
-
-void Caches::clearGarbage() {
- pathCache.clearGarbage();
- patchCache.clearGarbage();
-}
-
-void Caches::flush(FlushMode mode) {
- FLUSH_LOGD("Flushing caches (mode %d)", mode);
-
- switch (mode) {
- case FlushMode::Full:
- textureCache.clear();
- patchCache.clear();
- dropShadowCache.clear();
- gradientCache.clear();
- fontRenderer.clear();
- fboCache.clear();
- // fall through
- case FlushMode::Moderate:
- fontRenderer.flush();
- textureCache.flush();
- pathCache.clear();
- tessellationCache.clear();
- // fall through
- case FlushMode::Layers:
- renderBufferCache.clear();
- break;
- }
-
- clearGarbage();
- glFinish();
- // Errors during cleanup should be considered non-fatal, dump them and
- // and move on. TODO: All errors or just errors like bad surface?
- GLUtils::dumpGLErrors();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Regions
-///////////////////////////////////////////////////////////////////////////////
-
-TextureVertex* Caches::getRegionMesh() {
- // Create the mesh, 2 triangles and 4 vertices per rectangle in the region
- if (!mRegionMesh) {
- mRegionMesh.reset(new TextureVertex[kMaxNumberOfQuads * 4]);
- }
-
- return mRegionMesh.get();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Temporary Properties
-///////////////////////////////////////////////////////////////////////////////
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
deleted file mode 100644
index 97328324df04..000000000000
--- a/libs/hwui/Caches.h
+++ /dev/null
@@ -1,201 +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.
- */
-
-#pragma once
-
-#include "DeviceInfo.h"
-#include "Extensions.h"
-#include "FboCache.h"
-#include "GammaFontRenderer.h"
-#include "GradientCache.h"
-#include "PatchCache.h"
-#include "PathCache.h"
-#include "ProgramCache.h"
-#include "RenderBufferCache.h"
-#include "ResourceCache.h"
-#include "TessellationCache.h"
-#include "TextDropShadowCache.h"
-#include "TextureCache.h"
-#include "renderstate/PixelBufferState.h"
-#include "renderstate/TextureState.h"
-#include "thread/TaskManager.h"
-#include "thread/TaskProcessor.h"
-
-#include <memory>
-#include <vector>
-
-#include <GLES3/gl3.h>
-
-#include <utils/KeyedVector.h>
-
-#include <cutils/compiler.h>
-
-#include <SkPath.h>
-
-#include <vector>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Caches
-///////////////////////////////////////////////////////////////////////////////
-
-class RenderNode;
-class RenderState;
-
-class ANDROID_API Caches {
-public:
- static Caches& createInstance(RenderState& renderState) {
- LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted");
- sInstance = new Caches(renderState);
- return *sInstance;
- }
-
- static Caches& getInstance() {
- LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created");
- return *sInstance;
- }
-
- static bool hasInstance() { return sInstance != nullptr; }
-
-private:
- explicit Caches(RenderState& renderState);
- static Caches* sInstance;
-
-public:
- enum class FlushMode { Layers = 0, Moderate, Full };
-
- /**
- * Initialize caches.
- */
- bool init();
-
- bool isInitialized() { return mInitialized; }
-
- /**
- * Flush the cache.
- *
- * @param mode Indicates how much of the cache should be flushed
- */
- void flush(FlushMode mode);
-
- /**
- * Destroys all resources associated with this cache. This should
- * be called after a flush(FlushMode::Full).
- */
- void terminate();
-
- /**
- * Returns a non-premultiplied ARGB color for the specified
- * amount of overdraw (1 for 1x, 2 for 2x, etc.)
- */
- uint32_t getOverdrawColor(uint32_t amount) const;
-
- /**
- * Call this on each frame to ensure that garbage is deleted from
- * GPU memory.
- */
- void clearGarbage();
-
- /**
- * Can be used to delete a layer from a non EGL thread.
- */
- void deleteLayerDeferred(Layer* layer);
-
- /**
- * Returns the mesh used to draw regions. Calling this method will
- * bind a VBO of type GL_ELEMENT_ARRAY_BUFFER that contains the
- * indices for the region mesh.
- */
- TextureVertex* getRegionMesh();
-
- /**
- * Returns the GL RGBA internal format to use for the current device
- * If the device supports linear blending and needSRGB is true,
- * this function returns GL_SRGB8_ALPHA8, otherwise it returns GL_RGBA
- */
- constexpr GLint rgbaInternalFormat(bool needSRGB = true) const {
- return extensions().hasLinearBlending() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA;
- }
-
- /**
- * Displays the memory usage of each cache and the total sum.
- */
- void dumpMemoryUsage();
- void dumpMemoryUsage(String8& log);
-
- // Misc
- GLint maxTextureSize;
-
-public:
- TextureCache textureCache;
- RenderBufferCache renderBufferCache;
- GradientCache gradientCache;
- PatchCache patchCache;
- PathCache pathCache;
- ProgramCache programCache;
- TessellationCache tessellationCache;
- TextDropShadowCache dropShadowCache;
- FboCache fboCache;
-
- GammaFontRenderer fontRenderer;
-
- TaskManager tasks;
-
- bool gpuPixelBuffersEnabled;
-
- // Debug methods
- PFNGLINSERTEVENTMARKEREXTPROC eventMark;
- PFNGLPUSHGROUPMARKEREXTPROC startMark;
- PFNGLPOPGROUPMARKEREXTPROC endMark;
-
- void setProgram(const ProgramDescription& description);
- void setProgram(Program* program);
-
- const Extensions& extensions() const { return DeviceInfo::get()->extensions(); }
- Program& program() { return *mProgram; }
- PixelBufferState& pixelBufferState() { return *mPixelBufferState; }
- TextureState& textureState() { return *mTextureState; }
-
-private:
- void initExtensions();
- void initConstraints();
- void initStaticProperties();
-
- static void eventMarkNull(GLsizei length, const GLchar* marker) {}
- static void startMarkNull(GLsizei length, const GLchar* marker) {}
- static void endMarkNull() {}
-
- RenderState* mRenderState;
-
- // Used to render layers
- std::unique_ptr<TextureVertex[]> mRegionMesh;
-
- mutable Mutex mGarbageLock;
- std::vector<Layer*> mLayerGarbage;
-
- bool mInitialized;
-
- // TODO: move below to RenderState
- PixelBufferState* mPixelBufferState = nullptr;
- TextureState* mTextureState = nullptr;
- Program* mProgram = nullptr; // note: object owned by ProgramCache
-
-}; // class Caches
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
deleted file mode 100644
index d18c4abde7f2..000000000000
--- a/libs/hwui/CanvasState.cpp
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "CanvasState.h"
-#include "hwui/Canvas.h"
-#include "utils/MathUtils.h"
-
-namespace android {
-namespace uirenderer {
-
-CanvasState::CanvasState(CanvasStateClient& renderer)
- : mWidth(-1), mHeight(-1), mSaveCount(1), mCanvas(renderer), mSnapshot(&mFirstSnapshot) {}
-
-CanvasState::~CanvasState() {
- // First call freeSnapshot on all but mFirstSnapshot
- // to invoke all the dtors
- freeAllSnapshots();
-
- // Now actually release the memory
- while (mSnapshotPool) {
- void* temp = mSnapshotPool;
- mSnapshotPool = mSnapshotPool->previous;
- free(temp);
- }
-}
-
-void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHeight) {
- if (mWidth != viewportWidth || mHeight != viewportHeight) {
- mWidth = viewportWidth;
- mHeight = viewportHeight;
- mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
- mCanvas.onViewportInitialized();
- }
-
- freeAllSnapshots();
- mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
- mSnapshot->setRelativeLightCenter(Vector3());
- mSaveCount = 1;
-}
-
-void CanvasState::initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft,
- float clipTop, float clipRight, float clipBottom,
- const Vector3& lightCenter) {
- if (mWidth != viewportWidth || mHeight != viewportHeight) {
- mWidth = viewportWidth;
- mHeight = viewportHeight;
- mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
- mCanvas.onViewportInitialized();
- }
-
- freeAllSnapshots();
- mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
- mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
- mSnapshot->fbo = mCanvas.getTargetFbo();
- mSnapshot->setRelativeLightCenter(lightCenter);
- mSaveCount = 1;
-}
-
-Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) {
- void* memory;
- if (mSnapshotPool) {
- memory = mSnapshotPool;
- mSnapshotPool = mSnapshotPool->previous;
- mSnapshotPoolCount--;
- } else {
- memory = malloc(sizeof(Snapshot));
- }
- return new (memory) Snapshot(previous, savecount);
-}
-
-void CanvasState::freeSnapshot(Snapshot* snapshot) {
- snapshot->~Snapshot();
- // Arbitrary number, just don't let this grown unbounded
- if (mSnapshotPoolCount > 10) {
- free((void*)snapshot);
- } else {
- snapshot->previous = mSnapshotPool;
- mSnapshotPool = snapshot;
- mSnapshotPoolCount++;
- }
-}
-
-void CanvasState::freeAllSnapshots() {
- while (mSnapshot != &mFirstSnapshot) {
- Snapshot* temp = mSnapshot;
- mSnapshot = mSnapshot->previous;
- freeSnapshot(temp);
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Save (layer)
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Guaranteed to save without side-effects
- *
- * This approach, here and in restoreSnapshot(), allows subclasses to directly manipulate the save
- * stack, and ensures restoreToCount() doesn't call back into subclass overrides.
- */
-int CanvasState::saveSnapshot(int flags) {
- mSnapshot = allocSnapshot(mSnapshot, flags);
- return mSaveCount++;
-}
-
-int CanvasState::save(int flags) {
- return saveSnapshot(flags);
-}
-
-/**
- * Guaranteed to restore without side-effects.
- */
-void CanvasState::restoreSnapshot() {
- Snapshot* toRemove = mSnapshot;
- Snapshot* toRestore = mSnapshot->previous;
-
- mSaveCount--;
- mSnapshot = toRestore;
-
- // subclass handles restore implementation
- mCanvas.onSnapshotRestored(*toRemove, *toRestore);
-
- freeSnapshot(toRemove);
-}
-
-void CanvasState::restore() {
- if (mSaveCount > 1) {
- restoreSnapshot();
- }
-}
-
-void CanvasState::restoreToCount(int saveCount) {
- if (saveCount < 1) saveCount = 1;
-
- while (mSaveCount > saveCount) {
- restoreSnapshot();
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Matrix
-///////////////////////////////////////////////////////////////////////////////
-
-void CanvasState::getMatrix(SkMatrix* matrix) const {
- mSnapshot->transform->copyTo(*matrix);
-}
-
-void CanvasState::translate(float dx, float dy, float dz) {
- mSnapshot->transform->translate(dx, dy, dz);
-}
-
-void CanvasState::rotate(float degrees) {
- mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f);
-}
-
-void CanvasState::scale(float sx, float sy) {
- mSnapshot->transform->scale(sx, sy, 1.0f);
-}
-
-void CanvasState::skew(float sx, float sy) {
- mSnapshot->transform->skew(sx, sy);
-}
-
-void CanvasState::setMatrix(const SkMatrix& matrix) {
- mSnapshot->transform->load(matrix);
-}
-
-void CanvasState::setMatrix(const Matrix4& matrix) {
- *(mSnapshot->transform) = matrix;
-}
-
-void CanvasState::concatMatrix(const SkMatrix& matrix) {
- mat4 transform(matrix);
- mSnapshot->transform->multiply(transform);
-}
-
-void CanvasState::concatMatrix(const Matrix4& matrix) {
- mSnapshot->transform->multiply(matrix);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Clip
-///////////////////////////////////////////////////////////////////////////////
-
-bool CanvasState::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
- mSnapshot->clip(Rect(left, top, right, bottom), op);
- return !mSnapshot->clipIsEmpty();
-}
-
-bool CanvasState::clipPath(const SkPath* path, SkClipOp op) {
- mSnapshot->clipPath(*path, op);
- return !mSnapshot->clipIsEmpty();
-}
-
-void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
- Rect bounds;
- float radius;
- if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported
-
- bool outlineIsRounded = MathUtils::isPositive(radius);
- if (!outlineIsRounded || currentTransform()->isSimple()) {
- // TODO: consider storing this rect separately, so that this can't be replaced with clip ops
- clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkClipOp::kIntersect);
- }
- if (outlineIsRounded) {
- setClippingRoundRect(allocator, bounds, radius, false);
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Quick Rejection
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Calculates whether content drawn within the passed bounds would be outside of, or intersect with
- * the clipRect. Does not modify the scissor.
- *
- * @param clipRequired if not null, will be set to true if element intersects clip
- * (and wasn't rejected)
- *
- * @param snapOut if set, the geometry will be treated as having an AA ramp.
- * See Rect::snapGeometryToPixelBoundaries()
- */
-bool CanvasState::calculateQuickRejectForScissor(float left, float top, float right, float bottom,
- bool* clipRequired, bool* roundRectClipRequired,
- bool snapOut) const {
- if (bottom <= top || right <= left) {
- return true;
- }
-
- Rect r(left, top, right, bottom);
- currentTransform()->mapRect(r);
- r.snapGeometryToPixelBoundaries(snapOut);
-
- Rect clipRect(currentRenderTargetClip());
- clipRect.snapToPixelBoundaries();
-
- if (!clipRect.intersects(r)) return true;
-
- // clip is required if geometry intersects clip rect
- if (clipRequired) {
- *clipRequired = !clipRect.contains(r);
- }
-
- // round rect clip is required if RR clip exists, and geometry intersects its corners
- if (roundRectClipRequired) {
- *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr &&
- mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r);
- }
- return false;
-}
-
-bool CanvasState::quickRejectConservative(float left, float top, float right, float bottom) const {
- if (bottom <= top || right <= left) {
- return true;
- }
-
- Rect r(left, top, right, bottom);
- currentTransform()->mapRect(r);
- r.roundOut(); // rounded out to be conservative
-
- Rect clipRect(currentRenderTargetClip());
- clipRect.snapToPixelBoundaries();
-
- if (!clipRect.intersects(r)) return true;
-
- return false;
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
deleted file mode 100644
index 9ac35ff47dab..000000000000
--- a/libs/hwui/CanvasState.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Snapshot.h"
-
-#include <SkClipOp.h>
-#include <SkMatrix.h>
-#include <SkPath.h>
-#include <SkRegion.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Abstract base class for any class containing CanvasState.
- * Defines three mandatory callbacks.
- */
-class CanvasStateClient {
-public:
- CanvasStateClient() {}
- virtual ~CanvasStateClient() {}
-
- /**
- * Callback allowing embedder to take actions in the middle of a
- * setViewport() call.
- */
- virtual void onViewportInitialized() = 0;
-
- /**
- * Callback allowing embedder to take actions in the middle of a
- * restore() call. May be called several times sequentially.
- */
- virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) = 0;
-
- /**
- * Allows subclasses to control what value is stored in snapshot's
- * fbo field in * initializeSaveStack.
- */
- virtual GLuint getTargetFbo() const = 0;
-
-}; // class CanvasStateClient
-
-/**
- * Implements Canvas state methods on behalf of Renderers.
- *
- * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the
- * Renderer interface. Drawing and recording classes that include a CanvasState will have
- * different use cases:
- *
- * Drawing code maintaining canvas state (e.g. FrameBuilder) can query attributes (such as
- * transform) or hook into changes (e.g. save/restore) with minimal surface area for manipulating
- * the stack itself.
- *
- * Recording code maintaining canvas state (e.g. RecordingCanvas) can both record and pass
- * through state operations to CanvasState, so that not only will querying operations work
- * (getClip/Matrix), but so that quickRejection can also be used.
- */
-
-class CanvasState {
-public:
- explicit CanvasState(CanvasStateClient& renderer);
- ~CanvasState();
-
- /**
- * Initializes the first snapshot, computing the projection matrix,
- * and stores the dimensions of the render target.
- */
- void initializeRecordingSaveStack(int viewportWidth, int viewportHeight);
-
- /**
- * Initializes the first snapshot, computing the projection matrix,
- * and stores the dimensions of the render target.
- */
- void initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft, float clipTop,
- float clipRight, float clipBottom, const Vector3& lightCenter);
-
- bool hasRectToRectTransform() const { return CC_LIKELY(currentTransform()->rectToRect()); }
-
- // Save (layer)
- int getSaveCount() const { return mSaveCount; }
- int save(int flags);
- void restore();
- void restoreToCount(int saveCount);
-
- // Save/Restore without side-effects
- int saveSnapshot(int flags);
- void restoreSnapshot();
-
- // Matrix
- void getMatrix(SkMatrix* outMatrix) const;
- void translate(float dx, float dy, float dz = 0.0f);
- void rotate(float degrees);
- void scale(float sx, float sy);
- void skew(float sx, float sy);
-
- void setMatrix(const SkMatrix& matrix);
- void setMatrix(const Matrix4& matrix); // internal only convenience method
- void concatMatrix(const SkMatrix& matrix);
- void concatMatrix(const Matrix4& matrix); // internal only convenience method
-
- // Clip
- const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); }
- const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); }
-
- bool quickRejectConservative(float left, float top, float right, float bottom) const;
-
- bool clipRect(float left, float top, float right, float bottom, SkClipOp op);
- bool clipPath(const SkPath* path, SkClipOp op);
-
- /**
- * Sets a "clipping outline", which is independent from the regular clip.
- * Currently only supports rectangles or rounded rectangles; passing in a
- * more complicated outline fails silently. Replaces any previous clipping
- * outline.
- */
- void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
- void setClippingRoundRect(LinearAllocator& allocator, const Rect& rect, float radius,
- bool highPriority = true) {
- mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority);
- }
- void setProjectionPathMask(const SkPath* path) { mSnapshot->setProjectionPathMask(path); }
-
- /**
- * Returns true if drawing in the rectangle (left, top, right, bottom)
- * will be clipped out. Is conservative: might return false when subpixel-
- * perfect tests would return true.
- */
- bool calculateQuickRejectForScissor(float left, float top, float right, float bottom,
- bool* clipRequired, bool* roundRectClipRequired,
- bool snapOut) const;
-
- void scaleAlpha(float alpha) { mSnapshot->alpha *= alpha; }
-
- inline const mat4* currentTransform() const { return currentSnapshot()->transform; }
- inline const Rect& currentRenderTargetClip() const {
- return currentSnapshot()->getRenderTargetClip();
- }
- inline int currentFlags() const { return currentSnapshot()->flags; }
- const Vector3& currentLightCenter() const {
- return currentSnapshot()->getRelativeLightCenter();
- }
- int getViewportWidth() const { return currentSnapshot()->getViewportWidth(); }
- int getViewportHeight() const { return currentSnapshot()->getViewportHeight(); }
- int getWidth() const { return mWidth; }
- int getHeight() const { return mHeight; }
- bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); }
-
- inline const Snapshot* currentSnapshot() const { return mSnapshot; }
- inline Snapshot* writableSnapshot() { return mSnapshot; }
- inline const Snapshot* firstSnapshot() const { return &mFirstSnapshot; }
-
-private:
- Snapshot* allocSnapshot(Snapshot* previous, int savecount);
- void freeSnapshot(Snapshot* snapshot);
- void freeAllSnapshots();
-
- /// Dimensions of the drawing surface
- int mWidth, mHeight;
-
- /// Number of saved states
- int mSaveCount;
-
- /// Base state
- Snapshot mFirstSnapshot;
-
- /// Host providing callbacks
- CanvasStateClient& mCanvas;
-
- /// Current state
- Snapshot* mSnapshot;
-
- // Pool of allocated snapshots to re-use
- // NOTE: The dtors have already been invoked!
- Snapshot* mSnapshotPool = nullptr;
- int mSnapshotPoolCount = 0;
-
-}; // class CanvasState
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
new file mode 100644
index 000000000000..06e937ab66f4
--- /dev/null
+++ b/libs/hwui/CanvasTransform.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 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 "CanvasTransform.h"
+#include "Properties.h"
+#include "utils/Color.h"
+
+#include <SkColorFilter.h>
+#include <SkGradientShader.h>
+#include <SkPaint.h>
+#include <SkShader.h>
+#include <ui/ColorSpace.h>
+
+#include <algorithm>
+#include <cmath>
+
+#include <log/log.h>
+#include <SkHighContrastFilter.h>
+
+namespace android::uirenderer {
+
+static SkColor makeLight(SkColor color) {
+ Lab lab = sRGBToLab(color);
+ float invertedL = std::min(110 - lab.L, 100.0f);
+ if (invertedL > lab.L) {
+ lab.L = invertedL;
+ return LabToSRGB(lab, SkColorGetA(color));
+ } else {
+ return color;
+ }
+}
+
+static SkColor makeDark(SkColor color) {
+ Lab lab = sRGBToLab(color);
+ float invertedL = std::min(110 - lab.L, 100.0f);
+ if (invertedL < lab.L) {
+ lab.L = invertedL;
+ return LabToSRGB(lab, SkColorGetA(color));
+ } else {
+ return color;
+ }
+}
+
+static SkColor transformColor(ColorTransform transform, SkColor color) {
+ switch (transform) {
+ case ColorTransform::Light:
+ return makeLight(color);
+ case ColorTransform::Dark:
+ return makeDark(color);
+ default:
+ return color;
+ }
+}
+
+static void applyColorTransform(ColorTransform transform, SkPaint& paint) {
+ if (transform == ColorTransform::None) return;
+
+ SkColor newColor = transformColor(transform, paint.getColor());
+ paint.setColor(newColor);
+
+ if (paint.getShader()) {
+ SkShader::GradientInfo info;
+ std::array<SkColor, 10> _colorStorage;
+ std::array<SkScalar, _colorStorage.size()> _offsetStorage;
+ info.fColorCount = _colorStorage.size();
+ info.fColors = _colorStorage.data();
+ info.fColorOffsets = _offsetStorage.data();
+ SkShader::GradientType type = paint.getShader()->asAGradient(&info);
+
+ if (info.fColorCount <= 10) {
+ switch (type) {
+ case SkShader::kLinear_GradientType:
+ for (int i = 0; i < info.fColorCount; i++) {
+ info.fColors[i] = transformColor(transform, info.fColors[i]);
+ }
+ paint.setShader(SkGradientShader::MakeLinear(info.fPoint, info.fColors,
+ info.fColorOffsets, info.fColorCount,
+ info.fTileMode, info.fGradientFlags, nullptr));
+ break;
+ default:break;
+ }
+
+ }
+ }
+
+ if (paint.getColorFilter()) {
+ SkBlendMode mode;
+ SkColor color;
+ // TODO: LRU this or something to avoid spamming new color mode filters
+ if (paint.getColorFilter()->asColorMode(&color, &mode)) {
+ color = transformColor(transform, color);
+ paint.setColorFilter(SkColorFilter::MakeModeFilter(color, mode));
+ }
+ }
+}
+
+static BitmapPalette paletteForColorHSV(SkColor color) {
+ float hsv[3];
+ SkColorToHSV(color, hsv);
+ return hsv[2] >= .5f ? BitmapPalette::Light : BitmapPalette::Dark;
+}
+
+static BitmapPalette filterPalette(const SkPaint* paint, BitmapPalette palette) {
+ if (palette == BitmapPalette::Unknown || !paint || !paint->getColorFilter()) {
+ return palette;
+ }
+
+ SkColor color = palette == BitmapPalette::Light ? SK_ColorWHITE : SK_ColorBLACK;
+ color = paint->getColorFilter()->filterColor(color);
+ return paletteForColorHSV(color);
+}
+
+bool transformPaint(ColorTransform transform, SkPaint* paint) {
+ // TODO
+ applyColorTransform(transform, *paint);
+ return true;
+}
+
+bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette) {
+ palette = filterPalette(paint, palette);
+ bool shouldInvert = false;
+ if (palette == BitmapPalette::Light && transform == ColorTransform::Dark) {
+ shouldInvert = true;
+ }
+ if (palette == BitmapPalette::Dark && transform == ColorTransform::Light) {
+ shouldInvert = true;
+ }
+ if (shouldInvert) {
+ SkHighContrastConfig config;
+ config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
+ paint->setColorFilter(SkHighContrastFilter::Make(config)->makeComposed(paint->refColorFilter()));
+ }
+ return shouldInvert;
+}
+
+}; // namespace android::uirenderer \ No newline at end of file
diff --git a/libs/hwui/CanvasTransform.h b/libs/hwui/CanvasTransform.h
new file mode 100644
index 000000000000..e723d645e05e
--- /dev/null
+++ b/libs/hwui/CanvasTransform.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "hwui/Bitmap.h"
+
+#include <SkCanvas.h>
+#include <SkPaintFilterCanvas.h>
+
+#include <memory>
+
+namespace android::uirenderer {
+
+enum class UsageHint {
+ Unknown = 0,
+ Background = 1,
+ Foreground = 2,
+};
+
+enum class ColorTransform {
+ None,
+ Light,
+ Dark,
+};
+
+// True if the paint was modified, false otherwise
+bool transformPaint(ColorTransform transform, SkPaint* paint);
+
+bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette);
+
+} // namespace android::uirenderer; \ No newline at end of file
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
deleted file mode 100644
index 27d93cfa0391..000000000000
--- a/libs/hwui/ClipArea.cpp
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "ClipArea.h"
-
-#include "utils/LinearAllocator.h"
-
-#include <SkPath.h>
-#include <limits>
-#include <type_traits>
-
-namespace android {
-namespace uirenderer {
-
-static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
- Vertex v = {x, y};
- transform.mapPoint(v.x, v.y);
- transformedBounds.expandToCover(v.x, v.y);
-}
-
-Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) {
- const float kMinFloat = std::numeric_limits<float>::lowest();
- const float kMaxFloat = std::numeric_limits<float>::max();
- Rect transformedBounds = {kMaxFloat, kMaxFloat, kMinFloat, kMinFloat};
- handlePoint(transformedBounds, transform, r.left, r.top);
- handlePoint(transformedBounds, transform, r.right, r.top);
- handlePoint(transformedBounds, transform, r.left, r.bottom);
- handlePoint(transformedBounds, transform, r.right, r.bottom);
- return transformedBounds;
-}
-
-void ClipBase::dump() const {
- ALOGD("mode %d" RECT_STRING, mode, RECT_ARGS(rect));
-}
-
-/*
- * TransformedRectangle
- */
-
-TransformedRectangle::TransformedRectangle() {}
-
-TransformedRectangle::TransformedRectangle(const Rect& bounds, const Matrix4& transform)
- : mBounds(bounds), mTransform(transform) {}
-
-bool TransformedRectangle::canSimplyIntersectWith(const TransformedRectangle& other) const {
- return mTransform == other.mTransform;
-}
-
-void TransformedRectangle::intersectWith(const TransformedRectangle& other) {
- mBounds.doIntersect(other.mBounds);
-}
-
-bool TransformedRectangle::isEmpty() const {
- return mBounds.isEmpty();
-}
-
-/*
- * RectangleList
- */
-
-RectangleList::RectangleList() : mTransformedRectanglesCount(0) {}
-
-bool RectangleList::isEmpty() const {
- if (mTransformedRectanglesCount < 1) {
- return true;
- }
-
- for (int i = 0; i < mTransformedRectanglesCount; i++) {
- if (mTransformedRectangles[i].isEmpty()) {
- return true;
- }
- }
- return false;
-}
-
-int RectangleList::getTransformedRectanglesCount() const {
- return mTransformedRectanglesCount;
-}
-
-const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
- return mTransformedRectangles[i];
-}
-
-void RectangleList::setEmpty() {
- mTransformedRectanglesCount = 0;
-}
-
-void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
- mTransformedRectanglesCount = 1;
- mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
-}
-
-bool RectangleList::intersectWith(const Rect& bounds, const Matrix4& transform) {
- TransformedRectangle newRectangle(bounds, transform);
-
- // Try to find a rectangle with a compatible transformation
- int index = 0;
- for (; index < mTransformedRectanglesCount; index++) {
- TransformedRectangle& tr(mTransformedRectangles[index]);
- if (tr.canSimplyIntersectWith(newRectangle)) {
- tr.intersectWith(newRectangle);
- return true;
- }
- }
-
- // Add it to the list if there is room
- if (index < kMaxTransformedRectangles) {
- mTransformedRectangles[index] = newRectangle;
- mTransformedRectanglesCount += 1;
- return true;
- }
-
- // This rectangle list is full
- return false;
-}
-
-Rect RectangleList::calculateBounds() const {
- Rect bounds;
- for (int index = 0; index < mTransformedRectanglesCount; index++) {
- const TransformedRectangle& tr(mTransformedRectangles[index]);
- if (index == 0) {
- bounds = tr.transformedBounds();
- } else {
- bounds.doIntersect(tr.transformedBounds());
- }
- }
- return bounds;
-}
-
-static SkPath pathFromTransformedRectangle(const Rect& bounds, const Matrix4& transform) {
- SkPath rectPath;
- SkPath rectPathTransformed;
- rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
- SkMatrix skTransform;
- transform.copyTo(skTransform);
- rectPath.transform(skTransform, &rectPathTransformed);
- return rectPathTransformed;
-}
-
-SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
- SkRegion rectangleListAsRegion;
- for (int index = 0; index < mTransformedRectanglesCount; index++) {
- const TransformedRectangle& tr(mTransformedRectangles[index]);
- SkPath rectPathTransformed =
- pathFromTransformedRectangle(tr.getBounds(), tr.getTransform());
- if (index == 0) {
- rectangleListAsRegion.setPath(rectPathTransformed, clip);
- } else {
- SkRegion rectRegion;
- rectRegion.setPath(rectPathTransformed, clip);
- rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
- }
- }
- return rectangleListAsRegion;
-}
-
-void RectangleList::transform(const Matrix4& transform) {
- for (int index = 0; index < mTransformedRectanglesCount; index++) {
- mTransformedRectangles[index].transform(transform);
- }
-}
-
-/*
- * ClipArea
- */
-
-ClipArea::ClipArea() : mMode(ClipMode::Rectangle) {}
-
-/*
- * Interface
- */
-
-void ClipArea::setViewportDimensions(int width, int height) {
- mPostViewportClipObserved = false;
- mViewportBounds.set(0, 0, width, height);
- mClipRect = mViewportBounds;
-}
-
-void ClipArea::setEmpty() {
- onClipUpdated();
- mMode = ClipMode::Rectangle;
- mClipRect.setEmpty();
- mClipRegion.setEmpty();
- mRectangleList.setEmpty();
-}
-
-void ClipArea::setClip(float left, float top, float right, float bottom) {
- onClipUpdated();
- mMode = ClipMode::Rectangle;
- mClipRect.set(left, top, right, bottom);
- mClipRegion.setEmpty();
-}
-
-void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) {
- if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
- if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
- onClipUpdated();
- switch (mMode) {
- case ClipMode::Rectangle:
- rectangleModeClipRectWithTransform(r, transform, op);
- break;
- case ClipMode::RectangleList:
- rectangleListModeClipRectWithTransform(r, transform, op);
- break;
- case ClipMode::Region:
- regionModeClipRectWithTransform(r, transform, op);
- break;
- }
-}
-
-void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
- if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
- if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
- onClipUpdated();
- enterRegionMode();
- mClipRegion.op(region, op);
- onClipRegionUpdated();
-}
-
-void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) {
- if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
- if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
- onClipUpdated();
- SkMatrix skTransform;
- transform->copyTo(skTransform);
- SkPath transformed;
- path.transform(skTransform, &transformed);
- SkRegion region;
- regionFromPath(transformed, region);
- enterRegionMode();
- mClipRegion.op(region, op);
- onClipRegionUpdated();
-}
-
-/*
- * Rectangle mode
- */
-
-void ClipArea::enterRectangleMode() {
- // Entering rectangle mode discards any
- // existing clipping information from the other modes.
- // The only way this occurs is by a clip setting operation.
- mMode = ClipMode::Rectangle;
-}
-
-void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform,
- SkRegion::Op op) {
- if (op == SkRegion::kReplace_Op && transform->rectToRect()) {
- mClipRect = r;
- transform->mapRect(mClipRect);
- return;
- } else if (op != SkRegion::kIntersect_Op) {
- enterRegionMode();
- regionModeClipRectWithTransform(r, transform, op);
- return;
- }
-
- if (transform->rectToRect()) {
- Rect transformed(r);
- transform->mapRect(transformed);
- mClipRect.doIntersect(transformed);
- return;
- }
-
- enterRectangleListMode();
- rectangleListModeClipRectWithTransform(r, transform, op);
-}
-
-/*
- * RectangleList mode implementation
- */
-
-void ClipArea::enterRectangleListMode() {
- // Is is only legal to enter rectangle list mode from
- // rectangle mode, since rectangle list mode cannot represent
- // all clip areas that can be represented by a region.
- ALOG_ASSERT(mMode == ClipMode::Rectangle);
- mMode = ClipMode::RectangleList;
- mRectangleList.set(mClipRect, Matrix4::identity());
-}
-
-void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform,
- SkRegion::Op op) {
- if (op != SkRegion::kIntersect_Op || !mRectangleList.intersectWith(r, *transform)) {
- enterRegionMode();
- regionModeClipRectWithTransform(r, transform, op);
- }
-}
-
-/*
- * Region mode implementation
- */
-
-void ClipArea::enterRegionMode() {
- ClipMode oldMode = mMode;
- mMode = ClipMode::Region;
- if (oldMode != ClipMode::Region) {
- if (oldMode == ClipMode::Rectangle) {
- mClipRegion.setRect(mClipRect.toSkIRect());
- } else {
- mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
- onClipRegionUpdated();
- }
- }
-}
-
-void ClipArea::regionModeClipRectWithTransform(const Rect& r, const mat4* transform,
- SkRegion::Op op) {
- SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
- SkRegion transformedRectRegion;
- regionFromPath(transformedRect, transformedRectRegion);
- mClipRegion.op(transformedRectRegion, op);
- onClipRegionUpdated();
-}
-
-void ClipArea::onClipRegionUpdated() {
- if (!mClipRegion.isEmpty()) {
- mClipRect.set(mClipRegion.getBounds());
-
- if (mClipRegion.isRect()) {
- mClipRegion.setEmpty();
- enterRectangleMode();
- }
- } else {
- mClipRect.setEmpty();
- }
-}
-
-/**
- * Clip serialization
- */
-
-const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) {
- if (!mPostViewportClipObserved) {
- // Only initial clip-to-viewport observed, so no serialization of clip necessary
- return nullptr;
- }
-
- static_assert(std::is_trivially_destructible<Rect>::value,
- "expect Rect to be trivially destructible");
- static_assert(std::is_trivially_destructible<RectangleList>::value,
- "expect RectangleList to be trivially destructible");
-
- if (mLastSerialization == nullptr) {
- ClipBase* serialization = nullptr;
- switch (mMode) {
- case ClipMode::Rectangle:
- serialization = allocator.create<ClipRect>(mClipRect);
- break;
- case ClipMode::RectangleList:
- serialization = allocator.create<ClipRectList>(mRectangleList);
- serialization->rect = mRectangleList.calculateBounds();
- break;
- case ClipMode::Region:
- serialization = allocator.create<ClipRegion>(mClipRegion);
- serialization->rect.set(mClipRegion.getBounds());
- break;
- }
- serialization->intersectWithRoot = mReplaceOpObserved;
- // TODO: this is only done for draw time, should eventually avoid for record time
- serialization->rect.snapToPixelBoundaries();
- mLastSerialization = serialization;
- }
- return mLastSerialization;
-}
-
-inline static const RectangleList& getRectList(const ClipBase* scb) {
- return reinterpret_cast<const ClipRectList*>(scb)->rectList;
-}
-
-inline static const SkRegion& getRegion(const ClipBase* scb) {
- return reinterpret_cast<const ClipRegion*>(scb)->region;
-}
-
-// Conservative check for too many rectangles to fit in rectangle list.
-// For simplicity, doesn't account for rect merging
-static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) {
- int currentRectCount = clipArea.isRectangleList()
- ? clipArea.getRectangleList().getTransformedRectanglesCount()
- : 1;
- int recordedRectCount = (scb->mode == ClipMode::RectangleList)
- ? getRectList(scb).getTransformedRectanglesCount()
- : 1;
- return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles;
-}
-
-static const ClipRect sEmptyClipRect(Rect(0, 0));
-
-const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator,
- const ClipBase* recordedClip,
- const Matrix4& recordedClipTransform) {
- // if no recordedClip passed, just serialize current state
- if (!recordedClip) return serializeClip(allocator);
-
- // if either is empty, clip is empty
- if (CC_UNLIKELY(recordedClip->rect.isEmpty()) || mClipRect.isEmpty()) return &sEmptyClipRect;
-
- if (!mLastResolutionResult || recordedClip != mLastResolutionClip ||
- recordedClipTransform != mLastResolutionTransform) {
- mLastResolutionClip = recordedClip;
- mLastResolutionTransform = recordedClipTransform;
-
- if (CC_LIKELY(mMode == ClipMode::Rectangle && recordedClip->mode == ClipMode::Rectangle &&
- recordedClipTransform.rectToRect())) {
- // common case - result is a single rectangle
- auto rectClip = allocator.create<ClipRect>(recordedClip->rect);
- recordedClipTransform.mapRect(rectClip->rect);
- rectClip->rect.doIntersect(mClipRect);
- rectClip->rect.snapToPixelBoundaries();
- mLastResolutionResult = rectClip;
- } else if (CC_UNLIKELY(mMode == ClipMode::Region ||
- recordedClip->mode == ClipMode::Region ||
- cannotFitInRectangleList(*this, recordedClip))) {
- // region case
- SkRegion other;
- switch (recordedClip->mode) {
- case ClipMode::Rectangle:
- if (CC_LIKELY(recordedClipTransform.rectToRect())) {
- // simple transform, skip creating SkPath
- Rect resultClip(recordedClip->rect);
- recordedClipTransform.mapRect(resultClip);
- other.setRect(resultClip.toSkIRect());
- } else {
- SkPath transformedRect = pathFromTransformedRectangle(
- recordedClip->rect, recordedClipTransform);
- other.setPath(transformedRect, createViewportRegion());
- }
- break;
- case ClipMode::RectangleList: {
- RectangleList transformedList(getRectList(recordedClip));
- transformedList.transform(recordedClipTransform);
- other = transformedList.convertToRegion(createViewportRegion());
- break;
- }
- case ClipMode::Region:
- other = getRegion(recordedClip);
- applyTransformToRegion(recordedClipTransform, &other);
- }
-
- ClipRegion* regionClip = allocator.create<ClipRegion>();
- switch (mMode) {
- case ClipMode::Rectangle:
- regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op);
- break;
- case ClipMode::RectangleList:
- regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()),
- other, SkRegion::kIntersect_Op);
- break;
- case ClipMode::Region:
- regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op);
- break;
- }
- // Don't need to snap, since region's in int bounds
- regionClip->rect.set(regionClip->region.getBounds());
- mLastResolutionResult = regionClip;
- } else {
- auto rectListClip = allocator.create<ClipRectList>(mRectangleList);
- auto&& rectList = rectListClip->rectList;
- if (mMode == ClipMode::Rectangle) {
- rectList.set(mClipRect, Matrix4::identity());
- }
-
- if (recordedClip->mode == ClipMode::Rectangle) {
- rectList.intersectWith(recordedClip->rect, recordedClipTransform);
- } else {
- const RectangleList& other = getRectList(recordedClip);
- for (int i = 0; i < other.getTransformedRectanglesCount(); i++) {
- auto&& tr = other.getTransformedRectangle(i);
- Matrix4 totalTransform(recordedClipTransform);
- totalTransform.multiply(tr.getTransform());
- rectList.intersectWith(tr.getBounds(), totalTransform);
- }
- }
- rectListClip->rect = rectList.calculateBounds();
- rectListClip->rect.snapToPixelBoundaries();
- mLastResolutionResult = rectListClip;
- }
- }
- return mLastResolutionResult;
-}
-
-void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) {
- if (!clip) return; // nothing to do
-
- if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) {
- clipRectWithTransform(clip->rect, &transform, SkRegion::kIntersect_Op);
- } else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
- auto&& rectList = getRectList(clip);
- for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) {
- auto&& tr = rectList.getTransformedRectangle(i);
- Matrix4 totalTransform(transform);
- totalTransform.multiply(tr.getTransform());
- clipRectWithTransform(tr.getBounds(), &totalTransform, SkRegion::kIntersect_Op);
- }
- } else {
- SkRegion region(getRegion(clip));
- applyTransformToRegion(transform, &region);
- clipRegion(region, SkRegion::kIntersect_Op);
- }
-}
-
-void ClipArea::applyTransformToRegion(const Matrix4& transform, SkRegion* region) {
- if (transform.rectToRect() && !transform.isPureTranslate()) {
- // handle matrices with scale manually by mapping each rect
- SkRegion other;
- SkRegion::Iterator it(*region);
- while (!it.done()) {
- Rect rect(it.rect());
- transform.mapRect(rect);
- rect.snapGeometryToPixelBoundaries(true);
- other.op(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kUnion_Op);
- it.next();
- }
- region->swap(other);
- } else {
- // TODO: handle non-translate transforms properly!
- region->translate(transform.getTranslateX(), transform.getTranslateY());
- }
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
deleted file mode 100644
index a7a11801cfe2..000000000000
--- a/libs/hwui/ClipArea.h
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef CLIPAREA_H
-#define CLIPAREA_H
-
-#include "Matrix.h"
-#include "Rect.h"
-#include "utils/Pair.h"
-
-#include <SkRegion.h>
-
-namespace android {
-namespace uirenderer {
-
-class LinearAllocator;
-
-Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform);
-
-class TransformedRectangle {
-public:
- TransformedRectangle();
- TransformedRectangle(const Rect& bounds, const Matrix4& transform);
-
- bool canSimplyIntersectWith(const TransformedRectangle& other) const;
- void intersectWith(const TransformedRectangle& other);
-
- bool isEmpty() const;
-
- const Rect& getBounds() const { return mBounds; }
-
- Rect transformedBounds() const {
- Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform));
- return transformedBounds;
- }
-
- const Matrix4& getTransform() const { return mTransform; }
-
- void transform(const Matrix4& transform) {
- Matrix4 t;
- t.loadMultiply(transform, mTransform);
- mTransform = t;
- }
-
-private:
- Rect mBounds;
- Matrix4 mTransform;
-};
-
-class RectangleList {
-public:
- RectangleList();
-
- bool isEmpty() const;
- int getTransformedRectanglesCount() const;
- const TransformedRectangle& getTransformedRectangle(int i) const;
-
- void setEmpty();
- void set(const Rect& bounds, const Matrix4& transform);
- bool intersectWith(const Rect& bounds, const Matrix4& transform);
- void transform(const Matrix4& transform);
-
- SkRegion convertToRegion(const SkRegion& clip) const;
- Rect calculateBounds() const;
-
- enum { kMaxTransformedRectangles = 5 };
-
-private:
- int mTransformedRectanglesCount;
- TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles];
-};
-
-enum class ClipMode {
- Rectangle,
- RectangleList,
-
- // region and path - intersected. if either is empty, don't use
- Region
-};
-
-struct ClipBase {
- explicit ClipBase(ClipMode mode) : mode(mode) {}
- explicit ClipBase(const Rect& rect) : mode(ClipMode::Rectangle), rect(rect) {}
- const ClipMode mode;
- bool intersectWithRoot = false;
- // Bounds of the clipping area, used to define the scissor, and define which
- // portion of the stencil is updated/used
- Rect rect;
-
- void dump() const;
-};
-
-struct ClipRect : ClipBase {
- explicit ClipRect(const Rect& rect) : ClipBase(rect) {}
-};
-
-struct ClipRectList : ClipBase {
- explicit ClipRectList(const RectangleList& rectList)
- : ClipBase(ClipMode::RectangleList), rectList(rectList) {}
- RectangleList rectList;
-};
-
-struct ClipRegion : ClipBase {
- explicit ClipRegion(const SkRegion& region) : ClipBase(ClipMode::Region), region(region) {}
- ClipRegion() : ClipBase(ClipMode::Region) {}
- SkRegion region;
-};
-
-class ClipArea {
-public:
- ClipArea();
-
- void setViewportDimensions(int width, int height);
-
- bool isEmpty() const { return mClipRect.isEmpty(); }
-
- void setEmpty();
- void setClip(float left, float top, float right, float bottom);
- void clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
- void clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op);
-
- const Rect& getClipRect() const { return mClipRect; }
-
- const SkRegion& getClipRegion() const { return mClipRegion; }
-
- const RectangleList& getRectangleList() const { return mRectangleList; }
-
- bool isRegion() const { return ClipMode::Region == mMode; }
-
- bool isSimple() const { return mMode == ClipMode::Rectangle; }
-
- bool isRectangleList() const { return mMode == ClipMode::RectangleList; }
-
- WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator);
- WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(
- LinearAllocator& allocator, const ClipBase* recordedClip,
- const Matrix4& recordedClipTransform);
- void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
-
- static void applyTransformToRegion(const Matrix4& transform, SkRegion* region);
-
-private:
- void enterRectangleMode();
- void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
-
- void enterRectangleListMode();
- void rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform,
- SkRegion::Op op);
-
- void enterRegionModeFromRectangleMode();
- void enterRegionModeFromRectangleListMode();
- void enterRegionMode();
- void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
-
- void clipRegion(const SkRegion& region, SkRegion::Op op);
- void ensureClipRegion();
- void onClipRegionUpdated();
-
- // Called by every state modifying public method.
- void onClipUpdated() {
- mPostViewportClipObserved = true;
- mLastSerialization = nullptr;
- mLastResolutionResult = nullptr;
- }
-
- SkRegion createViewportRegion() { return SkRegion(mViewportBounds.toSkIRect()); }
-
- void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) {
- // TODO: this should not mask every path to the viewport - this makes it impossible to use
- // paths to clip to larger areas (which is valid e.g. with SkRegion::kReplace_Op)
- pathAsRegion.setPath(path, createViewportRegion());
- }
-
- ClipMode mMode;
- bool mPostViewportClipObserved = false;
- bool mReplaceOpObserved = false;
-
- /**
- * If mLastSerialization is non-null, it represents an already serialized copy
- * of the current clip state. If null, it has not been computed.
- */
- const ClipBase* mLastSerialization = nullptr;
-
- /**
- * This pair of pointers is a single entry cache of most recently seen
- */
- const ClipBase* mLastResolutionResult = nullptr;
- const ClipBase* mLastResolutionClip = nullptr;
- Matrix4 mLastResolutionTransform;
-
- Rect mViewportBounds;
- Rect mClipRect;
- SkRegion mClipRegion;
- RectangleList mRectangleList;
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif /* CLIPAREA_H_ */
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index c060740dc9a4..b772e5b87f2a 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -15,33 +15,30 @@
*/
#include "DeferredLayerUpdater.h"
-#include "GlLayer.h"
-#include "VkLayer.h"
#include "renderstate/RenderState.h"
-#include "renderthread/EglManager.h"
-#include "renderthread/RenderTask.h"
#include "utils/PaintUtils.h"
namespace android {
namespace uirenderer {
-DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn,
- Layer::Api layerApi)
+DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState)
: mRenderState(renderState)
, mBlend(false)
, mSurfaceTexture(nullptr)
, mTransform(nullptr)
, mGLContextAttached(false)
, mUpdateTexImage(false)
- , mLayer(nullptr)
- , mLayerApi(layerApi)
- , mCreateLayerFn(createLayerFn) {
- renderState.registerDeferredLayerUpdater(this);
+ , mLayer(nullptr) {
+ renderState.registerContextCallback(this);
}
DeferredLayerUpdater::~DeferredLayerUpdater() {
setTransform(nullptr);
- mRenderState.unregisterDeferredLayerUpdater(this);
+ mRenderState.removeContextCallback(this);
+ destroyLayer();
+}
+
+void DeferredLayerUpdater::onContextDestroyed() {
destroyLayer();
}
@@ -50,16 +47,13 @@ void DeferredLayerUpdater::destroyLayer() {
return;
}
- if (mSurfaceTexture.get() && mLayerApi == Layer::Api::OpenGL && mGLContextAttached) {
- status_t err = mSurfaceTexture->detachFromContext();
+ if (mSurfaceTexture.get() && mGLContextAttached) {
+ mSurfaceTexture->detachFromView();
mGLContextAttached = false;
- if (err != 0) {
- // TODO: Elevate to fatal exception
- ALOGE("Failed to detach SurfaceTexture from context %d", err);
- }
}
mLayer->postDecStrong();
+
mLayer = nullptr;
}
@@ -75,100 +69,53 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
void DeferredLayerUpdater::apply() {
if (!mLayer) {
- mLayer = mCreateLayerFn(mRenderState, mWidth, mHeight, mColorFilter, mAlpha, mMode, mBlend);
+ mLayer = new Layer(mRenderState, mColorFilter, mAlpha, mMode);
}
mLayer->setColorFilter(mColorFilter);
mLayer->setAlpha(mAlpha, mMode);
if (mSurfaceTexture.get()) {
- if (mLayer->getApi() == Layer::Api::Vulkan) {
- if (mUpdateTexImage) {
- mUpdateTexImage = false;
- doUpdateVkTexImage();
- }
- } else {
- LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL,
- "apply surfaceTexture with non GL backend %x, GL %x, VK %x",
- mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan);
- if (!mGLContextAttached) {
- mGLContextAttached = true;
- mUpdateTexImage = true;
- mSurfaceTexture->attachToContext(static_cast<GlLayer*>(mLayer)->getTextureId());
- }
- if (mUpdateTexImage) {
- mUpdateTexImage = false;
- doUpdateTexImage();
+ if (!mGLContextAttached) {
+ mGLContextAttached = true;
+ mUpdateTexImage = true;
+ mSurfaceTexture->attachToView();
+ }
+ if (mUpdateTexImage) {
+ mUpdateTexImage = false;
+ sk_sp<SkImage> layerImage;
+ SkMatrix textureTransform;
+ android_dataspace dataSpace;
+ bool queueEmpty = true;
+ // If the SurfaceTexture queue is in synchronous mode, need to discard all
+ // but latest frame. Since we can't tell which mode it is in,
+ // do this unconditionally.
+ do {
+ layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty,
+ mRenderState);
+ } while (layerImage.get() && (!queueEmpty));
+ if (layerImage.get()) {
+ // force filtration if buffer size != layer size
+ bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height();
+ updateLayer(forceFilter, textureTransform, dataSpace, layerImage);
}
- GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget();
- static_cast<GlLayer*>(mLayer)->setRenderTarget(renderTarget);
}
+
if (mTransform) {
- mLayer->getTransform().load(*mTransform);
+ mLayer->getTransform() = *mTransform;
setTransform(nullptr);
}
}
}
-void DeferredLayerUpdater::doUpdateTexImage() {
- LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL,
- "doUpdateTexImage non GL backend %x, GL %x, VK %x", mLayer->getApi(),
- Layer::Api::OpenGL, Layer::Api::Vulkan);
- if (mSurfaceTexture->updateTexImage() == NO_ERROR) {
- float transform[16];
-
- int64_t frameNumber = mSurfaceTexture->getFrameNumber();
- // If the GLConsumer queue is in synchronous mode, need to discard all
- // but latest frame, using the frame number to tell when we no longer
- // have newer frames to target. Since we can't tell which mode it is in,
- // do this unconditionally.
- int dropCounter = 0;
- while (mSurfaceTexture->updateTexImage() == NO_ERROR) {
- int64_t newFrameNumber = mSurfaceTexture->getFrameNumber();
- if (newFrameNumber == frameNumber) break;
- frameNumber = newFrameNumber;
- dropCounter++;
- }
-
- bool forceFilter = false;
- sp<GraphicBuffer> buffer = mSurfaceTexture->getCurrentBuffer();
- if (buffer != nullptr) {
- mLayer->setBufferSize(buffer->getWidth(), buffer->getHeight());
- // force filtration if buffer size != layer size
- forceFilter = mWidth != static_cast<int>(mLayer->getBufferWidth()) ||
- mHeight != static_cast<int>(mLayer->getBufferHeight());
- }
-
-#if DEBUG_RENDERER
- if (dropCounter > 0) {
- RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter);
- }
-#endif
- mSurfaceTexture->getTransformMatrix(transform);
-
- updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace());
- }
-}
-
-void DeferredLayerUpdater::doUpdateVkTexImage() {
- LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::Vulkan,
- "updateLayer non Vulkan backend %x, GL %x, VK %x", mLayer->getApi(),
- Layer::Api::OpenGL, Layer::Api::Vulkan);
-
- static const mat4 identityMatrix;
- updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN);
-
- VkLayer* vkLayer = static_cast<VkLayer*>(mLayer);
- vkLayer->updateTexture();
-}
-
-void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform,
- android_dataspace dataspace) {
+void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform,
+ android_dataspace dataspace, const sk_sp<SkImage>& layerImage) {
mLayer->setBlend(mBlend);
mLayer->setForceFilter(forceFilter);
mLayer->setSize(mWidth, mHeight);
- mLayer->getTexTransform().load(textureTransform);
+ mLayer->getTexTransform() = textureTransform;
mLayer->setDataSpace(dataspace);
+ mLayer->setImage(layerImage);
}
void DeferredLayerUpdater::detachSurfaceTexture() {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index fe3ee7a2b4c6..b2c5131dd613 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -17,18 +17,20 @@
#pragma once
#include <SkColorFilter.h>
+#include <SkImage.h>
#include <SkMatrix.h>
#include <cutils/compiler.h>
-#include <gui/GLConsumer.h>
+#include <map>
#include <system/graphics.h>
#include <utils/StrongPointer.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include "renderstate/RenderState.h"
+#include "surfacetexture/SurfaceTexture.h"
#include "Layer.h"
#include "Rect.h"
-#include "renderthread/RenderThread.h"
namespace android {
namespace uirenderer {
@@ -37,16 +39,11 @@ class RenderState;
// Container to hold the properties a layer should be set to at the start
// of a render pass
-class DeferredLayerUpdater : public VirtualLightRefBase {
+class DeferredLayerUpdater : public VirtualLightRefBase, public IGpuContextCallback {
public:
// Note that DeferredLayerUpdater assumes it is taking ownership of the layer
// and will not call incrementRef on it as a result.
- typedef std::function<Layer*(RenderState& renderState, uint32_t layerWidth,
- uint32_t layerHeight, sk_sp<SkColorFilter> colorFilter, int alpha,
- SkBlendMode mode, bool blend)>
- CreateLayerFn;
- ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn,
- Layer::Api layerApi);
+ ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState);
ANDROID_API ~DeferredLayerUpdater();
@@ -70,13 +67,13 @@ public:
return false;
}
- ANDROID_API void setSurfaceTexture(const sp<GLConsumer>& texture) {
- if (texture.get() != mSurfaceTexture.get()) {
- mSurfaceTexture = texture;
+ ANDROID_API void setSurfaceTexture(const sp<SurfaceTexture>& consumer) {
+ if (consumer.get() != mSurfaceTexture.get()) {
+ mSurfaceTexture = consumer;
- GLenum target = texture->getCurrentTextureTarget();
+ GLenum target = consumer->getCurrentTextureTarget();
LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES,
- "set unsupported GLConsumer with target %x", target);
+ "set unsupported SurfaceTexture with target %x", target);
}
}
@@ -97,11 +94,13 @@ public:
void detachSurfaceTexture();
- void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace);
+ void updateLayer(bool forceFilter, const SkMatrix& textureTransform,
+ android_dataspace dataspace, const sk_sp<SkImage>& layerImage);
void destroyLayer();
- Layer::Api getBackingLayerApi() { return mLayerApi; }
+protected:
+ void onContextDestroyed() override;
private:
RenderState& mRenderState;
@@ -113,17 +112,12 @@ private:
sk_sp<SkColorFilter> mColorFilter;
int mAlpha = 255;
SkBlendMode mMode = SkBlendMode::kSrcOver;
- sp<GLConsumer> mSurfaceTexture;
+ sp<SurfaceTexture> mSurfaceTexture;
SkMatrix* mTransform;
bool mGLContextAttached;
bool mUpdateTexImage;
Layer* mLayer;
- Layer::Api mLayerApi;
- CreateLayerFn mCreateLayerFn;
-
- void doUpdateTexImage();
- void doUpdateVkTexImage();
};
} /* namespace uirenderer */
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 40cc73a82846..0b9d82b105a3 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -26,12 +26,10 @@
#include <log/log.h>
-#include <GLES2/gl2.h>
-
namespace android {
namespace uirenderer {
-static constexpr android::DisplayInfo sDummyDisplay {
+static constexpr android::DisplayInfo sDummyDisplay{
1080, // w
1920, // h
320.0, // xdpi
@@ -42,37 +40,16 @@ static constexpr android::DisplayInfo sDummyDisplay {
false, // secure?
0, // appVsyncOffset
0, // presentationDeadline
+ 1080, // viewportW
+ 1920, // viewportH
};
-static DeviceInfo* sDeviceInfo = nullptr;
-static std::once_flag sInitializedFlag;
-
const DeviceInfo* DeviceInfo::get() {
- LOG_ALWAYS_FATAL_IF(!sDeviceInfo, "DeviceInfo not yet initialized.");
- return sDeviceInfo;
-}
-
-void DeviceInfo::initialize() {
- std::call_once(sInitializedFlag, []() {
- sDeviceInfo = new DeviceInfo();
- sDeviceInfo->load();
- });
-}
-
-void DeviceInfo::initialize(int maxTextureSize) {
- std::call_once(sInitializedFlag, [maxTextureSize]() {
- sDeviceInfo = new DeviceInfo();
- sDeviceInfo->mDisplayInfo = DeviceInfo::queryDisplayInfo();
- sDeviceInfo->mMaxTextureSize = maxTextureSize;
- });
-}
-
-void DeviceInfo::load() {
- mDisplayInfo = queryDisplayInfo();
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+ static DeviceInfo sDeviceInfo;
+ return &sDeviceInfo;
}
-DisplayInfo DeviceInfo::queryDisplayInfo() {
+DisplayInfo QueryDisplayInfo() {
if (Properties::isolatedProcess) {
return sDummyDisplay;
}
@@ -84,5 +61,23 @@ DisplayInfo DeviceInfo::queryDisplayInfo() {
return displayInfo;
}
+DeviceInfo::DeviceInfo() {
+#if HWUI_NULL_GPU
+ mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE;
+#else
+ mMaxTextureSize = -1;
+#endif
+ mDisplayInfo = QueryDisplayInfo();
+}
+
+int DeviceInfo::maxTextureSize() const {
+ LOG_ALWAYS_FATAL_IF(mMaxTextureSize < 0, "MaxTextureSize has not been initialized yet.");
+ return mMaxTextureSize;
+}
+
+void DeviceInfo::setMaxTextureSize(int maxTextureSize) {
+ const_cast<DeviceInfo*>(DeviceInfo::get())->mMaxTextureSize = maxTextureSize;
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 297b2664414b..595621573e6e 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -18,46 +18,34 @@
#include <ui/DisplayInfo.h>
-#include "Extensions.h"
#include "utils/Macros.h"
namespace android {
namespace uirenderer {
+namespace renderthread {
+ class RenderThread;
+}
+
class DeviceInfo {
PREVENT_COPY_AND_ASSIGN(DeviceInfo);
public:
- // returns nullptr if DeviceInfo is not initialized yet
- // Note this does not have a memory fence so it's up to the caller
- // to use one if required. Normally this should not be necessary
static const DeviceInfo* get();
- // only call this after GL has been initialized, or at any point if compiled
- // with HWUI_NULL_GPU
- static void initialize();
- static void initialize(int maxTextureSize);
-
- int maxTextureSize() const { return mMaxTextureSize; }
+ // this value is only valid after the GPU has been initialized and there is a valid graphics
+ // context or if you are using the HWUI_NULL_GPU
+ int maxTextureSize() const;
const DisplayInfo& displayInfo() const { return mDisplayInfo; }
- const Extensions& extensions() const { return mExtensions; }
-
- static uint32_t multiplyByResolution(uint32_t in) {
- auto di = DeviceInfo::get()->displayInfo();
- return di.w * di.h * in;
- }
-
- static DisplayInfo queryDisplayInfo();
private:
- DeviceInfo() {}
- ~DeviceInfo() {}
+ friend class renderthread::RenderThread;
+ static void setMaxTextureSize(int maxTextureSize);
- void load();
+ DeviceInfo();
int mMaxTextureSize;
DisplayInfo mDisplayInfo;
- Extensions mExtensions;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
deleted file mode 100644
index aa87aea8b374..000000000000
--- a/libs/hwui/DisplayList.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <SkCanvas.h>
-#include <algorithm>
-
-#include <utils/Trace.h>
-
-#include "DamageAccumulator.h"
-#include "Debug.h"
-#include "DisplayList.h"
-#include "OpDumper.h"
-#include "RecordedOp.h"
-#include "RenderNode.h"
-#include "VectorDrawable.h"
-#include "renderthread/CanvasContext.h"
-
-namespace android {
-namespace uirenderer {
-
-DisplayList::DisplayList()
- : projectionReceiveIndex(-1)
- , stdAllocator(allocator)
- , chunks(stdAllocator)
- , ops(stdAllocator)
- , children(stdAllocator)
- , bitmapResources(stdAllocator)
- , pathResources(stdAllocator)
- , patchResources(stdAllocator)
- , paints(stdAllocator)
- , regions(stdAllocator)
- , referenceHolders(stdAllocator)
- , functors(stdAllocator)
- , vectorDrawables(stdAllocator) {}
-
-DisplayList::~DisplayList() {
- cleanupResources();
-}
-
-void DisplayList::cleanupResources() {
- if (CC_UNLIKELY(patchResources.size())) {
- ResourceCache& resourceCache = ResourceCache::getInstance();
- resourceCache.lock();
-
- for (size_t i = 0; i < patchResources.size(); i++) {
- resourceCache.decrementRefcountLocked(patchResources[i]);
- }
-
- resourceCache.unlock();
- }
-
- for (size_t i = 0; i < pathResources.size(); i++) {
- const SkPath* path = pathResources[i];
- if (path->unique() && Caches::hasInstance()) {
- Caches::getInstance().pathCache.removeDeferred(path);
- }
- delete path;
- }
-
- for (auto& iter : functors) {
- if (iter.listener) {
- iter.listener->onGlFunctorReleased(iter.functor);
- }
- }
-
- patchResources.clear();
- pathResources.clear();
- paints.clear();
- regions.clear();
-}
-
-size_t DisplayList::addChild(NodeOpType* op) {
- referenceHolders.push_back(op->renderNode);
- size_t index = children.size();
- children.push_back(op);
- return index;
-}
-
-void DisplayList::syncContents() {
- for (auto& iter : functors) {
- (*iter.functor)(DrawGlInfo::kModeSync, nullptr);
- }
- for (auto& vectorDrawable : vectorDrawables) {
- vectorDrawable->syncProperties();
- }
-}
-
-void DisplayList::updateChildren(std::function<void(RenderNode*)> updateFn) {
- for (auto&& child : children) {
- updateFn(child->renderNode);
- }
-}
-
-bool DisplayList::prepareListAndChildren(
- TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
- std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
- info.prepareTextures = info.canvasContext.pinImages(bitmapResources);
-
- for (auto&& op : children) {
- RenderNode* childNode = op->renderNode;
- info.damageAccumulator->pushTransform(&op->localMatrix);
- bool childFunctorsNeedLayer =
- functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip;
- childFn(childNode, observer, info, childFunctorsNeedLayer);
- info.damageAccumulator->popTransform();
- }
-
- bool isDirty = false;
- for (auto& vectorDrawable : vectorDrawables) {
- // If any vector drawable in the display list needs update, damage the node.
- if (vectorDrawable->isDirty()) {
- isDirty = true;
- }
- vectorDrawable->setPropertyChangeWillBeConsumed(true);
- }
- return isDirty;
-}
-
-void DisplayList::output(std::ostream& output, uint32_t level) {
- for (auto&& op : getOps()) {
- OpDumper::dump(*op, output, level + 1);
- if (op->opId == RecordedOpId::RenderNodeOp) {
- auto rnOp = reinterpret_cast<const RenderNodeOp*>(op);
- rnOp->renderNode->output(output, level + 1);
- } else {
- output << std::endl;
- }
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 0cfc3b701aff..a952cc23e1ef 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -16,148 +16,20 @@
#pragma once
-#include <SkCamera.h>
-#include <SkDrawable.h>
-#include <SkMatrix.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/LinearAllocator.h>
-#include <utils/RefBase.h>
-#include <utils/SortedVector.h>
-#include <utils/String8.h>
-
-#include <cutils/compiler.h>
-
-#include <androidfw/ResourceTypes.h>
-
-#include "CanvasProperty.h"
-#include "Debug.h"
-#include "GlFunctorLifecycleListener.h"
-#include "Matrix.h"
-#include "RenderProperties.h"
-#include "TreeInfo.h"
-#include "hwui/Bitmap.h"
-
-#include <vector>
-
-class SkBitmap;
-class SkPaint;
-class SkPath;
-class SkRegion;
+#include "pipeline/skia/SkiaDisplayList.h"
namespace android {
namespace uirenderer {
-class Rect;
-class Layer;
-
-struct RecordedOp;
-struct RenderNodeOp;
-
-typedef RecordedOp BaseOpType;
-typedef RenderNodeOp NodeOpType;
-
namespace VectorDrawable {
class Tree;
};
typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
-struct FunctorContainer {
- Functor* functor;
- GlFunctorLifecycleListener* listener;
-};
-
/**
* Data structure that holds the list of commands used in display list stream
*/
-class DisplayList {
- friend class RecordingCanvas;
-
-public:
- struct Chunk {
- // range of included ops in DisplayList::ops()
- size_t beginOpIndex;
- size_t endOpIndex;
-
- // range of included children in DisplayList::children()
- size_t beginChildIndex;
- size_t endChildIndex;
-
- // whether children with non-zero Z in the chunk should be reordered
- bool reorderChildren;
-
- // clip at the beginning of a reorder section, applied to reordered children
- const ClipBase* reorderClip;
- };
-
- DisplayList();
- virtual ~DisplayList();
-
- // index of DisplayListOp restore, after which projected descendants should be drawn
- int projectionReceiveIndex;
-
- const LsaVector<Chunk>& getChunks() const { return chunks; }
- const LsaVector<BaseOpType*>& getOps() const { return ops; }
-
- const LsaVector<NodeOpType*>& getChildren() const { return children; }
-
- const LsaVector<sk_sp<Bitmap>>& getBitmapResources() const { return bitmapResources; }
-
- size_t addChild(NodeOpType* childOp);
-
- void ref(VirtualLightRefBase* prop) { referenceHolders.push_back(prop); }
-
- size_t getUsedSize() { return allocator.usedSize(); }
-
- virtual bool isEmpty() const { return ops.empty(); }
- virtual bool hasFunctor() const { return !functors.empty(); }
- virtual bool hasVectorDrawables() const { return !vectorDrawables.empty(); }
- virtual bool isSkiaDL() const { return false; }
- virtual bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) {
- return false;
- }
-
- virtual void syncContents();
- virtual void updateChildren(std::function<void(RenderNode*)> updateFn);
- virtual bool prepareListAndChildren(
- TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
- std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn);
-
- virtual void output(std::ostream& output, uint32_t level);
-
-protected:
- // allocator into which all ops and LsaVector arrays allocated
- LinearAllocator allocator;
- LinearStdAllocator<void*> stdAllocator;
-
-private:
- LsaVector<Chunk> chunks;
- LsaVector<BaseOpType*> ops;
-
- // list of Ops referring to RenderNode children for quick, non-drawing traversal
- LsaVector<NodeOpType*> children;
-
- // Resources - Skia objects + 9 patches referred to by this DisplayList
- LsaVector<sk_sp<Bitmap>> bitmapResources;
- LsaVector<const SkPath*> pathResources;
- LsaVector<const Res_png_9patch*> patchResources;
- LsaVector<std::unique_ptr<const SkPaint>> paints;
- LsaVector<std::unique_ptr<const SkRegion>> regions;
- LsaVector<sp<VirtualLightRefBase>> referenceHolders;
-
- // List of functors
- LsaVector<FunctorContainer> functors;
-
- // List of VectorDrawables that need to be notified of pushStaging. Note that this list gets
- // nothing
- // but a callback during sync DisplayList, unlike the list of functors defined above, which
- // gets special treatment exclusive for webview.
- LsaVector<VectorDrawableRoot*> vectorDrawables;
-
- void cleanupResources();
-};
+using DisplayList = skiapipeline::SkiaDisplayList;
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
new file mode 100644
index 000000000000..04cf611c8322
--- /dev/null
+++ b/libs/hwui/DisplayListOps.in
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+X(Flush)
+X(Save)
+X(Restore)
+X(SaveLayer)
+X(Concat)
+X(SetMatrix)
+X(Translate)
+X(ClipPath)
+X(ClipRect)
+X(ClipRRect)
+X(ClipRegion)
+X(DrawPaint)
+X(DrawPath)
+X(DrawRect)
+X(DrawRegion)
+X(DrawOval)
+X(DrawArc)
+X(DrawRRect)
+X(DrawDRRect)
+X(DrawAnnotation)
+X(DrawDrawable)
+X(DrawPicture)
+X(DrawImage)
+X(DrawImageNine)
+X(DrawImageRect)
+X(DrawImageLattice)
+X(DrawText)
+X(DrawPosText)
+X(DrawPosTextH)
+X(DrawTextRSXform)
+X(DrawTextBlob)
+X(DrawPatch)
+X(DrawPoints)
+X(DrawVertices)
+X(DrawAtlas)
+X(DrawShadowRec)
+X(DrawVectorDrawable) \ No newline at end of file
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
deleted file mode 100644
index 530e82e65a28..000000000000
--- a/libs/hwui/Extensions.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Extensions.h"
-
-#include "Debug.h"
-#include "Properties.h"
-#include "utils/StringUtils.h"
-
-#include <cutils/compiler.h>
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <utils/Log.h>
-
-namespace android {
-namespace uirenderer {
-
-Extensions::Extensions() {
- if (Properties::isSkiaEnabled()) {
- return;
- }
- const char* version = (const char*)glGetString(GL_VERSION);
-
- // Section 6.1.5 of the OpenGL ES specification indicates the GL version
- // string strictly follows this format:
- //
- // OpenGL<space>ES<space><version number><space><vendor-specific information>
- //
- // In addition section 6.1.5 describes the version number thusly:
- //
- // "The version number is either of the form major number.minor number or
- // major number.minor number.release number, where the numbers all have one
- // or more digits. The release number and vendor specific information are
- // optional."
-
- if (sscanf(version, "OpenGL ES %d.%d", &mVersionMajor, &mVersionMinor) != 2) {
- // If we cannot parse the version number, assume OpenGL ES 2.0
- mVersionMajor = 2;
- mVersionMinor = 0;
- }
-
- auto extensions = StringUtils::split((const char*)glGetString(GL_EXTENSIONS));
- mHasNPot = extensions.has("GL_OES_texture_npot");
- mHasFramebufferFetch = extensions.has("GL_NV_shader_framebuffer_fetch");
- mHasDiscardFramebuffer = extensions.has("GL_EXT_discard_framebuffer");
- mHasDebugMarker = extensions.has("GL_EXT_debug_marker");
- mHas1BitStencil = extensions.has("GL_OES_stencil1");
- mHas4BitStencil = extensions.has("GL_OES_stencil4");
- mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage");
- mHasRenderableFloatTexture = extensions.has("GL_OES_texture_half_float");
-
- mHasSRGB = mVersionMajor >= 3 || extensions.has("GL_EXT_sRGB");
- mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control");
-
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
- // If linear blending is enabled, the device must have (ES3.0 or EXT_sRGB)
- // and EXT_sRGB_write_control
- LOG_ALWAYS_FATAL_IF(!mHasSRGB, "Linear blending requires ES 3.0 or EXT_sRGB");
- LOG_ALWAYS_FATAL_IF(!mHasSRGBWriteControl, "Linear blending requires EXT_sRGB_write_control");
-
- mHasLinearBlending = true;
-#else
- mHasLinearBlending = false;
-#endif
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
deleted file mode 100644
index 214ee0bbeefd..000000000000
--- a/libs/hwui/Extensions.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_EXTENSIONS_H
-#define ANDROID_HWUI_EXTENSIONS_H
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Classes
-///////////////////////////////////////////////////////////////////////////////
-
-class Extensions {
-public:
- Extensions();
-
- inline bool hasNPot() const { return mHasNPot; }
- inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
- inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; }
- inline bool hasDebugMarker() const { return mHasDebugMarker; }
- inline bool has1BitStencil() const { return mHas1BitStencil; }
- inline bool has4BitStencil() const { return mHas4BitStencil; }
- inline bool hasUnpackRowLength() const { return mVersionMajor >= 3 || mHasUnpackSubImage; }
- inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
- inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
- inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
- inline bool hasRenderableFloatTextures() const {
- return (mVersionMajor >= 3 && mVersionMinor >= 2) || mHasRenderableFloatTexture;
- }
- inline bool hasSRGB() const { return mHasSRGB; }
- inline bool hasSRGBWriteControl() const { return hasSRGB() && mHasSRGBWriteControl; }
- inline bool hasLinearBlending() const { return hasSRGB() && mHasLinearBlending; }
-
- inline int getMajorGlVersion() const { return mVersionMajor; }
- inline int getMinorGlVersion() const { return mVersionMinor; }
-
-private:
- bool mHasNPot;
- bool mHasFramebufferFetch;
- bool mHasDiscardFramebuffer;
- bool mHasDebugMarker;
- bool mHas1BitStencil;
- bool mHas4BitStencil;
- bool mHasUnpackSubImage;
- bool mHasSRGB;
- bool mHasSRGBWriteControl;
- bool mHasLinearBlending;
- bool mHasRenderableFloatTexture;
-
- int mVersionMajor;
- int mVersionMinor;
-}; // class Extensions
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_EXTENSIONS_H
diff --git a/libs/hwui/FboCache.cpp b/libs/hwui/FboCache.cpp
deleted file mode 100644
index 88302cc52c2b..000000000000
--- a/libs/hwui/FboCache.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-
-#include "Debug.h"
-#include "FboCache.h"
-#include "Properties.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-FboCache::FboCache() : mMaxSize(0) {}
-
-FboCache::~FboCache() {
- clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Size management
-///////////////////////////////////////////////////////////////////////////////
-
-uint32_t FboCache::getSize() {
- return mCache.size();
-}
-
-uint32_t FboCache::getMaxSize() {
- return mMaxSize;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-void FboCache::clear() {
- for (size_t i = 0; i < mCache.size(); i++) {
- const GLuint fbo = mCache.itemAt(i);
- glDeleteFramebuffers(1, &fbo);
- }
- mCache.clear();
-}
-
-GLuint FboCache::get() {
- GLuint fbo;
- if (mCache.size() > 0) {
- fbo = mCache.itemAt(mCache.size() - 1);
- mCache.removeAt(mCache.size() - 1);
- } else {
- glGenFramebuffers(1, &fbo);
- }
- return fbo;
-}
-
-bool FboCache::put(GLuint fbo) {
- if (mCache.size() < mMaxSize) {
- mCache.add(fbo);
- return true;
- }
-
- glDeleteFramebuffers(1, &fbo);
- return false;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/FboCache.h b/libs/hwui/FboCache.h
deleted file mode 100644
index 5e8bb0c7a7a7..000000000000
--- a/libs/hwui/FboCache.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_FBO_CACHE_H
-#define ANDROID_HWUI_FBO_CACHE_H
-
-#include <GLES2/gl2.h>
-
-#include <utils/SortedVector.h>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Cache
-///////////////////////////////////////////////////////////////////////////////
-
-class FboCache {
-public:
- FboCache();
- ~FboCache();
-
- /**
- * Returns an FBO from the cache. If no FBO is available, a new one
- * is created. If creating a new FBO fails, 0 is returned.
- *
- * When an FBO is obtained from the cache, it is removed and the
- * total number of FBOs available in the cache decreases.
- *
- * @return The name of the FBO, or 0 if no FBO can be obtained.
- */
- GLuint get();
-
- /**
- * Adds the specified FBO to the cache.
- *
- * @param fbo The FBO to add to the cache.
- *
- * @return True if the FBO was added, false otherwise.
- */
- bool put(GLuint fbo);
-
- /**
- * Clears the cache. This causes all FBOs to be deleted.
- */
- void clear();
-
- /**
- * Returns the current size of the cache.
- */
- uint32_t getSize();
-
- /**
- * Returns the maximum number of FBOs that the cache can hold.
- */
- uint32_t getMaxSize();
-
-private:
- SortedVector<GLuint> mCache;
- uint32_t mMaxSize;
-}; // class FboCache
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_FBO_CACHE_H
diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h
deleted file mode 100644
index b424f97a5004..000000000000
--- a/libs/hwui/FloatColor.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef FLOATCOLOR_H
-#define FLOATCOLOR_H
-
-#include "utils/Color.h"
-#include "utils/Macros.h"
-#include "utils/MathUtils.h"
-
-#include <stdint.h>
-
-namespace android {
-namespace uirenderer {
-
-struct FloatColor {
- // "color" is a gamma-encoded sRGB color
- // After calling this method, the color is stored as a pre-multiplied linear color
- // if linear blending is enabled. Otherwise, the color is stored as a pre-multiplied
- // gamma-encoded sRGB color
- void set(uint32_t color) {
- a = ((color >> 24) & 0xff) / 255.0f;
- r = a * EOCF(((color >> 16) & 0xff) / 255.0f);
- g = a * EOCF(((color >> 8) & 0xff) / 255.0f);
- b = a * EOCF(((color)&0xff) / 255.0f);
- }
-
- // "color" is a gamma-encoded sRGB color
- // After calling this method, the color is stored as a un-premultiplied linear color
- // if linear blending is enabled. Otherwise, the color is stored as a un-premultiplied
- // gamma-encoded sRGB color
- void setUnPreMultiplied(uint32_t color) {
- a = ((color >> 24) & 0xff) / 255.0f;
- r = EOCF(((color >> 16) & 0xff) / 255.0f);
- g = EOCF(((color >> 8) & 0xff) / 255.0f);
- b = EOCF(((color)&0xff) / 255.0f);
- }
-
- bool isNotBlack() { return a < 1.0f || r > 0.0f || g > 0.0f || b > 0.0f; }
-
- bool operator==(const FloatColor& other) const {
- return MathUtils::areEqual(r, other.r) && MathUtils::areEqual(g, other.g) &&
- MathUtils::areEqual(b, other.b) && MathUtils::areEqual(a, other.a);
- }
-
- bool operator!=(const FloatColor& other) const { return !(*this == other); }
-
- float r;
- float g;
- float b;
- float a;
-};
-
-REQUIRE_COMPATIBLE_LAYOUT(FloatColor);
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif /* FLOATCOLOR_H */
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
deleted file mode 100644
index bbcedb15335d..000000000000
--- a/libs/hwui/FontRenderer.cpp
+++ /dev/null
@@ -1,802 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FontRenderer.h"
-
-#include "BakedOpDispatcher.h"
-#include "BakedOpRenderer.h"
-#include "BakedOpState.h"
-#include "Caches.h"
-#include "Debug.h"
-#include "Extensions.h"
-#include "Glop.h"
-#include "GlopBuilder.h"
-#include "PixelBuffer.h"
-#include "Rect.h"
-#include "font/Font.h"
-#include "renderstate/RenderState.h"
-#include "utils/Blur.h"
-#include "utils/Timing.h"
-
-#include <RenderScript.h>
-#include <SkGlyph.h>
-#include <SkUtils.h>
-#include <utils/Log.h>
-#include <algorithm>
-
-namespace android {
-namespace uirenderer {
-
-// blur inputs smaller than this constant will bypass renderscript
-#define RS_MIN_INPUT_CUTOFF 10000
-
-///////////////////////////////////////////////////////////////////////////////
-// TextSetupFunctor
-///////////////////////////////////////////////////////////////////////////////
-
-void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) {
- int textureFillFlags = TextureFillFlags::None;
- if (texture.getFormat() == GL_ALPHA) {
- textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
- }
- if (linearFiltering) {
- textureFillFlags |= TextureFillFlags::ForceFilter;
- }
- int transformFlags =
- pureTranslate ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None;
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
- bool gammaCorrection = true;
-#else
- bool gammaCorrection = false;
-#endif
- Glop glop;
- GlopBuilder(renderer->renderState(), renderer->caches(), &glop)
- .setRoundRectClipState(bakedState->roundRectClipState)
- .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
- .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha)
- .setGammaCorrection(gammaCorrection)
- .setTransform(bakedState->computedState.transform, transformFlags)
- .setModelViewIdentityEmptyBounds()
- .build();
- // Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer
- renderer->renderGlop(nullptr, clip, glop);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// FontRenderer
-///////////////////////////////////////////////////////////////////////////////
-
-static bool sLogFontRendererCreate = true;
-
-FontRenderer::FontRenderer(const uint8_t* gammaTable)
- : mGammaTable(gammaTable)
- , mCurrentFont(nullptr)
- , mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity)
- , mCurrentCacheTexture(nullptr)
- , mUploadTexture(false)
- , mFunctor(nullptr)
- , mClip(nullptr)
- , mBounds(nullptr)
- , mDrawn(false)
- , mInitialized(false)
- , mLinearFiltering(false) {
- if (sLogFontRendererCreate) {
- INIT_LOGD("Creating FontRenderer");
- }
-
- auto deviceInfo = DeviceInfo::get();
- auto displayInfo = deviceInfo->displayInfo();
- int maxTextureSize = deviceInfo->maxTextureSize();
-
- // Adjust cache size based on Pixel's desnsity.
- constexpr float PIXEL_DENSITY = 2.6;
- const float densityRatio = displayInfo.density / PIXEL_DENSITY;
-
- // TODO: Most devices are hardcoded with this configuration, does it need to be dynamic?
- mSmallCacheWidth =
- OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
- mSmallCacheHeight =
- OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
- mLargeCacheWidth =
- OffscreenBuffer::computeIdealDimension(std::min(2048, maxTextureSize) * densityRatio);
- mLargeCacheHeight =
- OffscreenBuffer::computeIdealDimension(std::min(1024, maxTextureSize) * densityRatio);
-
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
- mSmallCacheWidth, mSmallCacheHeight, mLargeCacheWidth, mLargeCacheHeight >> 1,
- mLargeCacheWidth, mLargeCacheHeight >> 1, mLargeCacheWidth, mLargeCacheHeight);
- }
-
- sLogFontRendererCreate = false;
-}
-
-void clearCacheTextures(std::vector<CacheTexture*>& cacheTextures) {
- for (uint32_t i = 0; i < cacheTextures.size(); i++) {
- delete cacheTextures[i];
- }
- cacheTextures.clear();
-}
-
-FontRenderer::~FontRenderer() {
- clearCacheTextures(mACacheTextures);
- clearCacheTextures(mRGBACacheTextures);
-
- LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
- while (it.next()) {
- delete it.value();
- }
- mActiveFonts.clear();
-}
-
-void FontRenderer::flushAllAndInvalidate() {
- issueDrawCommand();
-
- LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
- while (it.next()) {
- it.value()->invalidateTextureCache();
- }
-
- for (uint32_t i = 0; i < mACacheTextures.size(); i++) {
- mACacheTextures[i]->init();
-
-#ifdef BUGREPORT_FONT_CACHE_USAGE
- mHistoryTracker.glyphsCleared(mACacheTextures[i]);
-#endif
- }
-
- for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
- mRGBACacheTextures[i]->init();
-#ifdef BUGREPORT_FONT_CACHE_USAGE
- mHistoryTracker.glyphsCleared(mRGBACacheTextures[i]);
-#endif
- }
-
- mDrawn = false;
-}
-
-void FontRenderer::flushLargeCaches(std::vector<CacheTexture*>& cacheTextures) {
- // Start from 1; don't deallocate smallest/default texture
- for (uint32_t i = 1; i < cacheTextures.size(); i++) {
- CacheTexture* cacheTexture = cacheTextures[i];
- if (cacheTexture->getPixelBuffer()) {
- cacheTexture->init();
-#ifdef BUGREPORT_FONT_CACHE_USAGE
- mHistoryTracker.glyphsCleared(cacheTexture);
-#endif
- LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
- while (it.next()) {
- it.value()->invalidateTextureCache(cacheTexture);
- }
- cacheTexture->releasePixelBuffer();
- }
- }
-}
-
-void FontRenderer::flushLargeCaches() {
- flushLargeCaches(mACacheTextures);
- flushLargeCaches(mRGBACacheTextures);
-}
-
-CacheTexture* FontRenderer::cacheBitmapInTexture(std::vector<CacheTexture*>& cacheTextures,
- const SkGlyph& glyph, uint32_t* startX,
- uint32_t* startY) {
- for (uint32_t i = 0; i < cacheTextures.size(); i++) {
- if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) {
- return cacheTextures[i];
- }
- }
- // Could not fit glyph into current cache textures
- return nullptr;
-}
-
-void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
- uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
- checkInit();
-
- // If the glyph bitmap is empty let's assum the glyph is valid
- // so we can avoid doing extra work later on
- if (glyph.fWidth == 0 || glyph.fHeight == 0) {
- cachedGlyph->mIsValid = true;
- cachedGlyph->mCacheTexture = nullptr;
- return;
- }
-
- cachedGlyph->mIsValid = false;
-
- // choose an appropriate cache texture list for this glyph format
- SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
- std::vector<CacheTexture*>* cacheTextures = nullptr;
- switch (format) {
- case SkMask::kA8_Format:
- case SkMask::kBW_Format:
- cacheTextures = &mACacheTextures;
- break;
- case SkMask::kARGB32_Format:
- cacheTextures = &mRGBACacheTextures;
- break;
- default:
-#if DEBUG_FONT_RENDERER
- ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format);
-#endif
- return;
- }
-
- // If the glyph is too tall, don't cache it
- if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
- (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) {
- ALOGE("Font size too large to fit in cache. width, height = %i, %i", (int)glyph.fWidth,
- (int)glyph.fHeight);
- return;
- }
-
- // Now copy the bitmap into the cache texture
- uint32_t startX = 0;
- uint32_t startY = 0;
-
- CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
-
- if (!cacheTexture) {
- if (!precaching) {
- // If the new glyph didn't fit and we are not just trying to precache it,
- // clear out the cache and try again
- flushAllAndInvalidate();
- cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
- }
-
- if (!cacheTexture) {
- // either the glyph didn't fit or we're precaching and will cache it when we draw
- return;
- }
- }
-
- cachedGlyph->mCacheTexture = cacheTexture;
-
- *retOriginX = startX;
- *retOriginY = startY;
-
- uint32_t endX = startX + glyph.fWidth;
- uint32_t endY = startY + glyph.fHeight;
-
- uint32_t cacheWidth = cacheTexture->getWidth();
-
- if (!cacheTexture->getPixelBuffer()) {
- Caches::getInstance().textureState().activateTexture(0);
- // Large-glyph texture memory is allocated only as needed
- cacheTexture->allocatePixelBuffer();
- }
- if (!cacheTexture->mesh()) {
- cacheTexture->allocateMesh();
- }
-
- uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
- uint8_t* bitmapBuffer = (uint8_t*)glyph.fImage;
- int srcStride = glyph.rowBytes();
-
- // Copy the glyph image, taking the mask format into account
- switch (format) {
- case SkMask::kA8_Format: {
- uint32_t row =
- (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
- // write leading border line
- memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
- // write glyph data
- if (mGammaTable) {
- for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
- row = cacheY * cacheWidth;
- cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
- for (uint32_t cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
- uint8_t tempCol = bitmapBuffer[bY + bX];
- cacheBuffer[row + cacheX] = mGammaTable[tempCol];
- }
- cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
- }
- } else {
- for (uint32_t cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
- row = cacheY * cacheWidth;
- memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
- cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
- cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
- }
- }
- // write trailing border line
- row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
- memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
- break;
- }
- case SkMask::kARGB32_Format: {
- // prep data lengths
- const size_t formatSize = PixelBuffer::formatSize(GL_RGBA);
- const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE;
- size_t rowSize = formatSize * glyph.fWidth;
- // prep advances
- size_t dstStride = formatSize * cacheWidth;
- // prep indices
- // - we actually start one row early, and then increment before first copy
- uint8_t* src = &bitmapBuffer[0 - srcStride];
- uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)];
- uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)];
- uint8_t* dstL = dst - borderSize;
- uint8_t* dstR = dst + rowSize;
- // write leading border line
- memset(dstL, 0, rowSize + 2 * borderSize);
- // write glyph data
- while (dst < dstEnd) {
- memset(dstL += dstStride, 0, borderSize); // leading border column
- memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data
- memset(dstR += dstStride, 0, borderSize); // trailing border column
- }
- // write trailing border line
- memset(dstL += dstStride, 0, rowSize + 2 * borderSize);
- break;
- }
- case SkMask::kBW_Format: {
- uint32_t cacheX = 0, cacheY = 0;
- uint32_t row =
- (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
- static const uint8_t COLORS[2] = {0, 255};
- // write leading border line
- memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
- // write glyph data
- for (cacheY = startY; cacheY < endY; cacheY++) {
- cacheX = startX;
- int rowBytes = srcStride;
- uint8_t* buffer = bitmapBuffer;
-
- row = cacheY * cacheWidth;
- cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
- while (--rowBytes >= 0) {
- uint8_t b = *buffer++;
- for (int8_t mask = 7; mask >= 0 && cacheX < endX; mask--) {
- cacheBuffer[cacheY * cacheWidth + cacheX++] = COLORS[(b >> mask) & 0x1];
- }
- }
- cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
-
- bitmapBuffer += srcStride;
- }
- // write trailing border line
- row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
- memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
- break;
- }
- default:
- ALOGW("Unknown glyph format: 0x%x", format);
- break;
- }
-
- cachedGlyph->mIsValid = true;
-
-#ifdef BUGREPORT_FONT_CACHE_USAGE
- mHistoryTracker.glyphUploaded(cacheTexture, startX, startY, glyph.fWidth, glyph.fHeight);
-#endif
-}
-
-CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
- bool allocate) {
- CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads);
-
- if (allocate) {
- Caches::getInstance().textureState().activateTexture(0);
- cacheTexture->allocatePixelBuffer();
- cacheTexture->allocateMesh();
- }
-
- return cacheTexture;
-}
-
-void FontRenderer::initTextTexture() {
- clearCacheTextures(mACacheTextures);
- clearCacheTextures(mRGBACacheTextures);
-
- mUploadTexture = false;
- mACacheTextures.push_back(
- createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, GL_ALPHA, true));
- mACacheTextures.push_back(
- createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_ALPHA, false));
- mACacheTextures.push_back(
- createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_ALPHA, false));
- mACacheTextures.push_back(
- createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, GL_ALPHA, false));
- mRGBACacheTextures.push_back(
- createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, GL_RGBA, false));
- mRGBACacheTextures.push_back(
- createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, GL_RGBA, false));
- mCurrentCacheTexture = mACacheTextures[0];
-}
-
-// We don't want to allocate anything unless we actually draw text
-void FontRenderer::checkInit() {
- if (mInitialized) {
- return;
- }
-
- initTextTexture();
-
- mInitialized = true;
-}
-
-void checkTextureUpdateForCache(Caches& caches, std::vector<CacheTexture*>& cacheTextures,
- bool& resetPixelStore, GLuint& lastTextureId) {
- for (uint32_t i = 0; i < cacheTextures.size(); i++) {
- CacheTexture* cacheTexture = cacheTextures[i];
- if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
- if (cacheTexture->getTextureId() != lastTextureId) {
- lastTextureId = cacheTexture->getTextureId();
- caches.textureState().activateTexture(0);
- caches.textureState().bindTexture(lastTextureId);
- }
-
- if (cacheTexture->upload()) {
- resetPixelStore = true;
- }
- }
- }
-}
-
-void FontRenderer::checkTextureUpdate() {
- if (!mUploadTexture) {
- return;
- }
-
- Caches& caches = Caches::getInstance();
- GLuint lastTextureId = 0;
-
- bool resetPixelStore = false;
-
- // Iterate over all the cache textures and see which ones need to be updated
- checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId);
- checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);
-
- // Unbind any PBO we might have used to update textures
- caches.pixelBufferState().unbind();
-
- // Reset to default unpack row length to avoid affecting texture
- // uploads in other parts of the renderer
- if (resetPixelStore) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- }
-
- mUploadTexture = false;
-}
-
-void FontRenderer::issueDrawCommand(std::vector<CacheTexture*>& cacheTextures) {
- if (!mFunctor) return;
-
- bool first = true;
- for (uint32_t i = 0; i < cacheTextures.size(); i++) {
- CacheTexture* texture = cacheTextures[i];
- if (texture->canDraw()) {
- if (first) {
- checkTextureUpdate();
- first = false;
- mDrawn = true;
- }
-
- mFunctor->draw(*texture, mLinearFiltering);
-
- texture->resetMesh();
- }
- }
-}
-
-void FontRenderer::issueDrawCommand() {
- issueDrawCommand(mACacheTextures);
- issueDrawCommand(mRGBACacheTextures);
-}
-
-void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, float x2, float y2,
- float u2, float v2, float x3, float y3, float u3, float v3,
- float x4, float y4, float u4, float v4,
- CacheTexture* texture) {
- if (texture != mCurrentCacheTexture) {
- // Now use the new texture id
- mCurrentCacheTexture = texture;
- }
-
- mCurrentCacheTexture->addQuad(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4);
-}
-
-void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2,
- float u2, float v2, float x3, float y3, float u3, float v3,
- float x4, float y4, float u4, float v4, CacheTexture* texture) {
- if (mClip && (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
- return;
- }
-
- appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
-
- if (mBounds) {
- mBounds->left = std::min(mBounds->left, x1);
- mBounds->top = std::min(mBounds->top, y3);
- mBounds->right = std::max(mBounds->right, x3);
- mBounds->bottom = std::max(mBounds->bottom, y1);
- }
-
- if (mCurrentCacheTexture->endOfMesh()) {
- issueDrawCommand();
- }
-}
-
-void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2,
- float u2, float v2, float x3, float y3, float u3, float v3,
- float x4, float y4, float u4, float v4,
- CacheTexture* texture) {
- appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
-
- if (mBounds) {
- mBounds->left = std::min(mBounds->left, std::min(x1, std::min(x2, std::min(x3, x4))));
- mBounds->top = std::min(mBounds->top, std::min(y1, std::min(y2, std::min(y3, y4))));
- mBounds->right = std::max(mBounds->right, std::max(x1, std::max(x2, std::max(x3, x4))));
- mBounds->bottom = std::max(mBounds->bottom, std::max(y1, std::max(y2, std::max(y3, y4))));
- }
-
- if (mCurrentCacheTexture->endOfMesh()) {
- issueDrawCommand();
- }
-}
-
-void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) {
- mCurrentFont = Font::create(this, paint, matrix);
-}
-
-FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t* glyphs,
- int numGlyphs, float radius,
- const float* positions) {
- checkInit();
-
- DropShadow image;
- image.width = 0;
- image.height = 0;
- image.image = nullptr;
- image.penX = 0;
- image.penY = 0;
-
- if (!mCurrentFont) {
- return image;
- }
-
- mDrawn = false;
- mClip = nullptr;
- mBounds = nullptr;
-
- Rect bounds;
- mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions);
-
- uint32_t intRadius = Blur::convertRadiusToInt(radius);
- uint32_t paddedWidth = (uint32_t)(bounds.right - bounds.left) + 2 * intRadius;
- uint32_t paddedHeight = (uint32_t)(bounds.top - bounds.bottom) + 2 * intRadius;
-
- uint32_t maxSize = Caches::getInstance().maxTextureSize;
- if (paddedWidth > maxSize || paddedHeight > maxSize) {
- return image;
- }
-
- // Align buffers for renderscript usage
- if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
- paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
- }
- int size = paddedWidth * paddedHeight;
- uint8_t* dataBuffer = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
-
- memset(dataBuffer, 0, size);
-
- int penX = intRadius - bounds.left;
- int penY = intRadius - bounds.bottom;
-
- if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
- // text has non-whitespace, so draw and blur to create the shadow
- // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
- // TODO: don't draw pure whitespace in the first place, and avoid needing this check
- mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY, Font::BITMAP, dataBuffer,
- paddedWidth, paddedHeight, nullptr, positions);
-
- // Unbind any PBO we might have used
- Caches::getInstance().pixelBufferState().unbind();
-
- blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
- }
-
- image.width = paddedWidth;
- image.height = paddedHeight;
- image.image = dataBuffer;
- image.penX = penX;
- image.penY = penY;
-
- return image;
-}
-
-void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor) {
- checkInit();
-
- mDrawn = false;
- mBounds = bounds;
- mFunctor = functor;
- mClip = clip;
-}
-
-void FontRenderer::finishRender() {
- mBounds = nullptr;
- mClip = nullptr;
-
- issueDrawCommand();
-}
-
-void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
- const SkMatrix& matrix) {
- Font* font = Font::create(this, paint, matrix);
- font->precache(paint, glyphs, numGlyphs);
-}
-
-void FontRenderer::endPrecaching() {
- checkTextureUpdate();
-}
-
-bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
- int numGlyphs, int x, int y, const float* positions, Rect* bounds,
- TextDrawFunctor* functor, bool forceFinish) {
- if (!mCurrentFont) {
- ALOGE("No font set");
- return false;
- }
-
- initRender(clip, bounds, functor);
- mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions);
-
- if (forceFinish) {
- finishRender();
- }
-
- return mDrawn;
-}
-
-bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
- int numGlyphs, const SkPath* path, float hOffset, float vOffset,
- Rect* bounds, TextDrawFunctor* functor) {
- if (!mCurrentFont) {
- ALOGE("No font set");
- return false;
- }
-
- initRender(clip, bounds, functor);
- mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset);
- finishRender();
-
- return mDrawn;
-}
-
-void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
- uint32_t intRadius = Blur::convertRadiusToInt(radius);
- if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF && radius <= 25.0f) {
- uint8_t* outImage = (uint8_t*)memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
-
- if (mRs == nullptr) {
- mRs = new RSC::RS();
- // a null path is OK because there are no custom kernels used
- // hence nothing gets cached by RS
- if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) {
- mRs.clear();
- ALOGE("blur RS failed to init");
- } else {
- mRsElement = RSC::Element::A_8(mRs);
- mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement);
- }
- }
- if (mRs != nullptr) {
- RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
- RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(
- mRs, t, RS_ALLOCATION_MIPMAP_NONE,
- RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
- RSC::sp<RSC::Allocation> aout = RSC::Allocation::createTyped(
- mRs, t, RS_ALLOCATION_MIPMAP_NONE,
- RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
-
- mRsScript->setRadius(radius);
- mRsScript->setInput(ain);
- mRsScript->forEach(aout);
-
- // replace the original image's pointer, avoiding a copy back to the original buffer
- free(*image);
- *image = outImage;
-
- return;
- }
- }
-
- std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]);
- Blur::generateGaussianWeights(gaussian.get(), radius);
-
- std::unique_ptr<uint8_t[]> scratch(new uint8_t[width * height]);
- Blur::horizontal(gaussian.get(), intRadius, *image, scratch.get(), width, height);
- Blur::vertical(gaussian.get(), intRadius, scratch.get(), *image, width, height);
-}
-
-static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
- uint32_t size = 0;
- for (uint32_t i = 0; i < cacheTextures.size(); i++) {
- CacheTexture* cacheTexture = cacheTextures[i];
- if (cacheTexture && cacheTexture->getPixelBuffer()) {
- size += cacheTexture->getPixelBuffer()->getSize();
- }
- }
- return size;
-}
-
-static uint32_t calculateFreeCacheSize(const std::vector<CacheTexture*>& cacheTextures) {
- uint32_t size = 0;
- for (uint32_t i = 0; i < cacheTextures.size(); i++) {
- CacheTexture* cacheTexture = cacheTextures[i];
- if (cacheTexture && cacheTexture->getPixelBuffer()) {
- size += cacheTexture->calculateFreeMemory();
- }
- }
- return size;
-}
-
-const std::vector<CacheTexture*>& FontRenderer::cacheTexturesForFormat(GLenum format) const {
- switch (format) {
- case GL_ALPHA: {
- return mACacheTextures;
- }
- case GL_RGBA: {
- return mRGBACacheTextures;
- }
- default: {
- LOG_ALWAYS_FATAL("Unsupported format: %d", format);
- // Impossible to hit this, but the compiler doesn't know that
- return *(new std::vector<CacheTexture*>());
- }
- }
-}
-
-static void dumpTextures(String8& log, const char* tag,
- const std::vector<CacheTexture*>& cacheTextures) {
- for (uint32_t i = 0; i < cacheTextures.size(); i++) {
- CacheTexture* cacheTexture = cacheTextures[i];
- if (cacheTexture && cacheTexture->getPixelBuffer()) {
- uint32_t free = cacheTexture->calculateFreeMemory();
- uint32_t total = cacheTexture->getPixelBuffer()->getSize();
- log.appendFormat(" %-4s texture %d %8d / %8d\n", tag, i, total - free, total);
- }
- }
-}
-
-void FontRenderer::dumpMemoryUsage(String8& log) const {
- const uint32_t sizeA8 = getCacheSize(GL_ALPHA);
- const uint32_t usedA8 = sizeA8 - getFreeCacheSize(GL_ALPHA);
- const uint32_t sizeRGBA = getCacheSize(GL_RGBA);
- const uint32_t usedRGBA = sizeRGBA - getFreeCacheSize(GL_RGBA);
- log.appendFormat(" FontRenderer A8 %8d / %8d\n", usedA8, sizeA8);
- dumpTextures(log, "A8", cacheTexturesForFormat(GL_ALPHA));
- log.appendFormat(" FontRenderer RGBA %8d / %8d\n", usedRGBA, sizeRGBA);
- dumpTextures(log, "RGBA", cacheTexturesForFormat(GL_RGBA));
- log.appendFormat(" FontRenderer total %8d / %8d\n", usedA8 + usedRGBA, sizeA8 + sizeRGBA);
-}
-
-uint32_t FontRenderer::getCacheSize(GLenum format) const {
- return calculateCacheSize(cacheTexturesForFormat(format));
-}
-
-uint32_t FontRenderer::getFreeCacheSize(GLenum format) const {
- return calculateFreeCacheSize(cacheTexturesForFormat(format));
-}
-
-uint32_t FontRenderer::getSize() const {
- return getCacheSize(GL_ALPHA) + getCacheSize(GL_RGBA);
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
deleted file mode 100644
index 6b9dec4719cb..000000000000
--- a/libs/hwui/FontRenderer.h
+++ /dev/null
@@ -1,208 +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.
- */
-
-#pragma once
-
-#include "font/CacheTexture.h"
-#include "font/CachedGlyphInfo.h"
-#include "font/Font.h"
-#include "font/FontUtil.h"
-#ifdef BUGREPORT_FONT_CACHE_USAGE
-#include "font/FontCacheHistoryTracker.h"
-#endif
-
-#include <utils/LruCache.h>
-#include <utils/String8.h>
-#include <utils/StrongPointer.h>
-
-#include <SkPaint.h>
-
-#include <GLES2/gl2.h>
-
-#include <vector>
-
-#include "RenderScript.h"
-namespace RSC {
-class Element;
-class RS;
-class ScriptIntrinsicBlur;
-class sp;
-}
-
-namespace android {
-namespace uirenderer {
-
-class BakedOpState;
-class BakedOpRenderer;
-struct ClipBase;
-
-class TextDrawFunctor {
-public:
- TextDrawFunctor(BakedOpRenderer* renderer, const BakedOpState* bakedState, const ClipBase* clip,
- float x, float y, bool pureTranslate, int alpha, SkBlendMode mode,
- const SkPaint* paint)
- : renderer(renderer)
- , bakedState(bakedState)
- , clip(clip)
- , x(x)
- , y(y)
- , pureTranslate(pureTranslate)
- , alpha(alpha)
- , mode(mode)
- , paint(paint) {}
-
- void draw(CacheTexture& texture, bool linearFiltering);
-
- BakedOpRenderer* renderer;
- const BakedOpState* bakedState;
- const ClipBase* clip;
- float x;
- float y;
- bool pureTranslate;
- int alpha;
- SkBlendMode mode;
- const SkPaint* paint;
-};
-
-class FontRenderer {
-public:
- explicit FontRenderer(const uint8_t* gammaTable);
- ~FontRenderer();
-
- void flushLargeCaches(std::vector<CacheTexture*>& cacheTextures);
- void flushLargeCaches();
-
- void setFont(const SkPaint* paint, const SkMatrix& matrix);
-
- void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
- const SkMatrix& matrix);
- void endPrecaching();
-
- bool renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, int numGlyphs,
- int x, int y, const float* positions, Rect* outBounds,
- TextDrawFunctor* functor, bool forceFinish = true);
-
- bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
- int numGlyphs, const SkPath* path, float hOffset, float vOffset,
- Rect* outBounds, TextDrawFunctor* functor);
-
- struct DropShadow {
- uint32_t width;
- uint32_t height;
- uint8_t* image;
- int32_t penX;
- int32_t penY;
- };
-
- // After renderDropShadow returns, the called owns the memory in DropShadow.image
- // and is responsible for releasing it when it's done with it
- DropShadow renderDropShadow(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
- float radius, const float* positions);
-
- void setTextureFiltering(bool linearFiltering) { mLinearFiltering = linearFiltering; }
-
- uint32_t getSize() const;
- void dumpMemoryUsage(String8& log) const;
-
-#ifdef BUGREPORT_FONT_CACHE_USAGE
- FontCacheHistoryTracker& historyTracker() { return mHistoryTracker; }
-#endif
-
-private:
- friend class Font;
-
- const uint8_t* mGammaTable;
-
- void allocateTextureMemory(CacheTexture* cacheTexture);
- void deallocateTextureMemory(CacheTexture* cacheTexture);
- void initTextTexture();
- CacheTexture* createCacheTexture(int width, int height, GLenum format, bool allocate);
- void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, uint32_t* retOriginX,
- uint32_t* retOriginY, bool precaching);
- CacheTexture* cacheBitmapInTexture(std::vector<CacheTexture*>& cacheTextures,
- const SkGlyph& glyph, uint32_t* startX, uint32_t* startY);
-
- void flushAllAndInvalidate();
-
- void checkInit();
- void initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor);
- void finishRender();
-
- void issueDrawCommand(std::vector<CacheTexture*>& cacheTextures);
- void issueDrawCommand();
- void appendMeshQuadNoClip(float x1, float y1, float u1, float v1, float x2, float y2, float u2,
- float v2, float x3, float y3, float u3, float v3, float x4, float y4,
- float u4, float v4, CacheTexture* texture);
- void appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2,
- float v2, float x3, float y3, float u3, float v3, float x4, float y4,
- float u4, float v4, CacheTexture* texture);
- void appendRotatedMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2,
- float v2, float x3, float y3, float u3, float v3, float x4, float y4,
- float u4, float v4, CacheTexture* texture);
-
- void checkTextureUpdate();
-
- void setTextureDirty() { mUploadTexture = true; }
-
- const std::vector<CacheTexture*>& cacheTexturesForFormat(GLenum format) const;
- uint32_t getCacheSize(GLenum format) const;
- uint32_t getFreeCacheSize(GLenum format) const;
-
- uint32_t mSmallCacheWidth;
- uint32_t mSmallCacheHeight;
- uint32_t mLargeCacheWidth;
- uint32_t mLargeCacheHeight;
-
- std::vector<CacheTexture*> mACacheTextures;
- std::vector<CacheTexture*> mRGBACacheTextures;
-
- Font* mCurrentFont;
- LruCache<Font::FontDescription, Font*> mActiveFonts;
-
- CacheTexture* mCurrentCacheTexture;
-
- bool mUploadTexture;
-
- TextDrawFunctor* mFunctor;
- const Rect* mClip;
- Rect* mBounds;
- bool mDrawn;
-
- bool mInitialized;
-
- bool mLinearFiltering;
-
-#ifdef BUGREPORT_FONT_CACHE_USAGE
- FontCacheHistoryTracker mHistoryTracker;
-#endif
-
- // RS constructs
- RSC::sp<RSC::RS> mRs;
- RSC::sp<const RSC::Element> mRsElement;
- RSC::sp<RSC::ScriptIntrinsicBlur> mRsScript;
-
- static void computeGaussianWeights(float* weights, int32_t radius);
- static void horizontalBlur(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest,
- int32_t width, int32_t height);
- static void verticalBlur(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest,
- int32_t width, int32_t height);
-
- // the input image handle may have its pointer replaced (to avoid copies)
- void blurImage(uint8_t** image, int32_t width, int32_t height, float radius);
-};
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
deleted file mode 100644
index ced37ede0746..000000000000
--- a/libs/hwui/FrameBuilder.cpp
+++ /dev/null
@@ -1,976 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FrameBuilder.h"
-
-#include "DeferredLayerUpdater.h"
-#include "LayerUpdateQueue.h"
-#include "RenderNode.h"
-#include "VectorDrawable.h"
-#include "hwui/Canvas.h"
-#include "renderstate/OffscreenBufferPool.h"
-#include "utils/FatVector.h"
-#include "utils/PaintUtils.h"
-#include "utils/TraceUtils.h"
-
-#include <SkPathOps.h>
-#include <utils/TypeHelpers.h>
-
-namespace android {
-namespace uirenderer {
-
-FrameBuilder::FrameBuilder(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
- const LightGeometry& lightGeometry, Caches& caches)
- : mStdAllocator(mAllocator)
- , mLayerBuilders(mStdAllocator)
- , mLayerStack(mStdAllocator)
- , mCanvasState(*this)
- , mCaches(caches)
- , mLightRadius(lightGeometry.radius)
- , mDrawFbo0(true) {
- // Prepare to defer Fbo0
- auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip));
- mLayerBuilders.push_back(fbo0);
- mLayerStack.push_back(0);
- mCanvasState.initializeSaveStack(viewportWidth, viewportHeight, clip.fLeft, clip.fTop,
- clip.fRight, clip.fBottom, lightGeometry.center);
-}
-
-FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const LightGeometry& lightGeometry,
- Caches& caches)
- : mStdAllocator(mAllocator)
- , mLayerBuilders(mStdAllocator)
- , mLayerStack(mStdAllocator)
- , mCanvasState(*this)
- , mCaches(caches)
- , mLightRadius(lightGeometry.radius)
- , mDrawFbo0(false) {
- // TODO: remove, with each layer on its own save stack
-
- // Prepare to defer Fbo0 (which will be empty)
- auto fbo0 = mAllocator.create<LayerBuilder>(1, 1, Rect(1, 1));
- mLayerBuilders.push_back(fbo0);
- mLayerStack.push_back(0);
- mCanvasState.initializeSaveStack(1, 1, 0, 0, 1, 1, lightGeometry.center);
-
- deferLayers(layers);
-}
-
-void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) {
- // Render all layers to be updated, in order. Defer in reverse order, so that they'll be
- // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
- for (int i = layers.entries().size() - 1; i >= 0; i--) {
- RenderNode* layerNode = layers.entries()[i].renderNode.get();
- // only schedule repaint if node still on layer - possible it may have been
- // removed during a dropped frame, but layers may still remain scheduled so
- // as not to lose info on what portion is damaged
- OffscreenBuffer* layer = layerNode->getLayer();
- if (CC_LIKELY(layer)) {
- ATRACE_FORMAT("Optimize HW Layer DisplayList %s %ux%u", layerNode->getName(),
- layerNode->getWidth(), layerNode->getHeight());
-
- Rect layerDamage = layers.entries()[i].damage;
- // TODO: ensure layer damage can't be larger than layer
- layerDamage.doIntersect(0, 0, layer->viewportWidth, layer->viewportHeight);
- layerNode->computeOrdering();
-
- // map current light center into RenderNode's coordinate space
- Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter();
- layer->inverseTransformInWindow.mapPoint3d(lightCenter);
-
- saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0, layerDamage,
- lightCenter, nullptr, layerNode);
-
- if (layerNode->getDisplayList()) {
- deferNodeOps(*layerNode);
- }
- restoreForLayer();
- }
- }
-}
-
-void FrameBuilder::deferRenderNode(RenderNode& renderNode) {
- renderNode.computeOrdering();
-
- mCanvasState.save(SaveFlags::MatrixClip);
- deferNodePropsAndOps(renderNode);
- mCanvasState.restore();
-}
-
-void FrameBuilder::deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode) {
- renderNode.computeOrdering();
-
- mCanvasState.save(SaveFlags::MatrixClip);
- mCanvasState.translate(tx, ty);
- mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
- SkClipOp::kIntersect);
- deferNodePropsAndOps(renderNode);
- mCanvasState.restore();
-}
-
-static Rect nodeBounds(RenderNode& node) {
- auto& props = node.properties();
- return Rect(props.getLeft(), props.getTop(), props.getRight(), props.getBottom());
-}
-
-void FrameBuilder::deferRenderNodeScene(const std::vector<sp<RenderNode> >& nodes,
- const Rect& contentDrawBounds) {
- if (nodes.size() < 1) return;
- if (nodes.size() == 1) {
- if (!nodes[0]->nothingToDraw()) {
- deferRenderNode(*nodes[0]);
- }
- return;
- }
- // It there are multiple render nodes, they are laid out as follows:
- // #0 - backdrop (content + caption)
- // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
- // #2 - additional overlay nodes
- // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
- // resizing however it might become partially visible. The following render loop will crop the
- // backdrop against the content and draw the remaining part of it. It will then draw the content
- // cropped to the backdrop (since that indicates a shrinking of the window).
- //
- // Additional nodes will be drawn on top with no particular clipping semantics.
-
- // Usually the contents bounds should be mContentDrawBounds - however - we will
- // move it towards the fixed edge to give it a more stable appearance (for the moment).
- // If there is no content bounds we ignore the layering as stated above and start with 2.
-
- // Backdrop bounds in render target space
- const Rect backdrop = nodeBounds(*nodes[0]);
-
- // Bounds that content will fill in render target space (note content node bounds may be bigger)
- Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
- content.translate(backdrop.left, backdrop.top);
- if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
- // Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
-
- // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
- // also fill left/top. Currently, both 2up and freeform position content at the top/left of
- // the backdrop, so this isn't necessary.
- if (content.right < backdrop.right) {
- // draw backdrop to right side of content
- deferRenderNode(0, 0,
- Rect(content.right, backdrop.top, backdrop.right, backdrop.bottom),
- *nodes[0]);
- }
- if (content.bottom < backdrop.bottom) {
- // draw backdrop to bottom of content
- // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
- deferRenderNode(0, 0,
- Rect(content.left, content.bottom, content.right, backdrop.bottom),
- *nodes[0]);
- }
- }
-
- if (!nodes[1]->nothingToDraw()) {
- if (!backdrop.isEmpty()) {
- // content node translation to catch up with backdrop
- float dx = contentDrawBounds.left - backdrop.left;
- float dy = contentDrawBounds.top - backdrop.top;
-
- Rect contentLocalClip = backdrop;
- contentLocalClip.translate(dx, dy);
- deferRenderNode(-dx, -dy, contentLocalClip, *nodes[1]);
- } else {
- deferRenderNode(*nodes[1]);
- }
- }
-
- // remaining overlay nodes, simply defer
- for (size_t index = 2; index < nodes.size(); index++) {
- if (!nodes[index]->nothingToDraw()) {
- deferRenderNode(*nodes[index]);
- }
- }
-}
-
-void FrameBuilder::onViewportInitialized() {}
-
-void FrameBuilder::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
-
-void FrameBuilder::deferNodePropsAndOps(RenderNode& node) {
- const RenderProperties& properties = node.properties();
- const Outline& outline = properties.getOutline();
- if (properties.getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty()) ||
- properties.getScaleX() == 0 || properties.getScaleY() == 0) {
- return; // rejected
- }
-
- if (properties.getLeft() != 0 || properties.getTop() != 0) {
- mCanvasState.translate(properties.getLeft(), properties.getTop());
- }
- if (properties.getStaticMatrix()) {
- mCanvasState.concatMatrix(*properties.getStaticMatrix());
- } else if (properties.getAnimationMatrix()) {
- mCanvasState.concatMatrix(*properties.getAnimationMatrix());
- }
- if (properties.hasTransformMatrix()) {
- if (properties.isTransformTranslateOnly()) {
- mCanvasState.translate(properties.getTranslationX(), properties.getTranslationY());
- } else {
- mCanvasState.concatMatrix(*properties.getTransformMatrix());
- }
- }
-
- const int width = properties.getWidth();
- const int height = properties.getHeight();
-
- Rect saveLayerBounds; // will be set to non-empty if saveLayer needed
- const bool isLayer = properties.effectiveLayerType() != LayerType::None;
- int clipFlags = properties.getClippingFlags();
- if (properties.getAlpha() < 1) {
- if (isLayer) {
- clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
- }
- if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
- // simply scale rendering content's alpha
- mCanvasState.scaleAlpha(properties.getAlpha());
- } else {
- // schedule saveLayer by initializing saveLayerBounds
- saveLayerBounds.set(0, 0, width, height);
- if (clipFlags) {
- properties.getClippingRectForFlags(clipFlags, &saveLayerBounds);
- clipFlags = 0; // all clipping done by savelayer
- }
- }
-
- if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) {
- // pretend alpha always causes savelayer to warn about
- // performance problem affecting old versions
- ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", node.getName(), width, height);
- }
- }
- if (clipFlags) {
- Rect clipRect;
- properties.getClippingRectForFlags(clipFlags, &clipRect);
- mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
- SkClipOp::kIntersect);
- }
-
- if (properties.getRevealClip().willClip()) {
- Rect bounds;
- properties.getRevealClip().getBounds(&bounds);
- mCanvasState.setClippingRoundRect(mAllocator, bounds,
- properties.getRevealClip().getRadius());
- } else if (properties.getOutline().willClip()) {
- mCanvasState.setClippingOutline(mAllocator, &(properties.getOutline()));
- }
-
- bool quickRejected = mCanvasState.currentSnapshot()->getRenderTargetClip().isEmpty() ||
- (properties.getClipToBounds() &&
- mCanvasState.quickRejectConservative(0, 0, width, height));
- if (!quickRejected) {
- // not rejected, so defer render as either Layer, or direct (possibly wrapped in saveLayer)
- if (node.getLayer()) {
- // HW layer
- LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(node);
- BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
- if (bakedOpState) {
- // Node's layer already deferred, schedule it to render into parent layer
- currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
- }
- } else if (CC_UNLIKELY(!saveLayerBounds.isEmpty())) {
- // draw DisplayList contents within temporary, since persisted layer could not be used.
- // (temp layers are clipped to viewport, since they don't persist offscreen content)
- SkPaint saveLayerPaint;
- saveLayerPaint.setAlpha(properties.getAlpha());
- deferBeginLayerOp(*mAllocator.create_trivial<BeginLayerOp>(
- saveLayerBounds, Matrix4::identity(),
- nullptr, // no record-time clip - need only respect defer-time one
- &saveLayerPaint));
- deferNodeOps(node);
- deferEndLayerOp(*mAllocator.create_trivial<EndLayerOp>());
- } else {
- deferNodeOps(node);
- }
- }
-}
-
-typedef key_value_pair_t<float, const RenderNodeOp*> ZRenderNodeOpPair;
-
-template <typename V>
-static void buildZSortedChildList(V* zTranslatedNodes, const DisplayList& displayList,
- const DisplayList::Chunk& chunk) {
- if (chunk.beginChildIndex == chunk.endChildIndex) return;
-
- for (size_t i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
- RenderNodeOp* childOp = displayList.getChildren()[i];
- RenderNode* child = childOp->renderNode;
- float childZ = child->properties().getZ();
-
- if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
- zTranslatedNodes->push_back(ZRenderNodeOpPair(childZ, childOp));
- childOp->skipInOrderDraw = true;
- } else if (!child->properties().getProjectBackwards()) {
- // regular, in order drawing DisplayList
- childOp->skipInOrderDraw = false;
- }
- }
-
- // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
- std::stable_sort(zTranslatedNodes->begin(), zTranslatedNodes->end());
-}
-
-template <typename V>
-static size_t findNonNegativeIndex(const V& zTranslatedNodes) {
- for (size_t i = 0; i < zTranslatedNodes.size(); i++) {
- if (zTranslatedNodes[i].key >= 0.0f) return i;
- }
- return zTranslatedNodes.size();
-}
-
-template <typename V>
-void FrameBuilder::defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMode mode,
- const V& zTranslatedNodes) {
- const int size = zTranslatedNodes.size();
- if (size == 0 || (mode == ChildrenSelectMode::Negative && zTranslatedNodes[0].key > 0.0f) ||
- (mode == ChildrenSelectMode::Positive && zTranslatedNodes[size - 1].key < 0.0f)) {
- // no 3d children to draw
- return;
- }
-
- /**
- * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
- * with very similar Z heights to draw together.
- *
- * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
- * underneath both, and neither's shadow is drawn on top of the other.
- */
- const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
- size_t drawIndex, shadowIndex, endIndex;
- if (mode == ChildrenSelectMode::Negative) {
- drawIndex = 0;
- endIndex = nonNegativeIndex;
- shadowIndex = endIndex; // draw no shadows
- } else {
- drawIndex = nonNegativeIndex;
- endIndex = size;
- shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
- }
-
- float lastCasterZ = 0.0f;
- while (shadowIndex < endIndex || drawIndex < endIndex) {
- if (shadowIndex < endIndex) {
- const RenderNodeOp* casterNodeOp = zTranslatedNodes[shadowIndex].value;
- const float casterZ = zTranslatedNodes[shadowIndex].key;
- // attempt to render the shadow if the caster about to be drawn is its caster,
- // OR if its caster's Z value is similar to the previous potential caster
- if (shadowIndex == drawIndex || casterZ - lastCasterZ < 0.1f) {
- deferShadow(reorderClip, *casterNodeOp);
-
- lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
- shadowIndex++;
- continue;
- }
- }
-
- const RenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
- deferRenderNodeOpImpl(*childOp);
- drawIndex++;
- }
-}
-
-void FrameBuilder::deferShadow(const ClipBase* reorderClip, const RenderNodeOp& casterNodeOp) {
- auto& node = *casterNodeOp.renderNode;
- auto& properties = node.properties();
-
- if (properties.getAlpha() <= 0.0f || properties.getOutline().getAlpha() <= 0.0f ||
- !properties.getOutline().getPath() || properties.getScaleX() == 0 ||
- properties.getScaleY() == 0) {
- // no shadow to draw
- return;
- }
-
- const SkPath* casterOutlinePath = properties.getOutline().getPath();
- const SkPath* revealClipPath = properties.getRevealClip().getPath();
- if (revealClipPath && revealClipPath->isEmpty()) return;
-
- float casterAlpha = properties.getAlpha() * properties.getOutline().getAlpha();
-
- // holds temporary SkPath to store the result of intersections
- SkPath* frameAllocatedPath = nullptr;
- const SkPath* casterPath = casterOutlinePath;
-
- // intersect the shadow-casting path with the reveal, if present
- if (revealClipPath) {
- frameAllocatedPath = createFrameAllocatedPath();
-
- Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, frameAllocatedPath);
- casterPath = frameAllocatedPath;
- }
-
- // intersect the shadow-casting path with the clipBounds, if present
- if (properties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS) {
- if (!frameAllocatedPath) {
- frameAllocatedPath = createFrameAllocatedPath();
- }
- Rect clipBounds;
- properties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
- SkPath clipBoundsPath;
- clipBoundsPath.addRect(clipBounds.left, clipBounds.top, clipBounds.right,
- clipBounds.bottom);
-
- Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath);
- casterPath = frameAllocatedPath;
- }
-
- // apply reorder clip to shadow, so it respects clip at beginning of reorderable chunk
- int restoreTo = mCanvasState.save(SaveFlags::MatrixClip);
- mCanvasState.writableSnapshot()->applyClip(reorderClip,
- *mCanvasState.currentSnapshot()->transform);
- if (CC_LIKELY(!mCanvasState.getRenderTargetClipBounds().isEmpty())) {
- Matrix4 shadowMatrixXY(casterNodeOp.localMatrix);
- Matrix4 shadowMatrixZ(casterNodeOp.localMatrix);
- node.applyViewPropertyTransforms(shadowMatrixXY, false);
- node.applyViewPropertyTransforms(shadowMatrixZ, true);
-
- sp<TessellationCache::ShadowTask> task = mCaches.tessellationCache.getShadowTask(
- mCanvasState.currentTransform(), mCanvasState.getLocalClipBounds(),
- casterAlpha >= 1.0f, casterPath, &shadowMatrixXY, &shadowMatrixZ,
- mCanvasState.currentSnapshot()->getRelativeLightCenter(), mLightRadius);
- ShadowOp* shadowOp = mAllocator.create<ShadowOp>(task, casterAlpha);
- BakedOpState* bakedOpState = BakedOpState::tryShadowOpConstruct(
- mAllocator, *mCanvasState.writableSnapshot(), shadowOp);
- if (CC_LIKELY(bakedOpState)) {
- currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Shadow);
- }
- }
- mCanvasState.restoreToCount(restoreTo);
-}
-
-void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
- int count = mCanvasState.save(SaveFlags::MatrixClip);
- const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
-
- SkPath transformedMaskPath; // on stack, since BakedOpState makes a deep copy
- if (projectionReceiverOutline) {
- // transform the mask for this projector into render target space
- // TODO: consider combining both transforms by stashing transform instead of applying
- SkMatrix skCurrentTransform;
- mCanvasState.currentTransform()->copyTo(skCurrentTransform);
- projectionReceiverOutline->transform(skCurrentTransform, &transformedMaskPath);
- mCanvasState.setProjectionPathMask(&transformedMaskPath);
- }
-
- for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) {
- RenderNodeOp* childOp = renderNode.mProjectedNodes[i];
- RenderNode& childNode = *childOp->renderNode;
-
- // Draw child if it has content, but ignore state in childOp - matrix already applied to
- // transformFromCompositingAncestor, and record-time clip is ignored when projecting
- if (!childNode.nothingToDraw()) {
- int restoreTo = mCanvasState.save(SaveFlags::MatrixClip);
-
- // Apply transform between ancestor and projected descendant
- mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor);
-
- deferNodePropsAndOps(childNode);
-
- mCanvasState.restoreToCount(restoreTo);
- }
- }
- mCanvasState.restoreToCount(count);
-}
-
-/**
- * Used to define a list of lambdas referencing private FrameBuilder::onXX::defer() methods.
- *
- * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
- * E.g. a BitmapOp op then would be dispatched to FrameBuilder::onBitmapOp(const BitmapOp&)
- */
-#define OP_RECEIVER(Type) \
- [](FrameBuilder& frameBuilder, const RecordedOp& op) { \
- frameBuilder.defer##Type(static_cast<const Type&>(op)); \
- },
-void FrameBuilder::deferNodeOps(const RenderNode& renderNode) {
- typedef void (*OpDispatcher)(FrameBuilder & frameBuilder, const RecordedOp& op);
- static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER);
-
- // can't be null, since DL=null node rejection happens before deferNodePropsAndOps
- const DisplayList& displayList = *(renderNode.getDisplayList());
- for (auto& chunk : displayList.getChunks()) {
- FatVector<ZRenderNodeOpPair, 16> zTranslatedNodes;
- buildZSortedChildList(&zTranslatedNodes, displayList, chunk);
-
- defer3dChildren(chunk.reorderClip, ChildrenSelectMode::Negative, zTranslatedNodes);
- for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
- const RecordedOp* op = displayList.getOps()[opIndex];
- receivers[op->opId](*this, *op);
-
- if (CC_UNLIKELY(!renderNode.mProjectedNodes.empty() &&
- displayList.projectionReceiveIndex >= 0 &&
- static_cast<int>(opIndex) == displayList.projectionReceiveIndex)) {
- deferProjectedChildren(renderNode);
- }
- }
- defer3dChildren(chunk.reorderClip, ChildrenSelectMode::Positive, zTranslatedNodes);
- }
-}
-
-void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) {
- if (op.renderNode->nothingToDraw()) return;
- int count = mCanvasState.save(SaveFlags::MatrixClip);
-
- // apply state from RecordedOp (clip first, since op's clip is transformed by current matrix)
- mCanvasState.writableSnapshot()->applyClip(op.localClip,
- *mCanvasState.currentSnapshot()->transform);
- mCanvasState.concatMatrix(op.localMatrix);
-
- // then apply state from node properties, and defer ops
- deferNodePropsAndOps(*op.renderNode);
-
- mCanvasState.restoreToCount(count);
-}
-
-void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) {
- if (!op.skipInOrderDraw) {
- deferRenderNodeOpImpl(op);
- }
-}
-
-/**
- * Defers an unmergeable, strokeable op, accounting correctly
- * for paint's style on the bounds being computed.
- */
-BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
- BakedOpState::StrokeBehavior strokeBehavior,
- bool expandForPathTexture) {
- // Note: here we account for stroke when baking the op
- BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
- mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior, expandForPathTexture);
- if (!bakedState) return nullptr; // quick rejected
-
- if (op.opId == RecordedOpId::RectOp && op.paint->getStyle() != SkPaint::kStroke_Style) {
- bakedState->setupOpacity(op.paint);
- }
-
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId);
- return bakedState;
-}
-
-/**
- * Returns batch id for tessellatable shapes, based on paint. Checks to see if path effect/AA will
- * be used, since they trigger significantly different rendering paths.
- *
- * Note: not used for lines/points, since they don't currently support path effects.
- */
-static batchid_t tessBatchId(const RecordedOp& op) {
- const SkPaint& paint = *(op.paint);
- return paint.getPathEffect()
- ? OpBatchType::AlphaMaskTexture
- : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
-}
-
-void FrameBuilder::deferArcOp(const ArcOp& op) {
- // Pass true below since arcs have a tendency to draw outside their expected bounds within
- // their path textures. Passing true makes it more likely that we'll scissor, instead of
- // corrupting the frame by drawing outside of clip bounds.
- deferStrokeableOp(op, tessBatchId(op), BakedOpState::StrokeBehavior::StyleDefined, true);
-}
-
-static bool hasMergeableClip(const BakedOpState& state) {
- return !state.computedState.clipState ||
- state.computedState.clipState->mode == ClipMode::Rectangle;
-}
-
-void FrameBuilder::deferBitmapOp(const BitmapOp& op) {
- BakedOpState* bakedState = tryBakeOpState(op);
- if (!bakedState) return; // quick rejected
-
- if (op.bitmap->isOpaque()) {
- bakedState->setupOpacity(op.paint);
- }
-
- // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation
- // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
- // MergingDrawBatch::canMergeWith()
- if (bakedState->computedState.transform.isSimple() &&
- bakedState->computedState.transform.positiveScale() &&
- PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver &&
- op.bitmap->colorType() != kAlpha_8_SkColorType && hasMergeableClip(*bakedState)) {
- mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
- currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId);
- } else {
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
- }
-}
-
-void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) {
- BakedOpState* bakedState = tryBakeOpState(op);
- if (!bakedState) return; // quick rejected
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
-}
-
-void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) {
- BakedOpState* bakedState = tryBakeOpState(op);
- if (!bakedState) return; // quick rejected
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
-}
-
-void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) {
- Bitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
- SkPaint* paint = op.vectorDrawable->getPaint();
- const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>(
- op.unmappedBounds, op.localMatrix, op.localClip, paint, &bitmap,
- Rect(bitmap.width(), bitmap.height()));
- deferBitmapRectOp(*resolvedOp);
-}
-
-void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) {
- // allocate a temporary oval op (with mAllocator, so it persists until render), so the
- // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
- float x = *(op.x);
- float y = *(op.y);
- float radius = *(op.radius);
- Rect unmappedBounds(x - radius, y - radius, x + radius, y + radius);
- const OvalOp* resolvedOp = mAllocator.create_trivial<OvalOp>(unmappedBounds, op.localMatrix,
- op.localClip, op.paint);
- deferOvalOp(*resolvedOp);
-}
-
-void FrameBuilder::deferColorOp(const ColorOp& op) {
- BakedOpState* bakedState = tryBakeUnboundedOpState(op);
- if (!bakedState) return; // quick rejected
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
-}
-
-void FrameBuilder::deferFunctorOp(const FunctorOp& op) {
- BakedOpState* bakedState = tryBakeUnboundedOpState(op);
- if (!bakedState) return; // quick rejected
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor);
-}
-
-void FrameBuilder::deferLinesOp(const LinesOp& op) {
- batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
- deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
-}
-
-void FrameBuilder::deferOvalOp(const OvalOp& op) {
- deferStrokeableOp(op, tessBatchId(op));
-}
-
-void FrameBuilder::deferPatchOp(const PatchOp& op) {
- BakedOpState* bakedState = tryBakeOpState(op);
- if (!bakedState) return; // quick rejected
-
- if (bakedState->computedState.transform.isPureTranslate() &&
- PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver &&
- hasMergeableClip(*bakedState)) {
- mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
-
- // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together
- currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId);
- } else {
- // Use Bitmap batchId since Bitmap+Patch use same shader
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
- }
-}
-
-void FrameBuilder::deferPathOp(const PathOp& op) {
- auto state = deferStrokeableOp(op, OpBatchType::AlphaMaskTexture);
- if (CC_LIKELY(state)) {
- mCaches.pathCache.precache(op.path, op.paint);
- }
-}
-
-void FrameBuilder::deferPointsOp(const PointsOp& op) {
- batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
- deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
-}
-
-void FrameBuilder::deferRectOp(const RectOp& op) {
- deferStrokeableOp(op, tessBatchId(op));
-}
-
-void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) {
- auto state = deferStrokeableOp(op, tessBatchId(op));
- if (CC_LIKELY(state && !op.paint->getPathEffect())) {
- // TODO: consider storing tessellation task in BakedOpState
- mCaches.tessellationCache.precacheRoundRect(state->computedState.transform, *(op.paint),
- op.unmappedBounds.getWidth(),
- op.unmappedBounds.getHeight(), op.rx, op.ry);
- }
-}
-
-void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
- // allocate a temporary round rect op (with mAllocator, so it persists until render), so the
- // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
- const RoundRectOp* resolvedOp = mAllocator.create_trivial<RoundRectOp>(
- Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)), op.localMatrix, op.localClip,
- op.paint, *op.rx, *op.ry);
- deferRoundRectOp(*resolvedOp);
-}
-
-void FrameBuilder::deferSimpleRectsOp(const SimpleRectsOp& op) {
- BakedOpState* bakedState = tryBakeOpState(op);
- if (!bakedState) return; // quick rejected
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
-}
-
-static batchid_t textBatchId(const SkPaint& paint) {
- // TODO: better handling of shader (since we won't care about color then)
- return paint.getColor() == SK_ColorBLACK ? OpBatchType::Text : OpBatchType::ColorText;
-}
-
-void FrameBuilder::deferTextOp(const TextOp& op) {
- BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
- mAllocator, *mCanvasState.writableSnapshot(), op,
- BakedOpState::StrokeBehavior::StyleDefined, false);
- if (!bakedState) return; // quick rejected
-
- batchid_t batchId = textBatchId(*(op.paint));
- if (bakedState->computedState.transform.isPureTranslate() &&
- PaintUtils::getBlendModeDirect(op.paint) == SkBlendMode::kSrcOver &&
- hasMergeableClip(*bakedState)) {
- mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor());
- currentLayer().deferMergeableOp(mAllocator, bakedState, batchId, mergeId);
- } else {
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId);
- }
-
- FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
- auto& totalTransform = bakedState->computedState.transform;
- if (totalTransform.isPureTranslate() || totalTransform.isPerspective()) {
- fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::I());
- } else {
- // Partial transform case, see BakedOpDispatcher::renderTextOp
- float sx, sy;
- totalTransform.decomposeScale(sx, sy);
- fontRenderer.precache(
- op.paint, op.glyphs, op.glyphCount,
- SkMatrix::MakeScale(roundf(std::max(1.0f, sx)), roundf(std::max(1.0f, sy))));
- }
-}
-
-void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) {
- BakedOpState* bakedState = tryBakeUnboundedOpState(op);
- if (!bakedState) return; // quick rejected
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint)));
-
- mCaches.fontRenderer.getFontRenderer().precache(op.paint, op.glyphs, op.glyphCount,
- SkMatrix::I());
-}
-
-void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) {
- GlLayer* layer = static_cast<GlLayer*>(op.layerHandle->backingLayer());
- if (CC_UNLIKELY(!layer || !layer->isRenderable())) return;
-
- const TextureLayerOp* textureLayerOp = &op;
- // Now safe to access transform (which was potentially unready at record time)
- if (!layer->getTransform().isIdentity()) {
- // non-identity transform present, so 'inject it' into op by copying + replacing matrix
- Matrix4 combinedMatrix(op.localMatrix);
- combinedMatrix.multiply(layer->getTransform());
- textureLayerOp = mAllocator.create<TextureLayerOp>(op, combinedMatrix);
- }
- BakedOpState* bakedState = tryBakeOpState(*textureLayerOp);
-
- if (!bakedState) return; // quick rejected
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer);
-}
-
-void FrameBuilder::saveForLayer(uint32_t layerWidth, uint32_t layerHeight, float contentTranslateX,
- float contentTranslateY, const Rect& repaintRect,
- const Vector3& lightCenter, const BeginLayerOp* beginLayerOp,
- RenderNode* renderNode) {
- mCanvasState.save(SaveFlags::MatrixClip);
- mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight);
- mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
- mCanvasState.writableSnapshot()->setRelativeLightCenter(lightCenter);
- mCanvasState.writableSnapshot()->transform->loadTranslate(contentTranslateX, contentTranslateY,
- 0);
- mCanvasState.writableSnapshot()->setClip(repaintRect.left, repaintRect.top, repaintRect.right,
- repaintRect.bottom);
-
- // create a new layer repaint, and push its index on the stack
- mLayerStack.push_back(mLayerBuilders.size());
- auto newFbo = mAllocator.create<LayerBuilder>(layerWidth, layerHeight, repaintRect,
- beginLayerOp, renderNode);
- mLayerBuilders.push_back(newFbo);
-}
-
-void FrameBuilder::restoreForLayer() {
- // restore canvas, and pop finished layer off of the stack
- mCanvasState.restore();
- mLayerStack.pop_back();
-}
-
-// TODO: defer time rejection (when bounds become empty) + tests
-// Option - just skip layers with no bounds at playback + defer?
-void FrameBuilder::deferBeginLayerOp(const BeginLayerOp& op) {
- uint32_t layerWidth = (uint32_t)op.unmappedBounds.getWidth();
- uint32_t layerHeight = (uint32_t)op.unmappedBounds.getHeight();
-
- auto previous = mCanvasState.currentSnapshot();
- Vector3 lightCenter = previous->getRelativeLightCenter();
-
- // Combine all transforms used to present saveLayer content:
- // parent content transform * canvas transform * bounds offset
- Matrix4 contentTransform(*(previous->transform));
- contentTransform.multiply(op.localMatrix);
- contentTransform.translate(op.unmappedBounds.left, op.unmappedBounds.top);
-
- Matrix4 inverseContentTransform;
- inverseContentTransform.loadInverse(contentTransform);
-
- // map the light center into layer-relative space
- inverseContentTransform.mapPoint3d(lightCenter);
-
- // Clip bounds of temporary layer to parent's clip rect, so:
- Rect saveLayerBounds(layerWidth, layerHeight);
- // 1) transform Rect(width, height) into parent's space
- // note: left/top offsets put in contentTransform above
- contentTransform.mapRect(saveLayerBounds);
- // 2) intersect with parent's clip
- saveLayerBounds.doIntersect(previous->getRenderTargetClip());
- // 3) and transform back
- inverseContentTransform.mapRect(saveLayerBounds);
- saveLayerBounds.doIntersect(Rect(layerWidth, layerHeight));
- saveLayerBounds.roundOut();
-
- // if bounds are reduced, will clip the layer's area by reducing required bounds...
- layerWidth = saveLayerBounds.getWidth();
- layerHeight = saveLayerBounds.getHeight();
- // ...and shifting drawing content to account for left/top side clipping
- float contentTranslateX = -saveLayerBounds.left;
- float contentTranslateY = -saveLayerBounds.top;
-
- saveForLayer(layerWidth, layerHeight, contentTranslateX, contentTranslateY,
- Rect(layerWidth, layerHeight), lightCenter, &op, nullptr);
-}
-
-void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) {
- const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
- int finishedLayerIndex = mLayerStack.back();
-
- restoreForLayer();
-
- // saveLayer will clip & translate the draw contents, so we need
- // to translate the drawLayer by how much the contents was translated
- // TODO: Unify this with beginLayerOp so we don't have to calculate this
- // twice
- uint32_t layerWidth = (uint32_t)beginLayerOp.unmappedBounds.getWidth();
- uint32_t layerHeight = (uint32_t)beginLayerOp.unmappedBounds.getHeight();
-
- auto previous = mCanvasState.currentSnapshot();
- Vector3 lightCenter = previous->getRelativeLightCenter();
-
- // Combine all transforms used to present saveLayer content:
- // parent content transform * canvas transform * bounds offset
- Matrix4 contentTransform(*(previous->transform));
- contentTransform.multiply(beginLayerOp.localMatrix);
- contentTransform.translate(beginLayerOp.unmappedBounds.left, beginLayerOp.unmappedBounds.top);
-
- Matrix4 inverseContentTransform;
- inverseContentTransform.loadInverse(contentTransform);
-
- // map the light center into layer-relative space
- inverseContentTransform.mapPoint3d(lightCenter);
-
- // Clip bounds of temporary layer to parent's clip rect, so:
- Rect saveLayerBounds(layerWidth, layerHeight);
- // 1) transform Rect(width, height) into parent's space
- // note: left/top offsets put in contentTransform above
- contentTransform.mapRect(saveLayerBounds);
- // 2) intersect with parent's clip
- saveLayerBounds.doIntersect(previous->getRenderTargetClip());
- // 3) and transform back
- inverseContentTransform.mapRect(saveLayerBounds);
- saveLayerBounds.doIntersect(Rect(layerWidth, layerHeight));
- saveLayerBounds.roundOut();
-
- Matrix4 localMatrix(beginLayerOp.localMatrix);
- localMatrix.translate(saveLayerBounds.left, saveLayerBounds.top);
-
- // record the draw operation into the previous layer's list of draw commands
- // uses state from the associated beginLayerOp, since it has all the state needed for drawing
- LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(
- beginLayerOp.unmappedBounds, localMatrix, beginLayerOp.localClip, beginLayerOp.paint,
- &(mLayerBuilders[finishedLayerIndex]->offscreenBuffer));
- BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
-
- if (bakedOpState) {
- // Layer will be drawn into parent layer (which is now current, since we popped mLayerStack)
- currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
- } else {
- // Layer won't be drawn - delete its drawing batches to prevent it from doing any work
- // TODO: need to prevent any render work from being done
- // - create layerop earlier for reject purposes?
- mLayerBuilders[finishedLayerIndex]->clear();
- return;
- }
-}
-
-void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
- Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform));
- boundsTransform.multiply(op.localMatrix);
-
- Rect dstRect(op.unmappedBounds);
- boundsTransform.mapRect(dstRect);
- dstRect.roundOut();
- dstRect.doIntersect(mCanvasState.currentSnapshot()->getRenderTargetClip());
-
- if (dstRect.isEmpty()) {
- // Unclipped layer rejected - push a null op, so next EndUnclippedLayerOp is ignored
- currentLayer().activeUnclippedSaveLayers.push_back(nullptr);
- } else {
- // Allocate a holding position for the layer object (copyTo will produce, copyFrom will
- // consume)
- OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr);
-
- /**
- * First, defer an operation to copy out the content from the rendertarget into a layer.
- */
- auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle);
- BakedOpState* bakedState = BakedOpState::directConstruct(
- mAllocator, &(currentLayer().repaintClip), dstRect, *copyToOp);
- currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer);
-
- /**
- * Defer a clear rect, so that clears from multiple unclipped layers can be drawn
- * both 1) simultaneously, and 2) as long after the copyToLayer executes as possible
- */
- currentLayer().deferLayerClear(dstRect);
-
- /**
- * And stash an operation to copy that layer back under the rendertarget until
- * a balanced EndUnclippedLayerOp is seen
- */
- auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle);
- bakedState = BakedOpState::directConstruct(mAllocator, &(currentLayer().repaintClip),
- dstRect, *copyFromOp);
- currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
- }
-}
-
-void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
- LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");
-
- BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
- currentLayer().activeUnclippedSaveLayers.pop_back();
- if (copyFromLayerOp) {
- currentLayer().deferUnmergeableOp(mAllocator, copyFromLayerOp, OpBatchType::CopyFromLayer);
- }
-}
-
-void FrameBuilder::finishDefer() {
- mCaches.fontRenderer.endPrecaching();
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
deleted file mode 100644
index 974daf8a17bb..000000000000
--- a/libs/hwui/FrameBuilder.h
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "BakedOpState.h"
-#include "CanvasState.h"
-#include "DisplayList.h"
-#include "LayerBuilder.h"
-#include "RecordedOp.h"
-#include "utils/GLUtils.h"
-
-#include <unordered_map>
-#include <vector>
-
-struct SkRect;
-
-namespace android {
-namespace uirenderer {
-
-class BakedOpState;
-class LayerUpdateQueue;
-class OffscreenBuffer;
-class Rect;
-
-/**
- * Processes, optimizes, and stores rendering commands from RenderNodes and
- * LayerUpdateQueue, building content needed to render a frame.
- *
- * Resolves final drawing state for each operation (including clip, alpha and matrix), and then
- * reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either
- * from the LayerUpdateQueue, or temporary layers created by saveLayer operations in the
- * draw stream) will create different reorder contexts, each in its own LayerBuilder.
- *
- * Then the prepared or 'baked' drawing commands can be issued by calling the templated
- * replayBakedOps() function, which will dispatch them (including any created merged op collections)
- * to a Dispatcher and Renderer. See BakedOpDispatcher for how these baked drawing operations are
- * resolved into Glops and rendered via BakedOpRenderer.
- *
- * This class is also the authoritative source for traversing RenderNodes, both for standard op
- * traversal within a DisplayList, and for out of order RenderNode traversal for Z and projection.
- */
-class FrameBuilder : public CanvasStateClient {
-public:
- struct LightGeometry {
- Vector3 center;
- float radius;
- };
-
- FrameBuilder(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
- const LightGeometry& lightGeometry, Caches& caches);
-
- FrameBuilder(const LayerUpdateQueue& layerUpdateQueue, const LightGeometry& lightGeometry,
- Caches& caches);
-
- void deferLayers(const LayerUpdateQueue& layers);
-
- void deferRenderNode(RenderNode& renderNode);
-
- void deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode);
-
- void deferRenderNodeScene(const std::vector<sp<RenderNode> >& nodes,
- const Rect& contentDrawBounds);
-
- virtual ~FrameBuilder() {}
-
- /**
- * replayBakedOps() is templated based on what class will receive ops being replayed.
- *
- * It constructs a lookup array of lambdas, which allows a recorded BakeOpState to use
- * state->op->opId to lookup a receiver that will be called when the op is replayed.
- */
- template <typename StaticDispatcher, typename Renderer>
- void replayBakedOps(Renderer& renderer) {
- std::vector<OffscreenBuffer*> temporaryLayers;
- finishDefer();
-/**
- * Defines a LUT of lambdas which allow a recorded BakedOpState to use state->op->opId to
- * dispatch the op via a method on a static dispatcher when the op is replayed.
- *
- * For example a BitmapOp would resolve, via the lambda lookup, to calling:
- *
- * StaticDispatcher::onBitmapOp(Renderer& renderer, const BitmapOp& op, const BakedOpState& state);
- */
-#define X(Type) \
- [](void* renderer, const BakedOpState& state) { \
- StaticDispatcher::on##Type(*(static_cast<Renderer*>(renderer)), \
- static_cast<const Type&>(*(state.op)), state); \
- },
- static BakedOpReceiver unmergedReceivers[] = BUILD_RENDERABLE_OP_LUT(X);
-#undef X
-
-/**
- * Defines a LUT of lambdas which allow merged arrays of BakedOpState* to be passed to a
- * static dispatcher when the group of merged ops is replayed.
- */
-#define X(Type) \
- [](void* renderer, const MergedBakedOpList& opList) { \
- StaticDispatcher::onMerged##Type##s(*(static_cast<Renderer*>(renderer)), opList); \
- },
- static MergedOpReceiver mergedReceivers[] = BUILD_MERGEABLE_OP_LUT(X);
-#undef X
-
- // Relay through layers in reverse order, since layers
- // later in the list will be drawn by earlier ones
- for (int i = mLayerBuilders.size() - 1; i >= 1; i--) {
- GL_CHECKPOINT(MODERATE);
- LayerBuilder& layer = *(mLayerBuilders[i]);
- if (layer.renderNode) {
- // cached HW layer - can't skip layer if empty
- renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect);
- GL_CHECKPOINT(MODERATE);
- layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
- GL_CHECKPOINT(MODERATE);
- renderer.endLayer();
- } else if (!layer.empty()) {
- // save layer - skip entire layer if empty (in which case, LayerOp has null layer).
- layer.offscreenBuffer = renderer.startTemporaryLayer(layer.width, layer.height);
- temporaryLayers.push_back(layer.offscreenBuffer);
- GL_CHECKPOINT(MODERATE);
- layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
- GL_CHECKPOINT(MODERATE);
- renderer.endLayer();
- }
- }
-
- GL_CHECKPOINT(MODERATE);
- if (CC_LIKELY(mDrawFbo0)) {
- const LayerBuilder& fbo0 = *(mLayerBuilders[0]);
- renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
- GL_CHECKPOINT(MODERATE);
- fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
- GL_CHECKPOINT(MODERATE);
- renderer.endFrame(fbo0.repaintRect);
- }
-
- for (auto& temporaryLayer : temporaryLayers) {
- renderer.recycleTemporaryLayer(temporaryLayer);
- }
- }
-
- void dump() const {
- for (auto&& layer : mLayerBuilders) {
- layer->dump();
- }
- }
-
- ///////////////////////////////////////////////////////////////////
- /// CanvasStateClient interface
- ///////////////////////////////////////////////////////////////////
- virtual void onViewportInitialized() override;
- virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
- virtual GLuint getTargetFbo() const override { return 0; }
-
-private:
- void finishDefer();
- enum class ChildrenSelectMode { Negative, Positive };
- void saveForLayer(uint32_t layerWidth, uint32_t layerHeight, float contentTranslateX,
- float contentTranslateY, const Rect& repaintRect, const Vector3& lightCenter,
- const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
- void restoreForLayer();
-
- LayerBuilder& currentLayer() { return *(mLayerBuilders[mLayerStack.back()]); }
-
- BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
- return BakedOpState::tryConstruct(mAllocator, *mCanvasState.writableSnapshot(), recordedOp);
- }
- BakedOpState* tryBakeUnboundedOpState(const RecordedOp& recordedOp) {
- return BakedOpState::tryConstructUnbounded(mAllocator, *mCanvasState.writableSnapshot(),
- recordedOp);
- }
-
- // should always be surrounded by a save/restore pair, and not called if DisplayList is null
- void deferNodePropsAndOps(RenderNode& node);
-
- template <typename V>
- void defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMode mode,
- const V& zTranslatedNodes);
-
- void deferShadow(const ClipBase* reorderClip, const RenderNodeOp& casterOp);
-
- void deferProjectedChildren(const RenderNode& renderNode);
-
- void deferNodeOps(const RenderNode& renderNode);
-
- void deferRenderNodeOpImpl(const RenderNodeOp& op);
-
- void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers);
-
- SkPath* createFrameAllocatedPath() { return mAllocator.create<SkPath>(); }
-
- BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
- BakedOpState::StrokeBehavior strokeBehavior =
- BakedOpState::StrokeBehavior::StyleDefined,
- bool expandForPathTexture = false);
-
-/**
- * Declares all FrameBuilder::deferXXXXOp() methods for every RecordedOp type.
- *
- * These private methods are called from within deferImpl to defer each individual op
- * type differently.
- */
-#define X(Type) void defer##Type(const Type& op);
- MAP_DEFERRABLE_OPS(X)
-#undef X
-
- // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
- LinearAllocator mAllocator;
- LinearStdAllocator<void*> mStdAllocator;
-
- // List of every deferred layer's render state. Replayed in reverse order to render a frame.
- LsaVector<LayerBuilder*> mLayerBuilders;
-
- /*
- * Stack of indices within mLayerBuilders representing currently active layers. If drawing
- * layerA within a layerB, will contain, in order:
- * - 0 (representing FBO 0, always present)
- * - layerB's index
- * - layerA's index
- *
- * Note that this doesn't vector doesn't always map onto all values of mLayerBuilders. When a
- * layer is finished deferring, it will still be represented in mLayerBuilders, but it's index
- * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
- * ops added to it.
- */
- LsaVector<size_t> mLayerStack;
-
- CanvasState mCanvasState;
-
- Caches& mCaches;
-
- float mLightRadius;
-
- const bool mDrawFbo0;
-};
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 5aea04d94cbe..b04c77430367 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -15,7 +15,6 @@
*/
#include "FrameInfoVisualizer.h"
-#include "BakedOpRenderer.h"
#include "IProfileRenderer.h"
#include "utils/Color.h"
@@ -67,6 +66,7 @@ static int dpToPx(int dp, float density) {
FrameInfoVisualizer::FrameInfoVisualizer(FrameInfoSource& source) : mFrameSource(source) {
setDensity(1);
+ consumeProperties();
}
FrameInfoVisualizer::~FrameInfoVisualizer() {
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
deleted file mode 100644
index 88fb16234388..000000000000
--- a/libs/hwui/GammaFontRenderer.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GammaFontRenderer.h"
-#include "Debug.h"
-#include "Properties.h"
-
-namespace android {
-namespace uirenderer {
-
-GammaFontRenderer::GammaFontRenderer() {
- INIT_LOGD("Creating lookup gamma font renderer");
-
-#ifndef ANDROID_ENABLE_LINEAR_BLENDING
- // Compute the gamma tables
- const float gamma = 1.0f / Properties::textGamma;
- for (uint32_t i = 0; i <= 255; i++) {
- mGammaTable[i] = uint8_t((float)::floor(pow(i / 255.0f, gamma) * 255.0f + 0.5f));
- }
-#endif
-}
-
-void GammaFontRenderer::endPrecaching() {
- if (mRenderer) {
- mRenderer->endPrecaching();
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
deleted file mode 100644
index e9002442c64f..000000000000
--- a/libs/hwui/GammaFontRenderer.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_GAMMA_FONT_RENDERER_H
-#define ANDROID_HWUI_GAMMA_FONT_RENDERER_H
-
-#include "FontRenderer.h"
-
-namespace android {
-namespace uirenderer {
-
-class GammaFontRenderer {
-public:
- GammaFontRenderer();
-
- void clear() { mRenderer.reset(nullptr); }
-
- void flush() {
- if (mRenderer) {
- mRenderer->flushLargeCaches();
- }
- }
-
- FontRenderer& getFontRenderer() {
- if (!mRenderer) {
- const uint8_t* table = nullptr;
-#ifndef ANDROID_ENABLE_LINEAR_BLENDING
- table = &mGammaTable[0];
-#endif
- mRenderer.reset(new FontRenderer(table));
- }
- return *mRenderer;
- }
-
- void dumpMemoryUsage(String8& log) const {
- if (mRenderer) {
- mRenderer->dumpMemoryUsage(log);
- } else {
- log.appendFormat("FontRenderer doesn't exist.\n");
- }
- }
-
- uint32_t getSize() const { return mRenderer ? mRenderer->getSize() : 0; }
-
- void endPrecaching();
-
-private:
- std::unique_ptr<FontRenderer> mRenderer;
-#ifndef ANDROID_ENABLE_LINEAR_BLENDING
- uint8_t mGammaTable[256];
-#endif
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_GAMMA_FONT_RENDERER_H
diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp
deleted file mode 100644
index 8357f8ebde2e..000000000000
--- a/libs/hwui/GlLayer.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GlLayer.h"
-
-#include "Caches.h"
-#include "RenderNode.h"
-#include "renderstate/RenderState.h"
-#include "utils/TraceUtils.h"
-
-#include <utils/Log.h>
-
-#define ATRACE_LAYER_WORK(label) \
- ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", label, \
- (renderNode.get() != NULL) ? renderNode->getName() : "", getWidth(), \
- getHeight())
-
-namespace android {
-namespace uirenderer {
-
-GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
- : Layer(renderState, Api::OpenGL, colorFilter, alpha, mode)
- , caches(Caches::getInstance())
- , texture(caches) {
- texture.mWidth = layerWidth;
- texture.mHeight = layerHeight;
- texture.blend = blend;
-}
-
-GlLayer::~GlLayer() {
- // There's a rare possibility that Caches could have been destroyed already
- // since this method is queued up as a task.
- // Since this is a reset method, treat this as non-fatal.
- if (caches.isInitialized() && texture.mId) {
- texture.deleteTexture();
- }
-}
-
-void GlLayer::onGlContextLost() {
- texture.deleteTexture();
-}
-
-void GlLayer::setRenderTarget(GLenum renderTarget) {
- if (renderTarget != getRenderTarget()) {
- // new render target: bind with new target, and update filter/wrap
- texture.mTarget = renderTarget;
- if (texture.mId) {
- caches.textureState().bindTexture(texture.target(), texture.mId);
- }
- texture.setFilter(GL_NEAREST, false, true);
- texture.setWrap(GL_CLAMP_TO_EDGE, false, true);
- }
-}
-
-void GlLayer::generateTexture() {
- if (!texture.mId) {
- glGenTextures(1, &texture.mId);
- }
-}
-
-SkBlendMode GlLayer::getMode() const {
- if (texture.blend || mode != SkBlendMode::kSrcOver) {
- return mode;
- } else {
- return SkBlendMode::kSrc;
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h
deleted file mode 100644
index 4cf8f2522ff9..000000000000
--- a/libs/hwui/GlLayer.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Layer.h"
-
-#include "Texture.h"
-
-namespace android {
-namespace uirenderer {
-
-// Forward declarations
-class Caches;
-
-/**
- * A layer has dimensions and is backed by an OpenGL texture or FBO.
- */
-class GlLayer : public Layer {
-public:
- GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend);
- virtual ~GlLayer();
-
- uint32_t getWidth() const override { return texture.mWidth; }
-
- uint32_t getHeight() const override { return texture.mHeight; }
-
- void setSize(uint32_t width, uint32_t height) override {
- texture.updateLayout(width, height, texture.internalFormat(), texture.format(),
- texture.target());
- }
-
- void setBlend(bool blend) override { texture.blend = blend; }
-
- bool isBlend() const override { return texture.blend; }
-
- inline GLuint getTextureId() const { return texture.id(); }
-
- inline Texture& getTexture() { return texture; }
-
- inline GLenum getRenderTarget() const { return texture.target(); }
-
- inline bool isRenderable() const { return texture.target() != GL_NONE; }
-
- void setRenderTarget(GLenum renderTarget);
-
- void generateTexture();
-
- /**
- * Lost the GL context but the layer is still around, mark it invalid internally
- * so the dtor knows not to do any GL work
- */
- void onGlContextLost();
-
- SkBlendMode getMode() const override;
-
-private:
- Caches& caches;
-
- /**
- * The texture backing this layer.
- */
- Texture texture;
-}; // struct GlLayer
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
deleted file mode 100644
index c68d51604bea..000000000000
--- a/libs/hwui/Glop.h
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "FloatColor.h"
-#include "Matrix.h"
-#include "Program.h"
-#include "Rect.h"
-#include "SkiaShader.h"
-#include "utils/Macros.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-namespace android {
-namespace uirenderer {
-
-class Program;
-class RoundRectClipState;
-class Texture;
-
-/*
- * Enumerates optional vertex attributes
- *
- * Position is always enabled by MeshState, these other attributes
- * are enabled/disabled dynamically based on mesh content.
- */
-
-namespace VertexAttribFlags {
-enum {
- // Mesh is pure x,y vertex pairs
- None = 0,
- // Mesh has texture coordinates embedded. Note that texture can exist without this flag
- // being set, if coordinates passed to sampler are determined another way.
- TextureCoord = 1 << 0,
- // Mesh has color embedded (to export to varying)
- Color = 1 << 1,
- // Mesh has alpha embedded (to export to varying)
- Alpha = 1 << 2,
-};
-};
-
-/*
- * Enumerates transform features
- */
-namespace TransformFlags {
-enum {
- None = 0,
-
- // offset the eventual drawing matrix by a tiny amount to
- // disambiguate sampling patterns with non-AA rendering
- OffsetByFudgeFactor = 1 << 0,
-
- // Canvas transform isn't applied to the mesh at draw time,
- // since it's already built in.
- MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove for HWUI_NEW_OPS
-};
-};
-
-/**
- * Structure containing all data required to issue an OpenGL draw
- *
- * Includes all of the mesh, fill, and GL state required to perform
- * the operation. Pieces of data are either directly copied into the
- * structure, or stored as a pointer or GL object reference to data
- * managed.
- *
- * Eventually, a Glop should be able to be drawn multiple times from
- * a single construction, up until GL context destruction. Currently,
- * vertex/index/Texture/RoundRectClipState pointers prevent this from
- * being safe.
- */
-struct Glop {
- PREVENT_COPY_AND_ASSIGN(Glop);
-
-public:
- Glop() {}
- struct Mesh {
- GLuint primitiveMode; // GL_TRIANGLES and GL_TRIANGLE_STRIP supported
-
- // buffer object and void* are mutually exclusive.
- // Only GL_UNSIGNED_SHORT supported.
- struct Indices {
- GLuint bufferObject;
- const void* indices;
- } indices;
-
- // buffer object and void*s are mutually exclusive.
- // TODO: enforce mutual exclusion with restricted setters and/or unions
- struct Vertices {
- GLuint bufferObject;
- int attribFlags;
- const void* position;
- const void* texCoord;
- const void* color;
- GLsizei stride;
- } vertices;
-
- int elementCount;
- int vertexCount; // only used for meshes (for glDrawRangeElements)
- TextureVertex mappedVertices[4];
- } mesh;
-
- struct Fill {
- Program* program;
-
- struct TextureData {
- Texture* texture;
- GLenum filter;
- GLenum clamp;
- Matrix4* textureTransform;
- } texture;
-
- bool colorEnabled;
- FloatColor color;
-
- ProgramDescription::ColorFilterMode filterMode;
- union Filter {
- struct Matrix {
- float matrix[16];
- float vector[4];
- } matrix;
- FloatColor color;
- } filter;
-
- SkiaShaderData skiaShaderData;
- } fill;
-
- struct Transform {
- // modelView transform, accounting for delta between mesh transform and content of the mesh
- // often represents x/y offsets within command, or scaling for mesh unit size
- Matrix4 modelView;
-
- // Canvas transform of Glop - not necessarily applied to geometry (see flags)
- Matrix4 canvas;
- int transformFlags;
-
- const Matrix4& meshTransform() const {
- return (transformFlags & TransformFlags::MeshIgnoresCanvasTransform)
- ? Matrix4::identity()
- : canvas;
- }
- } transform;
-
- const RoundRectClipState* roundRectClipState = nullptr;
-
- /**
- * Blending to be used by this draw - both GL_NONE if blending is disabled.
- *
- * Defined by fill step, but can be force-enabled by presence of kAlpha_Attrib
- */
- struct Blend {
- GLenum src;
- GLenum dst;
- } blend;
-
- /**
- * Additional render state to enumerate:
- * - scissor + (bits for whether each of LTRB needed?)
- * - stencil mode (draw into, mask, count, etc)
- */
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
deleted file mode 100644
index 2f107a004731..000000000000
--- a/libs/hwui/GlopBuilder.cpp
+++ /dev/null
@@ -1,691 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "GlopBuilder.h"
-
-#include "Caches.h"
-#include "GlLayer.h"
-#include "Glop.h"
-#include "Layer.h"
-#include "Matrix.h"
-#include "Patch.h"
-#include "PathCache.h"
-#include "SkiaShader.h"
-#include "Texture.h"
-#include "VertexBuffer.h"
-#include "renderstate/MeshState.h"
-#include "renderstate/RenderState.h"
-#include "utils/PaintUtils.h"
-
-#include <GLES2/gl2.h>
-#include <SkPaint.h>
-
-#define DEBUG_GLOP_BUILDER 0
-
-#if DEBUG_GLOP_BUILDER
-
-#define TRIGGER_STAGE(stageFlag) \
- LOG_ALWAYS_FATAL_IF((stageFlag)&mStageFlags, "Stage %d cannot be run twice", (stageFlag)); \
- mStageFlags = static_cast<StageFlags>(mStageFlags | (stageFlag))
-
-#define REQUIRE_STAGES(requiredFlags) \
- LOG_ALWAYS_FATAL_IF((mStageFlags & (requiredFlags)) != (requiredFlags), \
- "not prepared for current stage")
-
-#else
-
-#define TRIGGER_STAGE(stageFlag) ((void)0)
-#define REQUIRE_STAGES(requiredFlags) ((void)0)
-
-#endif
-
-namespace android {
-namespace uirenderer {
-
-static void setUnitQuadTextureCoords(Rect uvs, TextureVertex* quadVertex) {
- quadVertex[0] = {0, 0, uvs.left, uvs.top};
- quadVertex[1] = {1, 0, uvs.right, uvs.top};
- quadVertex[2] = {0, 1, uvs.left, uvs.bottom};
- quadVertex[3] = {1, 1, uvs.right, uvs.bottom};
-}
-
-GlopBuilder::GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop)
- : mRenderState(renderState), mCaches(caches), mShader(nullptr), mOutGlop(outGlop) {
- mStageFlags = kInitialStage;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Mesh
-////////////////////////////////////////////////////////////////////////////////
-
-GlopBuilder& GlopBuilder::setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount) {
- TRIGGER_STAGE(kMeshStage);
-
- mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
- mOutGlop->mesh.indices = {mRenderState.meshState().getQuadListIBO(), nullptr};
- mOutGlop->mesh.vertices = {vbo, VertexAttribFlags::TextureCoord,
- nullptr, (const void*)kMeshTextureOffset,
- nullptr, kTextureVertexStride};
- mOutGlop->mesh.elementCount = elementCount;
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setMeshUnitQuad() {
- TRIGGER_STAGE(kMeshStage);
-
- mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
- mOutGlop->mesh.indices = {0, nullptr};
- mOutGlop->mesh.vertices = {mRenderState.meshState().getUnitQuadVBO(),
- VertexAttribFlags::None,
- nullptr,
- nullptr,
- nullptr,
- kTextureVertexStride};
- mOutGlop->mesh.elementCount = 4;
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setMeshTexturedUnitQuad(const UvMapper* uvMapper) {
- if (uvMapper) {
- // can't use unit quad VBO, so build UV vertices manually
- return setMeshTexturedUvQuad(uvMapper, Rect(1, 1));
- }
-
- TRIGGER_STAGE(kMeshStage);
-
- mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
- mOutGlop->mesh.indices = {0, nullptr};
- mOutGlop->mesh.vertices = {mRenderState.meshState().getUnitQuadVBO(),
- VertexAttribFlags::TextureCoord,
- nullptr,
- (const void*)kMeshTextureOffset,
- nullptr,
- kTextureVertexStride};
- mOutGlop->mesh.elementCount = 4;
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setMeshTexturedUvQuad(const UvMapper* uvMapper, Rect uvs) {
- TRIGGER_STAGE(kMeshStage);
-
- if (CC_UNLIKELY(uvMapper)) {
- uvMapper->map(uvs);
- }
- setUnitQuadTextureCoords(uvs, &mOutGlop->mesh.mappedVertices[0]);
-
- const TextureVertex* textureVertex = mOutGlop->mesh.mappedVertices;
- mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
- mOutGlop->mesh.indices = {0, nullptr};
- mOutGlop->mesh.vertices = {0,
- VertexAttribFlags::TextureCoord,
- &textureVertex[0].x,
- &textureVertex[0].u,
- nullptr,
- kTextureVertexStride};
- mOutGlop->mesh.elementCount = 4;
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setMeshIndexedQuads(Vertex* vertexData, int quadCount) {
- TRIGGER_STAGE(kMeshStage);
-
- mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
- mOutGlop->mesh.indices = {mRenderState.meshState().getQuadListIBO(), nullptr};
- mOutGlop->mesh.vertices = {
- 0, VertexAttribFlags::None, vertexData, nullptr, nullptr, kVertexStride};
- mOutGlop->mesh.elementCount = 6 * quadCount;
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount) {
- TRIGGER_STAGE(kMeshStage);
-
- mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
- mOutGlop->mesh.indices = {mRenderState.meshState().getQuadListIBO(), nullptr};
- mOutGlop->mesh.vertices = {0,
- VertexAttribFlags::TextureCoord,
- &vertexData[0].x,
- &vertexData[0].u,
- nullptr,
- kTextureVertexStride};
- mOutGlop->mesh.elementCount = elementCount;
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexData,
- int elementCount) {
- TRIGGER_STAGE(kMeshStage);
-
- mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
- mOutGlop->mesh.indices = {0, nullptr};
- mOutGlop->mesh.vertices = {0,
- VertexAttribFlags::TextureCoord | VertexAttribFlags::Color,
- &vertexData[0].x,
- &vertexData[0].u,
- &vertexData[0].r,
- kColorTextureVertexStride};
- mOutGlop->mesh.elementCount = elementCount;
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer) {
- TRIGGER_STAGE(kMeshStage);
-
- const VertexBuffer::MeshFeatureFlags flags = vertexBuffer.getMeshFeatureFlags();
-
- bool alphaVertex = flags & VertexBuffer::kAlpha;
- bool indices = flags & VertexBuffer::kIndices;
-
- mOutGlop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
- mOutGlop->mesh.indices = {0, vertexBuffer.getIndices()};
- mOutGlop->mesh.vertices = {0,
- alphaVertex ? VertexAttribFlags::Alpha : VertexAttribFlags::None,
- vertexBuffer.getBuffer(),
- nullptr,
- nullptr,
- alphaVertex ? kAlphaVertexStride : kVertexStride};
- mOutGlop->mesh.elementCount =
- indices ? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount();
- mOutGlop->mesh.vertexCount = vertexBuffer.getVertexCount(); // used for glDrawRangeElements()
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setMeshPatchQuads(const Patch& patch) {
- TRIGGER_STAGE(kMeshStage);
-
- mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
- mOutGlop->mesh.indices = {mRenderState.meshState().getQuadListIBO(), nullptr};
- mOutGlop->mesh.vertices = {mCaches.patchCache.getMeshBuffer(),
- VertexAttribFlags::TextureCoord,
- (void*)patch.positionOffset,
- (void*)patch.textureOffset,
- nullptr,
- kTextureVertexStride};
- mOutGlop->mesh.elementCount = patch.indexCount;
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Fill
-////////////////////////////////////////////////////////////////////////////////
-
-void GlopBuilder::setFill(int color, float alphaScale, SkBlendMode mode,
- Blend::ModeOrderSwap modeUsage, const SkShader* shader,
- const SkColorFilter* colorFilter) {
- if (mode != SkBlendMode::kClear) {
- if (!shader) {
- FloatColor c;
- c.set(color);
- c.r *= alphaScale;
- c.g *= alphaScale;
- c.b *= alphaScale;
- c.a *= alphaScale;
- mOutGlop->fill.color = c;
- } else {
- float alpha = (SkColorGetA(color) / 255.0f) * alphaScale;
- mOutGlop->fill.color = {1, 1, 1, alpha};
- }
- } else {
- mOutGlop->fill.color = {0, 0, 0, 1};
- }
-
- mOutGlop->blend = {GL_ZERO, GL_ZERO};
- if (mOutGlop->fill.color.a < 1.0f ||
- (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha) ||
- (mOutGlop->fill.texture.texture && mOutGlop->fill.texture.texture->blend) ||
- mOutGlop->roundRectClipState || PaintUtils::isBlendedShader(shader) ||
- PaintUtils::isBlendedColorFilter(colorFilter) || mode != SkBlendMode::kSrcOver) {
- if (CC_LIKELY(mode <= SkBlendMode::kScreen)) {
- Blend::getFactors(mode, modeUsage, &mOutGlop->blend.src, &mOutGlop->blend.dst);
- } else {
- // These blend modes are not supported by OpenGL directly and have
- // to be implemented using shaders. Since the shader will perform
- // the blending, don't enable GL blending off here
- // If the blend mode cannot be implemented using shaders, fall
- // back to the default SrcOver blend mode instead
- if (CC_UNLIKELY(mCaches.extensions().hasFramebufferFetch())) {
- mDescription.framebufferMode = mode;
- mDescription.swapSrcDst = (modeUsage == Blend::ModeOrderSwap::Swap);
- // blending in shader, don't enable
- } else {
- // unsupported
- Blend::getFactors(SkBlendMode::kSrcOver, modeUsage, &mOutGlop->blend.src,
- &mOutGlop->blend.dst);
- }
- }
- }
- mShader = shader; // shader resolved in ::build()
-
- if (colorFilter) {
- SkColor color;
- SkBlendMode bmode;
- SkScalar srcColorMatrix[20];
- if (colorFilter->asColorMode(&color, &bmode)) {
- mOutGlop->fill.filterMode = mDescription.colorOp =
- ProgramDescription::ColorFilterMode::Blend;
- mDescription.colorMode = bmode;
- mOutGlop->fill.filter.color.set(color);
- } else if (colorFilter->asColorMatrix(srcColorMatrix)) {
- mOutGlop->fill.filterMode = mDescription.colorOp =
- ProgramDescription::ColorFilterMode::Matrix;
-
- float* colorMatrix = mOutGlop->fill.filter.matrix.matrix;
- memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float));
- memcpy(&colorMatrix[4], &srcColorMatrix[5], 4 * sizeof(float));
- memcpy(&colorMatrix[8], &srcColorMatrix[10], 4 * sizeof(float));
- memcpy(&colorMatrix[12], &srcColorMatrix[15], 4 * sizeof(float));
-
- // Skia uses the range [0..255] for the addition vector, but we need
- // the [0..1] range to apply the vector in GLSL
- float* colorVector = mOutGlop->fill.filter.matrix.vector;
- colorVector[0] = EOCF(srcColorMatrix[4] / 255.0f);
- colorVector[1] = EOCF(srcColorMatrix[9] / 255.0f);
- colorVector[2] = EOCF(srcColorMatrix[14] / 255.0f);
- colorVector[3] = srcColorMatrix[19] / 255.0f; // alpha is linear
- } else {
- ALOGE("unsupported ColorFilter type: %s", colorFilter->getTypeName());
- LOG_ALWAYS_FATAL("unsupported ColorFilter");
- }
- } else {
- mOutGlop->fill.filterMode = ProgramDescription::ColorFilterMode::None;
- }
-}
-
-GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture, const int textureFillFlags,
- const SkPaint* paint, float alphaScale) {
- TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
-
- GLenum filter = (textureFillFlags & TextureFillFlags::ForceFilter)
- ? GL_LINEAR
- : PaintUtils::getFilter(paint);
- mOutGlop->fill.texture = {&texture, filter, GL_CLAMP_TO_EDGE, nullptr};
-
- if (paint) {
- int color = paint->getColor();
- SkShader* shader = paint->getShader();
-
- if (!(textureFillFlags & TextureFillFlags::IsAlphaMaskTexture)) {
- // Texture defines color, so disable shaders, and reset all non-alpha color channels
- color |= 0x00FFFFFF;
- shader = nullptr;
- }
- setFill(color, alphaScale, paint->getBlendMode(), Blend::ModeOrderSwap::NoSwap, shader,
- paint->getColorFilter());
- } else {
- mOutGlop->fill.color = {alphaScale, alphaScale, alphaScale, alphaScale};
-
- if (alphaScale < 1.0f || (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha) ||
- texture.blend || mOutGlop->roundRectClipState) {
- Blend::getFactors(SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap,
- &mOutGlop->blend.src, &mOutGlop->blend.dst);
- } else {
- mOutGlop->blend = {GL_ZERO, GL_ZERO};
- }
- }
-
- if (textureFillFlags & TextureFillFlags::IsAlphaMaskTexture) {
- mDescription.modulate = mOutGlop->fill.color.isNotBlack();
- mDescription.hasAlpha8Texture = true;
- } else {
- mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
- }
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale, bool shadowInterp) {
- TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
-
- if (CC_LIKELY(!shadowInterp)) {
- mOutGlop->fill.texture = {nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr};
- } else {
- mOutGlop->fill.texture = {mCaches.textureState().getShadowLutTexture(), GL_INVALID_ENUM,
- GL_INVALID_ENUM, nullptr};
- }
-
- setFill(paint.getColor(), alphaScale, paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
- paint.getShader(), paint.getColorFilter());
- mDescription.useShadowAlphaInterp = shadowInterp;
- mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setFillPathTexturePaint(PathTexture& texture, const SkPaint& paint,
- float alphaScale) {
- TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
-
- // specify invalid filter/clamp, since these are always static for PathTextures
- mOutGlop->fill.texture = {&texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr};
-
- setFill(paint.getColor(), alphaScale, paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
- paint.getShader(), paint.getColorFilter());
-
- mDescription.hasAlpha8Texture = true;
- mDescription.modulate = mOutGlop->fill.color.isNotBlack();
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor,
- const SkPaint& paint, float alphaScale) {
- TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
-
- // specify invalid filter/clamp, since these are always static for ShadowTextures
- mOutGlop->fill.texture = {&texture, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr};
-
- const int ALPHA_BITMASK = SK_ColorBLACK;
- const int COLOR_BITMASK = ~ALPHA_BITMASK;
- if ((shadowColor & ALPHA_BITMASK) == ALPHA_BITMASK) {
- // shadow color is fully opaque: override its alpha with that of paint
- shadowColor &= paint.getColor() | COLOR_BITMASK;
- }
-
- setFill(shadowColor, alphaScale, paint.getBlendMode(), Blend::ModeOrderSwap::NoSwap,
- paint.getShader(), paint.getColorFilter());
-
- mDescription.hasAlpha8Texture = true;
- mDescription.modulate = mOutGlop->fill.color.isNotBlack();
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setFillBlack() {
- TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
-
- mOutGlop->fill.texture = {nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr};
- setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kSrcOver, Blend::ModeOrderSwap::NoSwap, nullptr,
- nullptr);
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setFillClear() {
- TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
-
- mOutGlop->fill.texture = {nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr};
- setFill(SK_ColorBLACK, 1.0f, SkBlendMode::kClear, Blend::ModeOrderSwap::NoSwap, nullptr,
- nullptr);
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* colorFilter,
- float alpha, SkBlendMode mode,
- Blend::ModeOrderSwap modeUsage) {
- TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
-
- mOutGlop->fill.texture = {&texture, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr};
-
- setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter);
-
- mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setFillTextureLayer(GlLayer& layer, float alpha) {
- TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
-
- mOutGlop->fill.texture = {&(layer.getTexture()), GL_LINEAR, GL_CLAMP_TO_EDGE,
- &layer.getTexTransform()};
-
- setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap, nullptr,
- layer.getColorFilter());
-
- mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
- mDescription.hasTextureTransform = true;
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& textureTransform,
- bool requiresFilter) {
- TRIGGER_STAGE(kFillStage);
- REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
-
- GLenum filter = requiresFilter ? GL_LINEAR : GL_NEAREST;
- mOutGlop->fill.texture = {&texture, filter, GL_CLAMP_TO_EDGE, &textureTransform};
-
- setFill(SK_ColorWHITE, 1.0f, SkBlendMode::kSrc, Blend::ModeOrderSwap::NoSwap, nullptr, nullptr);
-
- mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
- mDescription.hasTextureTransform = true;
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setGammaCorrection(bool enabled) {
- REQUIRE_STAGES(kFillStage);
-
- mDescription.hasGammaCorrection = enabled;
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Transform
-////////////////////////////////////////////////////////////////////////////////
-
-GlopBuilder& GlopBuilder::setTransform(const Matrix4& canvas, const int transformFlags) {
- TRIGGER_STAGE(kTransformStage);
-
- mOutGlop->transform.canvas = canvas;
- mOutGlop->transform.transformFlags = transformFlags;
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// ModelView
-////////////////////////////////////////////////////////////////////////////////
-
-GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) {
- TRIGGER_STAGE(kModelViewStage);
-
- mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f);
- mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setModelViewMapUnitToRectSnap(const Rect destination) {
- TRIGGER_STAGE(kModelViewStage);
- REQUIRE_STAGES(kTransformStage | kFillStage);
-
- float left = destination.left;
- float top = destination.top;
-
- const Matrix4& meshTransform = mOutGlop->transform.meshTransform();
- if (CC_LIKELY(meshTransform.isPureTranslate())) {
- // snap by adjusting the model view matrix
- const float translateX = meshTransform.getTranslateX();
- const float translateY = meshTransform.getTranslateY();
-
- left = (int)floorf(left + translateX + 0.5f) - translateX;
- top = (int)floorf(top + translateY + 0.5f) - translateY;
- mOutGlop->fill.texture.filter = GL_NEAREST;
- }
-
- mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f);
- mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, const Rect source) {
- TRIGGER_STAGE(kModelViewStage);
-
- mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
- return *this;
-}
-
-GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offsetY,
- const Rect source) {
- TRIGGER_STAGE(kModelViewStage);
- REQUIRE_STAGES(kTransformStage | kFillStage);
-
- const Matrix4& meshTransform = mOutGlop->transform.meshTransform();
- if (CC_LIKELY(meshTransform.isPureTranslate())) {
- // snap by adjusting the model view matrix
- const float translateX = meshTransform.getTranslateX();
- const float translateY = meshTransform.getTranslateY();
-
- offsetX = (int)floorf(offsetX + translateX + source.left + 0.5f) - translateX - source.left;
- offsetY = (int)floorf(offsetY + translateY + source.top + 0.5f) - translateY - source.top;
- mOutGlop->fill.texture.filter = GL_NEAREST;
- }
-
- mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// RoundRectClip
-////////////////////////////////////////////////////////////////////////////////
-
-GlopBuilder& GlopBuilder::setRoundRectClipState(const RoundRectClipState* roundRectClipState) {
- TRIGGER_STAGE(kRoundRectClipStage);
-
- mOutGlop->roundRectClipState = roundRectClipState;
- mDescription.hasRoundRectClip = roundRectClipState != nullptr;
- return *this;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Build
-////////////////////////////////////////////////////////////////////////////////
-
-void verify(const ProgramDescription& description, const Glop& glop) {
- if (glop.fill.texture.texture != nullptr) {
- LOG_ALWAYS_FATAL_IF(
- ((description.hasTexture && description.hasExternalTexture) ||
- (!description.hasTexture && !description.hasExternalTexture &&
- !description.useShadowAlphaInterp) ||
- ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0 &&
- !description.useShadowAlphaInterp)),
- "Texture %p, hT%d, hET %d, attribFlags %x", glop.fill.texture.texture,
- description.hasTexture, description.hasExternalTexture,
- glop.mesh.vertices.attribFlags);
- } else {
- LOG_ALWAYS_FATAL_IF(
- (description.hasTexture || description.hasExternalTexture ||
- ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) != 0)),
- "No texture, hT%d, hET %d, attribFlags %x", description.hasTexture,
- description.hasExternalTexture, glop.mesh.vertices.attribFlags);
- }
-
- if ((glop.mesh.vertices.attribFlags & VertexAttribFlags::Alpha) &&
- glop.mesh.vertices.bufferObject) {
- LOG_ALWAYS_FATAL("VBO and alpha attributes are not currently compatible");
- }
-
- if (description.hasTextureTransform != (glop.fill.texture.textureTransform != nullptr)) {
- LOG_ALWAYS_FATAL("Texture transform incorrectly specified");
- }
-}
-
-void GlopBuilder::build() {
- REQUIRE_STAGES(kAllStages);
- if (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) {
- Texture* texture = mOutGlop->fill.texture.texture;
- if (texture->target() == GL_TEXTURE_2D) {
- mDescription.hasTexture = true;
- } else {
- mDescription.hasExternalTexture = true;
- }
- mDescription.hasLinearTexture = texture->isLinear();
- mDescription.hasColorSpaceConversion = texture->hasColorSpaceConversion();
- mDescription.transferFunction = texture->getTransferFunctionType();
- mDescription.hasTranslucentConversion = texture->blend;
- }
-
- mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Color;
- mDescription.hasVertexAlpha = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Alpha;
-
- // Enable debug highlight when what we're about to draw is tested against
- // the stencil buffer and if stencil highlight debugging is on
- mDescription.hasDebugHighlight =
- !Properties::debugOverdraw &&
- Properties::debugStencilClip == StencilClipDebug::ShowHighlight &&
- mRenderState.stencil().isTestEnabled();
-
- // serialize shader info into ShaderData
- GLuint textureUnit = mOutGlop->fill.texture.texture ? 1 : 0;
-
- if (CC_LIKELY(!mShader)) {
- mOutGlop->fill.skiaShaderData.skiaShaderType = kNone_SkiaShaderType;
- } else {
- Matrix4 shaderMatrix;
- if (mOutGlop->transform.transformFlags & TransformFlags::MeshIgnoresCanvasTransform) {
- // canvas level transform was built into the modelView and geometry,
- // so the shader matrix must reverse this
- shaderMatrix.loadInverse(mOutGlop->transform.canvas);
- shaderMatrix.multiply(mOutGlop->transform.modelView);
- } else {
- shaderMatrix = mOutGlop->transform.modelView;
- }
- SkiaShader::store(mCaches, *mShader, shaderMatrix, &textureUnit, &mDescription,
- &(mOutGlop->fill.skiaShaderData));
- }
-
- // duplicates ProgramCache's definition of color uniform presence
- const bool singleColor = !mDescription.hasTexture && !mDescription.hasExternalTexture &&
- !mDescription.hasGradient && !mDescription.hasBitmap;
- mOutGlop->fill.colorEnabled = mDescription.modulate || singleColor;
-
- verify(mDescription, *mOutGlop);
-
- // Final step: populate program and map bounds into render target space
- mOutGlop->fill.program = mCaches.programCache.get(mDescription);
-}
-
-void GlopBuilder::dump(const Glop& glop) {
- ALOGD("Glop Mesh");
- const Glop::Mesh& mesh = glop.mesh;
- ALOGD(" primitive mode: %d", mesh.primitiveMode);
- ALOGD(" indices: buffer obj %x, indices %p", mesh.indices.bufferObject,
- mesh.indices.indices);
-
- const Glop::Mesh::Vertices& vertices = glop.mesh.vertices;
- ALOGD(" vertices: buffer obj %x, flags %x, pos %p, tex %p, clr %p, stride %d",
- vertices.bufferObject, vertices.attribFlags, vertices.position, vertices.texCoord,
- vertices.color, vertices.stride);
- ALOGD(" element count: %d", mesh.elementCount);
-
- ALOGD("Glop Fill");
- const Glop::Fill& fill = glop.fill;
- ALOGD(" program %p", fill.program);
- if (fill.texture.texture) {
- ALOGD(" texture %p, target %d, filter %d, clamp %d", fill.texture.texture,
- fill.texture.texture->target(), fill.texture.filter, fill.texture.clamp);
- if (fill.texture.textureTransform) {
- fill.texture.textureTransform->dump("texture transform");
- }
- }
- ALOGD_IF(fill.colorEnabled, " color (argb) %.2f %.2f %.2f %.2f", fill.color.a, fill.color.r,
- fill.color.g, fill.color.b);
- ALOGD_IF(fill.filterMode != ProgramDescription::ColorFilterMode::None, " filterMode %d",
- (int)fill.filterMode);
- ALOGD_IF(fill.skiaShaderData.skiaShaderType, " shader type %d",
- fill.skiaShaderData.skiaShaderType);
-
- ALOGD("Glop transform");
- glop.transform.modelView.dump(" model view");
- glop.transform.canvas.dump(" canvas");
- ALOGD_IF(glop.transform.transformFlags, " transformFlags 0x%x", glop.transform.transformFlags);
-
- ALOGD_IF(glop.roundRectClipState, "Glop RRCS %p", glop.roundRectClipState);
-
- ALOGD("Glop blend %d %d", glop.blend.src, glop.blend.dst);
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
deleted file mode 100644
index dac38223b877..000000000000
--- a/libs/hwui/GlopBuilder.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Glop.h"
-#include "Program.h"
-#include "renderstate/Blend.h"
-#include "utils/Macros.h"
-
-class SkPaint;
-class SkShader;
-
-namespace android {
-namespace uirenderer {
-
-class Caches;
-class GlLayer;
-class Matrix4;
-class Patch;
-class RenderState;
-class Texture;
-class UvMapper;
-class VertexBuffer;
-struct PathTexture;
-struct ShadowTexture;
-
-namespace TextureFillFlags {
-enum {
- None = 0,
- IsAlphaMaskTexture = 1 << 0,
- ForceFilter = 1 << 1,
-};
-}
-
-class GlopBuilder {
- PREVENT_COPY_AND_ASSIGN(GlopBuilder);
-
-public:
- GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop);
-
- GlopBuilder& setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount);
- GlopBuilder& setMeshUnitQuad();
- GlopBuilder& setMeshTexturedUnitQuad(const UvMapper* uvMapper);
- GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs);
- GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer);
- GlopBuilder& setMeshIndexedQuads(Vertex* vertexData, int quadCount);
- GlopBuilder& setMeshColoredTexturedMesh(ColorTextureVertex* vertexData,
- int elementCount); // TODO: use indexed quads
- GlopBuilder& setMeshTexturedIndexedQuads(TextureVertex* vertexData,
- int elementCount); // TODO: take quadCount
- GlopBuilder& setMeshPatchQuads(const Patch& patch);
-
- GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale,
- bool shadowInterp = false); // TODO: avoid boolean with default
- GlopBuilder& setFillTexturePaint(Texture& texture, const int textureFillFlags,
- const SkPaint* paint, float alphaScale);
- GlopBuilder& setFillPathTexturePaint(PathTexture& texture, const SkPaint& paint,
- float alphaScale);
- GlopBuilder& setFillShadowTexturePaint(ShadowTexture& texture, int shadowColor,
- const SkPaint& paint, float alphaScale);
- GlopBuilder& setFillBlack();
- GlopBuilder& setFillClear();
- GlopBuilder& setFillLayer(Texture& texture, const SkColorFilter* colorFilter, float alpha,
- SkBlendMode mode, Blend::ModeOrderSwap modeUsage);
- GlopBuilder& setFillTextureLayer(GlLayer& layer, float alpha);
- // TODO: setFillLayer normally forces its own wrap & filter mode,
- // which isn't always correct.
- GlopBuilder& setFillExternalTexture(Texture& texture, Matrix4& textureTransform,
- bool requiresFilter);
-
- GlopBuilder& setTransform(const Matrix4& canvas, const int transformFlags);
-
- GlopBuilder& setModelViewMapUnitToRect(const Rect destination);
- GlopBuilder& setModelViewMapUnitToRectSnap(const Rect destination);
- GlopBuilder& setModelViewMapUnitToRectOptionalSnap(bool snap, const Rect& destination) {
- if (snap) {
- return setModelViewMapUnitToRectSnap(destination);
- } else {
- return setModelViewMapUnitToRect(destination);
- }
- }
- GlopBuilder& setModelViewOffsetRect(float offsetX, float offsetY, const Rect source);
- GlopBuilder& setModelViewOffsetRectSnap(float offsetX, float offsetY, const Rect source);
- GlopBuilder& setModelViewOffsetRectOptionalSnap(bool snap, float offsetX, float offsetY,
- const Rect& source) {
- if (snap) {
- return setModelViewOffsetRectSnap(offsetX, offsetY, source);
- } else {
- return setModelViewOffsetRect(offsetX, offsetY, source);
- }
- }
- GlopBuilder& setModelViewIdentityEmptyBounds() {
- // pass empty rect since not needed for damage / snap
- return setModelViewOffsetRect(0, 0, Rect());
- }
-
- GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState);
-
- GlopBuilder& setGammaCorrection(bool enabled);
-
- void build();
-
- static void dump(const Glop& glop);
-
-private:
- void setFill(int color, float alphaScale, SkBlendMode mode, Blend::ModeOrderSwap modeUsage,
- const SkShader* shader, const SkColorFilter* colorFilter);
-
- enum StageFlags {
- kInitialStage = 0,
- kMeshStage = 1 << 0,
- kTransformStage = 1 << 1,
- kModelViewStage = 1 << 2,
- kFillStage = 1 << 3,
- kRoundRectClipStage = 1 << 4,
- kAllStages =
- kMeshStage | kFillStage | kTransformStage | kModelViewStage | kRoundRectClipStage,
- } mStageFlags;
-
- ProgramDescription mDescription;
- RenderState& mRenderState;
- Caches& mCaches;
- const SkShader* mShader;
- Glop* mOutGlop;
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp
index 612bfde1a3fa..a9a7af8f22f3 100644
--- a/libs/hwui/GpuMemoryTracker.cpp
+++ b/libs/hwui/GpuMemoryTracker.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "Texture.h"
#include "utils/StringUtils.h"
#include <GpuMemoryTracker.h>
@@ -117,22 +116,6 @@ void GpuMemoryTracker::onFrameCompleted() {
ATRACE_INT(buf, stats.count);
}
}
-
- std::vector<const Texture*> freeList;
- for (const auto& obj : gObjectSet) {
- if (obj->objectType() == GpuObjectType::Texture) {
- const Texture* texture = static_cast<Texture*>(obj);
- if (texture->cleanup) {
- ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", texture->id(),
- texture->width(), texture->height());
- freeList.push_back(texture);
- }
- }
- }
- for (auto& texture : freeList) {
- const_cast<Texture*>(texture)->deleteTexture();
- delete texture;
- }
}
} // namespace uirenderer
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
deleted file mode 100644
index 21e3c730cbec..000000000000
--- a/libs/hwui/GradientCache.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/JenkinsHash.h>
-
-#include "Caches.h"
-#include "Debug.h"
-#include "DeviceInfo.h"
-#include "GradientCache.h"
-#include "Properties.h"
-
-#include <cutils/properties.h>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Functions
-///////////////////////////////////////////////////////////////////////////////
-
-template <typename T>
-static inline T min(T a, T b) {
- return a < b ? a : b;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Cache entry
-///////////////////////////////////////////////////////////////////////////////
-
-hash_t GradientCacheEntry::hash() const {
- uint32_t hash = JenkinsHashMix(0, count);
- for (uint32_t i = 0; i < count; i++) {
- hash = JenkinsHashMix(hash, android::hash_type(colors[i]));
- hash = JenkinsHashMix(hash, android::hash_type(positions[i]));
- }
- return JenkinsHashWhiten(hash);
-}
-
-int GradientCacheEntry::compare(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs) {
- int deltaInt = int(lhs.count) - int(rhs.count);
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = memcmp(lhs.colors.get(), rhs.colors.get(), lhs.count * sizeof(uint32_t));
- if (deltaInt != 0) return deltaInt;
-
- return memcmp(lhs.positions.get(), rhs.positions.get(), lhs.count * sizeof(float));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-GradientCache::GradientCache(const Extensions& extensions)
- : mCache(LruCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity)
- , mSize(0)
- , mMaxSize(MB(1))
- , mUseFloatTexture(extensions.hasFloatTextures())
- , mHasNpot(extensions.hasNPot())
- , mHasLinearBlending(extensions.hasLinearBlending()) {
- mMaxTextureSize = DeviceInfo::get()->maxTextureSize();
-
- mCache.setOnEntryRemovedListener(this);
-}
-
-GradientCache::~GradientCache() {
- mCache.clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Size management
-///////////////////////////////////////////////////////////////////////////////
-
-uint32_t GradientCache::getSize() {
- return mSize;
-}
-
-uint32_t GradientCache::getMaxSize() {
- return mMaxSize;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Callbacks
-///////////////////////////////////////////////////////////////////////////////
-
-void GradientCache::operator()(GradientCacheEntry&, Texture*& texture) {
- if (texture) {
- mSize -= texture->objectSize();
- texture->deleteTexture();
- delete texture;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-Texture* GradientCache::get(uint32_t* colors, float* positions, int count) {
- GradientCacheEntry gradient(colors, positions, count);
- Texture* texture = mCache.get(gradient);
-
- if (!texture) {
- texture = addLinearGradient(gradient, colors, positions, count);
- }
-
- return texture;
-}
-
-void GradientCache::clear() {
- mCache.clear();
-}
-
-void GradientCache::getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info) {
- uint32_t width = 256 * (count - 1);
-
- // If the npot extension is not supported we cannot use non-clamp
- // wrap modes. We therefore find the nearest largest power of 2
- // unless width is already a power of 2
- if (!mHasNpot && (width & (width - 1)) != 0) {
- width = 1 << (32 - __builtin_clz(width));
- }
-
- bool hasAlpha = false;
- for (int i = 0; i < count; i++) {
- if (((colors[i] >> 24) & 0xff) < 255) {
- hasAlpha = true;
- break;
- }
- }
-
- info.width = min(width, uint32_t(mMaxTextureSize));
- info.hasAlpha = hasAlpha;
-}
-
-Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors,
- float* positions, int count) {
- GradientInfo info;
- getGradientInfo(colors, count, info);
-
- Texture* texture = new Texture(Caches::getInstance());
- texture->blend = info.hasAlpha;
- texture->generation = 1;
-
- // Assume the cache is always big enough
- const uint32_t size = info.width * 2 * bytesPerPixel();
- while (getSize() + size > mMaxSize) {
- LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(),
- "Ran out of things to remove from the cache? getSize() = %" PRIu32
- ", size = %" PRIu32 ", mMaxSize = %" PRIu32 ", width = %" PRIu32,
- getSize(), size, mMaxSize, info.width);
- }
-
- generateTexture(colors, positions, info.width, 2, texture);
-
- mSize += size;
- LOG_ALWAYS_FATAL_IF((int)size != texture->objectSize(),
- "size != texture->objectSize(), size %" PRIu32
- ", objectSize %d"
- " width = %" PRIu32 " bytesPerPixel() = %zu",
- size, texture->objectSize(), info.width, bytesPerPixel());
- mCache.put(gradient, texture);
-
- return texture;
-}
-
-size_t GradientCache::bytesPerPixel() const {
- // We use 4 channels (RGBA)
- return 4 * (mUseFloatTexture ? /* fp16 */ 2 : sizeof(uint8_t));
-}
-
-size_t GradientCache::sourceBytesPerPixel() const {
- // We use 4 channels (RGBA) and upload from floats (not half floats)
- return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
-}
-
-void GradientCache::mixBytes(const FloatColor& start, const FloatColor& end, float amount,
- uint8_t*& dst) const {
- float oppAmount = 1.0f - amount;
- float a = start.a * oppAmount + end.a * amount;
- *dst++ = uint8_t(OECF(start.r * oppAmount + end.r * amount) * 255.0f);
- *dst++ = uint8_t(OECF(start.g * oppAmount + end.g * amount) * 255.0f);
- *dst++ = uint8_t(OECF(start.b * oppAmount + end.b * amount) * 255.0f);
- *dst++ = uint8_t(a * 255.0f);
-}
-
-void GradientCache::mixFloats(const FloatColor& start, const FloatColor& end, float amount,
- uint8_t*& dst) const {
- float oppAmount = 1.0f - amount;
- float a = start.a * oppAmount + end.a * amount;
- float* d = (float*)dst;
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
- // We want to stay linear
- *d++ = (start.r * oppAmount + end.r * amount);
- *d++ = (start.g * oppAmount + end.g * amount);
- *d++ = (start.b * oppAmount + end.b * amount);
-#else
- *d++ = OECF(start.r * oppAmount + end.r * amount);
- *d++ = OECF(start.g * oppAmount + end.g * amount);
- *d++ = OECF(start.b * oppAmount + end.b * amount);
-#endif
- *d++ = a;
- dst += 4 * sizeof(float);
-}
-
-void GradientCache::generateTexture(uint32_t* colors, float* positions, const uint32_t width,
- const uint32_t height, Texture* texture) {
- const GLsizei rowBytes = width * sourceBytesPerPixel();
- uint8_t pixels[rowBytes * height];
-
- static ChannelMixer gMixers[] = {
- // colors are stored gamma-encoded
- &android::uirenderer::GradientCache::mixBytes,
- // colors are stored in linear (linear blending on)
- // or gamma-encoded (linear blending off)
- &android::uirenderer::GradientCache::mixFloats,
- };
- ChannelMixer mix = gMixers[mUseFloatTexture];
-
- FloatColor start;
- start.set(colors[0]);
-
- FloatColor end;
- end.set(colors[1]);
-
- int currentPos = 1;
- float startPos = positions[0];
- float distance = positions[1] - startPos;
-
- uint8_t* dst = pixels;
- for (uint32_t x = 0; x < width; x++) {
- float pos = x / float(width - 1);
- if (pos > positions[currentPos]) {
- start = end;
- startPos = positions[currentPos];
-
- currentPos++;
-
- end.set(colors[currentPos]);
- distance = positions[currentPos] - startPos;
- }
-
- float amount = (pos - startPos) / distance;
- (this->*mix)(start, end, amount, dst);
- }
-
- memcpy(pixels + rowBytes, pixels, rowBytes);
-
- if (mUseFloatTexture) {
- texture->upload(GL_RGBA16F, width, height, GL_RGBA, GL_FLOAT, pixels);
- } else {
- GLint internalFormat = mHasLinearBlending ? GL_SRGB8_ALPHA8 : GL_RGBA;
- texture->upload(internalFormat, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
- }
-
- texture->setFilter(GL_LINEAR);
- texture->setWrap(GL_CLAMP_TO_EDGE);
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
deleted file mode 100644
index ff426cd3425b..000000000000
--- a/libs/hwui/GradientCache.h
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_GRADIENT_CACHE_H
-#define ANDROID_HWUI_GRADIENT_CACHE_H
-
-#include <memory>
-
-#include <GLES3/gl3.h>
-
-#include <SkShader.h>
-
-#include <utils/LruCache.h>
-#include <utils/Mutex.h>
-
-#include "FloatColor.h"
-
-namespace android {
-namespace uirenderer {
-
-class Texture;
-
-struct GradientCacheEntry {
- GradientCacheEntry() {
- count = 0;
- colors = nullptr;
- positions = nullptr;
- }
-
- GradientCacheEntry(uint32_t* colors, float* positions, uint32_t count) {
- copy(colors, positions, count);
- }
-
- GradientCacheEntry(const GradientCacheEntry& entry) {
- copy(entry.colors.get(), entry.positions.get(), entry.count);
- }
-
- GradientCacheEntry& operator=(const GradientCacheEntry& entry) {
- if (this != &entry) {
- copy(entry.colors.get(), entry.positions.get(), entry.count);
- }
-
- return *this;
- }
-
- hash_t hash() const;
-
- static int compare(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs);
-
- bool operator==(const GradientCacheEntry& other) const { return compare(*this, other) == 0; }
-
- bool operator!=(const GradientCacheEntry& other) const { return compare(*this, other) != 0; }
-
- std::unique_ptr<uint32_t[]> colors;
- std::unique_ptr<float[]> positions;
- uint32_t count;
-
-private:
- void copy(uint32_t* colors, float* positions, uint32_t count) {
- this->count = count;
- this->colors.reset(new uint32_t[count]);
- this->positions.reset(new float[count]);
-
- memcpy(this->colors.get(), colors, count * sizeof(uint32_t));
- memcpy(this->positions.get(), positions, count * sizeof(float));
- }
-
-}; // GradientCacheEntry
-
-// Caching support
-
-inline int strictly_order_type(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs) {
- return GradientCacheEntry::compare(lhs, rhs) < 0;
-}
-
-inline int compare_type(const GradientCacheEntry& lhs, const GradientCacheEntry& rhs) {
- return GradientCacheEntry::compare(lhs, rhs);
-}
-
-inline hash_t hash_type(const GradientCacheEntry& entry) {
- return entry.hash();
-}
-
-/**
- * A simple LRU gradient cache. The cache has a maximum size expressed in bytes.
- * Any texture added to the cache causing the cache to grow beyond the maximum
- * allowed size will also cause the oldest texture to be kicked out.
- */
-class GradientCache : public OnEntryRemoved<GradientCacheEntry, Texture*> {
-public:
- explicit GradientCache(const Extensions& extensions);
- ~GradientCache();
-
- /**
- * Used as a callback when an entry is removed from the cache.
- * Do not invoke directly.
- */
- void operator()(GradientCacheEntry& shader, Texture*& texture) override;
-
- /**
- * Returns the texture associated with the specified shader.
- */
- Texture* get(uint32_t* colors, float* positions, int count);
-
- /**
- * Clears the cache. This causes all textures to be deleted.
- */
- void clear();
-
- /**
- * Returns the maximum size of the cache in bytes.
- */
- uint32_t getMaxSize();
- /**
- * Returns the current size of the cache in bytes.
- */
- uint32_t getSize();
-
-private:
- /**
- * Adds a new linear gradient to the cache. The generated texture is
- * returned.
- */
- Texture* addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, float* positions,
- int count);
-
- void generateTexture(uint32_t* colors, float* positions, const uint32_t width,
- const uint32_t height, Texture* texture);
-
- struct GradientInfo {
- uint32_t width;
- bool hasAlpha;
- };
-
- void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info);
-
- size_t bytesPerPixel() const;
- size_t sourceBytesPerPixel() const;
-
- typedef void (GradientCache::*ChannelMixer)(const FloatColor& start, const FloatColor& end,
- float amount, uint8_t*& dst) const;
-
- void mixBytes(const FloatColor& start, const FloatColor& end, float amount,
- uint8_t*& dst) const;
- void mixFloats(const FloatColor& start, const FloatColor& end, float amount,
- uint8_t*& dst) const;
-
- LruCache<GradientCacheEntry, Texture*> mCache;
-
- uint32_t mSize;
- const uint32_t mMaxSize;
-
- GLint mMaxTextureSize;
- bool mUseFloatTexture;
- bool mHasNpot;
- bool mHasLinearBlending;
-
- mutable Mutex mLock;
-}; // class GradientCache
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_GRADIENT_CACHE_H
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
new file mode 100644
index 000000000000..165fc4860fb2
--- /dev/null
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2018 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 "HardwareBitmapUploader.h"
+
+#include "hwui/Bitmap.h"
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
+
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+#include <SkCanvas.h>
+#include <utils/GLUtils.h>
+#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
+#include <thread>
+
+namespace android::uirenderer {
+
+static std::mutex sLock{};
+static ThreadBase* sUploadThread = nullptr;
+static renderthread::EglManager sEglManager;
+static int sPendingUploads = 0;
+static nsecs_t sLastUpload = 0;
+
+static bool shouldTimeOutLocked() {
+ nsecs_t durationSince = systemTime() - sLastUpload;
+ return durationSince > 2000_ms;
+}
+
+static void checkIdleTimeout() {
+ std::lock_guard _lock{sLock};
+ if (sPendingUploads == 0 && shouldTimeOutLocked()) {
+ sEglManager.destroy();
+ } else {
+ sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout);
+ }
+}
+
+static void beginUpload() {
+ std::lock_guard _lock{sLock};
+ sPendingUploads++;
+
+ if (!sUploadThread) {
+ sUploadThread = new ThreadBase{};
+ }
+
+ if (!sUploadThread->isRunning()) {
+ sUploadThread->start("GrallocUploadThread");
+ }
+
+ if (!sEglManager.hasEglContext()) {
+ sUploadThread->queue().runSync([]() {
+ sEglManager.initialize();
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ });
+ sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout);
+ }
+}
+
+static void endUpload() {
+ std::lock_guard _lock{sLock};
+ sPendingUploads--;
+ sLastUpload = systemTime();
+}
+
+static EGLDisplay getUploadEglDisplay() {
+ std::lock_guard _lock{sLock};
+ LOG_ALWAYS_FATAL_IF(!sEglManager.hasEglContext(), "Forgot to begin an upload?");
+ return sEglManager.eglDisplay();
+}
+
+static bool hasFP16Support() {
+ static std::once_flag sOnce;
+ static bool hasFP16Support = false;
+
+ // Gralloc shouldn't let us create a USAGE_HW_TEXTURE if GLES is unable to consume it, so
+ // we don't need to double-check the GLES version/extension.
+ std::call_once(sOnce, []() {
+ sp<GraphicBuffer> buffer = new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_FP16,
+ GraphicBuffer::USAGE_HW_TEXTURE |
+ GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER,
+ "tempFp16Buffer");
+ status_t error = buffer->initCheck();
+ hasFP16Support = !error;
+ });
+
+ return hasFP16Support;
+}
+
+#define FENCE_TIMEOUT 2000000000
+
+struct FormatInfo {
+ PixelFormat pixelFormat;
+ GLint format, type;
+ bool isSupported = false;
+ bool valid = true;
+};
+
+static FormatInfo determineFormat(const SkBitmap& skBitmap) {
+ FormatInfo formatInfo;
+ // TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined)
+ switch (skBitmap.info().colorType()) {
+ case kRGBA_8888_SkColorType:
+ formatInfo.isSupported = true;
+ // ARGB_4444 is upconverted to RGBA_8888
+ case kARGB_4444_SkColorType:
+ formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
+ formatInfo.format = GL_RGBA;
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ break;
+ case kRGBA_F16_SkColorType:
+ formatInfo.isSupported = hasFP16Support();
+ if (formatInfo.isSupported) {
+ formatInfo.type = GL_HALF_FLOAT;
+ formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
+ } else {
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
+ }
+ formatInfo.format = GL_RGBA;
+ break;
+ case kRGB_565_SkColorType:
+ formatInfo.isSupported = true;
+ formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565;
+ formatInfo.format = GL_RGB;
+ formatInfo.type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case kGray_8_SkColorType:
+ formatInfo.isSupported = true;
+ formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
+ formatInfo.format = GL_LUMINANCE;
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ break;
+ default:
+ ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
+ formatInfo.valid = false;
+ }
+ return formatInfo;
+}
+
+static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& source) {
+ if (format.isSupported) {
+ return source;
+ } else {
+ SkBitmap bitmap;
+ const SkImageInfo& info = source.info();
+ 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
+ source.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
+ bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
+ } else {
+ SkCanvas canvas(bitmap);
+ canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
+ }
+ return bitmap;
+ }
+}
+
+class ScopedUploadRequest {
+public:
+ ScopedUploadRequest() { beginUpload(); }
+ ~ScopedUploadRequest() { endUpload(); }
+};
+
+sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sourceBitmap) {
+ ATRACE_CALL();
+
+ FormatInfo format = determineFormat(sourceBitmap);
+ if (!format.valid) {
+ return nullptr;
+ }
+
+ ScopedUploadRequest _uploadRequest{};
+
+ SkBitmap bitmap = makeHwCompatible(format, sourceBitmap);
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(bitmap.height()),
+ format.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;
+ }
+
+ EGLDisplay display = getUploadEglDisplay();
+
+ 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;
+ }
+
+ {
+ ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height());
+ EGLSyncKHR fence = sUploadThread->queue().runSync([&]() -> EGLSyncKHR {
+ 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, bitmap.width(), bitmap.height(), format.format,
+ format.type, bitmap.getPixels());
+ GL_CHECKPOINT(MODERATE);
+
+ EGLSyncKHR uploadFence =
+ eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
+ LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR, "Could not create sync fence %#x",
+ eglGetError());
+ glFlush();
+ return uploadFence;
+ });
+
+ EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT);
+ LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
+ "Failed to wait for the fence %#x", eglGetError());
+
+ eglDestroySyncKHR(display, fence);
+ }
+
+ return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
+}
+
+}; // namespace android::uirenderer
diff --git a/libs/hwui/tests/unit/DeviceInfoTests.cpp b/libs/hwui/HardwareBitmapUploader.h
index af37938915e5..c0113d81fefb 100644
--- a/libs/hwui/tests/unit/DeviceInfoTests.cpp
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,15 @@
* limitations under the License.
*/
-#include <DeviceInfo.h>
+#pragma once
-#include <gtest/gtest.h>
-#include "tests/common/TestUtils.h"
+#include <hwui/Bitmap.h>
-using namespace android;
-using namespace android::uirenderer;
+namespace android::uirenderer {
-OPENGL_PIPELINE_TEST(DeviceInfo, basic) {
- // can't assert state before init - another test may have initialized the singleton
- DeviceInfo::initialize();
- const DeviceInfo* di = DeviceInfo::get();
- ASSERT_NE(nullptr, di) << "DeviceInfo initialization failed";
- EXPECT_EQ(2048, di->maxTextureSize()) << "Max texture size didn't match";
-}
+class HardwareBitmapUploader {
+public:
+ static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap);
+};
+
+}; // namespace android::uirenderer
diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp
deleted file mode 100644
index d30796d01479..000000000000
--- a/libs/hwui/Image.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/Log.h>
-
-#include "Caches.h"
-#include "Image.h"
-
-namespace android {
-namespace uirenderer {
-
-Image::Image(sp<GraphicBuffer> buffer) {
- // Create the EGLImage object that maps the GraphicBuffer
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- EGLClientBuffer clientBuffer = (EGLClientBuffer)buffer->getNativeBuffer();
- EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
-
- mImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
- attrs);
-
- if (mImage == EGL_NO_IMAGE_KHR) {
- ALOGW("Error creating image (%#x)", eglGetError());
- mTexture = 0;
- } else {
- // Create a 2D texture to sample from the EGLImage
- glGenTextures(1, &mTexture);
- Caches::getInstance().textureState().bindTexture(mTexture);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, mImage);
-
- GLenum status = GL_NO_ERROR;
- while ((status = glGetError()) != GL_NO_ERROR) {
- ALOGW("Error creating image (%#x)", status);
- }
- }
-}
-
-Image::~Image() {
- if (mImage != EGL_NO_IMAGE_KHR) {
- eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), mImage);
- mImage = EGL_NO_IMAGE_KHR;
-
- Caches::getInstance().textureState().deleteTexture(mTexture);
- mTexture = 0;
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Image.h b/libs/hwui/Image.h
deleted file mode 100644
index 989b6ff3648d..000000000000
--- a/libs/hwui/Image.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_IMAGE_H
-#define ANDROID_HWUI_IMAGE_H
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <ui/GraphicBuffer.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * A simple wrapper that creates an EGLImage and a texture for a GraphicBuffer.
- */
-class Image {
-public:
- /**
- * Creates a new image from the specified graphic buffer. If the image
- * cannot be created, getTexture() will return 0 and getImage() will
- * return EGL_NO_IMAGE_KHR.
- */
- explicit Image(sp<GraphicBuffer> buffer);
- ~Image();
-
- /**
- * Returns the name of the GL texture that can be used to sample
- * from this image.
- */
- GLuint getTexture() const { return mTexture; }
-
- /**
- * Returns the name of the EGL image represented by this object.
- */
- EGLImageKHR getImage() const { return mImage; }
-
-private:
- GLuint mTexture;
- EGLImageKHR mImage;
-}; // class Image
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_IMAGE_H
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index f2d50cd42523..e7ae7675f0b8 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -18,6 +18,7 @@
#include <errno.h>
#include <inttypes.h>
+#include <statslog.h>
#include <sys/mman.h>
#include <algorithm>
@@ -181,6 +182,7 @@ void JankTracker::finishFrame(const FrameInfo& frame) {
ALOGI("%s", ss.str().c_str());
// Just so we have something that counts up, the value is largely irrelevant
ATRACE_INT(ss.str().c_str(), ++sDaveyCount);
+ android::util::stats_write(android::util::DAVEY_OCCURRED, getuid(), ns2ms(totalDuration));
}
}
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index b86ae121af55..32aaa54e696c 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -17,25 +17,26 @@
#include "Layer.h"
#include "renderstate/RenderState.h"
+#include "utils/Color.h"
#include <SkToSRGBColorFilter.h>
namespace android {
namespace uirenderer {
-Layer::Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter> colorFilter, int alpha,
- SkBlendMode mode)
- : GpuMemoryTracker(GpuObjectType::Layer)
- , mRenderState(renderState)
- , mode(mode)
- , mApi(api)
+Layer::Layer(RenderState& renderState, sk_sp<SkColorFilter> colorFilter, int alpha,
+ SkBlendMode mode)
+ : mRenderState(renderState)
, mColorFilter(colorFilter)
- , alpha(alpha) {
+ , alpha(alpha)
+ , mode(mode) {
// TODO: This is a violation of Android's typical ref counting, but it
// preserves the old inc/dec ref locations. This should be changed...
incStrong(nullptr);
buildColorSpaceWithFilter();
renderState.registerLayer(this);
+ texTransform.setIdentity();
+ transform.setIdentity();
}
Layer::~Layer() {
@@ -76,5 +77,13 @@ void Layer::postDecStrong() {
mRenderState.postDecStrong(this);
}
+SkBlendMode Layer::getMode() const {
+ if (mBlend || mode != SkBlendMode::kSrcOver) {
+ return mode;
+ } else {
+ return SkBlendMode::kSrc;
+ }
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index d41c9703e908..e4f96e914c36 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -16,15 +16,15 @@
#pragma once
-#include <GpuMemoryTracker.h>
#include <utils/RefBase.h>
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkColorSpace.h>
-#include <SkBlendMode.h>
#include <SkPaint.h>
-
-#include "Matrix.h"
+#include <SkImage.h>
+#include <SkMatrix.h>
+#include <system/graphics.h>
namespace android {
namespace uirenderer {
@@ -38,26 +38,21 @@ class RenderState;
/**
* A layer has dimensions and is backed by a backend specific texture or framebuffer.
*/
-class Layer : public VirtualLightRefBase, GpuMemoryTracker {
+class Layer : public VirtualLightRefBase {
public:
- enum class Api {
- OpenGL = 0,
- Vulkan = 1,
- };
-
- Api getApi() const { return mApi; }
+ Layer(RenderState& renderState, sk_sp<SkColorFilter>, int alpha, SkBlendMode mode);
~Layer();
- virtual uint32_t getWidth() const = 0;
+ uint32_t getWidth() const { return mWidth; }
- virtual uint32_t getHeight() const = 0;
+ uint32_t getHeight() const { return mHeight; }
- virtual void setSize(uint32_t width, uint32_t height) = 0;
+ void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; }
- virtual void setBlend(bool blend) = 0;
+ void setBlend(bool blend) { mBlend = blend; }
- virtual bool isBlend() const = 0;
+ bool isBlend() const { return mBlend; }
inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; }
@@ -72,7 +67,7 @@ public:
inline int getAlpha() const { return alpha; }
- virtual SkBlendMode getMode() const { return mode; }
+ SkBlendMode getMode() const;
inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
@@ -84,9 +79,9 @@ public:
inline sk_sp<SkColorFilter> getColorSpaceWithFilter() const { return mColorSpaceWithFilter; }
- inline mat4& getTexTransform() { return texTransform; }
+ inline SkMatrix& getTexTransform() { return texTransform; }
- inline mat4& getTransform() { return transform; }
+ inline SkMatrix& getTransform() { return transform; }
/**
* Posts a decStrong call to the appropriate thread.
@@ -94,31 +89,17 @@ public:
*/
void postDecStrong();
- inline void setBufferSize(uint32_t width, uint32_t height) {
- mBufferWidth = width;
- mBufferHeight = height;
- }
-
- inline uint32_t getBufferWidth() const { return mBufferWidth; }
+ inline void setImage(const sk_sp<SkImage>& image) { this->layerImage = image; }
- inline uint32_t getBufferHeight() const { return mBufferHeight; }
+ inline sk_sp<SkImage> getImage() const { return this->layerImage; }
protected:
- Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter>, int alpha,
- SkBlendMode mode);
RenderState& mRenderState;
- /**
- * Blending mode of the layer.
- */
- SkBlendMode mode;
-
private:
void buildColorSpaceWithFilter();
- Api mApi;
-
/**
* Color filter used to draw this layer. Optional.
*/
@@ -145,18 +126,40 @@ private:
int alpha;
/**
+ * Blending mode of the layer.
+ */
+ SkBlendMode mode;
+
+ /**
* Optional texture coordinates transform.
*/
- mat4 texTransform;
+ SkMatrix texTransform;
/**
* Optional transform.
*/
- mat4 transform;
+ SkMatrix transform;
+
+ /**
+ * An image backing the layer.
+ */
+ sk_sp<SkImage> layerImage;
+
+ /**
+ * layer width.
+ */
+ uint32_t mWidth = 0;
- uint32_t mBufferWidth = 0;
+ /**
+ * layer height.
+ */
+ uint32_t mHeight = 0;
+
+ /**
+ * enable blending
+ */
+ bool mBlend = false;
- uint32_t mBufferHeight = 0;
}; // struct Layer
}; // namespace uirenderer
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
deleted file mode 100644
index 15ede4ca148a..000000000000
--- a/libs/hwui/LayerBuilder.cpp
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LayerBuilder.h"
-
-#include "BakedOpState.h"
-#include "RenderNode.h"
-#include "utils/PaintUtils.h"
-#include "utils/TraceUtils.h"
-
-#include <utils/TypeHelpers.h>
-
-namespace android {
-namespace uirenderer {
-
-class BatchBase {
-public:
- BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
- : mBatchId(batchId), mMerging(merging) {
- mBounds = op->computedState.clippedBounds;
- mOps.push_back(op);
- }
-
- bool intersects(const Rect& rect) const {
- if (!rect.intersects(mBounds)) return false;
-
- for (const BakedOpState* op : mOps) {
- if (rect.intersects(op->computedState.clippedBounds)) {
- return true;
- }
- }
- return false;
- }
-
- batchid_t getBatchId() const { return mBatchId; }
- bool isMerging() const { return mMerging; }
-
- const std::vector<BakedOpState*>& getOps() const { return mOps; }
-
- void dump() const {
- ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING, this, mBatchId,
- mMerging, (int)mOps.size(), RECT_ARGS(mBounds));
- }
-
-protected:
- batchid_t mBatchId;
- Rect mBounds;
- std::vector<BakedOpState*> mOps;
- bool mMerging;
-};
-
-class OpBatch : public BatchBase {
-public:
- OpBatch(batchid_t batchId, BakedOpState* op) : BatchBase(batchId, op, false) {}
-
- void batchOp(BakedOpState* op) {
- mBounds.unionWith(op->computedState.clippedBounds);
- mOps.push_back(op);
- }
-};
-
-class MergingOpBatch : public BatchBase {
-public:
- MergingOpBatch(batchid_t batchId, BakedOpState* op)
- : BatchBase(batchId, op, true), mClipSideFlags(op->computedState.clipSideFlags) {}
-
- /*
- * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
- * and clip side flags. Positive bounds delta means new bounds fit in old.
- */
- static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
- float boundsDelta) {
- bool currentClipExists = currentFlags & side;
- bool newClipExists = newFlags & side;
-
- // if current is clipped, we must be able to fit new bounds in current
- if (boundsDelta > 0 && currentClipExists) return false;
-
- // if new is clipped, we must be able to fit current bounds in new
- if (boundsDelta < 0 && newClipExists) return false;
-
- return true;
- }
-
- static bool paintIsDefault(const SkPaint& paint) {
- return paint.getAlpha() == 255 && paint.getColorFilter() == nullptr &&
- paint.getShader() == nullptr;
- }
-
- static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) {
- // Note: don't check color, since all currently mergeable ops can merge across colors
- return a.getAlpha() == b.getAlpha() && a.getColorFilter() == b.getColorFilter() &&
- a.getShader() == b.getShader();
- }
-
- /*
- * Checks if a (mergeable) op can be merged into this batch
- *
- * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
- * important to consider all paint attributes used in the draw calls in deciding both a) if an
- * op tries to merge at all, and b) if the op can merge with another set of ops
- *
- * False positives can lead to information from the paints of subsequent merged operations being
- * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
- */
- bool canMergeWith(BakedOpState* op) const {
- bool isTextBatch =
- getBatchId() == OpBatchType::Text || getBatchId() == OpBatchType::ColorText;
-
- // Overlapping other operations is only allowed for text without shadow. For other ops,
- // multiDraw isn't guaranteed to overdraw correctly
- if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) {
- if (intersects(op->computedState.clippedBounds)) return false;
- }
-
- const BakedOpState* lhs = op;
- const BakedOpState* rhs = mOps[0];
-
- if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false;
-
- // Identical round rect clip state means both ops will clip in the same way, or not at all.
- // As the state objects are const, we can compare their pointers to determine mergeability
- if (lhs->roundRectClipState != rhs->roundRectClipState) return false;
-
- // Local masks prevent merge, since they're potentially in different coordinate spaces
- if (lhs->computedState.localProjectionPathMask ||
- rhs->computedState.localProjectionPathMask)
- return false;
-
- /* Clipping compatibility check
- *
- * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
- * clip for that side.
- */
- const int currentFlags = mClipSideFlags;
- const int newFlags = op->computedState.clipSideFlags;
- if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) {
- const Rect& opBounds = op->computedState.clippedBounds;
- float boundsDelta = mBounds.left - opBounds.left;
- if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta))
- return false;
- boundsDelta = mBounds.top - opBounds.top;
- if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false;
-
- // right and bottom delta calculation reversed to account for direction
- boundsDelta = opBounds.right - mBounds.right;
- if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta))
- return false;
- boundsDelta = opBounds.bottom - mBounds.bottom;
- if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta))
- return false;
- }
-
- const SkPaint* newPaint = op->op->paint;
- const SkPaint* oldPaint = mOps[0]->op->paint;
-
- if (newPaint == oldPaint) {
- // if paints are equal, then modifiers + paint attribs don't need to be compared
- return true;
- } else if (newPaint && !oldPaint) {
- return paintIsDefault(*newPaint);
- } else if (!newPaint && oldPaint) {
- return paintIsDefault(*oldPaint);
- }
- return paintsAreEquivalent(*newPaint, *oldPaint);
- }
-
- void mergeOp(BakedOpState* op) {
- mBounds.unionWith(op->computedState.clippedBounds);
- mOps.push_back(op);
-
- // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat
- // check, and doesn't extend past a side of the clip that's in use by the merged batch.
- // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect.
- mClipSideFlags |= op->computedState.clipSideFlags;
- }
-
- int getClipSideFlags() const { return mClipSideFlags; }
- const Rect& getClipRect() const { return mBounds; }
-
-private:
- int mClipSideFlags;
-};
-
-LayerBuilder::LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect,
- const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
- : width(width)
- , height(height)
- , repaintRect(repaintRect)
- , repaintClip(repaintRect)
- , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
- , beginLayerOp(beginLayerOp)
- , renderNode(renderNode) {}
-
-// iterate back toward target to see if anything drawn since should overlap the new op
-// if no target, merging ops still iterate to find similar batch to insert after
-void LayerBuilder::locateInsertIndex(int batchId, const Rect& clippedBounds,
- BatchBase** targetBatch, size_t* insertBatchIndex) const {
- for (int i = mBatches.size() - 1; i >= 0; i--) {
- BatchBase* overBatch = mBatches[i];
-
- if (overBatch == *targetBatch) break;
-
- // TODO: also consider shader shared between batch types
- if (batchId == overBatch->getBatchId()) {
- *insertBatchIndex = i + 1;
- if (!*targetBatch) break; // found insert position, quit
- }
-
- if (overBatch->intersects(clippedBounds)) {
- // NOTE: it may be possible to optimize for special cases where two operations
- // of the same batch/paint could swap order, such as with a non-mergeable
- // (clipped) and a mergeable text operation
- *targetBatch = nullptr;
- break;
- }
- }
-}
-
-void LayerBuilder::deferLayerClear(const Rect& rect) {
- mClearRects.push_back(rect);
-}
-
-void LayerBuilder::onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState) {
- if (bakedState->op->opId != RecordedOpId::CopyToLayerOp) {
- // First non-CopyToLayer, so stop stashing up layer clears for unclipped save layers,
- // and issue them together in one draw.
- flushLayerClears(allocator);
-
- if (CC_UNLIKELY(activeUnclippedSaveLayers.empty() &&
- bakedState->computedState.opaqueOverClippedBounds &&
- bakedState->computedState.clippedBounds.contains(repaintRect) &&
- !Properties::debugOverdraw)) {
- // discard all deferred drawing ops, since new one will occlude them
- clear();
- }
- }
-}
-
-void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
- if (CC_UNLIKELY(!mClearRects.empty())) {
- const int vertCount = mClearRects.size() * 4;
- // put the verts in the frame allocator, since
- // 1) SimpleRectsOps needs verts, not rects
- // 2) even if mClearRects stored verts, std::vectors will move their contents
- Vertex* const verts = (Vertex*)allocator.create_trivial_array<Vertex>(vertCount);
-
- Vertex* currentVert = verts;
- Rect bounds = mClearRects[0];
- for (auto&& rect : mClearRects) {
- bounds.unionWith(rect);
- Vertex::set(currentVert++, rect.left, rect.top);
- Vertex::set(currentVert++, rect.right, rect.top);
- Vertex::set(currentVert++, rect.left, rect.bottom);
- Vertex::set(currentVert++, rect.right, rect.bottom);
- }
- mClearRects.clear(); // discard rects before drawing so this method isn't reentrant
-
- // One or more unclipped saveLayers have been enqueued, with deferred clears.
- // Flush all of these clears with a single draw
- SkPaint* paint = allocator.create<SkPaint>();
- paint->setBlendMode(SkBlendMode::kClear);
- SimpleRectsOp* op = allocator.create_trivial<SimpleRectsOp>(
- bounds, Matrix4::identity(), nullptr, paint, verts, vertCount);
- BakedOpState* bakedState =
- BakedOpState::directConstruct(allocator, &repaintClip, bounds, *op);
- deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
- }
-}
-
-void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op,
- batchid_t batchId) {
- onDeferOp(allocator, op);
- OpBatch* targetBatch = mBatchLookup[batchId];
-
- size_t insertBatchIndex = mBatches.size();
- if (targetBatch) {
- locateInsertIndex(batchId, op->computedState.clippedBounds, (BatchBase**)(&targetBatch),
- &insertBatchIndex);
- }
-
- if (targetBatch) {
- targetBatch->batchOp(op);
- } else {
- // new non-merging batch
- targetBatch = allocator.create<OpBatch>(batchId, op);
- mBatchLookup[batchId] = targetBatch;
- mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
- }
-}
-
-void LayerBuilder::deferMergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId,
- mergeid_t mergeId) {
- onDeferOp(allocator, op);
- MergingOpBatch* targetBatch = nullptr;
-
- // Try to merge with any existing batch with same mergeId
- auto getResult = mMergingBatchLookup[batchId].find(mergeId);
- if (getResult != mMergingBatchLookup[batchId].end()) {
- targetBatch = getResult->second;
- if (!targetBatch->canMergeWith(op)) {
- targetBatch = nullptr;
- }
- }
-
- size_t insertBatchIndex = mBatches.size();
- locateInsertIndex(batchId, op->computedState.clippedBounds, (BatchBase**)(&targetBatch),
- &insertBatchIndex);
-
- if (targetBatch) {
- targetBatch->mergeOp(op);
- } else {
- // new merging batch
- targetBatch = allocator.create<MergingOpBatch>(batchId, op);
- mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
-
- mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
- }
-}
-
-void LayerBuilder::replayBakedOpsImpl(void* arg, BakedOpReceiver* unmergedReceivers,
- MergedOpReceiver* mergedReceivers) const {
- if (renderNode) {
- ATRACE_FORMAT_BEGIN("Issue HW Layer DisplayList %s %ux%u", renderNode->getName(), width,
- height);
- } else {
- ATRACE_BEGIN("flush drawing commands");
- }
-
- for (const BatchBase* batch : mBatches) {
- size_t size = batch->getOps().size();
- if (size > 1 && batch->isMerging()) {
- int opId = batch->getOps()[0]->op->opId;
- const MergingOpBatch* mergingBatch = static_cast<const MergingOpBatch*>(batch);
- MergedBakedOpList data = {batch->getOps().data(), size,
- mergingBatch->getClipSideFlags(),
- mergingBatch->getClipRect()};
- mergedReceivers[opId](arg, data);
- } else {
- for (const BakedOpState* op : batch->getOps()) {
- unmergedReceivers[op->op->opId](arg, *op);
- }
- }
- }
- ATRACE_END();
-}
-
-void LayerBuilder::clear() {
- mBatches.clear();
- for (int i = 0; i < OpBatchType::Count; i++) {
- mBatchLookup[i] = nullptr;
- mMergingBatchLookup[i].clear();
- }
-}
-
-void LayerBuilder::dump() const {
- ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)", this, width, height,
- offscreenBuffer, beginLayerOp, renderNode, renderNode ? renderNode->getName() : "-");
- for (const BatchBase* batch : mBatches) {
- batch->dump();
- }
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/LayerBuilder.h b/libs/hwui/LayerBuilder.h
deleted file mode 100644
index c799d48f7821..000000000000
--- a/libs/hwui/LayerBuilder.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "ClipArea.h"
-#include "Rect.h"
-#include "utils/Macros.h"
-
-#include <unordered_map>
-#include <vector>
-
-struct SkRect;
-
-namespace android {
-namespace uirenderer {
-
-class BakedOpState;
-struct BeginLayerOp;
-class BatchBase;
-class LinearAllocator;
-struct MergedBakedOpList;
-class MergingOpBatch;
-class OffscreenBuffer;
-class OpBatch;
-class RenderNode;
-
-typedef int batchid_t;
-typedef const void* mergeid_t;
-
-namespace OpBatchType {
-enum {
- Bitmap,
- MergedPatch,
- AlphaVertices,
- Vertices,
- AlphaMaskTexture,
- Text,
- ColorText,
- Shadow,
- TextureLayer,
- Functor,
- CopyToLayer,
- CopyFromLayer,
-
- Count // must be last
-};
-}
-
-typedef void (*BakedOpReceiver)(void*, const BakedOpState&);
-typedef void (*MergedOpReceiver)(void*, const MergedBakedOpList& opList);
-
-/**
- * Stores the deferred render operations and state used to compute ordering
- * for a single FBO/layer.
- */
-class LayerBuilder {
- // Prevent copy/assign because users may stash pointer to offscreenBuffer and viewportClip
- PREVENT_COPY_AND_ASSIGN(LayerBuilder);
-
-public:
- // Create LayerBuilder for Fbo0
- LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect)
- : LayerBuilder(width, height, repaintRect, nullptr, nullptr){};
-
- // Create LayerBuilder for an offscreen layer, where beginLayerOp is present for a
- // saveLayer, renderNode is present for a HW layer.
- LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect,
- const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
-
- // iterate back toward target to see if anything drawn since should overlap the new op
- // if no target, merging ops still iterate to find similar batch to insert after
- void locateInsertIndex(int batchId, const Rect& clippedBounds, BatchBase** targetBatch,
- size_t* insertBatchIndex) const;
-
- void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId);
-
- // insertion point of a new batch, will hopefully be immediately after similar batch
- // (generally, should be similar shader)
- void deferMergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId,
- mergeid_t mergeId);
-
- void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers, MergedOpReceiver*) const;
-
- void deferLayerClear(const Rect& dstRect);
-
- bool empty() const { return mBatches.empty(); }
-
- void clear();
-
- void dump() const;
-
- const uint32_t width;
- const uint32_t height;
- const Rect repaintRect;
- const ClipRect repaintClip;
- OffscreenBuffer* offscreenBuffer;
- const BeginLayerOp* beginLayerOp;
- const RenderNode* renderNode;
-
- // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps
- std::vector<BakedOpState*> activeUnclippedSaveLayers;
-
-private:
- void onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState);
- void flushLayerClears(LinearAllocator& allocator);
-
- std::vector<BatchBase*> mBatches;
-
- /**
- * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
- * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
- * collide, which avoids the need to resolve mergeid collisions.
- */
- std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count];
-
- // Maps batch ids to the most recent *non-merging* batch of that id
- OpBatch* mBatchLookup[OpBatchType::Count] = {nullptr};
-
- std::vector<Rect> mClearRects;
-};
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
index b14b80cc598a..6857999500f0 100644
--- a/libs/hwui/LayerUpdateQueue.h
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -19,6 +19,7 @@
#include <utils/StrongPointer.h>
#include "Rect.h"
+#include "RenderNode.h"
#include "utils/Macros.h"
#include <unordered_map>
diff --git a/libs/hwui/OpDumper.h b/libs/hwui/Lighting.h
index edbe381fcc72..d972c2181aea 100644
--- a/libs/hwui/OpDumper.h
+++ b/libs/hwui/Lighting.h
@@ -16,17 +16,22 @@
#pragma once
-#include <ostream>
+#include "Vector.h"
namespace android {
namespace uirenderer {
-struct RecordedOp;
+struct LightGeometry {
+ Vector3 center;
+ float radius;
+};
-class OpDumper {
-public:
- static void dump(const RecordedOp& op, std::ostream& output, int level = 0);
- static const char* opName(const RecordedOp& op);
+struct LightInfo {
+ LightInfo() : LightInfo(0, 0) {}
+ LightInfo(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha)
+ : ambientShadowAlpha(ambientShadowAlpha), spotShadowAlpha(spotShadowAlpha) {}
+ uint8_t ambientShadowAlpha;
+ uint8_t spotShadowAlpha;
};
}; // namespace uirenderer
diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h
index db9509fff378..082e95fb1440 100644
--- a/libs/hwui/NinePatchUtils.h
+++ b/libs/hwui/NinePatchUtils.h
@@ -53,9 +53,8 @@ static inline int NumDistinctRects(const SkCanvas::Lattice& lattice) {
return xRects * yRects;
}
-static inline void SetLatticeFlags(SkCanvas::Lattice* lattice,
- SkCanvas::Lattice::RectType* flags, int numFlags, const Res_png_9patch& chunk,
- SkColor* colors) {
+static inline void SetLatticeFlags(SkCanvas::Lattice* lattice, SkCanvas::Lattice::RectType* flags,
+ int numFlags, const Res_png_9patch& chunk, SkColor* colors) {
lattice->fRectTypes = flags;
lattice->fColors = colors;
sk_bzero(flags, numFlags * sizeof(SkCanvas::Lattice::RectType));
diff --git a/libs/hwui/OpDumper.cpp b/libs/hwui/OpDumper.cpp
deleted file mode 100644
index 5d2ccc77e82f..000000000000
--- a/libs/hwui/OpDumper.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "OpDumper.h"
-
-#include "ClipArea.h"
-#include "RecordedOp.h"
-
-namespace android {
-namespace uirenderer {
-
-#define STRINGIFY(n) #n,
-static const char* sOpNameLut[] = BUILD_FULL_OP_LUT(STRINGIFY);
-
-void OpDumper::dump(const RecordedOp& op, std::ostream& output, int level) {
- for (int i = 0; i < level; i++) {
- output << " ";
- }
-
- Rect localBounds(op.unmappedBounds);
- op.localMatrix.mapRect(localBounds);
- output << sOpNameLut[op.opId] << " " << localBounds;
-
- if (op.localClip &&
- (!op.localClip->rect.contains(localBounds) || op.localClip->intersectWithRoot)) {
- output << std::fixed << std::setprecision(0) << " clip=" << op.localClip->rect
- << " mode=" << (int)op.localClip->mode;
-
- if (op.localClip->intersectWithRoot) {
- output << " iwr";
- }
- }
-}
-
-const char* OpDumper::opName(const RecordedOp& op) {
- return sOpNameLut[op.opId];
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
deleted file mode 100644
index 11432d629650..000000000000
--- a/libs/hwui/OpenGLReadback.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "OpenGLReadback.h"
-
-#include "Caches.h"
-#include "GlLayer.h"
-#include "GlopBuilder.h"
-#include "Image.h"
-#include "renderstate/RenderState.h"
-#include "renderthread/EglManager.h"
-#include "utils/GLUtils.h"
-
-#include <GLES2/gl2.h>
-#include <gui/Surface.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-
-namespace android {
-namespace uirenderer {
-
-CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect,
- SkBitmap* bitmap) {
- ATRACE_CALL();
- // Setup the source
- sp<GraphicBuffer> sourceBuffer;
- sp<Fence> sourceFence;
- Matrix4 texTransform;
- status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data);
- texTransform.invalidateType();
- if (err != NO_ERROR) {
- ALOGW("Failed to get last queued buffer, error = %d", err);
- return CopyResult::UnknownError;
- }
- if (!sourceBuffer.get()) {
- ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
- return CopyResult::SourceEmpty;
- }
- if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
- ALOGW("Surface is protected, unable to copy from it");
- return CopyResult::SourceInvalid;
- }
- err = sourceFence->wait(500 /* ms */);
- if (err != NO_ERROR) {
- ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
- return CopyResult::Timeout;
- }
-
- return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap);
-}
-
-CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
- Matrix4& texTransform, const Rect& srcRect,
- SkBitmap* bitmap) {
- mRenderThread.eglManager().initialize();
- // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
- // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
- // to be able to properly sample from the buffer.
-
- // Create the EGLImage object that maps the GraphicBuffer
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- EGLClientBuffer clientBuffer = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
- EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
-
- EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- clientBuffer, attrs);
-
- if (sourceImage == EGL_NO_IMAGE_KHR) {
- ALOGW("eglCreateImageKHR failed (%#x)", eglGetError());
- return CopyResult::UnknownError;
- }
-
- uint32_t width = graphicBuffer->getWidth();
- uint32_t height = graphicBuffer->getHeight();
- CopyResult copyResult =
- copyImageInto(sourceImage, texTransform, width, height, srcRect, bitmap);
-
- // All we're flushing & finishing is the deletion of the texture since
- // copyImageInto already did a major flush & finish as an implicit
- // part of glReadPixels, so this shouldn't pose any major stalls.
- glFinish();
- eglDestroyImageKHR(display, sourceImage);
- return copyResult;
-}
-
-CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) {
- Rect srcRect;
- Matrix4 transform;
- transform.loadScale(1, -1, 1);
- transform.translate(0, -1);
- return copyGraphicBufferInto(graphicBuffer, transform, srcRect, bitmap);
-}
-
-static float sFlipVInit[16] = {
- 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1,
-};
-
-static const Matrix4 sFlipV(sFlipVInit);
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, Texture& sourceTexture,
- const Matrix4& texTransform, const Rect& srcRect,
- SkBitmap* bitmap) {
- int destWidth = bitmap->width();
- int destHeight = bitmap->height();
- if (destWidth > caches.maxTextureSize || destHeight > caches.maxTextureSize) {
- ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d", destWidth,
- destHeight, caches.maxTextureSize);
- return CopyResult::DestinationInvalid;
- }
-
- if (bitmap->colorType() == kRGBA_F16_SkColorType &&
- !caches.extensions().hasRenderableFloatTextures()) {
- ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
- return CopyResult::DestinationInvalid;
- }
-
- GLuint fbo = renderState.createFramebuffer();
- if (!fbo) {
- ALOGW("Could not obtain an FBO");
- return CopyResult::UnknownError;
- }
-
- 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;
- }
-
- renderState.bindFramebuffer(fbo);
-
- // TODO: Use layerPool or something to get this maybe? But since we
- // need explicit format control we can't currently.
-
- // Setup the rendertarget
- glGenTextures(1, &texture);
- caches.textureState().activateTexture(0);
- caches.textureState().bindTexture(texture);
- glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, destWidth, destHeight, 0, format, type, nullptr);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
-
- {
- bool requiresFilter;
- // Draw & readback
- renderState.setViewport(destWidth, destHeight);
- renderState.scissor().setEnabled(false);
- renderState.blend().syncEnabled();
- renderState.stencil().disable();
-
- Matrix4 croppedTexTransform(texTransform);
- if (!srcRect.isEmpty()) {
- // We flipV to convert to 0,0 top-left for the srcRect
- // coordinates then flip back to 0,0 bottom-left for
- // GLES coordinates.
- croppedTexTransform.multiply(sFlipV);
- croppedTexTransform.translate(srcRect.left / sourceTexture.width(),
- srcRect.top / sourceTexture.height(), 0);
- croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(),
- srcRect.getHeight() / sourceTexture.height(), 1);
- croppedTexTransform.multiply(sFlipV);
- requiresFilter = srcRect.getWidth() != (float)destWidth ||
- srcRect.getHeight() != (float)destHeight;
- } else {
- requiresFilter = sourceTexture.width() != (uint32_t)destWidth ||
- sourceTexture.height() != (uint32_t)destHeight;
- }
- Glop glop;
- GlopBuilder(renderState, caches, &glop)
- .setRoundRectClipState(nullptr)
- .setMeshTexturedUnitQuad(nullptr)
- .setFillExternalTexture(sourceTexture, croppedTexTransform, requiresFilter)
- .setTransform(Matrix4::identity(), TransformFlags::None)
- .setModelViewMapUnitToRect(Rect(destWidth, destHeight))
- .build();
- Matrix4 ortho;
- ortho.loadOrtho(destWidth, destHeight);
- renderState.render(glop, ortho, false);
-
- // TODO: We should convert to linear space when the target is RGBA16F
- glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, type, bitmap->getPixels());
- bitmap->notifyPixelsChanged();
- }
-
- // Cleanup
- caches.textureState().deleteTexture(texture);
- renderState.deleteFramebuffer(fbo);
-
- GL_CHECKPOINT(MODERATE);
-
- return CopyResult::Success;
-}
-
-CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
- int imgWidth, int imgHeight, const Rect& srcRect,
- SkBitmap* bitmap) {
- // If this is a 90 or 270 degree rotation we need to swap width/height
- // This is a fuzzy way of checking that.
- if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) {
- std::swap(imgWidth, imgHeight);
- }
-
- Caches& caches = Caches::getInstance();
- GLuint sourceTexId;
- // Create a 2D texture to sample from the EGLImage
- glGenTextures(1, &sourceTexId);
- caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
-
- GLenum status = GL_NO_ERROR;
- while ((status = glGetError()) != GL_NO_ERROR) {
- ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status);
- return CopyResult::UnknownError;
- }
-
- Texture sourceTexture(caches);
- sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */,
- GL_TEXTURE_EXTERNAL_OES);
-
- CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(), sourceTexture,
- imgTransform, srcRect, bitmap);
- sourceTexture.deleteTexture();
- return copyResult;
-}
-
-bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread, GlLayer& layer,
- SkBitmap* bitmap) {
- if (!layer.isRenderable()) {
- // layer has never been updated by DeferredLayerUpdater, abort copy
- return false;
- }
-
- return CopyResult::Success == copyTextureInto(Caches::getInstance(), renderThread.renderState(),
- layer.getTexture(), layer.getTexTransform(),
- Rect(), bitmap);
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/OpenGLReadback.h b/libs/hwui/OpenGLReadback.h
deleted file mode 100644
index ca40738b4901..000000000000
--- a/libs/hwui/OpenGLReadback.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Readback.h"
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-namespace android {
-namespace uirenderer {
-
-class Matrix4;
-class GlLayer;
-
-class OpenGLReadback : public Readback {
-public:
- virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
- SkBitmap* bitmap) override;
- virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
- SkBitmap* bitmap) override;
-
-protected:
- explicit OpenGLReadback(renderthread::RenderThread& thread) : Readback(thread) {}
- virtual ~OpenGLReadback() {}
-
- virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
- int imgWidth, int imgHeight, const Rect& srcRect,
- SkBitmap* bitmap) = 0;
-
-private:
- CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform,
- const Rect& srcRect, SkBitmap* bitmap);
-};
-
-class OpenGLReadbackImpl : public OpenGLReadback {
-public:
- OpenGLReadbackImpl(renderthread::RenderThread& thread) : OpenGLReadback(thread) {}
-
- /**
- * Copies the layer's contents into the provided bitmap.
- */
- static bool copyLayerInto(renderthread::RenderThread& renderThread, GlLayer& layer,
- SkBitmap* bitmap);
-
-protected:
- virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
- int imgWidth, int imgHeight, const Rect& srcRect,
- SkBitmap* bitmap) override;
-};
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 4ddacc8a922d..f1c38031980e 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -42,9 +42,8 @@ public:
mBounds.set(left, top, right, bottom);
mRadius = radius;
-
// Reuse memory if previous outline was the same shape (rect or round rect).
- if ( mPath.countVerbs() > 10) {
+ if (mPath.countVerbs() > 10) {
mPath.reset();
} else {
mPath.rewind();
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
deleted file mode 100644
index c243dad01f04..000000000000
--- a/libs/hwui/Patch.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Patch.h"
-
-#include "Caches.h"
-#include "Properties.h"
-#include "UvMapper.h"
-#include "utils/MathUtils.h"
-
-#include <utils/Log.h>
-#include <algorithm>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Vertices management
-///////////////////////////////////////////////////////////////////////////////
-
-uint32_t Patch::getSize() const {
- return verticesCount * sizeof(TextureVertex);
-}
-
-Patch::Patch(const float bitmapWidth, const float bitmapHeight, float width, float height,
- const UvMapper& mapper, const Res_png_9patch* patch)
- : mColors(patch->getColors()) {
- int8_t emptyQuads = 0;
- const int8_t numColors = patch->numColors;
- if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
- for (int8_t i = 0; i < numColors; i++) {
- if (mColors[i] == 0x0) {
- emptyQuads++;
- }
- }
- }
-
- hasEmptyQuads = emptyQuads > 0;
-
- uint32_t xCount = patch->numXDivs;
- uint32_t yCount = patch->numYDivs;
-
- uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 4;
- if (maxVertices == 0) return;
-
- vertices.reset(new TextureVertex[maxVertices]);
- TextureVertex* vertex = vertices.get();
-
- const int32_t* xDivs = patch->getXDivs();
- const int32_t* yDivs = patch->getYDivs();
-
- const uint32_t xStretchCount = (xCount + 1) >> 1;
- const uint32_t yStretchCount = (yCount + 1) >> 1;
-
- float stretchX = 0.0f;
- float stretchY = 0.0f;
-
- float rescaleX = 1.0f;
- float rescaleY = 1.0f;
-
- if (xStretchCount > 0) {
- uint32_t stretchSize = 0;
- for (uint32_t i = 1; i < xCount; i += 2) {
- stretchSize += xDivs[i] - xDivs[i - 1];
- }
- const float xStretchTex = stretchSize;
- const float fixed = bitmapWidth - stretchSize;
- const float xStretch = std::max(width - fixed, 0.0f);
- stretchX = xStretch / xStretchTex;
- rescaleX = fixed == 0.0f ? 0.0f : std::min(std::max(width, 0.0f) / fixed, 1.0f);
- }
-
- if (yStretchCount > 0) {
- uint32_t stretchSize = 0;
- for (uint32_t i = 1; i < yCount; i += 2) {
- stretchSize += yDivs[i] - yDivs[i - 1];
- }
- const float yStretchTex = stretchSize;
- const float fixed = bitmapHeight - stretchSize;
- const float yStretch = std::max(height - fixed, 0.0f);
- stretchY = yStretch / yStretchTex;
- rescaleY = fixed == 0.0f ? 0.0f : std::min(std::max(height, 0.0f) / fixed, 1.0f);
- }
-
- uint32_t quadCount = 0;
-
- float previousStepY = 0.0f;
-
- float y1 = 0.0f;
- float y2 = 0.0f;
- float v1 = 0.0f;
-
- mUvMapper = mapper;
-
- for (uint32_t i = 0; i < yCount; i++) {
- float stepY = yDivs[i];
- const float segment = stepY - previousStepY;
-
- if (i & 1) {
- y2 = y1 + floorf(segment * stretchY + 0.5f);
- } else {
- y2 = y1 + segment * rescaleY;
- }
-
- float vOffset = y1 == y2 ? 0.0f : 0.5 - (0.5 * segment / (y2 - y1));
- float v2 = std::max(0.0f, stepY - vOffset) / bitmapHeight;
- v1 += vOffset / bitmapHeight;
-
- if (stepY > 0.0f) {
- generateRow(xDivs, xCount, vertex, y1, y2, v1, v2, stretchX, rescaleX, width,
- bitmapWidth, quadCount);
- }
-
- y1 = y2;
- v1 = stepY / bitmapHeight;
-
- previousStepY = stepY;
- }
-
- if (previousStepY != bitmapHeight) {
- y2 = height;
- generateRow(xDivs, xCount, vertex, y1, y2, v1, 1.0f, stretchX, rescaleX, width, bitmapWidth,
- quadCount);
- }
-
- if (verticesCount != maxVertices) {
- std::unique_ptr<TextureVertex[]> reducedVertices(new TextureVertex[verticesCount]);
- memcpy(reducedVertices.get(), vertices.get(), verticesCount * sizeof(TextureVertex));
- vertices = std::move(reducedVertices);
- }
-}
-
-void Patch::generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex, float y1,
- float y2, float v1, float v2, float stretchX, float rescaleX, float width,
- float bitmapWidth, uint32_t& quadCount) {
- float previousStepX = 0.0f;
-
- float x1 = 0.0f;
- float x2 = 0.0f;
- float u1 = 0.0f;
-
- // Generate the row quad by quad
- for (uint32_t i = 0; i < xCount; i++) {
- float stepX = xDivs[i];
- const float segment = stepX - previousStepX;
-
- if (i & 1) {
- x2 = x1 + floorf(segment * stretchX + 0.5f);
- } else {
- x2 = x1 + segment * rescaleX;
- }
-
- float uOffset = x1 == x2 ? 0.0f : 0.5 - (0.5 * segment / (x2 - x1));
- float u2 = std::max(0.0f, stepX - uOffset) / bitmapWidth;
- u1 += uOffset / bitmapWidth;
-
- if (stepX > 0.0f) {
- generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount);
- }
-
- x1 = x2;
- u1 = stepX / bitmapWidth;
-
- previousStepX = stepX;
- }
-
- if (previousStepX != bitmapWidth) {
- x2 = width;
- generateQuad(vertex, x1, y1, x2, y2, u1, v1, 1.0f, v2, quadCount);
- }
-}
-
-void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, float u1,
- float v1, float u2, float v2, uint32_t& quadCount) {
- const uint32_t oldQuadCount = quadCount;
- quadCount++;
-
- x1 = std::max(x1, 0.0f);
- x2 = std::max(x2, 0.0f);
- y1 = std::max(y1, 0.0f);
- y2 = std::max(y2, 0.0f);
-
- // Skip degenerate and transparent (empty) quads
- if ((mColors[oldQuadCount] == 0) || x1 >= x2 || y1 >= y2) {
-#if DEBUG_PATCHES_EMPTY_VERTICES
- PATCH_LOGD(" quad %d (empty)", oldQuadCount);
- PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.8f, %.8f", x1, y1, u1, v1);
- PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.8f, %.8f", x2, y2, u2, v2);
-#endif
- return;
- }
-
- // Record all non empty quads
- if (hasEmptyQuads) {
- quads.emplace_back(x1, y1, x2, y2);
- }
-
- mUvMapper.map(u1, v1, u2, v2);
-
- TextureVertex::set(vertex++, x1, y1, u1, v1);
- TextureVertex::set(vertex++, x2, y1, u2, v1);
- TextureVertex::set(vertex++, x1, y2, u1, v2);
- TextureVertex::set(vertex++, x2, y2, u2, v2);
-
- verticesCount += 4;
- indexCount += 6;
-
-#if DEBUG_PATCHES_VERTICES
- PATCH_LOGD(" quad %d", oldQuadCount);
- PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.8f, %.8f", x1, y1, u1, v1);
- PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.8f, %.8f", x2, y2, u2, v2);
-#endif
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
deleted file mode 100644
index a659ed227bec..000000000000
--- a/libs/hwui/Patch.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_PATCH_H
-#define ANDROID_HWUI_PATCH_H
-
-#include <sys/types.h>
-
-#include <GLES2/gl2.h>
-
-#include <androidfw/ResourceTypes.h>
-
-#include "Rect.h"
-#include "UvMapper.h"
-
-#include <vector>
-
-namespace android {
-namespace uirenderer {
-
-struct TextureVertex;
-
-///////////////////////////////////////////////////////////////////////////////
-// 9-patch structures
-///////////////////////////////////////////////////////////////////////////////
-
-class Patch {
-public:
- Patch(const float bitmapWidth, const float bitmapHeight, float width, float height,
- const UvMapper& mapper, const Res_png_9patch* patch);
-
- /**
- * Returns the size of this patch's mesh in bytes.
- */
- uint32_t getSize() const;
-
- std::unique_ptr<TextureVertex[]> vertices;
- uint32_t verticesCount = 0;
- uint32_t indexCount = 0;
- bool hasEmptyQuads = false;
- std::vector<Rect> quads;
-
- GLintptr positionOffset = 0;
- GLintptr textureOffset = 0;
-
-private:
- void generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex, float y1,
- float y2, float v1, float v2, float stretchX, float rescaleX, float width,
- float bitmapWidth, uint32_t& quadCount);
- void generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, float u1,
- float v1, float u2, float v2, uint32_t& quadCount);
-
- const uint32_t* mColors;
- UvMapper mUvMapper;
-}; // struct Patch
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_PATCH_H
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
deleted file mode 100644
index 673d73c5475d..000000000000
--- a/libs/hwui/PatchCache.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/JenkinsHash.h>
-#include <utils/Log.h>
-
-#include "Caches.h"
-#include "Patch.h"
-#include "PatchCache.h"
-#include "Properties.h"
-#include "renderstate/RenderState.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-PatchCache::PatchCache(RenderState& renderState)
- : mRenderState(renderState)
- , mMaxSize(KB(128))
- , mSize(0)
- , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
- , mMeshBuffer(0)
- , mFreeBlocks(nullptr) {}
-
-PatchCache::~PatchCache() {
- clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-hash_t PatchCache::PatchDescription::hash() const {
- uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch));
- hash = JenkinsHashMix(hash, mBitmapWidth);
- hash = JenkinsHashMix(hash, mBitmapHeight);
- hash = JenkinsHashMix(hash, mPixelWidth);
- hash = JenkinsHashMix(hash, mPixelHeight);
- return JenkinsHashWhiten(hash);
-}
-
-int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
- const PatchCache::PatchDescription& rhs) {
- return memcmp(&lhs, &rhs, sizeof(PatchDescription));
-}
-
-void PatchCache::clear() {
- clearCache();
-
- if (mMeshBuffer) {
- mRenderState.meshState().deleteMeshBuffer(mMeshBuffer);
- mMeshBuffer = 0;
- mSize = 0;
- }
-}
-
-void PatchCache::clearCache() {
- LruCache<PatchDescription, Patch*>::Iterator i(mCache);
- while (i.next()) {
- delete i.value();
- }
- mCache.clear();
-
- BufferBlock* block = mFreeBlocks;
- while (block) {
- BufferBlock* next = block->next;
- delete block;
- block = next;
- }
- mFreeBlocks = nullptr;
-}
-
-void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
- LruCache<PatchDescription, Patch*>::Iterator i(mCache);
- while (i.next()) {
- const PatchDescription& key = i.key();
- if (key.getPatch() == patch) {
- patchesToRemove.push(patch_pair_t(&key, i.value()));
- }
- }
-}
-
-void PatchCache::removeDeferred(Res_png_9patch* patch) {
- Mutex::Autolock _l(mLock);
-
- // Assert that patch is not already garbage
- size_t count = mGarbage.size();
- for (size_t i = 0; i < count; i++) {
- if (patch == mGarbage[i]) {
- patch = nullptr;
- break;
- }
- }
- LOG_ALWAYS_FATAL_IF(patch == nullptr);
-
- mGarbage.push(patch);
-}
-
-void PatchCache::clearGarbage() {
- Vector<patch_pair_t> patchesToRemove;
-
- { // scope for the mutex
- Mutex::Autolock _l(mLock);
- size_t count = mGarbage.size();
- for (size_t i = 0; i < count; i++) {
- Res_png_9patch* patch = mGarbage[i];
- remove(patchesToRemove, patch);
- // A Res_png_9patch is actually an array of byte that's larger
- // than sizeof(Res_png_9patch). It must be freed as an array.
- delete[](int8_t*) patch;
- }
- mGarbage.clear();
- }
-
- // TODO: We could sort patchesToRemove by offset to merge
- // adjacent free blocks
- for (size_t i = 0; i < patchesToRemove.size(); i++) {
- const patch_pair_t& pair = patchesToRemove[i];
-
- // Release the patch and mark the space in the free list
- Patch* patch = pair.getSecond();
- BufferBlock* block = new BufferBlock(patch->positionOffset, patch->getSize());
- block->next = mFreeBlocks;
- mFreeBlocks = block;
-
- mSize -= patch->getSize();
-
- mCache.remove(*pair.getFirst());
- delete patch;
- }
-
-#if DEBUG_PATCHES
- if (patchesToRemove.size() > 0) {
- dumpFreeBlocks("Removed garbage");
- }
-#endif
-}
-
-void PatchCache::createVertexBuffer() {
- mRenderState.meshState().genOrUpdateMeshBuffer(&mMeshBuffer, mMaxSize, nullptr,
- GL_DYNAMIC_DRAW);
- mSize = 0;
- mFreeBlocks = new BufferBlock(0, mMaxSize);
-}
-
-/**
- * Sets the mesh's offsets and copies its associated vertices into
- * the mesh buffer (VBO).
- */
-void PatchCache::setupMesh(Patch* newMesh) {
- // This call ensures the VBO exists and that it is bound
- if (!mMeshBuffer) {
- createVertexBuffer();
- }
-
- // If we're running out of space, let's clear the entire cache
- uint32_t size = newMesh->getSize();
- if (mSize + size > mMaxSize) {
- clearCache();
- createVertexBuffer();
- }
-
- // Find a block where we can fit the mesh
- BufferBlock* previous = nullptr;
- BufferBlock* block = mFreeBlocks;
- while (block) {
- // The mesh fits
- if (block->size >= size) {
- break;
- }
- previous = block;
- block = block->next;
- }
-
- // We have enough space left in the buffer, but it's
- // too fragmented, let's clear the cache
- if (!block) {
- clearCache();
- createVertexBuffer();
- previous = nullptr;
- block = mFreeBlocks;
- }
-
- // Copy the 9patch mesh in the VBO
- newMesh->positionOffset = (GLintptr)(block->offset);
- newMesh->textureOffset = newMesh->positionOffset + kMeshTextureOffset;
-
- mRenderState.meshState().updateMeshBufferSubData(mMeshBuffer, newMesh->positionOffset, size,
- newMesh->vertices.get());
-
- // Remove the block since we've used it entirely
- if (block->size == size) {
- if (previous) {
- previous->next = block->next;
- } else {
- mFreeBlocks = block->next;
- }
- delete block;
- } else {
- // Resize the block now that it's occupied
- block->offset += size;
- block->size -= size;
- }
-
- mSize += size;
-}
-
-static const UvMapper sIdentity;
-
-const Patch* PatchCache::get(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
- const float pixelWidth, const float pixelHeight,
- const Res_png_9patch* patch) {
- const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
- const Patch* mesh = mCache.get(description);
-
- if (!mesh) {
- Patch* newMesh =
- new Patch(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, sIdentity, patch);
-
- if (newMesh->vertices) {
- setupMesh(newMesh);
- }
-
-#if DEBUG_PATCHES
- dumpFreeBlocks("Adding patch");
-#endif
-
- mCache.put(description, newMesh);
- return newMesh;
- }
-
- return mesh;
-}
-
-#if DEBUG_PATCHES
-void PatchCache::dumpFreeBlocks(const char* prefix) {
- String8 dump;
- BufferBlock* block = mFreeBlocks;
- while (block) {
- dump.appendFormat("->(%d, %d)", block->positionOffset, block->size);
- block = block->next;
- }
- ALOGD("%s: Free blocks%s", prefix, dump.string());
-}
-#endif
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
deleted file mode 100644
index 273c3f56f2cd..000000000000
--- a/libs/hwui/PatchCache.h
+++ /dev/null
@@ -1,171 +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.
- */
-
-#pragma once
-
-#include <GLES2/gl2.h>
-
-#include <utils/LruCache.h>
-
-#include <androidfw/ResourceTypes.h>
-
-#include "Debug.h"
-#include "utils/Pair.h"
-
-namespace android {
-namespace uirenderer {
-
-class Patch;
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_PATCHES
-#define PATCH_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-#define PATCH_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Cache
-///////////////////////////////////////////////////////////////////////////////
-
-class Caches;
-class RenderState;
-
-class PatchCache {
-public:
- explicit PatchCache(RenderState& renderState);
- ~PatchCache();
-
- const Patch* get(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
- const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch);
- void clear();
-
- uint32_t getSize() const { return mSize; }
-
- uint32_t getMaxSize() const { return mMaxSize; }
-
- GLuint getMeshBuffer() const { return mMeshBuffer; }
-
- /**
- * Removes the entries associated with the specified 9-patch. This is meant
- * to be called from threads that are not the EGL context thread (GC thread
- * on the VM side for instance.)
- */
- void removeDeferred(Res_png_9patch* patch);
-
- /**
- * Process deferred removals.
- */
- void clearGarbage();
-
-private:
- struct PatchDescription {
- PatchDescription()
- : mPatch(nullptr)
- , mBitmapWidth(0)
- , mBitmapHeight(0)
- , mPixelWidth(0)
- , mPixelHeight(0) {}
-
- PatchDescription(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
- const float pixelWidth, const float pixelHeight,
- const Res_png_9patch* patch)
- : mPatch(patch)
- , mBitmapWidth(bitmapWidth)
- , mBitmapHeight(bitmapHeight)
- , mPixelWidth(pixelWidth)
- , mPixelHeight(pixelHeight) {}
-
- hash_t hash() const;
-
- const Res_png_9patch* getPatch() const { return mPatch; }
-
- static int compare(const PatchDescription& lhs, const PatchDescription& rhs);
-
- bool operator==(const PatchDescription& other) const { return compare(*this, other) == 0; }
-
- bool operator!=(const PatchDescription& other) const { return compare(*this, other) != 0; }
-
- friend inline int strictly_order_type(const PatchDescription& lhs,
- const PatchDescription& rhs) {
- return PatchDescription::compare(lhs, rhs) < 0;
- }
-
- friend inline int compare_type(const PatchDescription& lhs, const PatchDescription& rhs) {
- return PatchDescription::compare(lhs, rhs);
- }
-
- friend inline hash_t hash_type(const PatchDescription& entry) { return entry.hash(); }
-
- private:
- const Res_png_9patch* mPatch;
- uint32_t mBitmapWidth;
- uint32_t mBitmapHeight;
- float mPixelWidth;
- float mPixelHeight;
-
- }; // struct PatchDescription
-
- /**
- * A buffer block represents an empty range in the mesh buffer
- * that can be used to store vertices.
- *
- * The patch cache maintains a linked-list of buffer blocks
- * to track available regions of memory in the VBO.
- */
- struct BufferBlock {
- BufferBlock(uint32_t offset, uint32_t size) : offset(offset), size(size), next(nullptr) {}
-
- uint32_t offset;
- uint32_t size;
-
- BufferBlock* next;
- }; // struct BufferBlock
-
- typedef Pair<const PatchDescription*, Patch*> patch_pair_t;
-
- void clearCache();
- void createVertexBuffer();
-
- void setupMesh(Patch* newMesh);
-
- void remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch);
-
-#if DEBUG_PATCHES
- void dumpFreeBlocks(const char* prefix);
-#endif
-
- RenderState& mRenderState;
- const uint32_t mMaxSize;
- uint32_t mSize;
-
- LruCache<PatchDescription, Patch*> mCache;
-
- GLuint mMeshBuffer;
- // First available free block inside the mesh buffer
- BufferBlock* mFreeBlocks;
-
- // Garbage tracking, required to handle GC events on the VM side
- Vector<Res_png_9patch*> mGarbage;
- mutable Mutex mLock;
-}; // class PatchCache
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
deleted file mode 100644
index e67c5bddd354..000000000000
--- a/libs/hwui/PathCache.cpp
+++ /dev/null
@@ -1,559 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkColorFilter.h>
-#include <SkMaskFilter.h>
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <SkPathEffect.h>
-#include <SkRect.h>
-
-#include <utils/JenkinsHash.h>
-#include <utils/Trace.h>
-
-#include "Caches.h"
-#include "PathCache.h"
-
-#include "thread/Signal.h"
-#include "thread/TaskProcessor.h"
-
-#include <cutils/properties.h>
-
-namespace android {
-namespace uirenderer {
-
-static constexpr size_t PATH_CACHE_COUNT_LIMIT = 256;
-
-template <class T>
-static bool compareWidthHeight(const T& lhs, const T& rhs) {
- return (lhs.mWidth == rhs.mWidth) && (lhs.mHeight == rhs.mHeight);
-}
-
-static bool compareRoundRects(const PathDescription::Shape::RoundRect& lhs,
- const PathDescription::Shape::RoundRect& rhs) {
- return compareWidthHeight(lhs, rhs) && lhs.mRx == rhs.mRx && lhs.mRy == rhs.mRy;
-}
-
-static bool compareArcs(const PathDescription::Shape::Arc& lhs,
- const PathDescription::Shape::Arc& rhs) {
- return compareWidthHeight(lhs, rhs) && lhs.mStartAngle == rhs.mStartAngle &&
- lhs.mSweepAngle == rhs.mSweepAngle && lhs.mUseCenter == rhs.mUseCenter;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Cache entries
-///////////////////////////////////////////////////////////////////////////////
-
-PathDescription::PathDescription()
- : type(ShapeType::None)
- , join(SkPaint::kDefault_Join)
- , cap(SkPaint::kDefault_Cap)
- , style(SkPaint::kFill_Style)
- , miter(4.0f)
- , strokeWidth(1.0f)
- , pathEffect(nullptr) {
- // Shape bits should be set to zeroes, because they are used for hash calculation.
- memset(&shape, 0, sizeof(Shape));
-}
-
-PathDescription::PathDescription(ShapeType type, const SkPaint* paint)
- : type(type)
- , join(paint->getStrokeJoin())
- , cap(paint->getStrokeCap())
- , style(paint->getStyle())
- , miter(paint->getStrokeMiter())
- , strokeWidth(paint->getStrokeWidth())
- , pathEffect(paint->getPathEffect()) {
- // Shape bits should be set to zeroes, because they are used for hash calculation.
- memset(&shape, 0, sizeof(Shape));
-}
-
-hash_t PathDescription::hash() const {
- uint32_t hash = JenkinsHashMix(0, static_cast<int>(type));
- hash = JenkinsHashMix(hash, join);
- hash = JenkinsHashMix(hash, cap);
- hash = JenkinsHashMix(hash, style);
- hash = JenkinsHashMix(hash, android::hash_type(miter));
- hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
- hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
- hash = JenkinsHashMixBytes(hash, (uint8_t*)&shape, sizeof(Shape));
- return JenkinsHashWhiten(hash);
-}
-
-bool PathDescription::operator==(const PathDescription& rhs) const {
- if (type != rhs.type) return false;
- if (join != rhs.join) return false;
- if (cap != rhs.cap) return false;
- if (style != rhs.style) return false;
- if (miter != rhs.miter) return false;
- if (strokeWidth != rhs.strokeWidth) return false;
- if (pathEffect != rhs.pathEffect) return false;
- switch (type) {
- case ShapeType::None:
- return 0;
- case ShapeType::Rect:
- return compareWidthHeight(shape.rect, rhs.shape.rect);
- case ShapeType::RoundRect:
- return compareRoundRects(shape.roundRect, rhs.shape.roundRect);
- case ShapeType::Circle:
- return shape.circle.mRadius == rhs.shape.circle.mRadius;
- case ShapeType::Oval:
- return compareWidthHeight(shape.oval, rhs.shape.oval);
- case ShapeType::Arc:
- return compareArcs(shape.arc, rhs.shape.arc);
- case ShapeType::Path:
- return shape.path.mGenerationID == rhs.shape.path.mGenerationID;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Utilities
-///////////////////////////////////////////////////////////////////////////////
-
-static void computePathBounds(const SkPath* path, const SkPaint* paint, PathTexture* texture,
- uint32_t& width, uint32_t& height) {
- const SkRect& bounds = path->getBounds();
- const float pathWidth = std::max(bounds.width(), 1.0f);
- const float pathHeight = std::max(bounds.height(), 1.0f);
-
- texture->left = floorf(bounds.fLeft);
- texture->top = floorf(bounds.fTop);
-
- texture->offset = (int)floorf(std::max(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
-
- width = uint32_t(pathWidth + texture->offset * 2.0 + 0.5);
- height = uint32_t(pathHeight + texture->offset * 2.0 + 0.5);
-}
-
-static void initPaint(SkPaint& paint) {
- // Make sure the paint is opaque, color, alpha, filter, etc.
- // will be applied later when compositing the alpha8 texture
- paint.setColor(SK_ColorBLACK);
- paint.setAlpha(255);
- paint.setColorFilter(nullptr);
- paint.setMaskFilter(nullptr);
- paint.setShader(nullptr);
- paint.setBlendMode(SkBlendMode::kSrc);
-}
-
-static sk_sp<Bitmap> drawPath(const SkPath* path, const SkPaint* paint, PathTexture* texture,
- uint32_t maxTextureSize) {
- uint32_t width, height;
- computePathBounds(path, paint, texture, width, height);
- if (width > maxTextureSize || height > maxTextureSize) {
- ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)", width, height,
- maxTextureSize, maxTextureSize);
- return nullptr;
- }
-
- sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(width, height));
- SkPaint pathPaint(*paint);
- initPaint(pathPaint);
-
- SkBitmap skBitmap;
- bitmap->getSkBitmap(&skBitmap);
- skBitmap.eraseColor(0);
- SkCanvas canvas(skBitmap);
- canvas.translate(-texture->left + texture->offset, -texture->top + texture->offset);
- canvas.drawPath(*path, pathPaint);
- return bitmap;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Cache constructor/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-PathCache::PathCache()
- : mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity)
- , mSize(0)
- , mMaxSize(DeviceInfo::multiplyByResolution(4)) {
- mCache.setOnEntryRemovedListener(this);
- mMaxTextureSize = DeviceInfo::get()->maxTextureSize();
- mDebugEnabled = Properties::debugLevel & kDebugCaches;
-}
-
-PathCache::~PathCache() {
- mCache.clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Size management
-///////////////////////////////////////////////////////////////////////////////
-
-uint32_t PathCache::getSize() {
- return mSize;
-}
-
-uint32_t PathCache::getMaxSize() {
- return mMaxSize;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Callbacks
-///////////////////////////////////////////////////////////////////////////////
-
-void PathCache::operator()(PathDescription& entry, PathTexture*& texture) {
- removeTexture(texture);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-void PathCache::removeTexture(PathTexture* texture) {
- if (texture) {
- const uint32_t size = texture->width() * texture->height();
-
- // If there is a pending task we must wait for it to return
- // before attempting our cleanup
- const sp<PathTask>& task = texture->task();
- if (task != nullptr) {
- task->getResult();
- texture->clearTask();
- } else {
- // If there is a pending task, the path was not added
- // to the cache and the size wasn't increased
- if (size > mSize) {
- ALOGE("Removing path texture of size %d will leave "
- "the cache in an inconsistent state",
- size);
- }
- mSize -= size;
- }
-
- PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d", texture->id, size, mSize);
- if (mDebugEnabled) {
- ALOGD("Shape deleted, size = %d", size);
- }
-
- texture->deleteTexture();
- delete texture;
- }
-}
-
-void PathCache::purgeCache(uint32_t width, uint32_t height) {
- const uint32_t size = width * height;
- // Don't even try to cache a bitmap that's bigger than the cache
- if (size < mMaxSize) {
- while (mSize + size > mMaxSize) {
- mCache.removeOldest();
- }
- }
-}
-
-void PathCache::trim() {
- while (mSize > mMaxSize || mCache.size() > PATH_CACHE_COUNT_LIMIT) {
- LOG_ALWAYS_FATAL_IF(!mCache.size(),
- "Inconsistent mSize! Ran out of items to remove!"
- " mSize = %u, mMaxSize = %u",
- mSize, mMaxSize);
- mCache.removeOldest();
- }
-}
-
-PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath* path,
- const SkPaint* paint) {
- ATRACE_NAME("Generate Path Texture");
-
- PathTexture* texture = new PathTexture(Caches::getInstance(), path->getGenerationID());
- sk_sp<Bitmap> bitmap(drawPath(path, paint, texture, mMaxTextureSize));
- if (!bitmap) {
- delete texture;
- return nullptr;
- }
-
- purgeCache(bitmap->width(), bitmap->height());
- generateTexture(entry, *bitmap, texture);
- return texture;
-}
-
-void PathCache::generateTexture(const PathDescription& entry, Bitmap& bitmap, PathTexture* texture,
- bool addToCache) {
- generateTexture(bitmap, texture);
-
- // Note here that we upload to a texture even if it's bigger than mMaxSize.
- // Such an entry in mCache will only be temporary, since it will be evicted
- // immediately on trim, or on any other Path entering the cache.
- uint32_t size = texture->width() * texture->height();
- mSize += size;
- PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d", texture->id, size, mSize);
- if (mDebugEnabled) {
- ALOGD("Shape created, size = %d", size);
- }
- if (addToCache) {
- mCache.put(entry, texture);
- }
-}
-
-void PathCache::clear() {
- mCache.clear();
-}
-
-void PathCache::generateTexture(Bitmap& bitmap, Texture* texture) {
- ATRACE_NAME("Upload Path Texture");
- texture->upload(bitmap);
- texture->setFilter(GL_LINEAR);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Path precaching
-///////////////////////////////////////////////////////////////////////////////
-
-PathCache::PathProcessor::PathProcessor(Caches& caches)
- : TaskProcessor<sk_sp<Bitmap> >(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) {}
-
-void PathCache::PathProcessor::onProcess(const sp<Task<sk_sp<Bitmap> > >& task) {
- PathTask* t = static_cast<PathTask*>(task.get());
- ATRACE_NAME("pathPrecache");
-
- t->setResult(drawPath(&t->path, &t->paint, t->texture, mMaxTextureSize));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Paths
-///////////////////////////////////////////////////////////////////////////////
-
-void PathCache::removeDeferred(const SkPath* path) {
- Mutex::Autolock l(mLock);
- mGarbage.push_back(path->getGenerationID());
-}
-
-void PathCache::clearGarbage() {
- Vector<PathDescription> pathsToRemove;
-
- { // scope for the mutex
- Mutex::Autolock l(mLock);
- for (const uint32_t generationID : mGarbage) {
- LruCache<PathDescription, PathTexture*>::Iterator iter(mCache);
- while (iter.next()) {
- const PathDescription& key = iter.key();
- if (key.type == ShapeType::Path && key.shape.path.mGenerationID == generationID) {
- pathsToRemove.push(key);
- }
- }
- }
- mGarbage.clear();
- }
-
- for (size_t i = 0; i < pathsToRemove.size(); i++) {
- mCache.remove(pathsToRemove.itemAt(i));
- }
-}
-
-PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) {
- PathDescription entry(ShapeType::Path, paint);
- entry.shape.path.mGenerationID = path->getGenerationID();
-
- PathTexture* texture = mCache.get(entry);
-
- if (!texture) {
- texture = addTexture(entry, path, paint);
- } else {
- // A bitmap is attached to the texture, this means we need to
- // upload it as a GL texture
- const sp<PathTask>& task = texture->task();
- if (task != nullptr) {
- // But we must first wait for the worker thread to be done
- // producing the bitmap, so let's wait
- sk_sp<Bitmap> bitmap = task->getResult();
- if (bitmap) {
- generateTexture(entry, *bitmap, texture, false);
- texture->clearTask();
- } else {
- texture->clearTask();
- texture = nullptr;
- mCache.remove(entry);
- }
- }
- }
-
- return texture;
-}
-
-void PathCache::remove(const SkPath* path, const SkPaint* paint) {
- PathDescription entry(ShapeType::Path, paint);
- entry.shape.path.mGenerationID = path->getGenerationID();
- mCache.remove(entry);
-}
-
-void PathCache::precache(const SkPath* path, const SkPaint* paint) {
- if (!Caches::getInstance().tasks.canRunTasks()) {
- return;
- }
-
- PathDescription entry(ShapeType::Path, paint);
- entry.shape.path.mGenerationID = path->getGenerationID();
-
- PathTexture* texture = mCache.get(entry);
-
- bool generate = false;
- if (!texture) {
- generate = true;
- }
-
- if (generate) {
- // It is important to specify the generation ID so we do not
- // attempt to precache the same path several times
- texture = new PathTexture(Caches::getInstance(), path->getGenerationID());
- sp<PathTask> task = new PathTask(path, paint, texture);
- texture->setTask(task);
-
- // During the precaching phase we insert path texture objects into
- // the cache that do not point to any GL texture. They are instead
- // treated as a task for the precaching worker thread. This is why
- // we do not check the cache limit when inserting these objects.
- // The conversion into GL texture will happen in get(), when a client
- // asks for a path texture. This is also when the cache limit will
- // be enforced.
- mCache.put(entry, texture);
-
- if (mProcessor == nullptr) {
- mProcessor = new PathProcessor(Caches::getInstance());
- }
- mProcessor->add(task);
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Rounded rects
-///////////////////////////////////////////////////////////////////////////////
-
-PathTexture* PathCache::getRoundRect(float width, float height, float rx, float ry,
- const SkPaint* paint) {
- PathDescription entry(ShapeType::RoundRect, paint);
- entry.shape.roundRect.mWidth = width;
- entry.shape.roundRect.mHeight = height;
- entry.shape.roundRect.mRx = rx;
- entry.shape.roundRect.mRy = ry;
-
- PathTexture* texture = get(entry);
-
- if (!texture) {
- SkPath path;
- SkRect r;
- r.set(0.0f, 0.0f, width, height);
- path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
-
- texture = addTexture(entry, &path, paint);
- }
-
- return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Circles
-///////////////////////////////////////////////////////////////////////////////
-
-PathTexture* PathCache::getCircle(float radius, const SkPaint* paint) {
- PathDescription entry(ShapeType::Circle, paint);
- entry.shape.circle.mRadius = radius;
-
- PathTexture* texture = get(entry);
-
- if (!texture) {
- SkPath path;
- path.addCircle(radius, radius, radius, SkPath::kCW_Direction);
-
- texture = addTexture(entry, &path, paint);
- }
-
- return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Ovals
-///////////////////////////////////////////////////////////////////////////////
-
-PathTexture* PathCache::getOval(float width, float height, const SkPaint* paint) {
- PathDescription entry(ShapeType::Oval, paint);
- entry.shape.oval.mWidth = width;
- entry.shape.oval.mHeight = height;
-
- PathTexture* texture = get(entry);
-
- if (!texture) {
- SkPath path;
- SkRect r;
- r.set(0.0f, 0.0f, width, height);
- path.addOval(r, SkPath::kCW_Direction);
-
- texture = addTexture(entry, &path, paint);
- }
-
- return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Rects
-///////////////////////////////////////////////////////////////////////////////
-
-PathTexture* PathCache::getRect(float width, float height, const SkPaint* paint) {
- PathDescription entry(ShapeType::Rect, paint);
- entry.shape.rect.mWidth = width;
- entry.shape.rect.mHeight = height;
-
- PathTexture* texture = get(entry);
-
- if (!texture) {
- SkPath path;
- SkRect r;
- r.set(0.0f, 0.0f, width, height);
- path.addRect(r, SkPath::kCW_Direction);
-
- texture = addTexture(entry, &path, paint);
- }
-
- return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Arcs
-///////////////////////////////////////////////////////////////////////////////
-
-PathTexture* PathCache::getArc(float width, float height, float startAngle, float sweepAngle,
- bool useCenter, const SkPaint* paint) {
- PathDescription entry(ShapeType::Arc, paint);
- entry.shape.arc.mWidth = width;
- entry.shape.arc.mHeight = height;
- entry.shape.arc.mStartAngle = startAngle;
- entry.shape.arc.mSweepAngle = sweepAngle;
- entry.shape.arc.mUseCenter = useCenter;
-
- PathTexture* texture = get(entry);
-
- if (!texture) {
- SkPath path;
- SkRect r;
- r.set(0.0f, 0.0f, width, height);
- if (useCenter) {
- path.moveTo(r.centerX(), r.centerY());
- }
- path.arcTo(r, startAngle, sweepAngle, !useCenter);
- if (useCenter) {
- path.close();
- }
-
- texture = addTexture(entry, &path, paint);
- }
-
- return texture;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
deleted file mode 100644
index 28c8bbb8ca6e..000000000000
--- a/libs/hwui/PathCache.h
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_PATH_CACHE_H
-#define ANDROID_HWUI_PATH_CACHE_H
-
-#include "Debug.h"
-#include "Texture.h"
-#include "hwui/Bitmap.h"
-#include "thread/Task.h"
-#include "thread/TaskProcessor.h"
-#include "utils/Macros.h"
-#include "utils/Pair.h"
-
-#include <GLES2/gl2.h>
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <utils/LruCache.h>
-#include <utils/Mutex.h>
-
-#include <vector>
-
-class SkCanvas;
-class SkPaint;
-struct SkRect;
-
-namespace android {
-namespace uirenderer {
-
-class Caches;
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_PATHS
-#define PATH_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-#define PATH_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Classes
-///////////////////////////////////////////////////////////////////////////////
-
-struct PathTexture;
-class PathTask : public Task<sk_sp<Bitmap>> {
-public:
- PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture)
- : path(*path), paint(*paint), texture(texture) {}
-
- // copied, since input path not guaranteed to survive for duration of task
- const SkPath path;
-
- // copied, since input paint may not be immutable
- const SkPaint paint;
- PathTexture* texture;
-};
-
-/**
- * Alpha texture used to represent a path.
- */
-struct PathTexture : public Texture {
- PathTexture(Caches& caches, int generation) : Texture(caches) { this->generation = generation; }
-
- ~PathTexture() { clearTask(); }
-
- /**
- * Left coordinate of the path bounds.
- */
- float left = 0;
- /**
- * Top coordinate of the path bounds.
- */
- float top = 0;
- /**
- * Offset to draw the path at the correct origin.
- */
- float offset = 0;
-
- sp<PathTask> task() const { return mTask; }
-
- void setTask(const sp<PathTask>& task) { mTask = task; }
-
- void clearTask() {
- if (mTask != nullptr) {
- mTask.clear();
- }
- }
-
-private:
- sp<PathTask> mTask;
-}; // struct PathTexture
-
-enum class ShapeType { None, Rect, RoundRect, Circle, Oval, Arc, Path };
-
-struct PathDescription {
- HASHABLE_TYPE(PathDescription);
- ShapeType type;
- SkPaint::Join join;
- SkPaint::Cap cap;
- SkPaint::Style style;
- float miter;
- float strokeWidth;
- SkPathEffect* pathEffect;
- union Shape {
- struct Path {
- uint32_t mGenerationID;
- } path;
- struct RoundRect {
- float mWidth;
- float mHeight;
- float mRx;
- float mRy;
- } roundRect;
- struct Circle {
- float mRadius;
- } circle;
- struct Oval {
- float mWidth;
- float mHeight;
- } oval;
- struct Rect {
- float mWidth;
- float mHeight;
- } rect;
- struct Arc {
- float mWidth;
- float mHeight;
- float mStartAngle;
- float mSweepAngle;
- bool mUseCenter;
- } arc;
- } shape;
-
- PathDescription();
- PathDescription(ShapeType shapeType, const SkPaint* paint);
-};
-
-/**
- * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
- * Any texture added to the cache causing the cache to grow beyond the maximum
- * allowed size will also cause the oldest texture to be kicked out.
- */
-class PathCache : public OnEntryRemoved<PathDescription, PathTexture*> {
-public:
- PathCache();
- ~PathCache();
-
- /**
- * Used as a callback when an entry is removed from the cache.
- * Do not invoke directly.
- */
- void operator()(PathDescription& path, PathTexture*& texture) override;
-
- /**
- * Clears the cache. This causes all textures to be deleted.
- */
- void clear();
-
- /**
- * Returns the maximum size of the cache in bytes.
- */
- uint32_t getMaxSize();
- /**
- * Returns the current size of the cache in bytes.
- */
- uint32_t getSize();
-
- PathTexture* getRoundRect(float width, float height, float rx, float ry, const SkPaint* paint);
- PathTexture* getCircle(float radius, const SkPaint* paint);
- PathTexture* getOval(float width, float height, const SkPaint* paint);
- PathTexture* getRect(float width, float height, const SkPaint* paint);
- PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
- bool useCenter, const SkPaint* paint);
- PathTexture* get(const SkPath* path, const SkPaint* paint);
- void remove(const SkPath* path, const SkPaint* paint);
-
- /**
- * Removes the specified path. This is meant to be called from threads
- * that are not the EGL context thread.
- */
- ANDROID_API void removeDeferred(const SkPath* path);
- /**
- * Process deferred removals.
- */
- void clearGarbage();
- /**
- * Trims the contents of the cache, removing items until it's under its
- * specified limit.
- *
- * Trimming is used for caches that support pre-caching from a worker
- * thread. During pre-caching the maximum limit of the cache can be
- * exceeded for the duration of the frame. It is therefore required to
- * trim the cache at the end of the frame to keep the total amount of
- * memory used under control.
- */
- void trim();
-
- /**
- * Precaches the specified path using background threads.
- */
- void precache(const SkPath* path, const SkPaint* paint);
-
-private:
- PathTexture* addTexture(const PathDescription& entry, const SkPath* path, const SkPaint* paint);
-
- /**
- * Generates the texture from a bitmap into the specified texture structure.
- */
- void generateTexture(Bitmap& bitmap, Texture* texture);
- void generateTexture(const PathDescription& entry, Bitmap& bitmap, PathTexture* texture,
- bool addToCache = true);
-
- PathTexture* get(const PathDescription& entry) { return mCache.get(entry); }
-
- /**
- * Ensures there is enough space in the cache for a texture of the specified
- * dimensions.
- */
- void purgeCache(uint32_t width, uint32_t height);
-
- void removeTexture(PathTexture* texture);
-
- void init();
-
- class PathProcessor : public TaskProcessor<sk_sp<Bitmap>> {
- public:
- explicit PathProcessor(Caches& caches);
- ~PathProcessor() {}
-
- virtual void onProcess(const sp<Task<sk_sp<Bitmap>>>& task) override;
-
- private:
- uint32_t mMaxTextureSize;
- };
-
- LruCache<PathDescription, PathTexture*> mCache;
- uint32_t mSize;
- const uint32_t mMaxSize;
- GLuint mMaxTextureSize;
-
- bool mDebugEnabled;
-
- sp<PathProcessor> mProcessor;
-
- std::vector<uint32_t> mGarbage;
- mutable Mutex mLock;
-}; // class PathCache
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_PATH_CACHE_H
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index 47fcca92bcca..ad599e9ec316 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -210,9 +210,8 @@ void PathParser::validateVerbAndPoints(char verb, size_t points, PathParser::Par
if (numberOfPointsExpected > 0) {
result->failureMessage += "a multiple of ";
}
- result->failureMessage += std::to_string(numberOfPointsExpected)
- + " floats. However, " + std::to_string(points)
- + " float(s) are found. ";
+ result->failureMessage += std::to_string(numberOfPointsExpected) + " floats. However, " +
+ std::to_string(points) + " float(s) are found. ";
}
void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result,
@@ -242,8 +241,8 @@ void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result,
validateVerbAndPoints(pathStr[start], points.size(), result);
if (result->failureOccurred) {
// If either verb or points is not valid, return immediately.
- result->failureMessage += "Failure occurred at position " +
- std::to_string(start) + " of path: " + pathStr;
+ result->failureMessage += "Failure occurred at position " + std::to_string(start) +
+ " of path: " + pathStr;
return;
}
data->verbs.push_back(pathStr[start]);
@@ -257,8 +256,8 @@ void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result,
validateVerbAndPoints(pathStr[start], 0, result);
if (result->failureOccurred) {
// If either verb or points is not valid, return immediately.
- result->failureMessage += "Failure occurred at position " +
- std::to_string(start) + " of path: " + pathStr;
+ result->failureMessage += "Failure occurred at position " + std::to_string(start) +
+ " of path: " + pathStr;
return;
}
data->verbs.push_back(pathStr[start]);
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
deleted file mode 100644
index 973b1d14b77e..000000000000
--- a/libs/hwui/PathTessellator.cpp
+++ /dev/null
@@ -1,1069 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define LOG_NDEBUG 1
-
-#define VERTEX_DEBUG 0
-
-#if VERTEX_DEBUG
-#define DEBUG_DUMP_ALPHA_BUFFER() \
- for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
- ALOGD("point %d at %f %f, alpha %f", i, buffer[i].x, buffer[i].y, buffer[i].alpha); \
- }
-#define DEBUG_DUMP_BUFFER() \
- for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \
- ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \
- }
-#else
-#define DEBUG_DUMP_ALPHA_BUFFER()
-#define DEBUG_DUMP_BUFFER()
-#endif
-
-#include "PathTessellator.h"
-
-#include "Matrix.h"
-#include "Vector.h"
-#include "Vertex.h"
-#include "utils/MathUtils.h"
-
-#include <algorithm>
-
-#include <SkGeometry.h> // WARNING: Internal Skia Header
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <SkPoint.h>
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/types.h>
-
-#include <utils/Log.h>
-#include <utils/Trace.h>
-
-namespace android {
-namespace uirenderer {
-
-#define OUTLINE_REFINE_THRESHOLD 0.5f
-#define ROUND_CAP_THRESH 0.25f
-#define PI 3.1415926535897932f
-#define MAX_DEPTH 15
-
-/**
- * Extracts the x and y scale from the transform as positive values, and clamps them
- */
-void PathTessellator::extractTessellationScales(const Matrix4& transform, float* scaleX,
- float* scaleY) {
- if (CC_LIKELY(transform.isPureTranslate())) {
- *scaleX = 1.0f;
- *scaleY = 1.0f;
- } else {
- float m00 = transform.data[Matrix4::kScaleX];
- float m01 = transform.data[Matrix4::kSkewY];
- float m10 = transform.data[Matrix4::kSkewX];
- float m11 = transform.data[Matrix4::kScaleY];
- *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01));
- *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11));
- }
-}
-
-/**
- * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset
- * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices
- * will be offset by 1.0
- *
- * Note that we can't add and normalize the two vectors, that would result in a rectangle having an
- * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1)
- *
- * NOTE: assumes angles between normals 90 degrees or less
- */
-inline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) {
- return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
-}
-
-/**
- * Structure used for storing useful information about the SkPaint and scale used for tessellating
- */
-struct PaintInfo {
-public:
- PaintInfo(const SkPaint* paint, const mat4& transform)
- : style(paint->getStyle())
- , cap(paint->getStrokeCap())
- , isAA(paint->isAntiAlias())
- , halfStrokeWidth(paint->getStrokeWidth() * 0.5f)
- , maxAlpha(1.0f) {
- // compute inverse scales
- if (CC_LIKELY(transform.isPureTranslate())) {
- inverseScaleX = 1.0f;
- inverseScaleY = 1.0f;
- } else {
- float scaleX, scaleY;
- PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
- inverseScaleX = 1.0f / scaleX;
- inverseScaleY = 1.0f / scaleY;
- }
-
- if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
- 2 * halfStrokeWidth < inverseScaleX) {
- // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
- maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
- halfStrokeWidth = 0.0f;
- }
- }
-
- SkPaint::Style style;
- SkPaint::Cap cap;
- bool isAA;
- float inverseScaleX;
- float inverseScaleY;
- float halfStrokeWidth;
- float maxAlpha;
-
- inline void scaleOffsetForStrokeWidth(Vector2& offset) const {
- if (halfStrokeWidth == 0.0f) {
- // hairline - compensate for scale
- offset.x *= 0.5f * inverseScaleX;
- offset.y *= 0.5f * inverseScaleY;
- } else {
- offset *= halfStrokeWidth;
- }
- }
-
- /**
- * NOTE: the input will not always be a normal, especially for sharp edges - it should be the
- * result of totalOffsetFromNormals (see documentation there)
- */
- inline Vector2 deriveAAOffset(const Vector2& offset) const {
- return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY};
- }
-
- /**
- * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0)
- * Should only be used when stroking and drawing caps
- */
- inline int capExtraDivisions() const {
- if (cap == SkPaint::kRound_Cap) {
- // always use 2 points for hairline
- if (halfStrokeWidth == 0.0f) return 2;
-
- float threshold = std::min(inverseScaleX, inverseScaleY) * ROUND_CAP_THRESH;
- return MathUtils::divisionsNeededToApproximateArc(halfStrokeWidth, PI, threshold);
- }
- return 0;
- }
-
- /**
- * Outset the bounds of point data (for line endpoints or points) to account for stroke
- * geometry.
- *
- * bounds are in pre-scaled space.
- */
- void expandBoundsForStroke(Rect* bounds) const {
- if (halfStrokeWidth == 0) {
- // hairline, outset by (0.5f + fudge factor) in post-scaling space
- bounds->outset(fabs(inverseScaleX) * (0.5f + Vertex::GeometryFudgeFactor()),
- fabs(inverseScaleY) * (0.5f + Vertex::GeometryFudgeFactor()));
- } else {
- // non hairline, outset by half stroke width pre-scaled, and fudge factor post scaled
- bounds->outset(halfStrokeWidth + fabs(inverseScaleX) * Vertex::GeometryFudgeFactor(),
- halfStrokeWidth + fabs(inverseScaleY) * Vertex::GeometryFudgeFactor());
- }
- }
-};
-
-void getFillVerticesFromPerimeter(const std::vector<Vertex>& perimeter,
- VertexBuffer& vertexBuffer) {
- Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
-
- int currentIndex = 0;
- // zig zag between all previous points on the inside of the hull to create a
- // triangle strip that fills the hull
- int srcAindex = 0;
- int srcBindex = perimeter.size() - 1;
- while (srcAindex <= srcBindex) {
- buffer[currentIndex++] = perimeter[srcAindex];
- if (srcAindex == srcBindex) break;
- buffer[currentIndex++] = perimeter[srcBindex];
- srcAindex++;
- srcBindex--;
- }
-}
-
-/*
- * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a
- * tri-strip as wide as the stroke.
- *
- * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip
- * (for a total of perimeter.size() * 2 + 2 vertices)
- */
-void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo,
- const std::vector<Vertex>& perimeter,
- VertexBuffer& vertexBuffer) {
- Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
-
- int currentIndex = 0;
- const Vertex* last = &(perimeter[perimeter.size() - 1]);
- const Vertex* current = &(perimeter[0]);
- Vector2 lastNormal = {current->y - last->y, last->x - current->x};
- lastNormal.normalize();
- for (unsigned int i = 0; i < perimeter.size(); i++) {
- const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
- Vector2 nextNormal = {next->y - current->y, current->x - next->x};
- nextNormal.normalize();
-
- Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
- paintInfo.scaleOffsetForStrokeWidth(totalOffset);
-
- Vertex::set(&buffer[currentIndex++], current->x + totalOffset.x,
- current->y + totalOffset.y);
-
- Vertex::set(&buffer[currentIndex++], current->x - totalOffset.x,
- current->y - totalOffset.y);
-
- current = next;
- lastNormal = nextNormal;
- }
-
- // wrap around to beginning
- buffer[currentIndex++] = buffer[0];
- buffer[currentIndex++] = buffer[1];
-
- DEBUG_DUMP_BUFFER();
-}
-
-static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
- const Vector2& normal, Vertex* buffer, int& currentIndex,
- bool begin) {
- Vector2 strokeOffset = normal;
- paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
-
- Vector2 referencePoint = {center.x, center.y};
- if (paintInfo.cap == SkPaint::kSquare_Cap) {
- Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
- referencePoint += rotated * (begin ? -1 : 1);
- }
-
- Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
- Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
-}
-
-/**
- * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
- *
- * 1 - Doesn't need to wrap around, since the input vertices are unclosed
- *
- * 2 - can zig-zag across 'extra' vertices at either end, to create round caps
- */
-void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
- const std::vector<Vertex>& vertices,
- VertexBuffer& vertexBuffer) {
- const int extra = paintInfo.capExtraDivisions();
- const int allocSize = (vertices.size() + extra) * 2;
- Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
-
- const int lastIndex = vertices.size() - 1;
- if (extra > 0) {
- // tessellate both round caps
- float beginTheta = atan2(-(vertices[0].x - vertices[1].x), vertices[0].y - vertices[1].y);
- float endTheta = atan2(-(vertices[lastIndex].x - vertices[lastIndex - 1].x),
- vertices[lastIndex].y - vertices[lastIndex - 1].y);
- const float dTheta = PI / (extra + 1);
-
- int capOffset;
- for (int i = 0; i < extra; i++) {
- if (i < extra / 2) {
- capOffset = extra - 2 * i - 1;
- } else {
- capOffset = 2 * i - extra;
- }
-
- beginTheta += dTheta;
- Vector2 beginRadialOffset = {cosf(beginTheta), sinf(beginTheta)};
- paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset);
- Vertex::set(&buffer[capOffset], vertices[0].x + beginRadialOffset.x,
- vertices[0].y + beginRadialOffset.y);
-
- endTheta += dTheta;
- Vector2 endRadialOffset = {cosf(endTheta), sinf(endTheta)};
- paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
- Vertex::set(&buffer[allocSize - 1 - capOffset],
- vertices[lastIndex].x + endRadialOffset.x,
- vertices[lastIndex].y + endRadialOffset.y);
- }
- }
-
- int currentIndex = extra;
- const Vertex* last = &(vertices[0]);
- const Vertex* current = &(vertices[1]);
- Vector2 lastNormal = {current->y - last->y, last->x - current->x};
- lastNormal.normalize();
-
- storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
-
- for (unsigned int i = 1; i < vertices.size() - 1; i++) {
- const Vertex* next = &(vertices[i + 1]);
- Vector2 nextNormal = {next->y - current->y, current->x - next->x};
- nextNormal.normalize();
-
- Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
- paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
-
- Vector2 center = {current->x, current->y};
- Vertex::set(&buffer[currentIndex++], center + strokeOffset);
- Vertex::set(&buffer[currentIndex++], center - strokeOffset);
-
- current = next;
- lastNormal = nextNormal;
- }
-
- storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
-
- DEBUG_DUMP_BUFFER();
-}
-
-/**
- * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
- *
- * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
- * the shape (using 2 * perimeter.size() vertices)
- *
- * 2 - wrap around to the beginning to complete the perimeter (2 vertices)
- *
- * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
- */
-void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo,
- const std::vector<Vertex>& perimeter,
- VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) {
- AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2);
-
- // generate alpha points - fill Alpha vertex gaps in between each point with
- // alpha 0 vertex, offset by a scaled normal.
- int currentIndex = 0;
- const Vertex* last = &(perimeter[perimeter.size() - 1]);
- const Vertex* current = &(perimeter[0]);
- Vector2 lastNormal = {current->y - last->y, last->x - current->x};
- lastNormal.normalize();
- for (unsigned int i = 0; i < perimeter.size(); i++) {
- const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
- Vector2 nextNormal = {next->y - current->y, current->x - next->x};
- nextNormal.normalize();
-
- // AA point offset from original point is that point's normal, such that each side is offset
- // by .5 pixels
- Vector2 totalOffset =
- paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal));
-
- AlphaVertex::set(&buffer[currentIndex++], current->x + totalOffset.x,
- current->y + totalOffset.y, 0.0f);
- AlphaVertex::set(&buffer[currentIndex++], current->x - totalOffset.x,
- current->y - totalOffset.y, maxAlpha);
-
- current = next;
- lastNormal = nextNormal;
- }
-
- // wrap around to beginning
- buffer[currentIndex++] = buffer[0];
- buffer[currentIndex++] = buffer[1];
-
- // zig zag between all previous points on the inside of the hull to create a
- // triangle strip that fills the hull, repeating the first inner point to
- // create degenerate tris to start inside path
- int srcAindex = 0;
- int srcBindex = perimeter.size() - 1;
- while (srcAindex <= srcBindex) {
- buffer[currentIndex++] = buffer[srcAindex * 2 + 1];
- if (srcAindex == srcBindex) break;
- buffer[currentIndex++] = buffer[srcBindex * 2 + 1];
- srcAindex++;
- srcBindex--;
- }
-
- DEBUG_DUMP_BUFFER();
-}
-
-/**
- * Stores geometry for a single, AA-perimeter (potentially rounded) cap
- *
- * For explanation of constants and general methodoloyg, see comments for
- * getStrokeVerticesFromUnclosedVerticesAA() below.
- */
-inline static void storeCapAA(const PaintInfo& paintInfo, const std::vector<Vertex>& vertices,
- AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) {
- const int extra = paintInfo.capExtraDivisions();
- const int extraOffset = (extra + 1) / 2;
- const int capIndex =
- isFirst ? 2 * offset + 6 + 2 * (extra + extraOffset) : offset + 2 + 2 * extraOffset;
- if (isFirst) normal *= -1;
-
- // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals()
- Vector2 AAOffset = paintInfo.deriveAAOffset(normal);
-
- Vector2 strokeOffset = normal;
- paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
- Vector2 outerOffset = strokeOffset + AAOffset;
- Vector2 innerOffset = strokeOffset - AAOffset;
-
- Vector2 capAAOffset = {0, 0};
- if (paintInfo.cap != SkPaint::kRound_Cap) {
- // if the cap is square or butt, the inside primary cap vertices will be inset in two
- // directions - both normal to the stroke, and parallel to it.
- capAAOffset = (Vector2){-AAOffset.y, AAOffset.x};
- }
-
- // determine referencePoint, the center point for the 4 primary cap vertices
- const Vertex& point = isFirst ? vertices.front() : vertices.back();
- Vector2 referencePoint = {point.x, point.y};
- if (paintInfo.cap == SkPaint::kSquare_Cap) {
- // To account for square cap, move the primary cap vertices (that create the AA edge) by the
- // stroke offset vector (rotated to be parallel to the stroke)
- Vector2 rotated = {-strokeOffset.y, strokeOffset.x};
- referencePoint += rotated;
- }
-
- AlphaVertex::set(&buffer[capIndex + 0], referencePoint.x + outerOffset.x + capAAOffset.x,
- referencePoint.y + outerOffset.y + capAAOffset.y, 0.0f);
- AlphaVertex::set(&buffer[capIndex + 1], referencePoint.x + innerOffset.x - capAAOffset.x,
- referencePoint.y + innerOffset.y - capAAOffset.y, paintInfo.maxAlpha);
-
- bool isRound = paintInfo.cap == SkPaint::kRound_Cap;
-
- const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra);
- AlphaVertex::set(&buffer[postCapIndex + 2], referencePoint.x - outerOffset.x + capAAOffset.x,
- referencePoint.y - outerOffset.y + capAAOffset.y, 0.0f);
- AlphaVertex::set(&buffer[postCapIndex + 3], referencePoint.x - innerOffset.x - capAAOffset.x,
- referencePoint.y - innerOffset.y - capAAOffset.y, paintInfo.maxAlpha);
-
- if (isRound) {
- const float dTheta = PI / (extra + 1);
- const float radialScale = 2.0f / (1 + cos(dTheta));
- float theta = atan2(normal.y, normal.x);
- int capPerimIndex = capIndex + 2;
-
- for (int i = 0; i < extra; i++) {
- theta += dTheta;
-
- Vector2 radialOffset = {cosf(theta), sinf(theta)};
-
- // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals()
- radialOffset *= radialScale;
-
- AAOffset = paintInfo.deriveAAOffset(radialOffset);
- paintInfo.scaleOffsetForStrokeWidth(radialOffset);
- AlphaVertex::set(&buffer[capPerimIndex++],
- referencePoint.x + radialOffset.x + AAOffset.x,
- referencePoint.y + radialOffset.y + AAOffset.y, 0.0f);
- AlphaVertex::set(&buffer[capPerimIndex++],
- referencePoint.x + radialOffset.x - AAOffset.x,
- referencePoint.y + radialOffset.y - AAOffset.y, paintInfo.maxAlpha);
-
- if (isFirst && i == extra - extraOffset) {
- // copy most recent two points to first two points
- buffer[0] = buffer[capPerimIndex - 2];
- buffer[1] = buffer[capPerimIndex - 1];
-
- capPerimIndex = 2; // start writing the rest of the round cap at index 2
- }
- }
-
- if (isFirst) {
- const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4;
- int capFillIndex = startCapFillIndex;
- for (int i = 0; i < extra + 2; i += 2) {
- buffer[capFillIndex++] = buffer[1 + i];
- // TODO: to support odd numbers of divisions, break here on the last iteration
- buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i];
- }
- } else {
- int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2);
- for (int i = 0; i < extra + 2; i += 2) {
- buffer[capFillIndex++] = buffer[capIndex + 1 + i];
- // TODO: to support odd numbers of divisions, break here on the last iteration
- buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i];
- }
- }
- return;
- }
- if (isFirst) {
- buffer[0] = buffer[postCapIndex + 2];
- buffer[1] = buffer[postCapIndex + 3];
- buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!)
- buffer[postCapIndex + 5] = buffer[postCapIndex + 1];
- } else {
- buffer[6 * vertices.size()] = buffer[postCapIndex + 1];
- buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3];
- }
-}
-
-/*
-the geometry for an aa, capped stroke consists of the following:
-
- # vertices | function
-----------------------------------------------------------------------
-a) 2 | Start AA perimeter
-b) 2, 2 * roundDivOff | First half of begin cap's perimeter
- |
- 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps)
- |
-a) 4 | End cap's
-b) 2, 2 * roundDivs, 2 | AA perimeter
- |
- 2 * middlePts | 'Inner' or 'bottom' AA perimeter half
- |
-a) 6 | Begin cap's perimeter
-b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter
- roundDivs, 2 |
- |
- 2 * middlePts | Stroke's full opacity center strip
- |
-a) 2 | end stroke
-b) 2, roundDivs | (and end cap fill, for round)
-
-Notes:
-* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round
-
-* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two
-
-* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the
- round cap's shape, and is at least two. This will increase with cap size to sufficiently
- define the cap's level of tessellation.
-
-* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where
- the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at
- this offset, the fill of the stroke is drawn from this point with minimal extra vertices.
-
-This means the outer perimeter starts at:
- outerIndex = (2) OR (2 + 2 * roundDivOff)
-the inner perimeter (since it is filled in reverse) starts at:
- innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1
-the stroke starts at:
- strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset))
-
-The total needed allocated space is either:
- 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts
-or, for rounded caps:
- (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1)
- + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts)
- = 14 + 6 * middlePts + 6 * roundDivs
- = 2 + 6 * pts + 6 * roundDivs
- */
-void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
- const std::vector<Vertex>& vertices,
- VertexBuffer& vertexBuffer) {
- const int extra = paintInfo.capExtraDivisions();
- const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
-
- AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize);
-
- const int extraOffset = (extra + 1) / 2;
- int offset = 2 * (vertices.size() - 2);
- // there is no outer/inner here, using them for consistency with below approach
- int currentAAOuterIndex = 2 + 2 * extraOffset;
- int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra);
- int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset);
-
- const Vertex* last = &(vertices[0]);
- const Vertex* current = &(vertices[1]);
- Vector2 lastNormal = {current->y - last->y, last->x - current->x};
- lastNormal.normalize();
-
- // TODO: use normal from bezier traversal for cap, instead of from vertices
- storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset);
-
- for (unsigned int i = 1; i < vertices.size() - 1; i++) {
- const Vertex* next = &(vertices[i + 1]);
- Vector2 nextNormal = {next->y - current->y, current->x - next->x};
- nextNormal.normalize();
-
- Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
- Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
-
- Vector2 innerOffset = totalOffset;
- paintInfo.scaleOffsetForStrokeWidth(innerOffset);
- Vector2 outerOffset = innerOffset + AAOffset;
- innerOffset -= AAOffset;
-
- AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + outerOffset.x,
- current->y + outerOffset.y, 0.0f);
- AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + innerOffset.x,
- current->y + innerOffset.y, paintInfo.maxAlpha);
-
- AlphaVertex::set(&buffer[currentStrokeIndex++], current->x + innerOffset.x,
- current->y + innerOffset.y, paintInfo.maxAlpha);
- AlphaVertex::set(&buffer[currentStrokeIndex++], current->x - innerOffset.x,
- current->y - innerOffset.y, paintInfo.maxAlpha);
-
- AlphaVertex::set(&buffer[currentAAInnerIndex--], current->x - innerOffset.x,
- current->y - innerOffset.y, paintInfo.maxAlpha);
- AlphaVertex::set(&buffer[currentAAInnerIndex--], current->x - outerOffset.x,
- current->y - outerOffset.y, 0.0f);
-
- current = next;
- lastNormal = nextNormal;
- }
-
- // TODO: use normal from bezier traversal for cap, instead of from vertices
- storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset);
-
- DEBUG_DUMP_ALPHA_BUFFER();
-}
-
-void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo,
- const std::vector<Vertex>& perimeter,
- VertexBuffer& vertexBuffer) {
- AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8);
-
- int offset = 2 * perimeter.size() + 3;
- int currentAAOuterIndex = 0;
- int currentStrokeIndex = offset;
- int currentAAInnerIndex = offset * 2;
-
- const Vertex* last = &(perimeter[perimeter.size() - 1]);
- const Vertex* current = &(perimeter[0]);
- Vector2 lastNormal = {current->y - last->y, last->x - current->x};
- lastNormal.normalize();
- for (unsigned int i = 0; i < perimeter.size(); i++) {
- const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]);
- Vector2 nextNormal = {next->y - current->y, current->x - next->x};
- nextNormal.normalize();
-
- Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
- Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset);
-
- Vector2 innerOffset = totalOffset;
- paintInfo.scaleOffsetForStrokeWidth(innerOffset);
- Vector2 outerOffset = innerOffset + AAOffset;
- innerOffset -= AAOffset;
-
- AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + outerOffset.x,
- current->y + outerOffset.y, 0.0f);
- AlphaVertex::set(&buffer[currentAAOuterIndex++], current->x + innerOffset.x,
- current->y + innerOffset.y, paintInfo.maxAlpha);
-
- AlphaVertex::set(&buffer[currentStrokeIndex++], current->x + innerOffset.x,
- current->y + innerOffset.y, paintInfo.maxAlpha);
- AlphaVertex::set(&buffer[currentStrokeIndex++], current->x - innerOffset.x,
- current->y - innerOffset.y, paintInfo.maxAlpha);
-
- AlphaVertex::set(&buffer[currentAAInnerIndex++], current->x - innerOffset.x,
- current->y - innerOffset.y, paintInfo.maxAlpha);
- AlphaVertex::set(&buffer[currentAAInnerIndex++], current->x - outerOffset.x,
- current->y - outerOffset.y, 0.0f);
-
- current = next;
- lastNormal = nextNormal;
- }
-
- // wrap each strip around to beginning, creating degenerate tris to bridge strips
- buffer[currentAAOuterIndex++] = buffer[0];
- buffer[currentAAOuterIndex++] = buffer[1];
- buffer[currentAAOuterIndex++] = buffer[1];
-
- buffer[currentStrokeIndex++] = buffer[offset];
- buffer[currentStrokeIndex++] = buffer[offset + 1];
- buffer[currentStrokeIndex++] = buffer[offset + 1];
-
- buffer[currentAAInnerIndex++] = buffer[2 * offset];
- buffer[currentAAInnerIndex++] = buffer[2 * offset + 1];
- // don't need to create last degenerate tri
-
- DEBUG_DUMP_ALPHA_BUFFER();
-}
-
-void PathTessellator::tessellatePath(const SkPath& path, const SkPaint* paint,
- const mat4& transform, VertexBuffer& vertexBuffer) {
- ATRACE_CALL();
-
- const PaintInfo paintInfo(paint, transform);
-
- std::vector<Vertex> tempVertices;
- float threshInvScaleX = paintInfo.inverseScaleX;
- float threshInvScaleY = paintInfo.inverseScaleY;
- if (paintInfo.style == SkPaint::kStroke_Style) {
- // alter the bezier recursion threshold values we calculate in order to compensate for
- // expansion done after the path vertices are found
- SkRect bounds = path.getBounds();
- if (!bounds.isEmpty()) {
- threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth());
- threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth());
- }
- }
-
- // force close if we're filling the path, since fill path expects closed perimeter.
- bool forceClose = paintInfo.style != SkPaint::kStroke_Style;
- PathApproximationInfo approximationInfo(threshInvScaleX, threshInvScaleY,
- OUTLINE_REFINE_THRESHOLD);
- bool wasClosed =
- approximatePathOutlineVertices(path, forceClose, approximationInfo, tempVertices);
-
- if (!tempVertices.size()) {
- // path was empty, return without allocating vertex buffer
- return;
- }
-
-#if VERTEX_DEBUG
- for (unsigned int i = 0; i < tempVertices.size(); i++) {
- ALOGD("orig path: point at %f %f", tempVertices[i].x, tempVertices[i].y);
- }
-#endif
-
- if (paintInfo.style == SkPaint::kStroke_Style) {
- if (!paintInfo.isAA) {
- if (wasClosed) {
- getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer);
- } else {
- getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
- }
-
- } else {
- if (wasClosed) {
- getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
- } else {
- getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
- }
- }
- } else {
- // For kStrokeAndFill style, the path should be adjusted externally.
- // It will be treated as a fill here.
- if (!paintInfo.isAA) {
- getFillVerticesFromPerimeter(tempVertices, vertexBuffer);
- } else {
- getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
- }
- }
-
- Rect bounds(path.getBounds());
- paintInfo.expandBoundsForStroke(&bounds);
- vertexBuffer.setBounds(bounds);
- vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
-}
-
-template <class TYPE>
-static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer, const float* points,
- int count, Rect& bounds) {
- bounds.set(points[0], points[1], points[0], points[1]);
-
- int numPoints = count / 2;
- int verticesPerPoint = srcBuffer.getVertexCount();
- dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
-
- for (int i = 0; i < count; i += 2) {
- bounds.expandToCover(points[i + 0], points[i + 1]);
- dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
- }
- dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
-}
-
-void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
- const mat4& transform, VertexBuffer& vertexBuffer) {
- const PaintInfo paintInfo(paint, transform);
-
- // determine point shape
- SkPath path;
- float radius = paintInfo.halfStrokeWidth;
- if (radius == 0.0f) radius = 0.5f;
-
- if (paintInfo.cap == SkPaint::kRound_Cap) {
- path.addCircle(0, 0, radius);
- } else {
- path.addRect(-radius, -radius, radius, radius);
- }
-
- // calculate outline
- std::vector<Vertex> outlineVertices;
- PathApproximationInfo approximationInfo(paintInfo.inverseScaleX, paintInfo.inverseScaleY,
- OUTLINE_REFINE_THRESHOLD);
- approximatePathOutlineVertices(path, true, approximationInfo, outlineVertices);
-
- if (!outlineVertices.size()) return;
-
- Rect bounds;
- // tessellate, then duplicate outline across points
- VertexBuffer tempBuffer;
- if (!paintInfo.isAA) {
- getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
- instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
- } else {
- // note: pass maxAlpha directly, since we want fill to be alpha modulated
- getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha);
- instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
- }
-
- // expand bounds from vertex coords to pixel data
- paintInfo.expandBoundsForStroke(&bounds);
- vertexBuffer.setBounds(bounds);
- vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
-}
-
-void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
- const mat4& transform, VertexBuffer& vertexBuffer) {
- ATRACE_CALL();
- const PaintInfo paintInfo(paint, transform);
-
- const int extra = paintInfo.capExtraDivisions();
- int numLines = count / 4;
- int lineAllocSize;
- // pre-allocate space for lines in the buffer, and degenerate tris in between
- if (paintInfo.isAA) {
- lineAllocSize = 6 * (2) + 2 + 6 * extra;
- vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2);
- } else {
- lineAllocSize = 2 * ((2) + extra);
- vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
- }
-
- std::vector<Vertex> tempVertices(2);
- Vertex* tempVerticesData = &tempVertices.front();
- Rect bounds;
- bounds.set(points[0], points[1], points[0], points[1]);
- for (int i = 0; i < count; i += 4) {
- Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
- Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]);
-
- if (paintInfo.isAA) {
- getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer);
- } else {
- getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer);
- }
-
- // calculate bounds
- bounds.expandToCover(tempVerticesData[0].x, tempVerticesData[0].y);
- bounds.expandToCover(tempVerticesData[1].x, tempVerticesData[1].y);
- }
-
- // since multiple objects tessellated into buffer, separate them with degen tris
- if (paintInfo.isAA) {
- vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize);
- } else {
- vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize);
- }
-
- // expand bounds from vertex coords to pixel data
- paintInfo.expandBoundsForStroke(&bounds);
- vertexBuffer.setBounds(bounds);
- vertexBuffer.setMeshFeatureFlags(paintInfo.isAA ? VertexBuffer::kAlpha : VertexBuffer::kNone);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Simple path line approximation
-///////////////////////////////////////////////////////////////////////////////
-
-bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float threshold,
- std::vector<Vertex>& outputVertices) {
- PathApproximationInfo approximationInfo(1.0f, 1.0f, threshold);
- return approximatePathOutlineVertices(path, true, approximationInfo, outputVertices);
-}
-
-class ClockwiseEnforcer {
-public:
- void addPoint(const SkPoint& point) {
- double x = point.x();
- double y = point.y();
-
- if (initialized) {
- sum += (x + lastX) * (y - lastY);
- } else {
- initialized = true;
- }
-
- lastX = x;
- lastY = y;
- }
- void reverseVectorIfNotClockwise(std::vector<Vertex>& vertices) {
- if (sum < 0) {
- // negative sum implies CounterClockwise
- const int size = vertices.size();
- for (int i = 0; i < size / 2; i++) {
- Vertex tmp = vertices[i];
- int k = size - 1 - i;
- vertices[i] = vertices[k];
- vertices[k] = tmp;
- }
- }
- }
-
-private:
- bool initialized = false;
- double lastX = 0;
- double lastY = 0;
- double sum = 0;
-};
-
-bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
- const PathApproximationInfo& approximationInfo,
- std::vector<Vertex>& outputVertices) {
- ATRACE_CALL();
-
- // TODO: to support joins other than sharp miter, join vertices should be labelled in the
- // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
- SkPath::Iter iter(path, forceClose);
- SkPoint pts[4];
- SkPath::Verb v;
- ClockwiseEnforcer clockwiseEnforcer;
- while (SkPath::kDone_Verb != (v = iter.next(pts))) {
- switch (v) {
- case SkPath::kMove_Verb:
- outputVertices.push_back(Vertex{pts[0].x(), pts[0].y()});
- ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
- clockwiseEnforcer.addPoint(pts[0]);
- break;
- case SkPath::kClose_Verb:
- ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
- clockwiseEnforcer.addPoint(pts[0]);
- break;
- case SkPath::kLine_Verb:
- ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
- outputVertices.push_back(Vertex{pts[1].x(), pts[1].y()});
- clockwiseEnforcer.addPoint(pts[1]);
- break;
- case SkPath::kQuad_Verb:
- ALOGV("kQuad_Verb");
- recursiveQuadraticBezierVertices(pts[0].x(), pts[0].y(), pts[2].x(), pts[2].y(),
- pts[1].x(), pts[1].y(), approximationInfo,
- outputVertices);
- clockwiseEnforcer.addPoint(pts[1]);
- clockwiseEnforcer.addPoint(pts[2]);
- break;
- case SkPath::kCubic_Verb:
- ALOGV("kCubic_Verb");
- recursiveCubicBezierVertices(pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y(),
- pts[3].x(), pts[3].y(), pts[2].x(), pts[2].y(),
- approximationInfo, outputVertices);
- clockwiseEnforcer.addPoint(pts[1]);
- clockwiseEnforcer.addPoint(pts[2]);
- clockwiseEnforcer.addPoint(pts[3]);
- break;
- case SkPath::kConic_Verb: {
- ALOGV("kConic_Verb");
- SkAutoConicToQuads converter;
- const SkPoint* quads = converter.computeQuads(
- pts, iter.conicWeight(), approximationInfo.thresholdForConicQuads);
- for (int i = 0; i < converter.countQuads(); ++i) {
- const int offset = 2 * i;
- recursiveQuadraticBezierVertices(quads[offset].x(), quads[offset].y(),
- quads[offset + 2].x(), quads[offset + 2].y(),
- quads[offset + 1].x(), quads[offset + 1].y(),
- approximationInfo, outputVertices);
- }
- clockwiseEnforcer.addPoint(pts[1]);
- clockwiseEnforcer.addPoint(pts[2]);
- break;
- }
- default:
- static_assert(SkPath::kMove_Verb == 0 && SkPath::kLine_Verb == 1 &&
- SkPath::kQuad_Verb == 2 && SkPath::kConic_Verb == 3 &&
- SkPath::kCubic_Verb == 4 && SkPath::kClose_Verb == 5 &&
- SkPath::kDone_Verb == 6,
- "Path enum changed, new types may have been added");
- break;
- }
- }
-
- bool wasClosed = false;
- int size = outputVertices.size();
- if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
- outputVertices[0].y == outputVertices[size - 1].y) {
- outputVertices.pop_back();
- wasClosed = true;
- }
-
- // ensure output vector is clockwise
- clockwiseEnforcer.reverseVectorIfNotClockwise(outputVertices);
- return wasClosed;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Bezier approximation
-//
-// All the inputs and outputs here are in path coordinates.
-// We convert the error threshold from screen coordinates into path coordinates.
-///////////////////////////////////////////////////////////////////////////////
-
-// Get a threshold in path coordinates, by scaling the thresholdSquared from screen coordinates.
-// TODO: Document the math behind this algorithm.
-static inline float getThreshold(const PathApproximationInfo& info, float dx, float dy) {
- // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
- float scale = (dx * dx * info.sqrInvScaleY + dy * dy * info.sqrInvScaleX);
- return info.thresholdSquared * scale;
-}
-
-void PathTessellator::recursiveCubicBezierVertices(float p1x, float p1y, float c1x, float c1y,
- float p2x, float p2y, float c2x, float c2y,
- const PathApproximationInfo& approximationInfo,
- std::vector<Vertex>& outputVertices, int depth) {
- float dx = p2x - p1x;
- float dy = p2y - p1y;
- float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
- float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx);
- float d = d1 + d2;
-
- if (depth >= MAX_DEPTH || d * d <= getThreshold(approximationInfo, dx, dy)) {
- // below thresh, draw line by adding endpoint
- outputVertices.push_back(Vertex{p2x, p2y});
- } else {
- float p1c1x = (p1x + c1x) * 0.5f;
- float p1c1y = (p1y + c1y) * 0.5f;
- float p2c2x = (p2x + c2x) * 0.5f;
- float p2c2y = (p2y + c2y) * 0.5f;
-
- float c1c2x = (c1x + c2x) * 0.5f;
- float c1c2y = (c1y + c2y) * 0.5f;
-
- float p1c1c2x = (p1c1x + c1c2x) * 0.5f;
- float p1c1c2y = (p1c1y + c1c2y) * 0.5f;
-
- float p2c1c2x = (p2c2x + c1c2x) * 0.5f;
- float p2c1c2y = (p2c2y + c1c2y) * 0.5f;
-
- float mx = (p1c1c2x + p2c1c2x) * 0.5f;
- float my = (p1c1c2y + p2c1c2y) * 0.5f;
-
- recursiveCubicBezierVertices(p1x, p1y, p1c1x, p1c1y, mx, my, p1c1c2x, p1c1c2y,
- approximationInfo, outputVertices, depth + 1);
- recursiveCubicBezierVertices(mx, my, p2c1c2x, p2c1c2y, p2x, p2y, p2c2x, p2c2y,
- approximationInfo, outputVertices, depth + 1);
- }
-}
-
-void PathTessellator::recursiveQuadraticBezierVertices(
- float ax, float ay, float bx, float by, float cx, float cy,
- const PathApproximationInfo& approximationInfo, std::vector<Vertex>& outputVertices,
- int depth) {
- float dx = bx - ax;
- float dy = by - ay;
- // d is the cross product of vector (B-A) and (C-B).
- float d = (cx - bx) * dy - (cy - by) * dx;
-
- if (depth >= MAX_DEPTH || d * d <= getThreshold(approximationInfo, dx, dy)) {
- // below thresh, draw line by adding endpoint
- outputVertices.push_back(Vertex{bx, by});
- } else {
- float acx = (ax + cx) * 0.5f;
- float bcx = (bx + cx) * 0.5f;
- float acy = (ay + cy) * 0.5f;
- float bcy = (by + cy) * 0.5f;
-
- // midpoint
- float mx = (acx + bcx) * 0.5f;
- float my = (acy + bcy) * 0.5f;
-
- recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, approximationInfo,
- outputVertices, depth + 1);
- recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, approximationInfo,
- outputVertices, depth + 1);
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
deleted file mode 100644
index ed268324b341..000000000000
--- a/libs/hwui/PathTessellator.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_PATH_TESSELLATOR_H
-#define ANDROID_HWUI_PATH_TESSELLATOR_H
-
-#include "Matrix.h"
-#include "Rect.h"
-#include "Vertex.h"
-#include "VertexBuffer.h"
-
-#include <algorithm>
-#include <vector>
-
-class SkPath;
-class SkPaint;
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Structure used for threshold values in outline path tessellation.
- *
- * TODO: PaintInfo should store one of this object, and initialized all values in constructor
- * depending on its type (point, line or path).
- */
-struct PathApproximationInfo {
- PathApproximationInfo(float invScaleX, float invScaleY, float pixelThreshold)
- : thresholdSquared(pixelThreshold * pixelThreshold)
- , sqrInvScaleX(invScaleX * invScaleX)
- , sqrInvScaleY(invScaleY * invScaleY)
- , thresholdForConicQuads(pixelThreshold * std::min(invScaleX, invScaleY) / 2.0f){};
-
- const float thresholdSquared;
- const float sqrInvScaleX;
- const float sqrInvScaleY;
- const float thresholdForConicQuads;
-};
-
-class PathTessellator {
-public:
- /**
- * Populates scaleX and scaleY with the 'tessellation scale' of the transform - the effective X
- * and Y scales that tessellation will take into account when generating the 1.0 pixel thick
- * ramp.
- *
- * Two instances of the same shape (size, paint, etc.) will only generate the same vertices if
- * their tessellation scales are equal.
- */
- static void extractTessellationScales(const Matrix4& transform, float* scaleX, float* scaleY);
-
- /**
- * Populates a VertexBuffer with a tessellated approximation of the input convex path, as a
- * single
- * triangle strip. Note: joins are not currently supported.
- *
- * @param path The path to be approximated
- * @param paint The paint the path will be drawn with, indicating AA, painting style
- * (stroke vs fill), stroke width, stroke cap & join style, etc.
- * @param transform The transform the path is to be drawn with, used to drive stretch-aware path
- * vertex approximation, and correct AA ramp offsetting.
- * @param vertexBuffer The output buffer
- */
- static void tessellatePath(const SkPath& path, const SkPaint* paint, const mat4& transform,
- VertexBuffer& vertexBuffer);
-
- /**
- * Populates a VertexBuffer with a tessellated approximation of points as a single triangle
- * strip (with degenerate tris separating), respecting the shape defined by the paint cap.
- *
- * @param points The center vertices of the points to be drawn
- * @param count The number of floats making up the point vertices
- * @param paint The paint the points will be drawn with indicating AA, stroke width & cap
- * @param transform The transform the points will be drawn with, used to drive stretch-aware
- * path
- * vertex approximation, and correct AA ramp offsetting
- * @param vertexBuffer The output buffer
- */
- static void tessellatePoints(const float* points, int count, const SkPaint* paint,
- const mat4& transform, VertexBuffer& vertexBuffer);
-
- /**
- * Populates a VertexBuffer with a tessellated approximation of lines as a single triangle
- * strip (with degenerate tris separating).
- *
- * @param points Pairs of endpoints defining the lines to be drawn
- * @param count The number of floats making up the line vertices
- * @param paint The paint the lines will be drawn with indicating AA, stroke width & cap
- * @param transform The transform the points will be drawn with, used to drive stretch-aware
- * path
- * vertex approximation, and correct AA ramp offsetting
- * @param vertexBuffer The output buffer
- */
- static void tessellateLines(const float* points, int count, const SkPaint* paint,
- const mat4& transform, VertexBuffer& vertexBuffer);
-
- /**
- * Approximates a convex outline into a clockwise Vector of 2d vertices.
- *
- * @param path The outline to be approximated
- * @param threshold The threshold of acceptable error (in pixels) when approximating
- * @param outputVertices An empty Vector which will be populated with the output
- */
- static bool approximatePathOutlineVertices(const SkPath& path, float threshold,
- std::vector<Vertex>& outputVertices);
-
-private:
- static bool approximatePathOutlineVertices(const SkPath& path, bool forceClose,
- const PathApproximationInfo& approximationInfo,
- std::vector<Vertex>& outputVertices);
-
- /*
- endpoints a & b,
- control c
- */
- static void recursiveQuadraticBezierVertices(float ax, float ay, float bx, float by, float cx,
- float cy,
- const PathApproximationInfo& approximationInfo,
- std::vector<Vertex>& outputVertices,
- int depth = 0);
-
- /*
- endpoints p1, p2
- control c1, c2
- */
- static void recursiveCubicBezierVertices(float p1x, float p1y, float c1x, float c1y, float p2x,
- float p2y, float c2x, float c2y,
- const PathApproximationInfo& approximationInfo,
- std::vector<Vertex>& outputVertices, int depth = 0);
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_PATH_TESSELLATOR_H
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
deleted file mode 100644
index 910a9889db1f..000000000000
--- a/libs/hwui/PixelBuffer.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "PixelBuffer.h"
-
-#include "Debug.h"
-#include "Extensions.h"
-#include "Properties.h"
-#include "renderstate/RenderState.h"
-#include "utils/GLUtils.h"
-
-#include <utils/Log.h>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// CPU pixel buffer
-///////////////////////////////////////////////////////////////////////////////
-
-class CpuPixelBuffer : public PixelBuffer {
-public:
- CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
-
- uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override;
-
- void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override;
-
-protected:
- void unmap() override;
-
-private:
- std::unique_ptr<uint8_t[]> mBuffer;
-};
-
-CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height)
- : PixelBuffer(format, width, height)
- , mBuffer(new uint8_t[width * height * formatSize(format)]) {}
-
-uint8_t* CpuPixelBuffer::map(AccessMode mode) {
- if (mAccessMode == kAccessMode_None) {
- mAccessMode = mode;
- }
- return mBuffer.get();
-}
-
-void CpuPixelBuffer::unmap() {
- mAccessMode = kAccessMode_None;
-}
-
-void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE,
- &mBuffer[offset]);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// GPU pixel buffer
-///////////////////////////////////////////////////////////////////////////////
-
-class GpuPixelBuffer : public PixelBuffer {
-public:
- GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
- ~GpuPixelBuffer();
-
- uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override;
-
- void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override;
-
-protected:
- void unmap() override;
-
-private:
- GLuint mBuffer;
- uint8_t* mMappedPointer;
- Caches& mCaches;
-};
-
-GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height)
- : PixelBuffer(format, width, height)
- , mMappedPointer(nullptr)
- , mCaches(Caches::getInstance()) {
- glGenBuffers(1, &mBuffer);
-
- mCaches.pixelBufferState().bind(mBuffer);
- glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW);
- mCaches.pixelBufferState().unbind();
-}
-
-GpuPixelBuffer::~GpuPixelBuffer() {
- glDeleteBuffers(1, &mBuffer);
-}
-
-uint8_t* GpuPixelBuffer::map(AccessMode mode) {
- if (mAccessMode == kAccessMode_None) {
- mCaches.pixelBufferState().bind(mBuffer);
- mMappedPointer = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode);
- if (CC_UNLIKELY(!mMappedPointer)) {
- GLUtils::dumpGLErrors();
- LOG_ALWAYS_FATAL("Failed to map PBO");
- }
- mAccessMode = mode;
- mCaches.pixelBufferState().unbind();
- }
-
- return mMappedPointer;
-}
-
-void GpuPixelBuffer::unmap() {
- if (mAccessMode != kAccessMode_None) {
- if (mMappedPointer) {
- mCaches.pixelBufferState().bind(mBuffer);
- GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
- if (status == GL_FALSE) {
- ALOGE("Corrupted GPU pixel buffer");
- }
- }
- mAccessMode = kAccessMode_None;
- mMappedPointer = nullptr;
- }
-}
-
-void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
- // If the buffer is not mapped, unmap() will not bind it
- mCaches.pixelBufferState().bind(mBuffer);
- unmap();
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE,
- reinterpret_cast<void*>(offset));
- mCaches.pixelBufferState().unbind();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Factory
-///////////////////////////////////////////////////////////////////////////////
-
-PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) {
- if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) {
- return new GpuPixelBuffer(format, width, height);
- }
- return new CpuPixelBuffer(format, width, height);
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h
deleted file mode 100644
index e7e341b90ad3..000000000000
--- a/libs/hwui/PixelBuffer.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_PIXEL_BUFFER_H
-#define ANDROID_HWUI_PIXEL_BUFFER_H
-
-#include <GLES3/gl3.h>
-
-#include <log/log.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Represents a pixel buffer. A pixel buffer will be backed either by a
- * PBO on OpenGL ES 3.0 and higher or by an array of uint8_t on other
- * versions. If the buffer is backed by a PBO it will of type
- * GL_PIXEL_UNPACK_BUFFER.
- *
- * To read from or write into a PixelBuffer you must first map the
- * buffer using the map(AccessMode) method. This method returns a
- * pointer to the beginning of the buffer.
- *
- * Before the buffer can be used by the GPU, for instance to upload
- * a texture, you must first unmap the buffer. To do so, call the
- * unmap() method.
- *
- * Mapping and unmapping a PixelBuffer can have the side effect of
- * changing the currently active GL_PIXEL_UNPACK_BUFFER. It is
- * therefore recommended to call Caches::unbindPixelbuffer() after
- * using a PixelBuffer to upload to a texture.
- */
-class PixelBuffer {
-public:
- enum BufferType { kBufferType_Auto, kBufferType_CPU };
-
- enum AccessMode {
- kAccessMode_None = 0,
- kAccessMode_Read = GL_MAP_READ_BIT,
- kAccessMode_Write = GL_MAP_WRITE_BIT,
- kAccessMode_ReadWrite = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT
- };
-
- /**
- * Creates a new PixelBuffer object with the specified format and
- * dimensions. The buffer is immediately allocated.
- *
- * The buffer type specifies how the buffer should be allocated.
- * By default this method will automatically choose whether to allocate
- * a CPU or GPU buffer.
- */
- static PixelBuffer* create(GLenum format, uint32_t width, uint32_t height,
- BufferType type = kBufferType_Auto);
-
- virtual ~PixelBuffer() {}
-
- /**
- * Returns the format of this render buffer.
- */
- GLenum getFormat() const { return mFormat; }
-
- /**
- * Maps this before with the specified access mode. This method
- * returns a pointer to the region of memory where the buffer was
- * mapped.
- *
- * If the buffer is already mapped when this method is invoked,
- * this method will return the previously mapped pointer. The
- * access mode can only be changed by calling unmap() first.
- *
- * The specified access mode cannot be kAccessMode_None.
- */
- virtual uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) = 0;
-
- /**
- * Returns the current access mode for this buffer. If the buffer
- * is not mapped, this method returns kAccessMode_None.
- */
- AccessMode getAccessMode() const { return mAccessMode; }
-
- /**
- * Upload the specified rectangle of this pixel buffer as a
- * GL_TEXTURE_2D texture. Calling this method will trigger
- * an unmap() if necessary.
- */
- virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0;
-
- /**
- * Upload the specified rectangle of this pixel buffer as a
- * GL_TEXTURE_2D texture. Calling this method will trigger
- * an unmap() if necessary.
- *
- * This is a convenience function provided to save callers the
- * trouble of computing the offset parameter.
- */
- void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
- upload(x, y, width, height, getOffset(x, y));
- }
-
- /**
- * Returns the width of the render buffer in pixels.
- */
- uint32_t getWidth() const { return mWidth; }
-
- /**
- * Returns the height of the render buffer in pixels.
- */
- uint32_t getHeight() const { return mHeight; }
-
- /**
- * Returns the size of this pixel buffer in bytes.
- */
- uint32_t getSize() const { return mWidth * mHeight * formatSize(mFormat); }
-
- /**
- * Returns the offset of a pixel in this pixel buffer, in bytes.
- */
- uint32_t getOffset(uint32_t x, uint32_t y) const {
- return (y * mWidth + x) * formatSize(mFormat);
- }
-
- /**
- * Returns the number of bytes per pixel in the specified format.
- *
- * Supported formats:
- * GL_ALPHA
- * GL_RGBA
- */
- static uint32_t formatSize(GLenum format) {
- switch (format) {
- case GL_ALPHA:
- return 1;
- case GL_RGBA:
- return 4;
- }
- return 0;
- }
-
- /**
- * Returns the alpha channel offset in the specified format.
- *
- * Supported formats:
- * GL_ALPHA
- * GL_RGBA
- */
- static uint32_t formatAlphaOffset(GLenum format) {
- switch (format) {
- case GL_ALPHA:
- return 0;
- case GL_RGBA:
- return 3;
- }
-
- ALOGE("unsupported format: %d", format);
- return 0;
- }
-
-protected:
- /**
- * Creates a new render buffer in the specified format and dimensions.
- * The format must be GL_ALPHA or GL_RGBA.
- */
- PixelBuffer(GLenum format, uint32_t width, uint32_t height)
- : mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) {}
-
- /**
- * Unmaps this buffer, if needed. After the buffer is unmapped,
- * the pointer previously returned by map() becomes invalid and
- * should not be used.
- */
- virtual void unmap() = 0;
-
- GLenum mFormat;
-
- uint32_t mWidth;
- uint32_t mHeight;
-
- AccessMode mAccessMode;
-
-}; // class PixelBuffer
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_PIXEL_BUFFER_H
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index 16966619aace..70ca4e3e8074 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -104,8 +104,8 @@ void ProfileData::dump(int fd) const {
dprintf(fd, "\nStats since: %" PRIu64 "ns", mStatStartTime);
dprintf(fd, "\nTotal frames rendered: %u", mTotalFrameCount);
dprintf(fd, "\nJanky frames: %u (%.2f%%)", mJankFrameCount,
- mTotalFrameCount == 0 ? 0.0f :
- (float)mJankFrameCount / (float)mTotalFrameCount * 100.0f);
+ mTotalFrameCount == 0 ? 0.0f
+ : (float)mJankFrameCount / (float)mTotalFrameCount * 100.0f);
dprintf(fd, "\n50th percentile: %ums", findPercentile(50));
dprintf(fd, "\n90th percentile: %ums", findPercentile(90));
dprintf(fd, "\n95th percentile: %ums", findPercentile(95));
diff --git a/libs/hwui/ProfileRenderer.cpp b/libs/hwui/ProfileRenderer.cpp
deleted file mode 100644
index 8a00ffa54c58..000000000000
--- a/libs/hwui/ProfileRenderer.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ProfileRenderer.h"
-
-namespace android {
-namespace uirenderer {
-
-void ProfileRenderer::drawRect(float left, float top, float right, float bottom,
- const SkPaint& paint) {
- mRenderer.drawRect(left, top, right, bottom, &paint);
-}
-
-void ProfileRenderer::drawRects(const float* rects, int count, const SkPaint& paint) {
- mRenderer.drawRects(rects, count, &paint);
-}
-
-uint32_t ProfileRenderer::getViewportWidth() {
- return mRenderer.getViewportWidth();
-}
-
-uint32_t ProfileRenderer::getViewportHeight() {
- return mRenderer.getViewportHeight();
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/ProfileRenderer.h b/libs/hwui/ProfileRenderer.h
deleted file mode 100644
index 5c8bb2529fb4..000000000000
--- a/libs/hwui/ProfileRenderer.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "IProfileRenderer.h"
-
-#include "BakedOpRenderer.h"
-
-namespace android {
-namespace uirenderer {
-
-class ProfileRenderer : public IProfileRenderer {
-public:
- ProfileRenderer(BakedOpRenderer& renderer) : mRenderer(renderer) {}
-
- void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
- void drawRects(const float* rects, int count, const SkPaint& paint) override;
- uint32_t getViewportWidth() override;
- uint32_t getViewportHeight() override;
-
- virtual ~ProfileRenderer() {}
-
-private:
- BakedOpRenderer& mRenderer;
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
deleted file mode 100644
index 052798b9cea9..000000000000
--- a/libs/hwui/Program.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/Trace.h>
-
-#include "Program.h"
-#include "Vertex.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Base program
-///////////////////////////////////////////////////////////////////////////////
-
-Program::Program(const ProgramDescription& description, const char* vertex, const char* fragment) {
- mInitialized = false;
- mHasColorUniform = false;
- mHasSampler = false;
- mUse = false;
-
- // No need to cache compiled shaders, rely instead on Android's
- // persistent shaders cache
- mVertexShader = buildShader(vertex, GL_VERTEX_SHADER);
- if (mVertexShader) {
- mFragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
- if (mFragmentShader) {
- mProgramId = glCreateProgram();
-
- glAttachShader(mProgramId, mVertexShader);
- glAttachShader(mProgramId, mFragmentShader);
-
- bindAttrib("position", kBindingPosition);
- if (description.hasTexture || description.hasExternalTexture) {
- texCoords = bindAttrib("texCoords", kBindingTexCoords);
- } else {
- texCoords = -1;
- }
-
- ATRACE_BEGIN("linkProgram");
- glLinkProgram(mProgramId);
- ATRACE_END();
-
- GLint status;
- glGetProgramiv(mProgramId, GL_LINK_STATUS, &status);
- if (status != GL_TRUE) {
- GLint infoLen = 0;
- glGetProgramiv(mProgramId, GL_INFO_LOG_LENGTH, &infoLen);
- if (infoLen > 1) {
- GLchar log[infoLen];
- glGetProgramInfoLog(mProgramId, infoLen, nullptr, &log[0]);
- ALOGE("%s", log);
- }
- LOG_ALWAYS_FATAL("Error while linking shaders");
- } else {
- mInitialized = true;
- }
- } else {
- glDeleteShader(mVertexShader);
- }
- }
-
- if (mInitialized) {
- transform = addUniform("transform");
- projection = addUniform("projection");
- }
-}
-
-Program::~Program() {
- if (mInitialized) {
- // This would ideally happen after linking the program
- // but Tegra drivers, especially when perfhud is enabled,
- // sometimes crash if we do so
- glDetachShader(mProgramId, mVertexShader);
- glDetachShader(mProgramId, mFragmentShader);
-
- glDeleteShader(mVertexShader);
- glDeleteShader(mFragmentShader);
-
- glDeleteProgram(mProgramId);
- }
-}
-
-int Program::addAttrib(const char* name) {
- int slot = glGetAttribLocation(mProgramId, name);
- mAttributes.add(name, slot);
- return slot;
-}
-
-int Program::bindAttrib(const char* name, ShaderBindings bindingSlot) {
- glBindAttribLocation(mProgramId, bindingSlot, name);
- mAttributes.add(name, bindingSlot);
- return bindingSlot;
-}
-
-int Program::getAttrib(const char* name) {
- ssize_t index = mAttributes.indexOfKey(name);
- if (index >= 0) {
- return mAttributes.valueAt(index);
- }
- return addAttrib(name);
-}
-
-int Program::addUniform(const char* name) {
- int slot = glGetUniformLocation(mProgramId, name);
- mUniforms.add(name, slot);
- return slot;
-}
-
-int Program::getUniform(const char* name) {
- ssize_t index = mUniforms.indexOfKey(name);
- if (index >= 0) {
- return mUniforms.valueAt(index);
- }
- return addUniform(name);
-}
-
-GLuint Program::buildShader(const char* source, GLenum type) {
- ATRACE_NAME("Build GL Shader");
-
- GLuint shader = glCreateShader(type);
- glShaderSource(shader, 1, &source, nullptr);
- glCompileShader(shader);
-
- GLint status;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
- if (status != GL_TRUE) {
- ALOGE("Error while compiling this shader:\n===\n%s\n===", source);
- // Some drivers return wrong values for GL_INFO_LOG_LENGTH
- // use a fixed size instead
- GLchar log[512];
- glGetShaderInfoLog(shader, sizeof(log), nullptr, &log[0]);
- LOG_ALWAYS_FATAL("Shader info log: %s", log);
- return 0;
- }
-
- return shader;
-}
-
-void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
- const mat4& transformMatrix, bool offset) {
- if (projectionMatrix != mProjection || offset != mOffset) {
- if (CC_LIKELY(!offset)) {
- glUniformMatrix4fv(projection, 1, GL_FALSE, &projectionMatrix.data[0]);
- } else {
- mat4 p(projectionMatrix);
- // offset screenspace xy by an amount that compensates for typical precision
- // issues in GPU hardware that tends to paint hor/vert lines in pixels shifted
- // up and to the left.
- // This offset value is based on an assumption that some hardware may use as
- // little as 12.4 precision, so we offset by slightly more than 1/16.
- p.translate(Vertex::GeometryFudgeFactor(), Vertex::GeometryFudgeFactor());
- glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]);
- }
- mProjection = projectionMatrix;
- mOffset = offset;
- }
-
- mat4 t(transformMatrix);
- t.multiply(modelViewMatrix);
- glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
-}
-
-void Program::setColor(FloatColor color) {
- if (!mHasColorUniform) {
- mColorUniform = getUniform("color");
- mHasColorUniform = true;
- }
- glUniform4f(mColorUniform, color.r, color.g, color.b, color.a);
-}
-
-void Program::use() {
- glUseProgram(mProgramId);
- if (texCoords >= 0 && !mHasSampler) {
- glUniform1i(getUniform("baseSampler"), 0);
- mHasSampler = true;
- }
- mUse = true;
-}
-
-void Program::remove() {
- mUse = false;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
deleted file mode 100644
index dcc2bd410ebd..000000000000
--- a/libs/hwui/Program.h
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_PROGRAM_H
-#define ANDROID_HWUI_PROGRAM_H
-
-#include <utils/KeyedVector.h>
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <SkBlendMode.h>
-
-#include "Debug.h"
-#include "FloatColor.h"
-#include "Matrix.h"
-#include "Properties.h"
-#include "utils/Color.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_PROGRAMS
-#define PROGRAM_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-#define PROGRAM_LOGD(...)
-#endif
-
-#define COLOR_COMPONENT_THRESHOLD 1.0f
-#define COLOR_COMPONENT_INV_THRESHOLD 0.0f
-
-#define PROGRAM_KEY_TEXTURE 0x01
-#define PROGRAM_KEY_A8_TEXTURE 0x02
-#define PROGRAM_KEY_BITMAP 0x04
-#define PROGRAM_KEY_GRADIENT 0x08
-#define PROGRAM_KEY_BITMAP_FIRST 0x10
-#define PROGRAM_KEY_COLOR_MATRIX 0x20
-#define PROGRAM_KEY_COLOR_BLEND 0x40
-#define PROGRAM_KEY_BITMAP_NPOT 0x80
-#define PROGRAM_KEY_BITMAP_EXTERNAL 0x100
-
-#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
-#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
-
-#define PROGRAM_KEY_SWAP_SRC_DST_SHIFT 13
-
-// Encode the xfermodes on 6 bits
-#define PROGRAM_MAX_XFERMODE 0x1f
-#define PROGRAM_XFERMODE_SHADER_SHIFT 26
-#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
-#define PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT 14
-
-#define PROGRAM_BITMAP_WRAPS_SHIFT 9
-#define PROGRAM_BITMAP_WRAPT_SHIFT 11
-
-#define PROGRAM_GRADIENT_TYPE_SHIFT 33 // 2 bits for gradient type
-#define PROGRAM_MODULATE_SHIFT 35
-
-#define PROGRAM_HAS_VERTEX_ALPHA_SHIFT 36
-#define PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT 37
-
-#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
-#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
-
-#define PROGRAM_IS_SIMPLE_GRADIENT 40
-
-#define PROGRAM_HAS_COLORS 41
-
-#define PROGRAM_HAS_DEBUG_HIGHLIGHT 42
-#define PROGRAM_HAS_ROUND_RECT_CLIP 43
-
-#define PROGRAM_HAS_GAMMA_CORRECTION 44
-#define PROGRAM_HAS_LINEAR_TEXTURE 45
-
-#define PROGRAM_HAS_COLOR_SPACE_CONVERSION 46
-#define PROGRAM_TRANSFER_FUNCTION 47 // 2 bits for transfer function
-#define PROGRAM_HAS_TRANSLUCENT_CONVERSION 49
-
-///////////////////////////////////////////////////////////////////////////////
-// Types
-///////////////////////////////////////////////////////////////////////////////
-
-typedef uint64_t programid;
-
-///////////////////////////////////////////////////////////////////////////////
-// Program description
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Describe the features required for a given program. The features
- * determine the generation of both the vertex and fragment shaders.
- * A ProgramDescription must be used in conjunction with a ProgramCache.
- */
-struct ProgramDescription {
- enum class ColorFilterMode : int8_t { None = 0, Matrix, Blend };
-
- enum Gradient : int8_t { kGradientLinear = 0, kGradientCircular, kGradientSweep };
-
- ProgramDescription() { reset(); }
-
- // Texturing
- bool hasTexture;
- bool hasAlpha8Texture;
- bool hasExternalTexture;
- bool hasTextureTransform;
-
- // Color attribute
- bool hasColors;
-
- // Modulate, this should only be set when setColor() return true
- bool modulate;
-
- // Shaders
- bool hasBitmap;
- bool isShaderBitmapExternal;
- bool useShaderBasedWrap;
-
- bool hasVertexAlpha;
- bool useShadowAlphaInterp;
-
- bool hasGradient;
- Gradient gradientType;
- bool isSimpleGradient;
-
- SkBlendMode shadersMode;
-
- bool isBitmapFirst;
- GLenum bitmapWrapS;
- GLenum bitmapWrapT;
-
- // Color operations
- ColorFilterMode colorOp;
- SkBlendMode colorMode;
-
- // Framebuffer blending (requires Extensions.hasFramebufferFetch())
- // Ignored for all values < SkBlendMode::kPlus
- SkBlendMode framebufferMode;
- bool swapSrcDst;
-
- bool hasDebugHighlight;
- bool hasRoundRectClip;
-
- // Extra gamma correction used for text
- bool hasGammaCorrection;
- // Set when sampling an image in linear space
- bool hasLinearTexture;
-
- bool hasColorSpaceConversion;
- TransferFunctionType transferFunction;
- // Indicates whether the bitmap to convert between color spaces is translucent
- bool hasTranslucentConversion;
-
- /**
- * Resets this description. All fields are reset back to the default
- * values they hold after building a new instance.
- */
- void reset() {
- hasTexture = false;
- hasAlpha8Texture = false;
- hasExternalTexture = false;
- hasTextureTransform = false;
-
- hasColors = false;
-
- hasVertexAlpha = false;
- useShadowAlphaInterp = false;
-
- modulate = false;
-
- hasBitmap = false;
- isShaderBitmapExternal = false;
- useShaderBasedWrap = false;
-
- hasGradient = false;
- gradientType = kGradientLinear;
- isSimpleGradient = false;
-
- shadersMode = SkBlendMode::kClear;
-
- isBitmapFirst = false;
- bitmapWrapS = GL_CLAMP_TO_EDGE;
- bitmapWrapT = GL_CLAMP_TO_EDGE;
-
- colorOp = ColorFilterMode::None;
- colorMode = SkBlendMode::kClear;
-
- framebufferMode = SkBlendMode::kClear;
- swapSrcDst = false;
-
- hasDebugHighlight = false;
- hasRoundRectClip = false;
-
- hasGammaCorrection = false;
- hasLinearTexture = false;
-
- hasColorSpaceConversion = false;
- transferFunction = TransferFunctionType::None;
- hasTranslucentConversion = false;
- }
-
- /**
- * Indicates, for a given color, whether color modulation is required in
- * the fragment shader. When this method returns true, the program should
- * be provided with a modulation color.
- */
- bool setColorModulate(const float a) {
- modulate = a < COLOR_COMPONENT_THRESHOLD;
- return modulate;
- }
-
- /**
- * Indicates, for a given color, whether color modulation is required in
- * the fragment shader. When this method returns true, the program should
- * be provided with a modulation color.
- */
- bool setAlpha8ColorModulate(const float r, const float g, const float b, const float a) {
- modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD ||
- g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD;
- return modulate;
- }
-
- /**
- * Computes the unique key identifying this program.
- */
- programid key() const {
- programid key = 0;
- if (hasTexture) key |= PROGRAM_KEY_TEXTURE;
- if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
- if (hasBitmap) {
- key |= PROGRAM_KEY_BITMAP;
- if (useShaderBasedWrap) {
- key |= PROGRAM_KEY_BITMAP_NPOT;
- key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT;
- key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT;
- }
- if (isShaderBitmapExternal) {
- key |= PROGRAM_KEY_BITMAP_EXTERNAL;
- }
- }
- if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
- key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT;
- if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST;
- if (hasBitmap && hasGradient) {
- key |= ((int)shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
- }
- switch (colorOp) {
- case ColorFilterMode::Matrix:
- key |= PROGRAM_KEY_COLOR_MATRIX;
- break;
- case ColorFilterMode::Blend:
- key |= PROGRAM_KEY_COLOR_BLEND;
- key |= ((int)colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
- break;
- case ColorFilterMode::None:
- break;
- }
- key |= ((int)framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
- key |= programid(swapSrcDst) << PROGRAM_KEY_SWAP_SRC_DST_SHIFT;
- key |= programid(modulate) << PROGRAM_MODULATE_SHIFT;
- key |= programid(hasVertexAlpha) << PROGRAM_HAS_VERTEX_ALPHA_SHIFT;
- key |= programid(useShadowAlphaInterp) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT;
- key |= programid(hasExternalTexture) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
- key |= programid(hasTextureTransform) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
- key |= programid(isSimpleGradient) << PROGRAM_IS_SIMPLE_GRADIENT;
- key |= programid(hasColors) << PROGRAM_HAS_COLORS;
- key |= programid(hasDebugHighlight) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
- key |= programid(hasRoundRectClip) << PROGRAM_HAS_ROUND_RECT_CLIP;
- key |= programid(hasGammaCorrection) << PROGRAM_HAS_GAMMA_CORRECTION;
- key |= programid(hasLinearTexture) << PROGRAM_HAS_LINEAR_TEXTURE;
- key |= programid(hasColorSpaceConversion) << PROGRAM_HAS_COLOR_SPACE_CONVERSION;
- key |= programid(transferFunction) << PROGRAM_TRANSFER_FUNCTION;
- key |= programid(hasTranslucentConversion) << PROGRAM_HAS_TRANSLUCENT_CONVERSION;
- return key;
- }
-
- /**
- * Logs the specified message followed by the key identifying this program.
- */
- void log(const char* message) const {
-#if DEBUG_PROGRAMS
- programid k = key();
- PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32), uint32_t(k & 0xffffffff));
-#endif
- }
-
-private:
- static inline uint32_t getEnumForWrap(GLenum wrap) {
- switch (wrap) {
- case GL_CLAMP_TO_EDGE:
- return 0;
- case GL_REPEAT:
- return 1;
- case GL_MIRRORED_REPEAT:
- return 2;
- }
- return 0;
- }
-
-}; // struct ProgramDescription
-
-/**
- * A program holds a vertex and a fragment shader. It offers several utility
- * methods to query attributes and uniforms.
- */
-class Program {
-public:
- enum ShaderBindings { kBindingPosition, kBindingTexCoords };
-
- /**
- * Creates a new program with the specified vertex and fragment
- * shaders sources.
- */
- Program(const ProgramDescription& description, const char* vertex, const char* fragment);
- virtual ~Program();
-
- /**
- * Binds this program to the GL context.
- */
- virtual void use();
-
- /**
- * Marks this program as unused. This will not unbind
- * the program from the GL context.
- */
- virtual void remove();
-
- /**
- * Returns the OpenGL name of the specified attribute.
- */
- int getAttrib(const char* name);
-
- /**
- * Returns the OpenGL name of the specified uniform.
- */
- int getUniform(const char* name);
-
- /**
- * Indicates whether this program is currently in use with
- * the GL context.
- */
- inline bool isInUse() const { return mUse; }
-
- /**
- * Indicates whether this program was correctly compiled and linked.
- */
- inline bool isInitialized() const { return mInitialized; }
-
- /**
- * Binds the program with the specified projection, modelView and
- * transform matrices.
- */
- void set(const mat4& projectionMatrix, const mat4& modelViewMatrix, const mat4& transformMatrix,
- bool offset = false);
-
- /**
- * Sets the color associated with this shader.
- */
- void setColor(FloatColor color);
-
- /**
- * Name of the texCoords attribute if it exists (kBindingTexCoords), -1 otherwise.
- */
- int texCoords;
-
- /**
- * Name of the transform uniform.
- */
- int transform;
-
- /**
- * Name of the projection uniform.
- */
- int projection;
-
-protected:
- /**
- * Adds an attribute with the specified name.
- *
- * @return The OpenGL name of the attribute.
- */
- int addAttrib(const char* name);
-
- /**
- * Binds the specified attribute name to the specified slot.
- */
- int bindAttrib(const char* name, ShaderBindings bindingSlot);
-
- /**
- * Adds a uniform with the specified name.
- *
- * @return The OpenGL name of the uniform.
- */
- int addUniform(const char* name);
-
-private:
- /**
- * Compiles the specified shader of the specified type.
- *
- * @return The name of the compiled shader.
- */
- GLuint buildShader(const char* source, GLenum type);
-
- // Name of the OpenGL program and shaders
- GLuint mProgramId;
- GLuint mVertexShader;
- GLuint mFragmentShader;
-
- // Keeps track of attributes and uniforms slots
- KeyedVector<const char*, int> mAttributes;
- KeyedVector<const char*, int> mUniforms;
-
- bool mUse;
- bool mInitialized;
-
- // Uniforms caching
- bool mHasColorUniform;
- int mColorUniform;
-
- bool mHasSampler;
-
- mat4 mProjection;
- bool mOffset;
-}; // class Program
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_PROGRAM_H
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
deleted file mode 100644
index 1164ebfdf1e5..000000000000
--- a/libs/hwui/ProgramCache.cpp
+++ /dev/null
@@ -1,865 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/String8.h>
-
-#include "Caches.h"
-#include "ProgramCache.h"
-#include "Properties.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define MODULATE_OP_NO_MODULATE 0
-#define MODULATE_OP_MODULATE 1
-#define MODULATE_OP_MODULATE_A8 2
-
-#define STR(x) STR1(x)
-#define STR1(x) #x
-
-///////////////////////////////////////////////////////////////////////////////
-// Vertex shaders snippets
-///////////////////////////////////////////////////////////////////////////////
-
-const char* gVS_Header_Start =
- "#version 100\n"
- "attribute vec4 position;\n";
-const char* gVS_Header_Attributes_TexCoords = "attribute vec2 texCoords;\n";
-const char* gVS_Header_Attributes_Colors = "attribute vec4 colors;\n";
-const char* gVS_Header_Attributes_VertexAlphaParameters = "attribute float vtxAlpha;\n";
-const char* gVS_Header_Uniforms_TextureTransform = "uniform mat4 mainTextureTransform;\n";
-const char* gVS_Header_Uniforms =
- "uniform mat4 projection;\n"
- "uniform mat4 transform;\n";
-const char* gVS_Header_Uniforms_HasGradient = "uniform mat4 screenSpace;\n";
-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 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 = "varying vec4 outColors;\n";
-const char* gVS_Header_Varyings_HasVertexAlpha = "varying float alpha;\n";
-const char* gVS_Header_Varyings_HasBitmap = "varying highp vec2 outBitmapTexCoords;\n";
-const char* gVS_Header_Varyings_HasGradient[6] = {
- // Linear
- "varying highp vec2 linear;\n", "varying float linear;\n",
-
- // Circular
- "varying highp vec2 circular;\n", "varying highp vec2 circular;\n",
-
- // Sweep
- "varying highp vec2 sweep;\n", "varying highp vec2 sweep;\n",
-};
-const char* gVS_Header_Varyings_HasRoundRectClip = "varying mediump vec2 roundRectPos;\n";
-const char* gVS_Main = "\nvoid main(void) {\n";
-const char* gVS_Main_OutTexCoords = " outTexCoords = texCoords;\n";
-const char* gVS_Main_OutColors = " outColors = colors;\n";
-const char* gVS_Main_OutTransformedTexCoords =
- " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n";
-const char* gVS_Main_OutGradient[6] = {
- // Linear
- " linear = vec2((screenSpace * position).x, 0.5);\n",
- " linear = (screenSpace * position).x;\n",
-
- // Circular
- " circular = (screenSpace * position).xy;\n",
- " circular = (screenSpace * position).xy;\n",
-
- // Sweep
- " sweep = (screenSpace * position).xy;\n", " sweep = (screenSpace * position).xy;\n"};
-const char* gVS_Main_OutBitmapTexCoords =
- " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
-const char* gVS_Main_Position =
- " vec4 transformedPosition = projection * transform * position;\n"
- " gl_Position = transformedPosition;\n";
-
-const char* gVS_Main_VertexAlpha = " alpha = vtxAlpha;\n";
-
-const char* gVS_Main_HasRoundRectClip =
- " roundRectPos = ((roundRectInvTransform * transformedPosition).xy / roundRectRadius) - "
- "roundRectInnerRectLTWH.xy;\n";
-const char* gVS_Footer = "}\n\n";
-
-///////////////////////////////////////////////////////////////////////////////
-// Fragment shaders snippets
-///////////////////////////////////////////////////////////////////////////////
-
-const char* gFS_Header_Start = "#version 100\n";
-const char* gFS_Header_Extension_FramebufferFetch =
- "#extension GL_NV_shader_framebuffer_fetch : enable\n\n";
-const char* gFS_Header_Extension_ExternalTexture =
- "#extension GL_OES_EGL_image_external : require\n\n";
-const char* gFS_Header = "precision mediump float;\n\n";
-const char* gFS_Uniforms_Color = "uniform vec4 color;\n";
-const char* gFS_Uniforms_TextureSampler = "uniform sampler2D baseSampler;\n";
-const char* gFS_Uniforms_ExternalTextureSampler = "uniform samplerExternalOES baseSampler;\n";
-const char* gFS_Uniforms_GradientSampler[2] = {
- "uniform vec2 screenSize;\n"
- "uniform sampler2D gradientSampler;\n",
-
- "uniform vec2 screenSize;\n"
- "uniform vec4 startColor;\n"
- "uniform vec4 endColor;\n"};
-const char* gFS_Uniforms_BitmapSampler = "uniform sampler2D bitmapSampler;\n";
-const char* gFS_Uniforms_BitmapExternalSampler = "uniform samplerExternalOES bitmapSampler;\n";
-const char* gFS_Uniforms_ColorOp[3] = {
- // None
- "",
- // Matrix
- "uniform mat4 colorMatrix;\n"
- "uniform vec4 colorMatrixVector;\n",
- // PorterDuff
- "uniform vec4 colorBlend;\n"};
-
-const char* gFS_Uniforms_HasRoundRectClip =
- "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?
- // 32x32x32 fp16 LUTs (for scRGB output) are large and heavy to generate...
- "uniform mat3 colorSpaceMatrix;\n";
-
-const char* gFS_Uniforms_TransferFunction[4] = {
- // In this order: g, a, b, c, d, e, f
- // See ColorSpace::TransferParameters
- // We'll use hardware sRGB conversion as much as possible
- "", "uniform float transferFunction[7];\n", "uniform float transferFunction[5];\n",
- "uniform float transferFunctionGamma;\n"};
-
-const char* gFS_OETF[2] = {
- R"__SHADER__(
- vec4 OETF(const vec4 linear) {
- return linear;
- }
- )__SHADER__",
- // We expect linear data to be scRGB so we mirror the gamma function
- R"__SHADER__(
- vec4 OETF(const vec4 linear) {
- return vec4(sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)), linear.a);
- }
- )__SHADER__"};
-
-const char* gFS_ColorConvert[3] = {
- // Just OETF
- R"__SHADER__(
- vec4 colorConvert(const vec4 color) {
- return OETF(color);
- }
- )__SHADER__",
- // Full color conversion for opaque bitmaps
- R"__SHADER__(
- vec4 colorConvert(const vec4 color) {
- return OETF(vec4(colorSpaceMatrix * EOTF_Parametric(color.rgb), color.a));
- }
- )__SHADER__",
- // Full color conversion for translucent bitmaps
- // Note: 0.5/256=0.0019
- R"__SHADER__(
- vec4 colorConvert(in vec4 color) {
- color.rgb /= color.a + 0.0019;
- color = OETF(vec4(colorSpaceMatrix * EOTF_Parametric(color.rgb), color.a));
- color.rgb *= color.a + 0.0019;
- return color;
- }
- )__SHADER__",
-};
-
-const char* gFS_sRGB_TransferFunctions = R"__SHADER__(
- float OETF_sRGB(const float linear) {
- // IEC 61966-2-1:1999
- return linear <= 0.0031308 ? linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
- }
-
- vec3 OETF_sRGB(const vec3 linear) {
- return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
-
- float EOTF_sRGB(float srgb) {
- // IEC 61966-2-1:1999
- return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
- }
-)__SHADER__";
-
-const char* gFS_TransferFunction[4] = {
- // Conversion done by the texture unit (sRGB)
- R"__SHADER__(
- vec3 EOTF_Parametric(const vec3 x) {
- return x;
- }
- )__SHADER__",
- // Full transfer function
- // TODO: We should probably use a 1D LUT (256x1 with texelFetch() since input is 8 bit)
- // TODO: That would cause 3 dependent texture fetches. Is it worth it?
- R"__SHADER__(
- float EOTF_Parametric(float x) {
- return x <= transferFunction[4]
- ? transferFunction[3] * x + transferFunction[6]
- : pow(transferFunction[1] * x + transferFunction[2], transferFunction[0])
- + transferFunction[5];
- }
-
- vec3 EOTF_Parametric(const vec3 x) {
- return vec3(EOTF_Parametric(x.r), EOTF_Parametric(x.g), EOTF_Parametric(x.b));
- }
- )__SHADER__",
- // Limited transfer function, e = f = 0.0
- R"__SHADER__(
- float EOTF_Parametric(float x) {
- return x <= transferFunction[4]
- ? transferFunction[3] * x
- : pow(transferFunction[1] * x + transferFunction[2], transferFunction[0]);
- }
-
- vec3 EOTF_Parametric(const vec3 x) {
- return vec3(EOTF_Parametric(x.r), EOTF_Parametric(x.g), EOTF_Parametric(x.b));
- }
- )__SHADER__",
- // Gamma transfer function, e = f = 0.0
- R"__SHADER__(
- vec3 EOTF_Parametric(const vec3 x) {
- return vec3(pow(x.r, transferFunctionGamma),
- pow(x.g, transferFunctionGamma),
- pow(x.b, transferFunctionGamma));
- }
- )__SHADER__"};
-
-// Dithering must be done in the quantization space
-// When we are writing to an sRGB framebuffer, we must do the following:
-// EOTF(OETF(color) + dither)
-// The dithering pattern is generated with a triangle noise generator in the range [-1.0,1.0]
-// TODO: Handle linear fp16 render targets
-const char* gFS_GradientFunctions = R"__SHADER__(
- float triangleNoise(const highp vec2 n) {
- highp vec2 p = fract(n * vec2(5.3987, 5.4421));
- p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
- highp float xy = p.x * p.y;
- return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
- }
-)__SHADER__";
-
-const char* gFS_GradientPreamble[2] = {
- // Linear framebuffer
- R"__SHADER__(
- vec4 dither(const vec4 color) {
- return color + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);
- }
- )__SHADER__",
- // sRGB framebuffer
- R"__SHADER__(
- vec4 dither(const vec4 color) {
- vec3 dithered = sqrt(color.rgb) + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);
- return vec4(dithered * dithered, color.a);
- }
- )__SHADER__",
-};
-
-// Uses luminance coefficients from Rec.709 to choose the appropriate gamma
-// The gamma() function assumes that bright text will be displayed on a dark
-// background and that dark text will be displayed on bright background
-// The gamma coefficient is chosen to thicken or thin the text accordingly
-// The dot product used to compute the luminance could be approximated with
-// a simple max(color.r, color.g, color.b)
-const char* gFS_Gamma_Preamble = R"__SHADER__(
- #define GAMMA (%.2f)
- #define GAMMA_INV (%.2f)
-
- float gamma(float a, const vec3 color) {
- float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
- return pow(a, luminance < 0.5 ? GAMMA_INV : GAMMA);
- }
-)__SHADER__";
-
-const char* gFS_Main =
- "\nvoid main(void) {\n"
- " vec4 fragColor;\n";
-
-const char* gFS_Main_AddDither = " fragColor = dither(fragColor);\n";
-
-// General case
-const char* gFS_Main_FetchColor = " fragColor = color;\n";
-const char* gFS_Main_ModulateColor = " fragColor *= color.a;\n";
-const char* gFS_Main_ApplyVertexAlphaLinearInterp = " fragColor *= alpha;\n";
-const char* gFS_Main_ApplyVertexAlphaShadowInterp =
- // map alpha through shadow alpha sampler
- " fragColor *= texture2D(baseSampler, vec2(alpha, 0.5)).a;\n";
-const char* gFS_Main_FetchTexture[2] = {
- // Don't modulate
- " fragColor = colorConvert(texture2D(baseSampler, outTexCoords));\n",
- // Modulate
- " fragColor = color * colorConvert(texture2D(baseSampler, outTexCoords));\n"};
-const char* gFS_Main_FetchA8Texture[4] = {
- // Don't modulate
- " fragColor = texture2D(baseSampler, outTexCoords);\n",
- " fragColor = texture2D(baseSampler, outTexCoords);\n",
- // Modulate
- " fragColor = color * texture2D(baseSampler, outTexCoords).a;\n",
- " fragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n",
-};
-const char* gFS_Main_FetchGradient[6] = {
- // Linear
- " vec4 gradientColor = texture2D(gradientSampler, linear);\n",
-
- " vec4 gradientColor = mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
-
- // Circular
- " vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n",
-
- " vec4 gradientColor = mix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
-
- // Sweep
- " highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
- " vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n",
-
- " highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
- " vec4 gradientColor = mix(startColor, endColor, clamp(index - floor(index), 0.0, "
- "1.0));\n"};
-const char* gFS_Main_FetchBitmap =
- " vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, outBitmapTexCoords));\n";
-const char* gFS_Main_FetchBitmapNpot =
- " vec4 bitmapColor = colorConvert(texture2D(bitmapSampler, "
- "wrap(outBitmapTexCoords)));\n";
-const char* gFS_Main_BlendShadersBG = " fragColor = blendShaders(gradientColor, bitmapColor)";
-const char* gFS_Main_BlendShadersGB = " fragColor = blendShaders(bitmapColor, gradientColor)";
-const char* gFS_Main_BlendShaders_Modulate[6] = {
- // Don't modulate
- ";\n", ";\n",
- // Modulate
- " * color.a;\n", " * color.a;\n",
- // Modulate with alpha 8 texture
- " * texture2D(baseSampler, outTexCoords).a;\n",
- " * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n",
-};
-const char* gFS_Main_GradientShader_Modulate[6] = {
- // Don't modulate
- " fragColor = gradientColor;\n", " fragColor = gradientColor;\n",
- // Modulate
- " fragColor = gradientColor * color.a;\n", " fragColor = gradientColor * color.a;\n",
- // Modulate with alpha 8 texture
- " fragColor = gradientColor * texture2D(baseSampler, outTexCoords).a;\n",
- " fragColor = gradientColor * gamma(texture2D(baseSampler, outTexCoords).a, "
- "gradientColor.rgb);\n",
-};
-const char* gFS_Main_BitmapShader_Modulate[6] = {
- // Don't modulate
- " fragColor = bitmapColor;\n", " fragColor = bitmapColor;\n",
- // Modulate
- " fragColor = bitmapColor * color.a;\n", " fragColor = bitmapColor * color.a;\n",
- // Modulate with alpha 8 texture
- " fragColor = bitmapColor * texture2D(baseSampler, outTexCoords).a;\n",
- " fragColor = bitmapColor * gamma(texture2D(baseSampler, outTexCoords).a, "
- "bitmapColor.rgb);\n",
-};
-const char* gFS_Main_FragColor = " gl_FragColor = fragColor;\n";
-const char* gFS_Main_FragColor_HasColors = " gl_FragColor *= outColors;\n";
-const char* gFS_Main_FragColor_Blend =
- " gl_FragColor = blendFramebuffer(fragColor, gl_LastFragColor);\n";
-const char* gFS_Main_FragColor_Blend_Swap =
- " gl_FragColor = blendFramebuffer(gl_LastFragColor, fragColor);\n";
-const char* gFS_Main_ApplyColorOp[3] = {
- // None
- "",
- // Matrix
- " fragColor.rgb /= (fragColor.a + 0.0019);\n" // un-premultiply
- " fragColor *= colorMatrix;\n"
- " fragColor += colorMatrixVector;\n"
- " fragColor.rgb *= (fragColor.a + 0.0019);\n", // re-premultiply
- // PorterDuff
- " fragColor = blendColors(colorBlend, fragColor);\n"};
-
-// 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 = -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";
-const char* gFS_Footer = "}\n\n";
-
-///////////////////////////////////////////////////////////////////////////////
-// PorterDuff snippets
-///////////////////////////////////////////////////////////////////////////////
-
-const char* gBlendOps[18] = {
- // Clear
- "return vec4(0.0, 0.0, 0.0, 0.0);\n",
- // Src
- "return src;\n",
- // Dst
- "return dst;\n",
- // SrcOver
- "return src + dst * (1.0 - src.a);\n",
- // DstOver
- "return dst + src * (1.0 - dst.a);\n",
- // SrcIn
- "return src * dst.a;\n",
- // DstIn
- "return dst * src.a;\n",
- // SrcOut
- "return src * (1.0 - dst.a);\n",
- // DstOut
- "return dst * (1.0 - src.a);\n",
- // SrcAtop
- "return vec4(src.rgb * dst.a + (1.0 - src.a) * dst.rgb, dst.a);\n",
- // DstAtop
- "return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n",
- // Xor
- "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, "
- "src.a + dst.a - 2.0 * src.a * dst.a);\n",
- // Plus
- "return min(src + dst, 1.0);\n",
- // Modulate
- "return src * dst;\n",
- // Screen
- "return src + dst - src * dst;\n",
- // Overlay
- "return clamp(vec4(mix("
- "2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), "
- "src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + "
- "dst.rgb * (1.0 - src.a), "
- "step(dst.a, 2.0 * dst.rgb)), "
- "src.a + dst.a - src.a * dst.a), 0.0, 1.0);\n",
- // Darken
- "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
- "min(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
- // Lighten
- "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
- "max(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructors
-///////////////////////////////////////////////////////////////////////////////
-
-ProgramCache::ProgramCache(const Extensions& extensions)
- : mHasES3(extensions.getMajorGlVersion() >= 3)
- , mHasLinearBlending(extensions.hasLinearBlending()) {}
-
-ProgramCache::~ProgramCache() {
- clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Cache management
-///////////////////////////////////////////////////////////////////////////////
-
-void ProgramCache::clear() {
- PROGRAM_LOGD("Clearing program cache");
- mCache.clear();
-}
-
-Program* ProgramCache::get(const ProgramDescription& description) {
- programid key = description.key();
- if (key == (PROGRAM_KEY_TEXTURE | PROGRAM_KEY_A8_TEXTURE)) {
- // program for A8, unmodulated, texture w/o shader (black text/path textures) is equivalent
- // to standard texture program (bitmaps, patches). Consider them equivalent.
- key = PROGRAM_KEY_TEXTURE;
- }
-
- auto iter = mCache.find(key);
- Program* program = nullptr;
- if (iter == mCache.end()) {
- description.log("Could not find program");
- program = generateProgram(description, key);
- mCache[key] = std::unique_ptr<Program>(program);
- } else {
- program = iter->second.get();
- }
- return program;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Program generation
-///////////////////////////////////////////////////////////////////////////////
-
-Program* ProgramCache::generateProgram(const ProgramDescription& description, programid key) {
- String8 vertexShader = generateVertexShader(description);
- String8 fragmentShader = generateFragmentShader(description);
-
- return new Program(description, vertexShader.string(), fragmentShader.string());
-}
-
-static inline size_t gradientIndex(const ProgramDescription& description) {
- return description.gradientType * 2 + description.isSimpleGradient;
-}
-
-String8 ProgramCache::generateVertexShader(const ProgramDescription& description) {
- // Add attributes
- String8 shader(gVS_Header_Start);
- if (description.hasTexture || description.hasExternalTexture) {
- shader.append(gVS_Header_Attributes_TexCoords);
- }
- if (description.hasVertexAlpha) {
- shader.append(gVS_Header_Attributes_VertexAlphaParameters);
- }
- if (description.hasColors) {
- shader.append(gVS_Header_Attributes_Colors);
- }
- // Uniforms
- shader.append(gVS_Header_Uniforms);
- if (description.hasTextureTransform) {
- shader.append(gVS_Header_Uniforms_TextureTransform);
- }
- if (description.hasGradient) {
- shader.append(gVS_Header_Uniforms_HasGradient);
- }
- if (description.hasBitmap) {
- shader.append(gVS_Header_Uniforms_HasBitmap);
- }
- if (description.hasRoundRectClip) {
- shader.append(gVS_Header_Uniforms_HasRoundRectClip);
- }
- // Varyings
- if (description.hasTexture || description.hasExternalTexture) {
- shader.append(gVS_Header_Varyings_HasTexture);
- }
- if (description.hasVertexAlpha) {
- shader.append(gVS_Header_Varyings_HasVertexAlpha);
- }
- if (description.hasColors) {
- shader.append(gVS_Header_Varyings_HasColors);
- }
- if (description.hasGradient) {
- shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
- }
- if (description.hasBitmap) {
- shader.append(gVS_Header_Varyings_HasBitmap);
- }
- if (description.hasRoundRectClip) {
- shader.append(gVS_Header_Varyings_HasRoundRectClip);
- }
-
- // Begin the shader
- shader.append(gVS_Main);
- {
- if (description.hasTextureTransform) {
- shader.append(gVS_Main_OutTransformedTexCoords);
- } else if (description.hasTexture || description.hasExternalTexture) {
- shader.append(gVS_Main_OutTexCoords);
- }
- if (description.hasVertexAlpha) {
- shader.append(gVS_Main_VertexAlpha);
- }
- if (description.hasColors) {
- shader.append(gVS_Main_OutColors);
- }
- if (description.hasBitmap) {
- shader.append(gVS_Main_OutBitmapTexCoords);
- }
- // Output transformed position
- shader.append(gVS_Main_Position);
- if (description.hasGradient) {
- shader.append(gVS_Main_OutGradient[gradientIndex(description)]);
- }
- if (description.hasRoundRectClip) {
- shader.append(gVS_Main_HasRoundRectClip);
- }
- }
- // End the shader
- shader.append(gVS_Footer);
-
- PROGRAM_LOGD("*** Generated vertex shader:\n\n%s", shader.string());
-
- return shader;
-}
-
-static bool shaderOp(const ProgramDescription& description, String8& shader, const int modulateOp,
- const char** snippets) {
- int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
- op = op * 2 + description.hasGammaCorrection;
- shader.append(snippets[op]);
- return description.hasAlpha8Texture;
-}
-
-String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
- String8 shader(gFS_Header_Start);
-
- const bool blendFramebuffer = description.framebufferMode >= SkBlendMode::kPlus;
- if (blendFramebuffer) {
- shader.append(gFS_Header_Extension_FramebufferFetch);
- }
- if (description.hasExternalTexture ||
- (description.hasBitmap && description.isShaderBitmapExternal)) {
- shader.append(gFS_Header_Extension_ExternalTexture);
- }
-
- shader.append(gFS_Header);
-
- // Varyings
- if (description.hasTexture || description.hasExternalTexture) {
- shader.append(gVS_Header_Varyings_HasTexture);
- }
- if (description.hasVertexAlpha) {
- shader.append(gVS_Header_Varyings_HasVertexAlpha);
- }
- if (description.hasColors) {
- shader.append(gVS_Header_Varyings_HasColors);
- }
- if (description.hasGradient) {
- shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
- }
- if (description.hasBitmap) {
- shader.append(gVS_Header_Varyings_HasBitmap);
- }
- if (description.hasRoundRectClip) {
- shader.append(gVS_Header_Varyings_HasRoundRectClip);
- }
-
- // Uniforms
- int modulateOp = MODULATE_OP_NO_MODULATE;
- const bool singleColor = !description.hasTexture && !description.hasExternalTexture &&
- !description.hasGradient && !description.hasBitmap;
-
- if (description.modulate || singleColor) {
- shader.append(gFS_Uniforms_Color);
- if (!singleColor) modulateOp = MODULATE_OP_MODULATE;
- }
- if (description.hasTexture || description.useShadowAlphaInterp) {
- shader.append(gFS_Uniforms_TextureSampler);
- } else if (description.hasExternalTexture) {
- shader.append(gFS_Uniforms_ExternalTextureSampler);
- }
- if (description.hasGradient) {
- shader.append(gFS_Uniforms_GradientSampler[description.isSimpleGradient]);
- }
- if (description.hasRoundRectClip) {
- shader.append(gFS_Uniforms_HasRoundRectClip);
- }
-
- if (description.hasGammaCorrection) {
- shader.appendFormat(gFS_Gamma_Preamble, Properties::textGamma,
- 1.0f / Properties::textGamma);
- }
-
- if (description.hasBitmap) {
- if (description.isShaderBitmapExternal) {
- shader.append(gFS_Uniforms_BitmapExternalSampler);
- } else {
- shader.append(gFS_Uniforms_BitmapSampler);
- }
- }
- shader.append(gFS_Uniforms_ColorOp[static_cast<int>(description.colorOp)]);
-
- if (description.hasColorSpaceConversion) {
- shader.append(gFS_Uniforms_ColorSpaceConversion);
- }
- shader.append(gFS_Uniforms_TransferFunction[static_cast<int>(description.transferFunction)]);
-
- // Generate required functions
- if (description.hasGradient && description.hasBitmap) {
- generateBlend(shader, "blendShaders", description.shadersMode);
- }
- if (description.colorOp == ProgramDescription::ColorFilterMode::Blend) {
- generateBlend(shader, "blendColors", description.colorMode);
- }
- if (blendFramebuffer) {
- generateBlend(shader, "blendFramebuffer", description.framebufferMode);
- }
- if (description.useShaderBasedWrap) {
- generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
- }
- if (description.hasGradient || description.hasLinearTexture ||
- description.hasColorSpaceConversion) {
- shader.append(gFS_sRGB_TransferFunctions);
- }
- if (description.hasBitmap || ((description.hasTexture || description.hasExternalTexture) &&
- !description.hasAlpha8Texture)) {
- shader.append(gFS_TransferFunction[static_cast<int>(description.transferFunction)]);
- shader.append(
- gFS_OETF[(description.hasLinearTexture || description.hasColorSpaceConversion) &&
- !mHasLinearBlending]);
- shader.append(gFS_ColorConvert[description.hasColorSpaceConversion
- ? 1 + description.hasTranslucentConversion
- : 0]);
- }
- if (description.hasGradient) {
- shader.append(gFS_GradientFunctions);
- shader.append(gFS_GradientPreamble[mHasLinearBlending]);
- }
-
- // Begin the shader
- shader.append(gFS_Main);
- {
- // Stores the result in fragColor directly
- if (description.hasTexture || description.hasExternalTexture) {
- if (description.hasAlpha8Texture) {
- if (!description.hasGradient && !description.hasBitmap) {
- shader.append(gFS_Main_FetchA8Texture[modulateOp * 2 +
- description.hasGammaCorrection]);
- }
- } else {
- shader.append(gFS_Main_FetchTexture[modulateOp]);
- }
- } else {
- if (!description.hasGradient && !description.hasBitmap) {
- shader.append(gFS_Main_FetchColor);
- }
- }
- if (description.hasGradient) {
- shader.append(gFS_Main_FetchGradient[gradientIndex(description)]);
- }
- if (description.hasBitmap) {
- if (!description.useShaderBasedWrap) {
- shader.append(gFS_Main_FetchBitmap);
- } else {
- shader.append(gFS_Main_FetchBitmapNpot);
- }
- }
- bool applyModulate = false;
- // Case when we have two shaders set
- if (description.hasGradient && description.hasBitmap) {
- if (description.isBitmapFirst) {
- shader.append(gFS_Main_BlendShadersBG);
- } else {
- shader.append(gFS_Main_BlendShadersGB);
- }
- applyModulate =
- shaderOp(description, shader, modulateOp, gFS_Main_BlendShaders_Modulate);
- } else {
- if (description.hasGradient) {
- applyModulate =
- shaderOp(description, shader, modulateOp, gFS_Main_GradientShader_Modulate);
- } else if (description.hasBitmap) {
- applyModulate =
- shaderOp(description, shader, modulateOp, gFS_Main_BitmapShader_Modulate);
- }
- }
-
- if (description.modulate && applyModulate) {
- shader.append(gFS_Main_ModulateColor);
- }
-
- // Apply the color op if needed
- shader.append(gFS_Main_ApplyColorOp[static_cast<int>(description.colorOp)]);
-
- if (description.hasVertexAlpha) {
- if (description.useShadowAlphaInterp) {
- shader.append(gFS_Main_ApplyVertexAlphaShadowInterp);
- } else {
- shader.append(gFS_Main_ApplyVertexAlphaLinearInterp);
- }
- }
-
- if (description.hasGradient) {
- shader.append(gFS_Main_AddDither);
- }
-
- // Output the fragment
- if (!blendFramebuffer) {
- shader.append(gFS_Main_FragColor);
- } else {
- shader.append(!description.swapSrcDst ? gFS_Main_FragColor_Blend
- : gFS_Main_FragColor_Blend_Swap);
- }
- if (description.hasColors) {
- shader.append(gFS_Main_FragColor_HasColors);
- }
- if (description.hasRoundRectClip) {
- shader.append(gFS_Main_FragColor_HasRoundRectClip);
- }
- if (description.hasDebugHighlight) {
- shader.append(gFS_Main_DebugHighlight);
- }
- }
- // End the shader
- shader.append(gFS_Footer);
-
-#if DEBUG_PROGRAMS
- PROGRAM_LOGD("*** Generated fragment shader:\n\n");
- printLongString(shader);
-#endif
-
- return shader;
-}
-
-void ProgramCache::generateBlend(String8& shader, const char* name, SkBlendMode mode) {
- shader.append("\nvec4 ");
- shader.append(name);
- shader.append("(vec4 src, vec4 dst) {\n");
- shader.append(" ");
- shader.append(gBlendOps[(int)mode]);
- shader.append("}\n");
-}
-
-void ProgramCache::generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT) {
- shader.append("\nhighp vec2 wrap(highp vec2 texCoords) {\n");
- if (wrapS == GL_MIRRORED_REPEAT) {
- shader.append(" highp float xMod2 = mod(texCoords.x, 2.0);\n");
- shader.append(" if (xMod2 > 1.0) xMod2 = 2.0 - xMod2;\n");
- }
- if (wrapT == GL_MIRRORED_REPEAT) {
- shader.append(" highp float yMod2 = mod(texCoords.y, 2.0);\n");
- shader.append(" if (yMod2 > 1.0) yMod2 = 2.0 - yMod2;\n");
- }
- shader.append(" return vec2(");
- switch (wrapS) {
- case GL_CLAMP_TO_EDGE:
- shader.append("texCoords.x");
- break;
- case GL_REPEAT:
- shader.append("mod(texCoords.x, 1.0)");
- break;
- case GL_MIRRORED_REPEAT:
- shader.append("xMod2");
- break;
- }
- shader.append(", ");
- switch (wrapT) {
- case GL_CLAMP_TO_EDGE:
- shader.append("texCoords.y");
- break;
- case GL_REPEAT:
- shader.append("mod(texCoords.y, 1.0)");
- break;
- case GL_MIRRORED_REPEAT:
- shader.append("yMod2");
- break;
- }
- shader.append(");\n");
- shader.append("}\n");
-}
-
-void ProgramCache::printLongString(const String8& shader) const {
- ssize_t index = 0;
- ssize_t lastIndex = 0;
- const char* str = shader.string();
- while ((index = shader.find("\n", index)) > -1) {
- String8 line(str, index - lastIndex);
- if (line.length() == 0) line.append("\n");
- ALOGD("%s", line.string());
- index++;
- str += (index - lastIndex);
- lastIndex = index;
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
deleted file mode 100644
index 488a4994ba95..000000000000
--- a/libs/hwui/ProgramCache.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_PROGRAM_CACHE_H
-#define ANDROID_HWUI_PROGRAM_CACHE_H
-
-#include <utils/KeyedVector.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <map>
-
-#include <GLES2/gl2.h>
-
-#include "Debug.h"
-#include "Program.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Cache
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Generates and caches program. Programs are generated based on
- * ProgramDescriptions.
- */
-class ProgramCache {
-public:
- explicit ProgramCache(const Extensions& extensions);
- ~ProgramCache();
-
- Program* get(const ProgramDescription& description);
-
- void clear();
-
-private:
- Program* generateProgram(const ProgramDescription& description, programid key);
- String8 generateVertexShader(const ProgramDescription& description);
- String8 generateFragmentShader(const ProgramDescription& description);
- void generateBlend(String8& shader, const char* name, SkBlendMode mode);
- void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT);
-
- void printLongString(const String8& shader) const;
-
- std::map<programid, std::unique_ptr<Program>> mCache;
-
- const bool mHasES3;
- const bool mHasLinearBlending;
-}; // class ProgramCache
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_PROGRAM_CACHE_H
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 0a6c45beedf9..3f2c616eb8ff 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -17,6 +17,7 @@
#include "Properties.h"
#include "Debug.h"
#include "DeviceInfo.h"
+#include "SkTraceEventCommon.h"
#include <algorithm>
#include <cstdlib>
@@ -59,6 +60,8 @@ bool Properties::forceDrawFrame = false;
bool Properties::filterOutTestOverhead = false;
bool Properties::disableVsync = false;
bool Properties::skpCaptureEnabled = false;
+bool Properties::forceDarkMode = false;
+bool Properties::enableForceDarkSupport = true;
bool Properties::enableRTAnimations = true;
bool Properties::runningInEmulator = false;
@@ -140,8 +143,15 @@ bool Properties::load() {
skpCaptureEnabled = debuggingEnabled && property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false);
+ SkAndroidFrameworkTraceUtil::setEnableTracing(
+ property_get_bool(PROPERTY_SKIA_ATRACE_ENABLED, false));
+
runningInEmulator = property_get_bool(PROPERTY_QEMU_KERNEL, false);
+ forceDarkMode = property_get_bool(PROPERTY_FORCE_DARK, false);
+
+ enableForceDarkSupport = property_get_bool(PROPERTY_ENABLE_FORCE_DARK, true);
+
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) ||
(prevDebugStencilClip != debugStencilClip);
}
@@ -191,15 +201,12 @@ RenderPipelineType Properties::getRenderPipelineType() {
}
char prop[PROPERTY_VALUE_MAX];
property_get(PROPERTY_RENDERER, prop, "skiagl");
- if (!strcmp(prop, "skiagl")) {
- ALOGD("Skia GL Pipeline");
- sRenderPipelineType = RenderPipelineType::SkiaGL;
- } else if (!strcmp(prop, "skiavk")) {
+ if (!strcmp(prop, "skiavk")) {
ALOGD("Skia Vulkan Pipeline");
sRenderPipelineType = RenderPipelineType::SkiaVulkan;
- } else { //"opengl"
- ALOGD("HWUI GL Pipeline");
- sRenderPipelineType = RenderPipelineType::OpenGL;
+ } else { //"skiagl"
+ ALOGD("Skia GL Pipeline");
+ sRenderPipelineType = RenderPipelineType::SkiaGL;
}
return sRenderPipelineType;
}
@@ -216,10 +223,5 @@ void Properties::overrideRenderPipelineType(RenderPipelineType type) {
sRenderPipelineType = type;
}
-bool Properties::isSkiaEnabled() {
- auto renderType = getRenderPipelineType();
- return RenderPipelineType::SkiaGL == renderType || RenderPipelineType::SkiaVulkan == renderType;
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 764c50259540..542bc71f7c72 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -171,6 +171,11 @@ enum DebugLevel {
#define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled"
/**
+ * Allows to record Skia drawing commands with systrace.
+ */
+#define PROPERTY_SKIA_ATRACE_ENABLED "debug.hwui.skia_atrace_enabled"
+
+/**
* Defines how many frames in a sequence to capture.
*/
#define PROPERTY_CAPTURE_SKP_FRAMES "debug.hwui.capture_skp_frames"
@@ -185,6 +190,10 @@ enum DebugLevel {
*/
#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
+#define PROPERTY_FORCE_DARK "debug.hwui.force_dark"
+
+#define PROPERTY_ENABLE_FORCE_DARK "debug.hwui.force_dark_enabled"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -200,7 +209,7 @@ enum class OverdrawColorSet { Default = 0, Deuteranomaly };
enum class StencilClipDebug { Hide, ShowHighlight, ShowRegion };
-enum class RenderPipelineType { OpenGL = 0, SkiaGL, SkiaVulkan, NotInitialized = 128 };
+enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 };
/**
* Renderthread-only singleton which manages several static rendering properties. Most of these
@@ -240,7 +249,6 @@ public:
static ProfileType getProfileType();
ANDROID_API static RenderPipelineType getRenderPipelineType();
- static bool isSkiaEnabled();
ANDROID_API static bool enableHighContrastText;
@@ -259,6 +267,8 @@ public:
static bool disableVsync;
static bool skpCaptureEnabled;
+ static bool forceDarkMode;
+ static bool enableForceDarkSupport;
// For experimentation b/68769804
ANDROID_API static bool enableRTAnimations;
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
new file mode 100644
index 000000000000..80f2b5714659
--- /dev/null
+++ b/libs/hwui/Readback.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2018 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 "Readback.h"
+
+#include "pipeline/skia/LayerDrawable.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/VulkanManager.h"
+
+#include <SkToSRGBColorFilter.h>
+#include <gui/Surface.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+#include "DeferredLayerUpdater.h"
+#include "Properties.h"
+#include "hwui/Bitmap.h"
+#include "utils/Color.h"
+#include "utils/MathUtils.h"
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+
+CopyResult Readback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) {
+ ATRACE_CALL();
+ // Setup the source
+ sp<GraphicBuffer> sourceBuffer;
+ sp<Fence> sourceFence;
+ Matrix4 texTransform;
+ status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data);
+ texTransform.invalidateType();
+ if (err != NO_ERROR) {
+ ALOGW("Failed to get last queued buffer, error = %d", err);
+ return CopyResult::UnknownError;
+ }
+ if (!sourceBuffer.get()) {
+ ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
+ return CopyResult::SourceEmpty;
+ }
+ if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
+ ALOGW("Surface is protected, unable to copy from it");
+ return CopyResult::SourceInvalid;
+ }
+ err = sourceFence->wait(500 /* ms */);
+ if (err != NO_ERROR) {
+ ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+ return CopyResult::Timeout;
+ }
+ if (!sourceBuffer.get()) {
+ return CopyResult::UnknownError;
+ }
+
+ sk_sp<SkColorSpace> colorSpace =
+ DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace()));
+ sk_sp<SkColorFilter> colorSpaceFilter;
+ if (colorSpace && !colorSpace->isSRGB()) {
+ colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
+ }
+ sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
+ reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()), kPremul_SkAlphaType);
+ return copyImageInto(image, colorSpaceFilter, texTransform, srcRect, bitmap);
+}
+
+CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
+ LOG_ALWAYS_FATAL_IF(!hwBitmap->isHardware());
+
+ Rect srcRect;
+ Matrix4 transform;
+ transform.loadScale(1, -1, 1);
+ transform.translate(0, -1);
+
+ // TODO: Try to take and reuse the image inside HW bitmap with "hwBitmap->makeImage".
+ // TODO: When this was attempted, it resulted in instability.
+ sk_sp<SkColorFilter> colorSpaceFilter;
+ sk_sp<SkColorSpace> colorSpace = hwBitmap->info().refColorSpace();
+ if (colorSpace && !colorSpace->isSRGB()) {
+ colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
+ }
+ sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
+ reinterpret_cast<AHardwareBuffer*>(hwBitmap->graphicBuffer()), kPremul_SkAlphaType);
+
+ // HW Bitmap currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 format
+ // and SRGB color space. ImageDecoder can create a new HW Bitmap with non-SRGB color space: for
+ // example see android.graphics.cts.BitmapColorSpaceTest#testEncodeP3hardware test.
+ return copyImageInto(image, colorSpaceFilter, transform, srcRect, bitmap);
+}
+
+CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
+ if (!mRenderThread.getGrContext()) {
+ return CopyResult::UnknownError;
+ }
+
+ // acquire most recent buffer for drawing
+ deferredLayer->updateTexImage();
+ deferredLayer->apply();
+ const SkRect dstRect = SkRect::MakeIWH(bitmap->width(), bitmap->height());
+ CopyResult copyResult = CopyResult::UnknownError;
+ Layer* layer = deferredLayer->backingLayer();
+ if (layer) {
+ if (copyLayerInto(layer, nullptr, &dstRect, bitmap)) {
+ copyResult = CopyResult::Success;
+ }
+ }
+ return copyResult;
+}
+
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image,
+ sk_sp<SkColorFilter>& colorSpaceFilter, Matrix4& texTransform,
+ const Rect& srcRect, SkBitmap* bitmap) {
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+ mRenderThread.requireGlContext();
+ } else {
+ mRenderThread.vulkanManager().initialize();
+ }
+ if (!image.get()) {
+ return CopyResult::UnknownError;
+ }
+ int imgWidth = image->width();
+ int imgHeight = image->height();
+ sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
+
+ if (bitmap->colorType() == kRGBA_F16_SkColorType &&
+ !grContext->colorTypeSupportedAsSurface(bitmap->colorType())) {
+ ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
+ return CopyResult::DestinationInvalid;
+ }
+
+ CopyResult copyResult = CopyResult::UnknownError;
+
+ int displayedWidth = imgWidth, displayedHeight = imgHeight;
+ // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
+ // size.
+ if (texTransform[Matrix4::kSkewX] >= 0.5f || texTransform[Matrix4::kSkewX] <= -0.5f) {
+ std::swap(displayedWidth, displayedHeight);
+ }
+ SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
+ SkRect skiaSrcRect = srcRect.toSkRect();
+ if (skiaSrcRect.isEmpty()) {
+ skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight);
+ }
+ bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight));
+ if (!srcNotEmpty) {
+ return copyResult;
+ }
+
+ // See Readback::copyLayerInto for an overview of color space conversion.
+ // HW Bitmap are allowed to be in a non-SRGB color space (for example coming from ImageDecoder).
+ // For Surface and HW Bitmap readback flows we pass colorSpaceFilter, which does the conversion.
+ // TextureView readback is using Layer::setDataSpace, which creates a SkColorFilter internally.
+ Layer layer(mRenderThread.renderState(), colorSpaceFilter, 255, SkBlendMode::kSrc);
+ bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) &&
+ MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
+ layer.setForceFilter(!disableFilter);
+ layer.setSize(displayedWidth, displayedHeight);
+ texTransform.copyTo(layer.getTexTransform());
+ layer.setImage(image);
+ if (copyLayerInto(&layer, &skiaSrcRect, &skiaDestRect, bitmap)) {
+ copyResult = CopyResult::Success;
+ }
+
+ return copyResult;
+}
+
+bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
+ SkBitmap* bitmap) {
+ /*
+ * In the past only TextureView readback was setting the temporary surface color space to null.
+ * Now all 3 readback flows are drawing into a SkSurface with null color space.
+ * At readback there are 3 options to convert the source image color space to the destination
+ * color space requested in "bitmap->info().colorSpace()":
+ * 1. Set color space for temporary surface render target to null (disables color management),
+ * colorspace tag from source SkImage is ignored by Skia,
+ * convert SkImage to SRGB at draw time with SkColorFilter/SkToSRGBColorFilter,
+ * do a readback from temporary SkSurface to a temporary SRGB SkBitmap "bitmap2",
+ * read back from SRGB "bitmap2" into non-SRGB "bitmap" which will do a CPU color conversion.
+ *
+ * 2. Set color space for temporary surface render target to SRGB (not nullptr),
+ * colorspace tag on the source SkImage is used by Skia to enable conversion,
+ * convert SkImage to SRGB at draw time with drawImage (no filters),
+ * do a readback from temporary SkSurface, which will do a color conversion from SRGB to
+ * bitmap->info().colorSpace() on the CPU.
+ *
+ * 3. Set color space for temporary surface render target to bitmap->info().colorSpace(),
+ * colorspace tag on the source SkImage is used by Skia to enable conversion,
+ * convert SkImage to bitmap->info().colorSpace() at draw time with drawImage (no filters),
+ * do a readback from SkSurface, which will not do any color conversion, because
+ * surface was created with the same color space as the "bitmap".
+ *
+ * Option 1 is used for all readback flows.
+ * Options 2 and 3 are new, because skia added support for non-SRGB render targets without
+ * linear blending.
+ * TODO: evaluate if options 2 or 3 for color space conversion are better.
+ */
+
+ // drop the colorSpace from the temporary surface.
+ SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr);
+
+ /* This intermediate surface is present to work around a bug in SwiftShader that
+ * prevents us from reading the contents of the layer's texture directly. The
+ * workaround involves first rendering that texture into an intermediate buffer and
+ * then reading from the intermediate buffer into the bitmap.
+ * Another reason to render in an offscreen buffer is to scale and to avoid an issue b/62262733
+ * with reading incorrect data from EGLImage backed SkImage (likely a driver bug).
+ */
+ sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
+ SkBudgeted::kYes, surfaceInfo);
+
+ if (!tmpSurface.get()) {
+ surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType);
+ tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
+ surfaceInfo);
+ if (!tmpSurface.get()) {
+ ALOGW("Unable to readback GPU contents into the provided bitmap");
+ return false;
+ }
+ }
+
+ if (skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
+ tmpSurface->getCanvas(), layer, srcRect, dstRect,
+ false)) {
+ // If bitmap->info().colorSpace() is non-SRGB, convert the data from SRGB to non-SRGB on
+ // CPU. We can't just pass bitmap->info() to SkSurface::readPixels, because "tmpSurface" has
+ // disabled color conversion.
+ SkColorSpace* destColorSpace = bitmap->info().colorSpace();
+ SkBitmap tempSRGBBitmap;
+ SkBitmap tmpN32Bitmap;
+ SkBitmap* bitmapInSRGB;
+ if (destColorSpace && !destColorSpace->isSRGB()) {
+ tempSRGBBitmap.allocPixels(bitmap->info().makeColorSpace(SkColorSpace::MakeSRGB()));
+ bitmapInSRGB = &tempSRGBBitmap; // Need to convert latter from SRGB to non-SRGB.
+ } else {
+ bitmapInSRGB = bitmap; // No need for color conversion - write directly into output.
+ }
+ bool success = false;
+
+ // TODO: does any of the readbacks below clamp F16 exSRGB?
+ // Readback into a SRGB SkBitmap.
+ if (tmpSurface->readPixels(bitmapInSRGB->info(), bitmapInSRGB->getPixels(),
+ bitmapInSRGB->rowBytes(), 0, 0)) {
+ success = true;
+ } else {
+ // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
+ // 8888 and then convert that into the destination format before giving up.
+ SkImageInfo bitmapInfo =
+ SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType(),
+ SkColorSpace::MakeSRGB());
+ if (tmpN32Bitmap.tryAllocPixels(bitmapInfo) &&
+ tmpSurface->readPixels(bitmapInfo, tmpN32Bitmap.getPixels(),
+ tmpN32Bitmap.rowBytes(), 0, 0)) {
+ success = true;
+ bitmapInSRGB = &tmpN32Bitmap;
+ }
+ }
+
+ if (success) {
+ if (bitmapInSRGB != bitmap) {
+ // Convert from SRGB to non-SRGB color space if needed. Convert from N32 to
+ // destination bitmap color format if needed.
+ if (!bitmapInSRGB->readPixels(bitmap->info(), bitmap->getPixels(),
+ bitmap->rowBytes(), 0, 0)) {
+ return false;
+ }
+ }
+ bitmap->notifyPixelsChanged();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index ad3a8b690617..d9e10cedc0e8 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -16,16 +16,21 @@
#pragma once
+#include "Matrix.h"
#include "Rect.h"
#include "renderthread/RenderThread.h"
#include <SkBitmap.h>
namespace android {
+class Bitmap;
class GraphicBuffer;
class Surface;
namespace uirenderer {
+class DeferredLayerUpdater;
+class Layer;
+
// Keep in sync with PixelCopy.java codes
enum class CopyResult {
Success = 0,
@@ -38,15 +43,22 @@ enum class CopyResult {
class Readback {
public:
+ explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
/**
* Copies the surface's most recently queued buffer into the provided bitmap.
*/
- virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) = 0;
- virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) = 0;
+ CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap);
-protected:
- explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
- virtual ~Readback() {}
+ CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
+
+ CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+
+private:
+ CopyResult copyImageInto(const sk_sp<SkImage>& image, sk_sp<SkColorFilter>& colorSpaceFilter,
+ Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap);
+
+ bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
+ SkBitmap* bitmap);
renderthread::RenderThread& mRenderThread;
};
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
deleted file mode 100644
index 2d0185aaa9e2..000000000000
--- a/libs/hwui/RecordedOp.h
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "GlLayer.h"
-#include "Matrix.h"
-#include "Rect.h"
-#include "RenderNode.h"
-#include "TessellationCache.h"
-#include "Vector.h"
-#include "font/FontUtil.h"
-#include "utils/LinearAllocator.h"
-#include "utils/PaintUtils.h"
-
-#include <androidfw/ResourceTypes.h>
-
-class SkBitmap;
-class SkPaint;
-
-namespace android {
-namespace uirenderer {
-
-struct ClipBase;
-class OffscreenBuffer;
-class RenderNode;
-class DeferredLayerUpdater;
-
-struct Vertex;
-
-namespace VectorDrawable {
-class Tree;
-}
-
-/**
- * Authoritative op list, used for generating the op ID enum, ID based LUTS, and
- * the functions to which they dispatch. Parameter macros are executed for each op,
- * in order, based on the op's type.
- *
- * There are 4 types of op, which defines dispatch/LUT capability:
- *
- * | DisplayList | Render | Merge |
- * -------------|-------------|-------------|-------------|
- * PRE RENDER | Yes | | |
- * RENDER ONLY | | Yes | |
- * UNMERGEABLE | Yes | Yes | |
- * MERGEABLE | Yes | Yes | Yes |
- *
- * PRE RENDER - These ops are recorded into DisplayLists, but can't be directly rendered. This
- * may be because they need to be transformed into other op types (e.g. CirclePropsOp),
- * be traversed to access multiple renderable ops within (e.g. RenderNodeOp), or because they
- * modify renderbuffer lifecycle, instead of directly rendering content (the various LayerOps).
- *
- * RENDER ONLY - These ops cannot be recorded into DisplayLists, and are instead implicitly
- * constructed from other commands/RenderNode properties. They cannot be merged.
- *
- * UNMERGEABLE - These ops can be recorded into DisplayLists and rendered directly, but do not
- * support merged rendering.
- *
- * MERGEABLE - These ops can be recorded into DisplayLists and rendered individually, or merged
- * under certain circumstances.
- */
-#define MAP_OPS_BASED_ON_TYPE(PRE_RENDER_OP_FN, RENDER_ONLY_OP_FN, UNMERGEABLE_OP_FN, \
- MERGEABLE_OP_FN) \
- PRE_RENDER_OP_FN(RenderNodeOp) \
- PRE_RENDER_OP_FN(CirclePropsOp) \
- PRE_RENDER_OP_FN(RoundRectPropsOp) \
- PRE_RENDER_OP_FN(BeginLayerOp) \
- PRE_RENDER_OP_FN(EndLayerOp) \
- PRE_RENDER_OP_FN(BeginUnclippedLayerOp) \
- PRE_RENDER_OP_FN(EndUnclippedLayerOp) \
- PRE_RENDER_OP_FN(VectorDrawableOp) \
- \
- RENDER_ONLY_OP_FN(ShadowOp) \
- RENDER_ONLY_OP_FN(LayerOp) \
- RENDER_ONLY_OP_FN(CopyToLayerOp) \
- RENDER_ONLY_OP_FN(CopyFromLayerOp) \
- \
- UNMERGEABLE_OP_FN(ArcOp) \
- UNMERGEABLE_OP_FN(BitmapMeshOp) \
- UNMERGEABLE_OP_FN(BitmapRectOp) \
- UNMERGEABLE_OP_FN(ColorOp) \
- UNMERGEABLE_OP_FN(FunctorOp) \
- UNMERGEABLE_OP_FN(LinesOp) \
- UNMERGEABLE_OP_FN(OvalOp) \
- UNMERGEABLE_OP_FN(PathOp) \
- UNMERGEABLE_OP_FN(PointsOp) \
- UNMERGEABLE_OP_FN(RectOp) \
- UNMERGEABLE_OP_FN(RoundRectOp) \
- UNMERGEABLE_OP_FN(SimpleRectsOp) \
- UNMERGEABLE_OP_FN(TextOnPathOp) \
- UNMERGEABLE_OP_FN(TextureLayerOp) \
- \
- MERGEABLE_OP_FN(BitmapOp) \
- MERGEABLE_OP_FN(PatchOp) \
- MERGEABLE_OP_FN(TextOp)
-
-/**
- * LUT generators, which will insert nullptr for unsupported ops
- */
-#define NULLPTR_OP_FN(Type) nullptr,
-
-#define BUILD_DEFERRABLE_OP_LUT(OP_FN) \
- { MAP_OPS_BASED_ON_TYPE(OP_FN, NULLPTR_OP_FN, OP_FN, OP_FN) }
-
-#define BUILD_MERGEABLE_OP_LUT(OP_FN) \
- { MAP_OPS_BASED_ON_TYPE(NULLPTR_OP_FN, NULLPTR_OP_FN, NULLPTR_OP_FN, OP_FN) }
-
-#define BUILD_RENDERABLE_OP_LUT(OP_FN) \
- { MAP_OPS_BASED_ON_TYPE(NULLPTR_OP_FN, OP_FN, OP_FN, OP_FN) }
-
-#define BUILD_FULL_OP_LUT(OP_FN) \
- { MAP_OPS_BASED_ON_TYPE(OP_FN, OP_FN, OP_FN, OP_FN) }
-
-/**
- * Op mapping functions, which skip unsupported ops.
- *
- * Note: Do not use for LUTS, since these do not preserve ID order.
- */
-#define NULL_OP_FN(Type)
-
-#define MAP_DEFERRABLE_OPS(OP_FN) MAP_OPS_BASED_ON_TYPE(OP_FN, NULL_OP_FN, OP_FN, OP_FN)
-
-#define MAP_MERGEABLE_OPS(OP_FN) MAP_OPS_BASED_ON_TYPE(NULL_OP_FN, NULL_OP_FN, NULL_OP_FN, OP_FN)
-
-#define MAP_RENDERABLE_OPS(OP_FN) MAP_OPS_BASED_ON_TYPE(NULL_OP_FN, OP_FN, OP_FN, OP_FN)
-
-// Generate OpId enum
-#define IDENTITY_FN(Type) Type,
-namespace RecordedOpId {
-enum {
- MAP_OPS_BASED_ON_TYPE(IDENTITY_FN, IDENTITY_FN, IDENTITY_FN, IDENTITY_FN) Count,
-};
-}
-static_assert(RecordedOpId::RenderNodeOp == 0, "First index must be zero for LUTs to work");
-
-#define BASE_PARAMS \
- const Rect &unmappedBounds, const Matrix4 &localMatrix, const ClipBase *localClip, \
- const SkPaint *paint
-#define BASE_PARAMS_PAINTLESS \
- const Rect &unmappedBounds, const Matrix4 &localMatrix, const ClipBase *localClip
-#define SUPER(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClip, paint)
-#define SUPER_PAINTLESS(Type) \
- RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClip, nullptr)
-
-struct RecordedOp {
- /* ID from RecordedOpId - generally used for jumping into function tables */
- const int opId;
-
- /* bounds in *local* space, without accounting for DisplayList transformation, or stroke */
- const Rect unmappedBounds;
-
- /* transform in recording space (vs DisplayList origin) */
- const Matrix4 localMatrix;
-
- /* clip in recording space - nullptr if not clipped */
- const ClipBase* localClip;
-
- /* optional paint, stored in base object to simplify merging logic */
- const SkPaint* paint;
-
-protected:
- RecordedOp(unsigned int opId, BASE_PARAMS)
- : opId(opId)
- , unmappedBounds(unmappedBounds)
- , localMatrix(localMatrix)
- , localClip(localClip)
- , paint(paint) {}
-};
-
-struct RenderNodeOp : RecordedOp {
- RenderNodeOp(BASE_PARAMS_PAINTLESS, RenderNode* renderNode)
- : SUPER_PAINTLESS(RenderNodeOp), renderNode(renderNode) {}
- RenderNode* renderNode; // not const, since drawing modifies it
-
- /**
- * Holds the transformation between the projection surface ViewGroup and this RenderNode
- * drawing instance. Represents any translations / transformations done within the drawing of
- * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this
- * DisplayList draw instance.
- *
- * Note: doesn't include transformation within the RenderNode, or its properties.
- */
- Matrix4 transformFromCompositingAncestor;
- bool skipInOrderDraw = false;
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Standard Ops
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-struct ArcOp : RecordedOp {
- ArcOp(BASE_PARAMS, float startAngle, float sweepAngle, bool useCenter)
- : SUPER(ArcOp), startAngle(startAngle), sweepAngle(sweepAngle), useCenter(useCenter) {}
- const float startAngle;
- const float sweepAngle;
- const bool useCenter;
-};
-
-struct BitmapOp : RecordedOp {
- BitmapOp(BASE_PARAMS, Bitmap* bitmap) : SUPER(BitmapOp), bitmap(bitmap) {}
- Bitmap* bitmap;
-};
-
-struct BitmapMeshOp : RecordedOp {
- BitmapMeshOp(BASE_PARAMS, Bitmap* bitmap, int meshWidth, int meshHeight, const float* vertices,
- const int* colors)
- : SUPER(BitmapMeshOp)
- , bitmap(bitmap)
- , meshWidth(meshWidth)
- , meshHeight(meshHeight)
- , vertices(vertices)
- , colors(colors) {}
- Bitmap* bitmap;
- const int meshWidth;
- const int meshHeight;
- const float* vertices;
- const int* colors;
-};
-
-struct BitmapRectOp : RecordedOp {
- BitmapRectOp(BASE_PARAMS, Bitmap* bitmap, const Rect& src)
- : SUPER(BitmapRectOp), bitmap(bitmap), src(src) {}
- Bitmap* bitmap;
- const Rect src;
-};
-
-struct CirclePropsOp : RecordedOp {
- CirclePropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint,
- float* x, float* y, float* radius)
- : RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClip, paint)
- , x(x)
- , y(y)
- , radius(radius) {}
- const float* x;
- const float* y;
- const float* radius;
-};
-
-struct ColorOp : RecordedOp {
- // Note: unbounded op that will fillclip, so no bounds/matrix needed
- ColorOp(const ClipBase* localClip, int color, SkBlendMode mode)
- : RecordedOp(RecordedOpId::ColorOp, Rect(), Matrix4::identity(), localClip, nullptr)
- , color(color)
- , mode(mode) {}
- const int color;
- const SkBlendMode mode;
-};
-
-struct FunctorOp : RecordedOp {
- // Note: undefined record-time bounds, since this op fills the clip
- // TODO: explicitly define bounds
- FunctorOp(const Matrix4& localMatrix, const ClipBase* localClip, Functor* functor)
- : RecordedOp(RecordedOpId::FunctorOp, Rect(), localMatrix, localClip, nullptr)
- , functor(functor) {}
- Functor* functor;
-};
-
-struct LinesOp : RecordedOp {
- LinesOp(BASE_PARAMS, const float* points, const int floatCount)
- : SUPER(LinesOp), points(points), floatCount(floatCount) {}
- const float* points;
- const int floatCount;
-};
-
-struct OvalOp : RecordedOp {
- OvalOp(BASE_PARAMS) : SUPER(OvalOp) {}
-};
-
-struct PatchOp : RecordedOp {
- PatchOp(BASE_PARAMS, Bitmap* bitmap, const Res_png_9patch* patch)
- : SUPER(PatchOp), bitmap(bitmap), patch(patch) {}
- Bitmap* bitmap;
- const Res_png_9patch* patch;
-};
-
-struct PathOp : RecordedOp {
- PathOp(BASE_PARAMS, const SkPath* path) : SUPER(PathOp), path(path) {}
- const SkPath* path;
-};
-
-struct PointsOp : RecordedOp {
- PointsOp(BASE_PARAMS, const float* points, const int floatCount)
- : SUPER(PointsOp), points(points), floatCount(floatCount) {}
- const float* points;
- const int floatCount;
-};
-
-struct RectOp : RecordedOp {
- RectOp(BASE_PARAMS) : SUPER(RectOp) {}
-};
-
-struct RoundRectOp : RecordedOp {
- RoundRectOp(BASE_PARAMS, float rx, float ry) : SUPER(RoundRectOp), rx(rx), ry(ry) {}
- const float rx;
- const float ry;
-};
-
-struct RoundRectPropsOp : RecordedOp {
- RoundRectPropsOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint,
- float* left, float* top, float* right, float* bottom, float* rx, float* ry)
- : RecordedOp(RecordedOpId::RoundRectPropsOp, Rect(), localMatrix, localClip, paint)
- , left(left)
- , top(top)
- , right(right)
- , bottom(bottom)
- , rx(rx)
- , ry(ry) {}
- const float* left;
- const float* top;
- const float* right;
- const float* bottom;
- const float* rx;
- const float* ry;
-};
-
-struct VectorDrawableOp : RecordedOp {
- VectorDrawableOp(VectorDrawable::Tree* tree, BASE_PARAMS_PAINTLESS)
- : SUPER_PAINTLESS(VectorDrawableOp), vectorDrawable(tree) {}
- VectorDrawable::Tree* vectorDrawable;
-};
-
-/**
- * Real-time, dynamic-lit shadow.
- *
- * Uses invalid/empty bounds and matrix since ShadowOp bounds aren't known at defer time,
- * and are resolved dynamically, and transform isn't needed.
- *
- * State construction handles these properties specially, ignoring matrix/bounds.
- */
-struct ShadowOp : RecordedOp {
- ShadowOp(sp<TessellationCache::ShadowTask>& shadowTask, float casterAlpha)
- : RecordedOp(RecordedOpId::ShadowOp, Rect(), Matrix4::identity(), nullptr, nullptr)
- , shadowTask(shadowTask)
- , casterAlpha(casterAlpha){};
- sp<TessellationCache::ShadowTask> shadowTask;
- const float casterAlpha;
-};
-
-struct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?)
- SimpleRectsOp(BASE_PARAMS, Vertex* vertices, size_t vertexCount)
- : SUPER(SimpleRectsOp), vertices(vertices), vertexCount(vertexCount) {}
- Vertex* vertices;
- const size_t vertexCount;
-};
-
-struct TextOp : RecordedOp {
- TextOp(BASE_PARAMS, const glyph_t* glyphs, const float* positions, int glyphCount, float x,
- float y)
- : SUPER(TextOp)
- , glyphs(glyphs)
- , positions(positions)
- , glyphCount(glyphCount)
- , x(x)
- , y(y) {}
- const glyph_t* glyphs;
- const float* positions;
- const int glyphCount;
- const float x;
- const float y;
-};
-
-struct TextOnPathOp : RecordedOp {
- // TODO: explicitly define bounds
- TextOnPathOp(const Matrix4& localMatrix, const ClipBase* localClip, const SkPaint* paint,
- const glyph_t* glyphs, int glyphCount, const SkPath* path, float hOffset,
- float vOffset)
- : RecordedOp(RecordedOpId::TextOnPathOp, Rect(), localMatrix, localClip, paint)
- , glyphs(glyphs)
- , glyphCount(glyphCount)
- , path(path)
- , hOffset(hOffset)
- , vOffset(vOffset) {}
- const glyph_t* glyphs;
- const int glyphCount;
-
- const SkPath* path;
- const float hOffset;
- const float vOffset;
-};
-
-struct TextureLayerOp : RecordedOp {
- TextureLayerOp(BASE_PARAMS_PAINTLESS, DeferredLayerUpdater* layer)
- : SUPER_PAINTLESS(TextureLayerOp), layerHandle(layer) {}
-
- // Copy an existing TextureLayerOp, replacing the underlying matrix
- TextureLayerOp(const TextureLayerOp& op, const Matrix4& replacementMatrix)
- : RecordedOp(RecordedOpId::TextureLayerOp, op.unmappedBounds, replacementMatrix,
- op.localClip, op.paint)
- , layerHandle(op.layerHandle) {}
- DeferredLayerUpdater* layerHandle;
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Layers
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Stateful operation! denotes the creation of an off-screen layer,
- * and that commands following will render into it.
- */
-struct BeginLayerOp : RecordedOp {
- BeginLayerOp(BASE_PARAMS) : SUPER(BeginLayerOp) {}
-};
-
-/**
- * Stateful operation! Denotes end of off-screen layer, and that
- * commands since last BeginLayerOp should be drawn into parent FBO.
- *
- * State in this op is empty, it just serves to signal that a layer has been finished.
- */
-struct EndLayerOp : RecordedOp {
- EndLayerOp()
- : RecordedOp(RecordedOpId::EndLayerOp, Rect(), Matrix4::identity(), nullptr, nullptr) {}
-};
-
-struct BeginUnclippedLayerOp : RecordedOp {
- BeginUnclippedLayerOp(BASE_PARAMS) : SUPER(BeginUnclippedLayerOp) {}
-};
-
-struct EndUnclippedLayerOp : RecordedOp {
- EndUnclippedLayerOp()
- : RecordedOp(RecordedOpId::EndUnclippedLayerOp, Rect(), Matrix4::identity(), nullptr,
- nullptr) {}
-};
-
-struct CopyToLayerOp : RecordedOp {
- CopyToLayerOp(const RecordedOp& op, OffscreenBuffer** layerHandle)
- : RecordedOp(RecordedOpId::CopyToLayerOp, op.unmappedBounds, op.localMatrix,
- nullptr, // clip intentionally ignored
- op.paint)
- , layerHandle(layerHandle) {}
-
- // Records a handle to the Layer object, since the Layer itself won't be
- // constructed until after this operation is constructed.
- OffscreenBuffer** layerHandle;
-};
-
-// draw the parameter layer underneath
-struct CopyFromLayerOp : RecordedOp {
- CopyFromLayerOp(const RecordedOp& op, OffscreenBuffer** layerHandle)
- : RecordedOp(RecordedOpId::CopyFromLayerOp, op.unmappedBounds, op.localMatrix,
- nullptr, // clip intentionally ignored
- op.paint)
- , layerHandle(layerHandle) {}
-
- // Records a handle to the Layer object, since the Layer itself won't be
- // constructed until after this operation is constructed.
- OffscreenBuffer** layerHandle;
-};
-
-/**
- * Draws an OffscreenBuffer.
- *
- * Alpha, mode, and colorfilter are embedded, since LayerOps are always dynamically generated,
- * when creating/tracking a SkPaint* during defer isn't worth the bother.
- */
-struct LayerOp : RecordedOp {
- // Records a one-use (saveLayer) layer for drawing.
- LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
- : SUPER_PAINTLESS(LayerOp)
- , layerHandle(layerHandle)
- , alpha(paint ? paint->getAlpha() / 255.0f : 1.0f)
- , mode(PaintUtils::getBlendModeDirect(paint))
- , colorFilter(paint ? paint->getColorFilter() : nullptr) {}
-
- explicit LayerOp(RenderNode& node)
- : RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()),
- Matrix4::identity(), nullptr, nullptr)
- , layerHandle(node.getLayerHandle())
- , alpha(node.properties().layerProperties().alpha() / 255.0f)
- , mode(node.properties().layerProperties().xferMode())
- , colorFilter(node.properties().layerProperties().colorFilter()) {}
-
- // Records a handle to the Layer object, since the Layer itself won't be
- // constructed until after this operation is constructed.
- OffscreenBuffer** layerHandle;
- const float alpha;
- const SkBlendMode mode;
-
- // pointer to object owned by either LayerProperties, or a recorded Paint object in a
- // BeginLayerOp. Lives longer than LayerOp in either case, so no skia ref counting is used.
- SkColorFilter* colorFilter;
-};
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index e1df1e7725b5..f928de9b92a6 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,615 +16,1016 @@
#include "RecordingCanvas.h"
-#include "DeferredLayerUpdater.h"
-#include "RecordedOp.h"
-#include "RenderNode.h"
#include "VectorDrawable.h"
-#include "hwui/MinikinUtils.h"
+
+#include "SkCanvas.h"
+#include "SkData.h"
+#include "SkDrawShadowInfo.h"
+#include "SkImage.h"
+#include "SkImageFilter.h"
+#include "SkLatticeIter.h"
+#include "SkMath.h"
+#include "SkPicture.h"
+#include "SkRSXform.h"
+#include "SkRegion.h"
+#include "SkTextBlob.h"
+#include "SkVertices.h"
+
+#include <experimental/type_traits>
namespace android {
namespace uirenderer {
-RecordingCanvas::RecordingCanvas(size_t width, size_t height)
- : mState(*this), mResourceCache(ResourceCache::getInstance()) {
- resetRecording(width, height);
-}
+#ifndef SKLITEDL_PAGE
+#define SKLITEDL_PAGE 4096
+#endif
-RecordingCanvas::~RecordingCanvas() {
- LOG_ALWAYS_FATAL_IF(mDisplayList, "Destroyed a RecordingCanvas during a record!");
+// A stand-in for an optional SkRect which was not set, e.g. bounds for a saveLayer().
+static const SkRect kUnset = {SK_ScalarInfinity, 0, 0, 0};
+static const SkRect* maybe_unset(const SkRect& r) {
+ return r.left() == SK_ScalarInfinity ? nullptr : &r;
}
-void RecordingCanvas::resetRecording(int width, int height, RenderNode* node) {
- LOG_ALWAYS_FATAL_IF(mDisplayList, "prepareDirty called a second time during a recording!");
- mDisplayList = new DisplayList();
-
- mState.initializeRecordingSaveStack(width, height);
+// copy_v(dst, src,n, src,n, ...) copies an arbitrary number of typed srcs into dst.
+static void copy_v(void* dst) {}
- mDeferredBarrierType = DeferredBarrierType::InOrder;
+template <typename S, typename... Rest>
+static void copy_v(void* dst, const S* src, int n, Rest&&... rest) {
+ SkASSERTF(((uintptr_t)dst & (alignof(S) - 1)) == 0,
+ "Expected %p to be aligned for at least %zu bytes.", dst, alignof(S));
+ sk_careful_memcpy(dst, src, n * sizeof(S));
+ copy_v(SkTAddOffset<void>(dst, n * sizeof(S)), std::forward<Rest>(rest)...);
}
-DisplayList* RecordingCanvas::finishRecording() {
- restoreToCount(1);
- mPaintMap.clear();
- mRegionMap.clear();
- mPathMap.clear();
- DisplayList* displayList = mDisplayList;
- mDisplayList = nullptr;
- mSkiaCanvasProxy.reset(nullptr);
- return displayList;
+// Helper for getting back at arrays which have been copy_v'd together after an Op.
+template <typename D, typename T>
+static const D* pod(const T* op, size_t offset = 0) {
+ return SkTAddOffset<const D>(op + 1, offset);
}
-void RecordingCanvas::insertReorderBarrier(bool enableReorder) {
- if (enableReorder) {
- mDeferredBarrierType = DeferredBarrierType::OutOfOrder;
- mDeferredBarrierClip = getRecordedClip();
- } else {
- mDeferredBarrierType = DeferredBarrierType::InOrder;
- mDeferredBarrierClip = nullptr;
+namespace {
+
+#define X(T) T,
+enum class Type : uint8_t {
+#include "DisplayListOps.in"
+};
+#undef X
+
+struct Op {
+ uint32_t type : 8;
+ uint32_t skip : 24;
+};
+static_assert(sizeof(Op) == 4, "");
+
+struct Flush final : Op {
+ static const auto kType = Type::Flush;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->flush(); }
+};
+
+struct Save final : Op {
+ static const auto kType = Type::Save;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->save(); }
+};
+struct Restore final : Op {
+ static const auto kType = Type::Restore;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->restore(); }
+};
+struct SaveLayer final : Op {
+ static const auto kType = Type::SaveLayer;
+ SaveLayer(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop,
+ const SkImage* clipMask, const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) {
+ if (bounds) {
+ this->bounds = *bounds;
+ }
+ if (paint) {
+ this->paint = *paint;
+ }
+ this->backdrop = sk_ref_sp(backdrop);
+ this->clipMask = sk_ref_sp(clipMask);
+ this->clipMatrix = clipMatrix ? *clipMatrix : SkMatrix::I();
+ this->flags = flags;
}
-}
-
-SkCanvas* RecordingCanvas::asSkCanvas() {
- LOG_ALWAYS_FATAL_IF(!mDisplayList, "attempting to get an SkCanvas when we are not recording!");
- if (!mSkiaCanvasProxy) {
- mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this));
+ SkRect bounds = kUnset;
+ SkPaint paint;
+ sk_sp<const SkImageFilter> backdrop;
+ sk_sp<const SkImage> clipMask;
+ SkMatrix clipMatrix;
+ SkCanvas::SaveLayerFlags flags;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ c->saveLayer({maybe_unset(bounds), &paint, backdrop.get(), clipMask.get(),
+ clipMatrix.isIdentity() ? nullptr : &clipMatrix, flags});
+ }
+};
+
+struct Concat final : Op {
+ static const auto kType = Type::Concat;
+ Concat(const SkMatrix& matrix) : matrix(matrix) {}
+ SkMatrix matrix;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); }
+};
+struct SetMatrix final : Op {
+ static const auto kType = Type::SetMatrix;
+ SetMatrix(const SkMatrix& matrix) : matrix(matrix) {}
+ SkMatrix matrix;
+ void draw(SkCanvas* c, const SkMatrix& original) const {
+ c->setMatrix(SkMatrix::Concat(original, matrix));
+ }
+};
+struct Translate final : Op {
+ static const auto kType = Type::Translate;
+ Translate(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {}
+ SkScalar dx, dy;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->translate(dx, dy); }
+};
+
+struct ClipPath final : Op {
+ static const auto kType = Type::ClipPath;
+ ClipPath(const SkPath& path, SkClipOp op, bool aa) : path(path), op(op), aa(aa) {}
+ SkPath path;
+ SkClipOp op;
+ bool aa;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->clipPath(path, op, aa); }
+};
+struct ClipRect final : Op {
+ static const auto kType = Type::ClipRect;
+ ClipRect(const SkRect& rect, SkClipOp op, bool aa) : rect(rect), op(op), aa(aa) {}
+ SkRect rect;
+ SkClipOp op;
+ bool aa;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->clipRect(rect, op, aa); }
+};
+struct ClipRRect final : Op {
+ static const auto kType = Type::ClipRRect;
+ ClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) : rrect(rrect), op(op), aa(aa) {}
+ SkRRect rrect;
+ SkClipOp op;
+ bool aa;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->clipRRect(rrect, op, aa); }
+};
+struct ClipRegion final : Op {
+ static const auto kType = Type::ClipRegion;
+ ClipRegion(const SkRegion& region, SkClipOp op) : region(region), op(op) {}
+ SkRegion region;
+ SkClipOp op;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->clipRegion(region, op); }
+};
+
+struct DrawPaint final : Op {
+ static const auto kType = Type::DrawPaint;
+ DrawPaint(const SkPaint& paint) : paint(paint) {}
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawPaint(paint); }
+};
+struct DrawPath final : Op {
+ static const auto kType = Type::DrawPath;
+ DrawPath(const SkPath& path, const SkPaint& paint) : path(path), paint(paint) {}
+ SkPath path;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawPath(path, paint); }
+};
+struct DrawRect final : Op {
+ static const auto kType = Type::DrawRect;
+ DrawRect(const SkRect& rect, const SkPaint& paint) : rect(rect), paint(paint) {}
+ SkRect rect;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawRect(rect, paint); }
+};
+struct DrawRegion final : Op {
+ static const auto kType = Type::DrawRegion;
+ DrawRegion(const SkRegion& region, const SkPaint& paint) : region(region), paint(paint) {}
+ SkRegion region;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawRegion(region, paint); }
+};
+struct DrawOval final : Op {
+ static const auto kType = Type::DrawOval;
+ DrawOval(const SkRect& oval, const SkPaint& paint) : oval(oval), paint(paint) {}
+ SkRect oval;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawOval(oval, paint); }
+};
+struct DrawArc final : Op {
+ static const auto kType = Type::DrawArc;
+ DrawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
+ const SkPaint& paint)
+ : oval(oval)
+ , startAngle(startAngle)
+ , sweepAngle(sweepAngle)
+ , useCenter(useCenter)
+ , paint(paint) {}
+ SkRect oval;
+ SkScalar startAngle;
+ SkScalar sweepAngle;
+ bool useCenter;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ c->drawArc(oval, startAngle, sweepAngle, useCenter, paint);
+ }
+};
+struct DrawRRect final : Op {
+ static const auto kType = Type::DrawRRect;
+ DrawRRect(const SkRRect& rrect, const SkPaint& paint) : rrect(rrect), paint(paint) {}
+ SkRRect rrect;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawRRect(rrect, paint); }
+};
+struct DrawDRRect final : Op {
+ static const auto kType = Type::DrawDRRect;
+ DrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint)
+ : outer(outer), inner(inner), paint(paint) {}
+ SkRRect outer, inner;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawDRRect(outer, inner, paint); }
+};
+
+struct DrawAnnotation final : Op {
+ static const auto kType = Type::DrawAnnotation;
+ DrawAnnotation(const SkRect& rect, SkData* value) : rect(rect), value(sk_ref_sp(value)) {}
+ SkRect rect;
+ sk_sp<SkData> value;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ c->drawAnnotation(rect, pod<char>(this), value.get());
+ }
+};
+struct DrawDrawable final : Op {
+ static const auto kType = Type::DrawDrawable;
+ DrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) : drawable(sk_ref_sp(drawable)) {
+ if (matrix) {
+ this->matrix = *matrix;
+ }
+ }
+ sk_sp<SkDrawable> drawable;
+ SkMatrix matrix = SkMatrix::I();
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawDrawable(drawable.get(), &matrix); }
+};
+struct DrawPicture final : Op {
+ static const auto kType = Type::DrawPicture;
+ DrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint)
+ : picture(sk_ref_sp(picture)) {
+ if (matrix) {
+ this->matrix = *matrix;
+ }
+ if (paint) {
+ this->paint = *paint;
+ has_paint = true;
+ }
+ }
+ sk_sp<const SkPicture> picture;
+ SkMatrix matrix = SkMatrix::I();
+ SkPaint paint;
+ bool has_paint = false; // TODO: why is a default paint not the same?
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ c->drawPicture(picture.get(), &matrix, has_paint ? &paint : nullptr);
+ }
+};
+
+struct DrawImage final : Op {
+ static const auto kType = Type::DrawImage;
+ DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint,
+ BitmapPalette palette)
+ : image(std::move(image)), x(x), y(y), palette(palette) {
+ if (paint) {
+ this->paint = *paint;
+ }
+ }
+ sk_sp<const SkImage> image;
+ SkScalar x, y;
+ SkPaint paint;
+ BitmapPalette palette;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawImage(image.get(), x, y, &paint); }
+};
+struct DrawImageNine final : Op {
+ static const auto kType = Type::DrawImageNine;
+ DrawImageNine(sk_sp<const SkImage>&& image, const SkIRect& center, const SkRect& dst,
+ const SkPaint* paint)
+ : image(std::move(image)), center(center), dst(dst) {
+ if (paint) {
+ this->paint = *paint;
+ }
+ }
+ sk_sp<const SkImage> image;
+ SkIRect center;
+ SkRect dst;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ c->drawImageNine(image.get(), center, dst, &paint);
+ }
+};
+struct DrawImageRect final : Op {
+ static const auto kType = Type::DrawImageRect;
+ DrawImageRect(sk_sp<const SkImage>&& image, const SkRect* src, const SkRect& dst,
+ const SkPaint* paint, SkCanvas::SrcRectConstraint constraint,
+ BitmapPalette palette)
+ : image(std::move(image)), dst(dst), constraint(constraint), palette(palette) {
+ this->src = src ? *src : SkRect::MakeIWH(this->image->width(), this->image->height());
+ if (paint) {
+ this->paint = *paint;
+ }
+ }
+ sk_sp<const SkImage> image;
+ SkRect src, dst;
+ SkPaint paint;
+ SkCanvas::SrcRectConstraint constraint;
+ BitmapPalette palette;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ c->drawImageRect(image.get(), src, dst, &paint, constraint);
+ }
+};
+struct DrawImageLattice final : Op {
+ static const auto kType = Type::DrawImageLattice;
+ DrawImageLattice(sk_sp<const SkImage>&& image, int xs, int ys, int fs, const SkIRect& src,
+ const SkRect& dst, const SkPaint* paint, BitmapPalette palette)
+ : image(std::move(image))
+ , xs(xs)
+ , ys(ys)
+ , fs(fs)
+ , src(src)
+ , dst(dst)
+ , palette(palette) {
+ if (paint) {
+ this->paint = *paint;
+ }
+ }
+ sk_sp<const SkImage> image;
+ int xs, ys, fs;
+ SkIRect src;
+ SkRect dst;
+ SkPaint paint;
+ BitmapPalette palette;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ auto xdivs = pod<int>(this, 0), ydivs = pod<int>(this, xs * sizeof(int));
+ auto colors = (0 == fs) ? nullptr : pod<SkColor>(this, (xs + ys) * sizeof(int));
+ auto flags =
+ (0 == fs) ? nullptr : pod<SkCanvas::Lattice::RectType>(
+ this, (xs + ys) * sizeof(int) + fs * sizeof(SkColor));
+ c->drawImageLattice(image.get(), {xdivs, ydivs, flags, xs, ys, &src, colors}, dst, &paint);
+ }
+};
+
+struct DrawText final : Op {
+ static const auto kType = Type::DrawText;
+ DrawText(size_t bytes, SkScalar x, SkScalar y, const SkPaint& paint)
+ : bytes(bytes), x(x), y(y), paint(paint) {}
+ size_t bytes;
+ SkScalar x, y;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ c->drawText(pod<void>(this), bytes, x, y, paint);
+ }
+};
+struct DrawPosText final : Op {
+ static const auto kType = Type::DrawPosText;
+ DrawPosText(size_t bytes, const SkPaint& paint, int n) : bytes(bytes), paint(paint), n(n) {}
+ size_t bytes;
+ SkPaint paint;
+ int n;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ auto points = pod<SkPoint>(this);
+ auto text = pod<void>(this, n * sizeof(SkPoint));
+ c->drawPosText(text, bytes, points, paint);
+ }
+};
+struct DrawPosTextH final : Op {
+ static const auto kType = Type::DrawPosTextH;
+ DrawPosTextH(size_t bytes, SkScalar y, const SkPaint& paint, int n)
+ : bytes(bytes), y(y), paint(paint), n(n) {}
+ size_t bytes;
+ SkScalar y;
+ SkPaint paint;
+ int n;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ auto xs = pod<SkScalar>(this);
+ auto text = pod<void>(this, n * sizeof(SkScalar));
+ c->drawPosTextH(text, bytes, xs, y, paint);
+ }
+};
+struct DrawTextRSXform final : Op {
+ static const auto kType = Type::DrawTextRSXform;
+ DrawTextRSXform(size_t bytes, int xforms, const SkRect* cull, const SkPaint& paint)
+ : bytes(bytes), xforms(xforms), paint(paint) {
+ if (cull) {
+ this->cull = *cull;
+ }
+ }
+ size_t bytes;
+ int xforms;
+ SkRect cull = kUnset;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ // For alignment, the SkRSXforms are first in the pod section, followed by the text.
+ c->drawTextRSXform(pod<void>(this, xforms * sizeof(SkRSXform)), bytes, pod<SkRSXform>(this),
+ maybe_unset(cull), paint);
+ }
+};
+struct DrawTextBlob final : Op {
+ static const auto kType = Type::DrawTextBlob;
+ DrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint)
+ : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint) {}
+ sk_sp<const SkTextBlob> blob;
+ SkScalar x, y;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->drawTextBlob(blob.get(), x, y, paint); }
+};
+
+struct DrawPatch final : Op {
+ static const auto kType = Type::DrawPatch;
+ DrawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texs[4],
+ SkBlendMode bmode, const SkPaint& paint)
+ : xfermode(bmode), paint(paint) {
+ copy_v(this->cubics, cubics, 12);
+ if (colors) {
+ copy_v(this->colors, colors, 4);
+ has_colors = true;
+ }
+ if (texs) {
+ copy_v(this->texs, texs, 4);
+ has_texs = true;
+ }
+ }
+ SkPoint cubics[12];
+ SkColor colors[4];
+ SkPoint texs[4];
+ SkBlendMode xfermode;
+ SkPaint paint;
+ bool has_colors = false;
+ bool has_texs = false;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ c->drawPatch(cubics, has_colors ? colors : nullptr, has_texs ? texs : nullptr, xfermode,
+ paint);
+ }
+};
+struct DrawPoints final : Op {
+ static const auto kType = Type::DrawPoints;
+ DrawPoints(SkCanvas::PointMode mode, size_t count, const SkPaint& paint)
+ : mode(mode), count(count), paint(paint) {}
+ SkCanvas::PointMode mode;
+ size_t count;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ c->drawPoints(mode, count, pod<SkPoint>(this), paint);
+ }
+};
+struct DrawVertices final : Op {
+ static const auto kType = Type::DrawVertices;
+ DrawVertices(const SkVertices* v, int bc, SkBlendMode m, const SkPaint& p)
+ : vertices(sk_ref_sp(const_cast<SkVertices*>(v))), boneCount(bc), mode(m), paint(p) {}
+ sk_sp<SkVertices> vertices;
+ int boneCount;
+ SkBlendMode mode;
+ SkPaint paint;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ c->drawVertices(vertices, pod<SkVertices::Bone>(this), boneCount, mode, paint);
+ }
+};
+struct DrawAtlas final : Op {
+ static const auto kType = Type::DrawAtlas;
+ DrawAtlas(const SkImage* atlas, int count, SkBlendMode xfermode, const SkRect* cull,
+ const SkPaint* paint, bool has_colors)
+ : atlas(sk_ref_sp(atlas)), count(count), xfermode(xfermode), has_colors(has_colors) {
+ if (cull) {
+ this->cull = *cull;
+ }
+ if (paint) {
+ this->paint = *paint;
+ }
+ }
+ sk_sp<const SkImage> atlas;
+ int count;
+ SkBlendMode xfermode;
+ SkRect cull = kUnset;
+ SkPaint paint;
+ bool has_colors;
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ auto xforms = pod<SkRSXform>(this, 0);
+ auto texs = pod<SkRect>(this, count * sizeof(SkRSXform));
+ auto colors = has_colors ? pod<SkColor>(this, count * (sizeof(SkRSXform) + sizeof(SkRect)))
+ : nullptr;
+ c->drawAtlas(atlas.get(), xforms, texs, colors, count, xfermode, maybe_unset(cull), &paint);
+ }
+};
+struct DrawShadowRec final : Op {
+ static const auto kType = Type::DrawShadowRec;
+ DrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) : fPath(path), fRec(rec) {}
+ SkPath fPath;
+ SkDrawShadowRec fRec;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->private_draw_shadow_rec(fPath, fRec); }
+};
+
+struct DrawVectorDrawable final : Op {
+ static const auto kType = Type::DrawVectorDrawable;
+ DrawVectorDrawable(VectorDrawableRoot* tree)
+ : mRoot(tree)
+ , mBounds(tree->stagingProperties().getBounds())
+ , palette(tree->computePalette()) {
+ // Recording, so use staging properties
+ tree->getPaintFor(&paint, tree->stagingProperties());
}
- // SkCanvas instances default to identity transform, but should inherit
- // the state of this Canvas; if this code was in the SkiaCanvasProxy
- // constructor, we couldn't cache mSkiaCanvasProxy.
- SkMatrix parentTransform;
- getMatrix(&parentTransform);
- mSkiaCanvasProxy.get()->setMatrix(parentTransform);
+ void draw(SkCanvas* canvas, const SkMatrix&) const { mRoot->draw(canvas, mBounds, paint); }
- return mSkiaCanvasProxy.get();
+ sp<VectorDrawableRoot> mRoot;
+ SkRect mBounds;
+ SkPaint paint;
+ BitmapPalette palette;
+};
}
-// ----------------------------------------------------------------------------
-// CanvasStateClient implementation
-// ----------------------------------------------------------------------------
-
-void RecordingCanvas::onViewportInitialized() {}
-
-void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
- if (removed.flags & Snapshot::kFlagIsFboLayer) {
- addOp(alloc().create_trivial<EndLayerOp>());
- } else if (removed.flags & Snapshot::kFlagIsLayer) {
- addOp(alloc().create_trivial<EndUnclippedLayerOp>());
+template <typename T, typename... Args>
+void* DisplayListData::push(size_t pod, Args&&... args) {
+ size_t skip = SkAlignPtr(sizeof(T) + pod);
+ SkASSERT(skip < (1 << 24));
+ if (fUsed + skip > fReserved) {
+ static_assert(SkIsPow2(SKLITEDL_PAGE), "This math needs updating for non-pow2.");
+ // Next greater multiple of SKLITEDL_PAGE.
+ fReserved = (fUsed + skip + SKLITEDL_PAGE) & ~(SKLITEDL_PAGE - 1);
+ fBytes.realloc(fReserved);
}
+ SkASSERT(fUsed + skip <= fReserved);
+ auto op = (T*)(fBytes.get() + fUsed);
+ fUsed += skip;
+ new (op) T{std::forward<Args>(args)...};
+ op->type = (uint32_t)T::kType;
+ op->skip = skip;
+ return op + 1;
}
-// ----------------------------------------------------------------------------
-// android/graphics/Canvas state operations
-// ----------------------------------------------------------------------------
-// Save (layer)
-int RecordingCanvas::save(SaveFlags::Flags flags) {
- return mState.save((int)flags);
+template <typename Fn, typename... Args>
+inline void DisplayListData::map(const Fn fns[], Args... args) const {
+ auto end = fBytes.get() + fUsed;
+ for (const uint8_t* ptr = fBytes.get(); ptr < end;) {
+ auto op = (const Op*)ptr;
+ auto type = op->type;
+ auto skip = op->skip;
+ if (auto fn = fns[type]) { // We replace no-op functions with nullptrs
+ fn(op, args...); // to avoid the overhead of a pointless call.
+ }
+ ptr += skip;
+ }
}
-void RecordingCanvas::RecordingCanvas::restore() {
- mState.restore();
+void DisplayListData::flush() {
+ this->push<Flush>(0);
}
-void RecordingCanvas::restoreToCount(int saveCount) {
- mState.restoreToCount(saveCount);
+void DisplayListData::save() {
+ this->push<Save>(0);
}
-
-int RecordingCanvas::saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, SaveFlags::Flags flags) {
- // force matrix/clip isolation for layer
- flags |= SaveFlags::MatrixClip;
- bool clippedLayer = flags & SaveFlags::ClipToLayer;
-
- const Snapshot& previous = *mState.currentSnapshot();
-
- // initialize the snapshot as though it almost represents an FBO layer so deferred draw
- // operations will be able to store and restore the current clip and transform info, and
- // quick rejection will be correct (for display lists)
-
- Rect unmappedBounds(left, top, right, bottom);
- unmappedBounds.roundOut();
-
- // determine clipped bounds relative to previous viewport.
- Rect visibleBounds = unmappedBounds;
- previous.transform->mapRect(visibleBounds);
-
- if (CC_UNLIKELY(!clippedLayer && previous.transform->rectToRect() &&
- visibleBounds.contains(previous.getRenderTargetClip()))) {
- // unlikely case where an unclipped savelayer is recorded with a clip it can use,
- // as none of its unaffected/unclipped area is visible
- clippedLayer = true;
- flags |= SaveFlags::ClipToLayer;
- }
-
- visibleBounds.doIntersect(previous.getRenderTargetClip());
- visibleBounds.snapToPixelBoundaries();
- visibleBounds.doIntersect(Rect(previous.getViewportWidth(), previous.getViewportHeight()));
-
- // Map visible bounds back to layer space, and intersect with parameter bounds
- Rect layerBounds = visibleBounds;
- if (CC_LIKELY(!layerBounds.isEmpty())) {
- // if non-empty, can safely map by the inverse transform
- Matrix4 inverse;
- inverse.loadInverse(*previous.transform);
- inverse.mapRect(layerBounds);
- layerBounds.doIntersect(unmappedBounds);
- }
-
- int saveValue = mState.save((int)flags);
- Snapshot& snapshot = *mState.writableSnapshot();
-
- // layerBounds is in original bounds space, but clipped by current recording clip
- if (!layerBounds.isEmpty() && !unmappedBounds.isEmpty()) {
- if (CC_LIKELY(clippedLayer)) {
- auto previousClip = getRecordedClip(); // capture before new snapshot clip has changed
- if (addOp(alloc().create_trivial<BeginLayerOp>(
- unmappedBounds,
- *previous.transform, // transform to *draw* with
- previousClip, // clip to *draw* with
- refPaint(paint))) >= 0) {
- snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer;
- snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight());
- snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f);
-
- Rect clip = layerBounds;
- clip.translate(-unmappedBounds.left, -unmappedBounds.top);
- snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
- snapshot.roundRectClipState = nullptr;
- return saveValue;
- }
- } else {
- if (addOp(alloc().create_trivial<BeginUnclippedLayerOp>(
- unmappedBounds, *mState.currentSnapshot()->transform, getRecordedClip(),
- refPaint(paint))) >= 0) {
- snapshot.flags |= Snapshot::kFlagIsLayer;
- return saveValue;
- }
- }
- }
-
- // Layer not needed, so skip recording it...
- if (CC_LIKELY(clippedLayer)) {
- // ... and set empty clip to reject inner content, if possible
- snapshot.resetClip(0, 0, 0, 0);
- }
- return saveValue;
+void DisplayListData::restore() {
+ this->push<Restore>(0);
}
-
-// Matrix
-void RecordingCanvas::rotate(float degrees) {
- if (degrees == 0) return;
-
- mState.rotate(degrees);
+void DisplayListData::saveLayer(const SkRect* bounds, const SkPaint* paint,
+ const SkImageFilter* backdrop, const SkImage* clipMask,
+ const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) {
+ this->push<SaveLayer>(0, bounds, paint, backdrop, clipMask, clipMatrix, flags);
}
-void RecordingCanvas::scale(float sx, float sy) {
- if (sx == 1 && sy == 1) return;
-
- mState.scale(sx, sy);
+void DisplayListData::concat(const SkMatrix& matrix) {
+ this->push<Concat>(0, matrix);
}
-
-void RecordingCanvas::skew(float sx, float sy) {
- mState.skew(sx, sy);
+void DisplayListData::setMatrix(const SkMatrix& matrix) {
+ this->push<SetMatrix>(0, matrix);
+}
+void DisplayListData::translate(SkScalar dx, SkScalar dy) {
+ this->push<Translate>(0, dx, dy);
}
-void RecordingCanvas::translate(float dx, float dy) {
- if (dx == 0 && dy == 0) return;
-
- mState.translate(dx, dy, 0);
+void DisplayListData::clipPath(const SkPath& path, SkClipOp op, bool aa) {
+ this->push<ClipPath>(0, path, op, aa);
+}
+void DisplayListData::clipRect(const SkRect& rect, SkClipOp op, bool aa) {
+ this->push<ClipRect>(0, rect, op, aa);
+}
+void DisplayListData::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
+ this->push<ClipRRect>(0, rrect, op, aa);
+}
+void DisplayListData::clipRegion(const SkRegion& region, SkClipOp op) {
+ this->push<ClipRegion>(0, region, op);
}
-// Clip
-bool RecordingCanvas::getClipBounds(SkRect* outRect) const {
- *outRect = mState.getLocalClipBounds().toSkRect();
- return !(outRect->isEmpty());
+void DisplayListData::drawPaint(const SkPaint& paint) {
+ this->push<DrawPaint>(0, paint);
}
-bool RecordingCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
- return mState.quickRejectConservative(left, top, right, bottom);
+void DisplayListData::drawPath(const SkPath& path, const SkPaint& paint) {
+ this->push<DrawPath>(0, path, paint);
}
-bool RecordingCanvas::quickRejectPath(const SkPath& path) const {
- SkRect bounds = path.getBounds();
- return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
+void DisplayListData::drawRect(const SkRect& rect, const SkPaint& paint) {
+ this->push<DrawRect>(0, rect, paint);
}
-bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
- return mState.clipRect(left, top, right, bottom, op);
+void DisplayListData::drawRegion(const SkRegion& region, const SkPaint& paint) {
+ this->push<DrawRegion>(0, region, paint);
}
-bool RecordingCanvas::clipPath(const SkPath* path, SkClipOp op) {
- return mState.clipPath(path, op);
+void DisplayListData::drawOval(const SkRect& oval, const SkPaint& paint) {
+ this->push<DrawOval>(0, oval, paint);
+}
+void DisplayListData::drawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+ bool useCenter, const SkPaint& paint) {
+ this->push<DrawArc>(0, oval, startAngle, sweepAngle, useCenter, paint);
+}
+void DisplayListData::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+ this->push<DrawRRect>(0, rrect, paint);
+}
+void DisplayListData::drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
+ this->push<DrawDRRect>(0, outer, inner, paint);
}
-// ----------------------------------------------------------------------------
-// android/graphics/Canvas draw operations
-// ----------------------------------------------------------------------------
-void RecordingCanvas::drawColor(int color, SkBlendMode mode) {
- addOp(alloc().create_trivial<ColorOp>(getRecordedClip(), color, mode));
+void DisplayListData::drawAnnotation(const SkRect& rect, const char* key, SkData* value) {
+ size_t bytes = strlen(key) + 1;
+ void* pod = this->push<DrawAnnotation>(bytes, rect, value);
+ copy_v(pod, key, bytes);
+}
+void DisplayListData::drawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
+ this->push<DrawDrawable>(0, drawable, matrix);
+}
+void DisplayListData::drawPicture(const SkPicture* picture, const SkMatrix* matrix,
+ const SkPaint* paint) {
+ this->push<DrawPicture>(0, picture, matrix, paint);
+}
+void DisplayListData::drawImage(sk_sp<const SkImage> image, SkScalar x, SkScalar y,
+ const SkPaint* paint, BitmapPalette palette) {
+ this->push<DrawImage>(0, std::move(image), x, y, paint, palette);
+}
+void DisplayListData::drawImageNine(sk_sp<const SkImage> image, const SkIRect& center,
+ const SkRect& dst, const SkPaint* paint) {
+ this->push<DrawImageNine>(0, std::move(image), center, dst, paint);
+}
+void DisplayListData::drawImageRect(sk_sp<const SkImage> image, const SkRect* src,
+ const SkRect& dst, const SkPaint* paint,
+ SkCanvas::SrcRectConstraint constraint, BitmapPalette palette) {
+ this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint, palette);
+}
+void DisplayListData::drawImageLattice(sk_sp<const SkImage> image, const SkCanvas::Lattice& lattice,
+ const SkRect& dst, const SkPaint* paint,
+ BitmapPalette palette) {
+ int xs = lattice.fXCount, ys = lattice.fYCount;
+ int fs = lattice.fRectTypes ? (xs + 1) * (ys + 1) : 0;
+ size_t bytes = (xs + ys) * sizeof(int) + fs * sizeof(SkCanvas::Lattice::RectType) +
+ fs * sizeof(SkColor);
+ SkASSERT(lattice.fBounds);
+ void* pod = this->push<DrawImageLattice>(bytes, std::move(image), xs, ys, fs, *lattice.fBounds,
+ dst, paint, palette);
+ copy_v(pod, lattice.fXDivs, xs, lattice.fYDivs, ys, lattice.fColors, fs, lattice.fRectTypes,
+ fs);
}
-void RecordingCanvas::drawPaint(const SkPaint& paint) {
- SkRect bounds;
- if (getClipBounds(&bounds)) {
- drawRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, paint);
- }
+void DisplayListData::drawText(const void* text, size_t bytes, SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ void* pod = this->push<DrawText>(bytes, bytes, x, y, paint);
+ copy_v(pod, (const char*)text, bytes);
+ mHasText = true;
+}
+void DisplayListData::drawPosText(const void* text, size_t bytes, const SkPoint pos[],
+ const SkPaint& paint) {
+ int n = paint.countText(text, bytes);
+ void* pod = this->push<DrawPosText>(n * sizeof(SkPoint) + bytes, bytes, paint, n);
+ copy_v(pod, pos, n, (const char*)text, bytes);
+ mHasText = true;
+}
+void DisplayListData::drawPosTextH(const void* text, size_t bytes, const SkScalar xs[], SkScalar y,
+ const SkPaint& paint) {
+ int n = paint.countText(text, bytes);
+ void* pod = this->push<DrawPosTextH>(n * sizeof(SkScalar) + bytes, bytes, y, paint, n);
+ copy_v(pod, xs, n, (const char*)text, bytes);
+ mHasText = true;
+}
+void DisplayListData::drawTextRSXform(const void* text, size_t bytes, const SkRSXform xforms[],
+ const SkRect* cull, const SkPaint& paint) {
+ int n = paint.countText(text, bytes);
+ void* pod = this->push<DrawTextRSXform>(bytes + n * sizeof(SkRSXform), bytes, n, cull, paint);
+ copy_v(pod, xforms, n, (const char*)text, bytes);
+ mHasText = true;
+}
+void DisplayListData::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ this->push<DrawTextBlob>(0, blob, x, y, paint);
+ mHasText = true;
}
-static Rect calcBoundsOfPoints(const float* points, int floatCount) {
- Rect unmappedBounds(points[0], points[1], points[0], points[1]);
- for (int i = 2; i < floatCount; i += 2) {
- unmappedBounds.expandToCover(points[i], points[i + 1]);
+void DisplayListData::drawPatch(const SkPoint points[12], const SkColor colors[4],
+ const SkPoint texs[4], SkBlendMode bmode, const SkPaint& paint) {
+ this->push<DrawPatch>(0, points, colors, texs, bmode, paint);
+}
+void DisplayListData::drawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint points[],
+ const SkPaint& paint) {
+ void* pod = this->push<DrawPoints>(count * sizeof(SkPoint), mode, count, paint);
+ copy_v(pod, points, count);
+}
+void DisplayListData::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
+ int boneCount, SkBlendMode mode, const SkPaint& paint) {
+ void* pod = this->push<DrawVertices>(boneCount * sizeof(SkVertices::Bone), vertices, boneCount,
+ mode, paint);
+ copy_v(pod, bones, boneCount);
+}
+void DisplayListData::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[],
+ const SkColor colors[], int count, SkBlendMode xfermode,
+ const SkRect* cull, const SkPaint* paint) {
+ size_t bytes = count * (sizeof(SkRSXform) + sizeof(SkRect));
+ if (colors) {
+ bytes += count * sizeof(SkColor);
}
- return unmappedBounds;
+ void* pod =
+ this->push<DrawAtlas>(bytes, atlas, count, xfermode, cull, paint, colors != nullptr);
+ copy_v(pod, xforms, count, texs, count, colors, colors ? count : 0);
+}
+void DisplayListData::drawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
+ this->push<DrawShadowRec>(0, path, rec);
+}
+void DisplayListData::drawVectorDrawable(VectorDrawableRoot* tree) {
+ this->push<DrawVectorDrawable>(0, tree);
}
-// Geometry
-void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPaint& paint) {
- if (CC_UNLIKELY(floatCount < 2 || paint.nothingToDraw())) return;
- floatCount &= ~0x1; // round down to nearest two
+typedef void (*draw_fn)(const void*, SkCanvas*, const SkMatrix&);
+typedef void (*void_fn)(const void*);
+typedef void (*color_transform_fn)(const void*, ColorTransform);
+
+// All ops implement draw().
+#define X(T) \
+ [](const void* op, SkCanvas* c, const SkMatrix& original) { \
+ ((const T*)op)->draw(c, original); \
+ },
+static const draw_fn draw_fns[] = {
+#include "DisplayListOps.in"
+};
+#undef X
+
+// Most state ops (matrix, clip, save, restore) have a trivial destructor.
+#define X(T) \
+ !std::is_trivially_destructible<T>::value ? [](const void* op) { ((const T*)op)->~T(); } \
+ : (void_fn) nullptr,
+
+static const void_fn dtor_fns[] = {
+#include "DisplayListOps.in"
+};
+#undef X
+
+void DisplayListData::draw(SkCanvas* canvas) const {
+ SkAutoCanvasRestore acr(canvas, false);
+ this->map(draw_fns, canvas, canvas->getTotalMatrix());
+}
- addOp(alloc().create_trivial<PointsOp>(
- calcBoundsOfPoints(points, floatCount), *mState.currentSnapshot()->transform,
- getRecordedClip(), refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
+DisplayListData::~DisplayListData() {
+ this->reset();
}
-void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) {
- if (CC_UNLIKELY(floatCount < 4 || paint.nothingToDraw())) return;
- floatCount &= ~0x3; // round down to nearest four
+void DisplayListData::reset() {
+ this->map(dtor_fns);
- addOp(alloc().create_trivial<LinesOp>(
- calcBoundsOfPoints(points, floatCount), *mState.currentSnapshot()->transform,
- getRecordedClip(), refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
+ // Leave fBytes and fReserved alone.
+ fUsed = 0;
}
-void RecordingCanvas::drawRect(float left, float top, float right, float bottom,
- const SkPaint& paint) {
- if (CC_UNLIKELY(paint.nothingToDraw())) return;
-
- addOp(alloc().create_trivial<RectOp>(Rect(left, top, right, bottom),
- *(mState.currentSnapshot()->transform), getRecordedClip(),
- refPaint(&paint)));
-}
-
-void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) {
- if (rects == nullptr) return;
-
- Vertex* rectData = (Vertex*)mDisplayList->allocator.create_trivial_array<Vertex>(vertexCount);
- Vertex* vertex = rectData;
-
- float left = FLT_MAX;
- float top = FLT_MAX;
- float right = FLT_MIN;
- float bottom = FLT_MIN;
- for (int index = 0; index < vertexCount; index += 4) {
- float l = rects[index + 0];
- float t = rects[index + 1];
- float r = rects[index + 2];
- float b = rects[index + 3];
-
- Vertex::set(vertex++, l, t);
- Vertex::set(vertex++, r, t);
- Vertex::set(vertex++, l, b);
- Vertex::set(vertex++, r, b);
-
- left = std::min(left, l);
- top = std::min(top, t);
- right = std::max(right, r);
- bottom = std::max(bottom, b);
- }
- addOp(alloc().create_trivial<SimpleRectsOp>(
- Rect(left, top, right, bottom), *(mState.currentSnapshot()->transform),
- getRecordedClip(), refPaint(paint), rectData, vertexCount));
-}
-
-void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
- if (CC_UNLIKELY(paint.nothingToDraw())) return;
-
- if (paint.getStyle() == SkPaint::kFill_Style &&
- (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) {
- int count = 0;
- Vector<float> rects;
- SkRegion::Iterator it(region);
- while (!it.done()) {
- const SkIRect& r = it.rect();
- rects.push(r.fLeft);
- rects.push(r.fTop);
- rects.push(r.fRight);
- rects.push(r.fBottom);
- count += 4;
- it.next();
+template <class T>
+using has_paint_helper = decltype(std::declval<T>().paint);
+
+template <class T>
+constexpr bool has_paint = std::experimental::is_detected_v<has_paint_helper, T>;
+
+template <class T>
+using has_palette_helper = decltype(std::declval<T>().palette);
+
+template <class T>
+constexpr bool has_palette = std::experimental::is_detected_v<has_palette_helper, T>;
+
+template <class T>
+constexpr color_transform_fn colorTransformForOp() {
+ if
+ constexpr(has_paint<T> && has_palette<T>) {
+ // It's a bitmap
+ return [](const void* opRaw, ColorTransform transform) {
+ // TODO: We should be const. Or not. Or just use a different map
+ // Unclear, but this is the quick fix
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette);
+ };
}
- drawSimpleRects(rects.array(), count, &paint);
- } else {
- SkRegion::Iterator it(region);
- while (!it.done()) {
- const SkIRect& r = it.rect();
- drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
- it.next();
+ else if
+ constexpr(has_paint<T>) {
+ return [](const void* opRaw, ColorTransform transform) {
+ // TODO: We should be const. Or not. Or just use a different map
+ // Unclear, but this is the quick fix
+ const T* op = reinterpret_cast<const T*>(opRaw);
+ transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
+ };
}
+ else {
+ return nullptr;
}
}
-void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx,
- float ry, const SkPaint& paint) {
- if (CC_UNLIKELY(paint.nothingToDraw())) return;
+#define X(T) colorTransformForOp<T>(),
+static const color_transform_fn color_transform_fns[] = {
+#include "DisplayListOps.in"
+};
+#undef X
- if (CC_LIKELY(MathUtils::isPositive(rx) || MathUtils::isPositive(ry))) {
- addOp(alloc().create_trivial<RoundRectOp>(Rect(left, top, right, bottom),
- *(mState.currentSnapshot()->transform),
- getRecordedClip(), refPaint(&paint), rx, ry));
- } else {
- drawRect(left, top, right, bottom, paint);
- }
-}
-
-void RecordingCanvas::drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
- CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
- CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
- CanvasPropertyPaint* paint) {
- mDisplayList->ref(left);
- mDisplayList->ref(top);
- mDisplayList->ref(right);
- mDisplayList->ref(bottom);
- mDisplayList->ref(rx);
- mDisplayList->ref(ry);
- mDisplayList->ref(paint);
- refBitmapsInShader(paint->value.getShader());
- addOp(alloc().create_trivial<RoundRectPropsOp>(
- *(mState.currentSnapshot()->transform), getRecordedClip(), &paint->value, &left->value,
- &top->value, &right->value, &bottom->value, &rx->value, &ry->value));
+void DisplayListData::applyColorTransform(ColorTransform transform) {
+ this->map(color_transform_fns, transform);
}
-void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
- // TODO: move to Canvas.h
- if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
+RecordingCanvas::RecordingCanvas() : INHERITED(1, 1), fDL(nullptr) {}
- drawOval(x - radius, y - radius, x + radius, y + radius, paint);
+void RecordingCanvas::reset(DisplayListData* dl, const SkIRect& bounds) {
+ this->resetCanvas(bounds.right(), bounds.bottom());
+ fDL = dl;
}
-void RecordingCanvas::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
- CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
- mDisplayList->ref(x);
- mDisplayList->ref(y);
- mDisplayList->ref(radius);
- mDisplayList->ref(paint);
- refBitmapsInShader(paint->value.getShader());
- addOp(alloc().create_trivial<CirclePropsOp>(*(mState.currentSnapshot()->transform),
- getRecordedClip(), &paint->value, &x->value,
- &y->value, &radius->value));
+sk_sp<SkSurface> RecordingCanvas::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
+ return nullptr;
}
-void RecordingCanvas::drawOval(float left, float top, float right, float bottom,
- const SkPaint& paint) {
- if (CC_UNLIKELY(paint.nothingToDraw())) return;
-
- addOp(alloc().create_trivial<OvalOp>(Rect(left, top, right, bottom),
- *(mState.currentSnapshot()->transform), getRecordedClip(),
- refPaint(&paint)));
+void RecordingCanvas::onFlush() {
+ fDL->flush();
}
-void RecordingCanvas::drawArc(float left, float top, float right, float bottom, float startAngle,
- float sweepAngle, bool useCenter, const SkPaint& paint) {
- if (CC_UNLIKELY(paint.nothingToDraw())) return;
-
- if (fabs(sweepAngle) >= 360.0f) {
- drawOval(left, top, right, bottom, paint);
- } else {
- addOp(alloc().create_trivial<ArcOp>(
- Rect(left, top, right, bottom), *(mState.currentSnapshot()->transform),
- getRecordedClip(), refPaint(&paint), startAngle, sweepAngle, useCenter));
- }
+void RecordingCanvas::willSave() {
+ fDL->save();
+}
+SkCanvas::SaveLayerStrategy RecordingCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
+ fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fClipMask, rec.fClipMatrix,
+ rec.fSaveLayerFlags);
+ return SkCanvas::kNoLayer_SaveLayerStrategy;
+}
+void RecordingCanvas::willRestore() {
+ fDL->restore();
}
-void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
- if (CC_UNLIKELY(paint.nothingToDraw())) return;
-
- addOp(alloc().create_trivial<PathOp>(Rect(path.getBounds()),
- *(mState.currentSnapshot()->transform), getRecordedClip(),
- refPaint(&paint), refPath(&path)));
+void RecordingCanvas::didConcat(const SkMatrix& matrix) {
+ fDL->concat(matrix);
+}
+void RecordingCanvas::didSetMatrix(const SkMatrix& matrix) {
+ fDL->setMatrix(matrix);
+}
+void RecordingCanvas::didTranslate(SkScalar dx, SkScalar dy) {
+ fDL->translate(dx, dy);
}
-void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
- mDisplayList->ref(tree);
- mDisplayList->vectorDrawables.push_back(tree);
- addOp(alloc().create_trivial<VectorDrawableOp>(
- tree, Rect(tree->stagingProperties()->getBounds()),
- *(mState.currentSnapshot()->transform), getRecordedClip()));
-}
-
-// Bitmap-based
-void RecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
- save(SaveFlags::Matrix);
- translate(left, top);
- drawBitmap(bitmap, paint);
- restore();
-}
-
-void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
- if (matrix.isIdentity()) {
- drawBitmap(bitmap, paint);
- } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) &&
- MathUtils::isPositive(matrix.getScaleX()) &&
- MathUtils::isPositive(matrix.getScaleY())) {
- // SkMatrix::isScaleTranslate() not available in L
- SkRect src;
- SkRect dst;
- bitmap.getBounds(&src);
- matrix.mapRect(&dst, src);
- drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft, dst.fTop,
- dst.fRight, dst.fBottom, paint);
- } else {
- save(SaveFlags::Matrix);
- concat(matrix);
- drawBitmap(bitmap, paint);
- restore();
- }
-}
-
-void RecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
- float srcBottom, float dstLeft, float dstTop, float dstRight,
- float dstBottom, const SkPaint* paint) {
- if (srcLeft == 0 && srcTop == 0 && srcRight == bitmap.width() && srcBottom == bitmap.height() &&
- (srcBottom - srcTop == dstBottom - dstTop) && (srcRight - srcLeft == dstRight - dstLeft)) {
- // transform simple rect to rect drawing case into position bitmap ops, since they merge
- save(SaveFlags::Matrix);
- translate(dstLeft, dstTop);
- drawBitmap(bitmap, paint);
- restore();
- } else {
- addOp(alloc().create_trivial<BitmapRectOp>(
- Rect(dstLeft, dstTop, dstRight, dstBottom), *(mState.currentSnapshot()->transform),
- getRecordedClip(), refPaint(paint), refBitmap(bitmap),
- Rect(srcLeft, srcTop, srcRight, srcBottom)));
- }
+void RecordingCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle style) {
+ fDL->clipRect(rect, op, style == kSoft_ClipEdgeStyle);
+ this->INHERITED::onClipRect(rect, op, style);
+}
+void RecordingCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle style) {
+ fDL->clipRRect(rrect, op, style == kSoft_ClipEdgeStyle);
+ this->INHERITED::onClipRRect(rrect, op, style);
+}
+void RecordingCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle style) {
+ fDL->clipPath(path, op, style == kSoft_ClipEdgeStyle);
+ this->INHERITED::onClipPath(path, op, style);
+}
+void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) {
+ fDL->clipRegion(region, op);
+ this->INHERITED::onClipRegion(region, op);
}
-void RecordingCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors,
- const SkPaint* paint) {
- int vertexCount = (meshWidth + 1) * (meshHeight + 1);
- addOp(alloc().create_trivial<BitmapMeshOp>(
- calcBoundsOfPoints(vertices, vertexCount * 2), *(mState.currentSnapshot()->transform),
- getRecordedClip(), refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight,
- refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex
- refBuffer<int>(colors, vertexCount))); // 1 color per vertex
+void RecordingCanvas::onDrawPaint(const SkPaint& paint) {
+ fDL->drawPaint(paint);
+}
+void RecordingCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
+ fDL->drawPath(path, paint);
+}
+void RecordingCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
+ fDL->drawRect(rect, paint);
+}
+void RecordingCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
+ fDL->drawRegion(region, paint);
+}
+void RecordingCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
+ fDL->drawOval(oval, paint);
+}
+void RecordingCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+ bool useCenter, const SkPaint& paint) {
+ fDL->drawArc(oval, startAngle, sweepAngle, useCenter, paint);
+}
+void RecordingCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
+ fDL->drawRRect(rrect, paint);
+}
+void RecordingCanvas::onDrawDRRect(const SkRRect& out, const SkRRect& in, const SkPaint& paint) {
+ fDL->drawDRRect(out, in, paint);
}
-void RecordingCanvas::drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& patch,
- float dstLeft, float dstTop, float dstRight, float dstBottom,
+void RecordingCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
+ fDL->drawDrawable(drawable, matrix);
+}
+void RecordingCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
const SkPaint* paint) {
- addOp(alloc().create_trivial<PatchOp>(Rect(dstLeft, dstTop, dstRight, dstBottom),
- *(mState.currentSnapshot()->transform), getRecordedClip(),
- refPaint(paint), refBitmap(bitmap), refPatch(&patch)));
-}
-
-double RecordingCanvas::drawAnimatedImage(AnimatedImageDrawable*) {
- // Unimplemented
- return 0;
-}
-
-// Text
-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>(Rect(boundsLeft, boundsTop, boundsRight, boundsBottom),
- *(mState.currentSnapshot()->transform), getRecordedClip(),
- refPaint(&paint), glyphs, positions, glyphCount, x, y));
- drawTextDecorations(x, y, totalAdvance, paint);
-}
-
-void RecordingCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
- const SkPaint& paint, const SkPath& path, size_t start,
- size_t end) {
- uint16_t glyphs[1];
- for (size_t i = start; i < end; i++) {
- glyphs[0] = layout.getGlyphId(i);
- float x = hOffset + layout.getX(i);
- float y = vOffset + layout.getY(i);
- if (paint.nothingToDraw()) return;
- const uint16_t* tempGlyphs = refBuffer<glyph_t>(glyphs, 1);
- addOp(alloc().create_trivial<TextOnPathOp>(*(mState.currentSnapshot()->transform),
- getRecordedClip(), refPaint(&paint), tempGlyphs,
- 1, refPath(&path), x, y));
- }
-}
-
-void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkPaint* paint) {
- addOp(alloc().create_trivial<BitmapOp>(Rect(bitmap.width(), bitmap.height()),
- *(mState.currentSnapshot()->transform),
- getRecordedClip(), refPaint(paint), refBitmap(bitmap)));
-}
-
-void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
- auto&& stagingProps = renderNode->stagingProperties();
- RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>(
- Rect(stagingProps.getWidth(), stagingProps.getHeight()),
- *(mState.currentSnapshot()->transform), getRecordedClip(), renderNode);
- int opIndex = addOp(op);
- if (CC_LIKELY(opIndex >= 0)) {
- int childIndex = mDisplayList->addChild(op);
-
- // update the chunk's child indices
- DisplayList::Chunk& chunk = mDisplayList->chunks.back();
- chunk.endChildIndex = childIndex + 1;
-
- if (renderNode->stagingProperties().isProjectionReceiver()) {
- // use staging property, since recording on UI thread
- mDisplayList->projectionReceiveIndex = opIndex;
- }
- }
+ fDL->drawPicture(picture, matrix, paint);
+}
+void RecordingCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* val) {
+ fDL->drawAnnotation(rect, key, val);
}
-void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) {
- // We ref the DeferredLayerUpdater due to its thread-safe ref-counting semantics.
- mDisplayList->ref(layerHandle);
+void RecordingCanvas::onDrawText(const void* text, size_t bytes, SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ fDL->drawText(text, bytes, x, y, paint);
+}
+void RecordingCanvas::onDrawPosText(const void* text, size_t bytes, const SkPoint pos[],
+ const SkPaint& paint) {
+ fDL->drawPosText(text, bytes, pos, paint);
+}
+void RecordingCanvas::onDrawPosTextH(const void* text, size_t bytes, const SkScalar xs[],
+ SkScalar y, const SkPaint& paint) {
+ fDL->drawPosTextH(text, bytes, xs, y, paint);
+}
+void RecordingCanvas::onDrawTextRSXform(const void* text, size_t bytes, const SkRSXform xform[],
+ const SkRect* cull, const SkPaint& paint) {
+ fDL->drawTextRSXform(text, bytes, xform, cull, paint);
+}
+void RecordingCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ fDL->drawTextBlob(blob, x, y, paint);
+}
- LOG_ALWAYS_FATAL_IF(layerHandle->getBackingLayerApi() != Layer::Api::OpenGL);
- // Note that the backing layer has *not* yet been updated, so don't trust
- // its width, height, transform, etc...!
- addOp(alloc().create_trivial<TextureLayerOp>(
- Rect(layerHandle->getWidth(), layerHandle->getHeight()),
- *(mState.currentSnapshot()->transform), getRecordedClip(), layerHandle));
+void RecordingCanvas::onDrawBitmap(const SkBitmap& bm, SkScalar x, SkScalar y,
+ const SkPaint* paint) {
+ fDL->drawImage(SkImage::MakeFromBitmap(bm), x, y, paint, BitmapPalette::Unknown);
+}
+void RecordingCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center, const SkRect& dst,
+ const SkPaint* paint) {
+ fDL->drawImageNine(SkImage::MakeFromBitmap(bm), center, dst, paint);
+}
+void RecordingCanvas::onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst,
+ const SkPaint* paint, SrcRectConstraint constraint) {
+ fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint,
+ BitmapPalette::Unknown);
+}
+void RecordingCanvas::onDrawBitmapLattice(const SkBitmap& bm, const SkCanvas::Lattice& lattice,
+ const SkRect& dst, const SkPaint* paint) {
+ fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint, BitmapPalette::Unknown);
}
-void RecordingCanvas::callDrawGLFunction(Functor* functor, GlFunctorLifecycleListener* listener) {
- mDisplayList->functors.push_back({functor, listener});
- mDisplayList->ref(listener);
- addOp(alloc().create_trivial<FunctorOp>(*(mState.currentSnapshot()->transform),
- getRecordedClip(), functor));
+void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y,
+ const SkPaint* paint, BitmapPalette palette) {
+ fDL->drawImage(image, x, y, paint, palette);
}
-int RecordingCanvas::addOp(RecordedOp* op) {
- // skip op with empty clip
- if (op->localClip && op->localClip->rect.isEmpty()) {
- // NOTE: this rejection happens after op construction/content ref-ing, so content ref'd
- // and held by renderthread isn't affected by clip rejection.
- // Could rewind alloc here if desired, but callers would have to not touch op afterwards.
- return -1;
+void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src,
+ const SkRect& dst, const SkPaint* paint,
+ SrcRectConstraint constraint, BitmapPalette palette) {
+ fDL->drawImageRect(image, &src, dst, paint, constraint, palette);
+}
+
+void RecordingCanvas::drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice,
+ const SkRect& dst, const SkPaint* paint,
+ BitmapPalette palette) {
+ if (!image || dst.isEmpty()) {
+ return;
}
- int insertIndex = mDisplayList->ops.size();
- mDisplayList->ops.push_back(op);
- if (mDeferredBarrierType != DeferredBarrierType::None) {
- // op is first in new chunk
- mDisplayList->chunks.emplace_back();
- DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
- newChunk.beginOpIndex = insertIndex;
- newChunk.endOpIndex = insertIndex + 1;
- newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder);
- newChunk.reorderClip = mDeferredBarrierClip;
+ SkIRect bounds;
+ Lattice latticePlusBounds = lattice;
+ if (!latticePlusBounds.fBounds) {
+ bounds = SkIRect::MakeWH(image->width(), image->height());
+ latticePlusBounds.fBounds = &bounds;
+ }
- int nextChildIndex = mDisplayList->children.size();
- newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
- mDeferredBarrierType = DeferredBarrierType::None;
+ if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
+ fDL->drawImageLattice(image, latticePlusBounds, dst, paint, palette);
} else {
- // standard case - append to existing chunk
- mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
+ fDL->drawImageRect(image, nullptr, dst, paint, SrcRectConstraint::kFast_SrcRectConstraint,
+ palette);
}
- return insertIndex;
}
-void RecordingCanvas::refBitmapsInShader(const SkShader* shader) {
- if (!shader) return;
+void RecordingCanvas::onDrawImage(const SkImage* img, SkScalar x, SkScalar y,
+ const SkPaint* paint) {
+ fDL->drawImage(sk_ref_sp(img), x, y, paint, BitmapPalette::Unknown);
+}
+void RecordingCanvas::onDrawImageNine(const SkImage* img, const SkIRect& center, const SkRect& dst,
+ const SkPaint* paint) {
+ fDL->drawImageNine(sk_ref_sp(img), center, dst, paint);
+}
+void RecordingCanvas::onDrawImageRect(const SkImage* img, const SkRect* src, const SkRect& dst,
+ const SkPaint* paint, SrcRectConstraint constraint) {
+ fDL->drawImageRect(sk_ref_sp(img), src, dst, paint, constraint, BitmapPalette::Unknown);
+}
+void RecordingCanvas::onDrawImageLattice(const SkImage* img, const SkCanvas::Lattice& lattice,
+ const SkRect& dst, const SkPaint* paint) {
+ fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint, BitmapPalette::Unknown);
+}
- // If this paint has an SkShader that has an SkBitmap add
- // it to the bitmap pile
- SkBitmap bitmap;
- SkShader::TileMode xy[2];
- if (shader->isABitmap(&bitmap, nullptr, xy)) {
- Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef());
- refBitmap(*hwuiBitmap);
- return;
- }
- SkShader::ComposeRec rec;
- if (shader->asACompose(&rec)) {
- refBitmapsInShader(rec.fShaderA);
- refBitmapsInShader(rec.fShaderB);
- return;
- }
+void RecordingCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+ const SkPoint texCoords[4], SkBlendMode bmode,
+ const SkPaint& paint) {
+ fDL->drawPatch(cubics, colors, texCoords, bmode, paint);
+}
+void RecordingCanvas::onDrawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ fDL->drawPoints(mode, count, pts, paint);
+}
+void RecordingCanvas::onDrawVerticesObject(const SkVertices* vertices,
+ const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode mode, const SkPaint& paint) {
+ fDL->drawVertices(vertices, bones, boneCount, mode, paint);
+}
+void RecordingCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xforms[],
+ const SkRect texs[], const SkColor colors[], int count,
+ SkBlendMode bmode, const SkRect* cull, const SkPaint* paint) {
+ fDL->drawAtlas(atlas, xforms, texs, colors, count, bmode, cull, paint);
+}
+void RecordingCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
+ fDL->drawShadowRec(path, rec);
+}
+
+void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+ fDL->drawVectorDrawable(tree);
}
}; // namespace uirenderer
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index e663402a80f3..099e0be433ea 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,307 +14,207 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_RECORDING_CANVAS_H
-#define ANDROID_HWUI_RECORDING_CANVAS_H
+#pragma once
-#include "CanvasState.h"
-#include "DisplayList.h"
-#include "ResourceCache.h"
-#include "SkiaCanvasProxy.h"
-#include "Snapshot.h"
+#include "CanvasTransform.h"
#include "hwui/Bitmap.h"
#include "hwui/Canvas.h"
-#include "utils/LinearAllocator.h"
#include "utils/Macros.h"
-
-#include <SkDrawFilter.h>
-#include <SkPaint.h>
-#include <SkTLazy.h>
+#include "utils/TypeLogic.h"
+
+#include "SkCanvas.h"
+#include "SkCanvasVirtualEnforcer.h"
+#include "SkDrawable.h"
+#include "SkNoDrawCanvas.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRect.h"
+#include "SkTDArray.h"
+#include "SkTemplates.h"
#include <vector>
namespace android {
namespace uirenderer {
-struct ClipBase;
-class DeferredLayerUpdater;
-struct RecordedOp;
+enum class DisplayListOpType : uint8_t {
+#define X(T) T,
+#include "DisplayListOps.in"
+#undef X
+};
+
+struct DisplayListOp {
+ const uint8_t type : 8;
+ const uint32_t skip : 24;
+};
+
+static_assert(sizeof(DisplayListOp) == 4);
+
+class RecordingCanvas;
+
+class DisplayListData final {
+public:
+ DisplayListData() : mHasText(false) {}
+ ~DisplayListData();
+
+ void draw(SkCanvas* canvas) const;
+
+ void reset();
+ bool empty() const { return fUsed == 0; }
+
+ void applyColorTransform(ColorTransform transform);
-class ANDROID_API RecordingCanvas : public Canvas, public CanvasStateClient {
- enum class DeferredBarrierType {
- None,
- InOrder,
- OutOfOrder,
- };
+ bool hasText() const { return mHasText; }
+private:
+ friend class RecordingCanvas;
+
+ void flush();
+
+ void save();
+ void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, const SkImage*,
+ const SkMatrix*, SkCanvas::SaveLayerFlags);
+ void restore();
+
+ void concat(const SkMatrix&);
+ void setMatrix(const SkMatrix&);
+ void translate(SkScalar, SkScalar);
+ void translateZ(SkScalar);
+
+ void clipPath(const SkPath&, SkClipOp, bool aa);
+ void clipRect(const SkRect&, SkClipOp, bool aa);
+ void clipRRect(const SkRRect&, SkClipOp, bool aa);
+ void clipRegion(const SkRegion&, SkClipOp);
+
+ void drawPaint(const SkPaint&);
+ void drawPath(const SkPath&, const SkPaint&);
+ void drawRect(const SkRect&, const SkPaint&);
+ void drawRegion(const SkRegion&, const SkPaint&);
+ void drawOval(const SkRect&, const SkPaint&);
+ void drawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&);
+ void drawRRect(const SkRRect&, const SkPaint&);
+ void drawDRRect(const SkRRect&, const SkRRect&, const SkPaint&);
+
+ void drawAnnotation(const SkRect&, const char*, SkData*);
+ void drawDrawable(SkDrawable*, const SkMatrix*);
+ void drawPicture(const SkPicture*, const SkMatrix*, const SkPaint*);
+
+ void drawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&);
+ void drawPosText(const void*, size_t, const SkPoint[], const SkPaint&);
+ void drawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&);
+ void drawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*, const SkPaint&);
+ void drawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&);
+
+ void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkPaint*, BitmapPalette palette);
+ void drawImageNine(sk_sp<const SkImage>, const SkIRect&, const SkRect&, const SkPaint*);
+ void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkPaint*,
+ SkCanvas::SrcRectConstraint, BitmapPalette palette);
+ void drawImageLattice(sk_sp<const SkImage>, const SkCanvas::Lattice&, const SkRect&,
+ const SkPaint*, BitmapPalette);
+
+ void drawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode,
+ const SkPaint&);
+ void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&);
+ void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
+ const SkPaint&);
+ void drawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
+ SkBlendMode, const SkRect*, const SkPaint*);
+ void drawShadowRec(const SkPath&, const SkDrawShadowRec&);
+ void drawVectorDrawable(VectorDrawableRoot* tree);
+
+ template <typename T, typename... Args>
+ void* push(size_t, Args&&...);
+
+ template <typename Fn, typename... Args>
+ void map(const Fn[], Args...) const;
+
+ SkAutoTMalloc<uint8_t> fBytes;
+ size_t fUsed = 0;
+ size_t fReserved = 0;
+
+ bool mHasText : 1;
+};
+
+class RecordingCanvas final : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> {
public:
- RecordingCanvas(size_t width, size_t height);
- virtual ~RecordingCanvas();
-
- virtual void resetRecording(int width, int height, RenderNode* node = nullptr) override;
- virtual WARN_UNUSED_RESULT DisplayList* finishRecording() override;
- // ----------------------------------------------------------------------------
- // MISC HWUI OPERATIONS - TODO: CATEGORIZE
- // ----------------------------------------------------------------------------
- virtual void insertReorderBarrier(bool enableReorder) override;
-
- virtual void drawLayer(DeferredLayerUpdater* layerHandle) override;
- virtual void drawRenderNode(RenderNode* renderNode) override;
- virtual void callDrawGLFunction(Functor* functor,
- GlFunctorLifecycleListener* listener) override;
-
- // ----------------------------------------------------------------------------
- // CanvasStateClient interface
- // ----------------------------------------------------------------------------
- virtual void onViewportInitialized() override;
- virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
- virtual GLuint getTargetFbo() const override { return -1; }
-
- // ----------------------------------------------------------------------------
- // HWUI Canvas draw operations
- // ----------------------------------------------------------------------------
-
- virtual void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
- CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
- CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
- CanvasPropertyPaint* paint) override;
- virtual void drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
- CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) override;
-
- // ----------------------------------------------------------------------------
- // android/graphics/Canvas interface
- // ----------------------------------------------------------------------------
- virtual SkCanvas* asSkCanvas() override;
-
- virtual void setBitmap(const SkBitmap& bitmap) override {
- LOG_ALWAYS_FATAL("RecordingCanvas is not backed by a bitmap.");
- }
-
- virtual bool isOpaque() override { return false; }
- virtual int width() override { return mState.getWidth(); }
- virtual int height() override { return mState.getHeight(); }
-
- // ----------------------------------------------------------------------------
- // android/graphics/Canvas state operations
- // ----------------------------------------------------------------------------
- // Save (layer)
- virtual int getSaveCount() const override { return mState.getSaveCount(); }
- virtual int save(SaveFlags::Flags flags) override;
- virtual void restore() override;
- virtual void restoreToCount(int saveCount) override;
-
- virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
- SaveFlags::Flags flags) override;
- virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
- SaveFlags::Flags flags) override {
- SkPaint paint;
- paint.setAlpha(alpha);
- return saveLayer(left, top, right, bottom, &paint, flags);
- }
-
- // Matrix
- virtual void getMatrix(SkMatrix* outMatrix) const override { mState.getMatrix(outMatrix); }
- virtual void setMatrix(const SkMatrix& matrix) override { mState.setMatrix(matrix); }
-
- virtual void concat(const SkMatrix& matrix) override { mState.concatMatrix(matrix); }
- virtual void rotate(float degrees) override;
- virtual void scale(float sx, float sy) override;
- virtual void skew(float sx, float sy) override;
- virtual void translate(float dx, float dy) override;
-
- // Clip
- virtual bool getClipBounds(SkRect* outRect) const override;
- virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
- virtual bool quickRejectPath(const SkPath& path) const override;
-
- virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override;
- virtual bool clipPath(const SkPath* path, SkClipOp op) override;
-
- // Misc
- virtual SkDrawFilter* getDrawFilter() override { return mDrawFilter.get(); }
- virtual void setDrawFilter(SkDrawFilter* filter) override {
- mDrawFilter.reset(SkSafeRef(filter));
- }
-
- // ----------------------------------------------------------------------------
- // android/graphics/Canvas draw operations
- // ----------------------------------------------------------------------------
- virtual void drawColor(int color, SkBlendMode mode) override;
- virtual void drawPaint(const SkPaint& paint) override;
-
- // Geometry
- virtual void drawPoint(float x, float y, const SkPaint& paint) override {
- float points[2] = {x, y};
- drawPoints(points, 2, paint);
- }
- virtual void drawPoints(const float* points, int floatCount, const SkPaint& paint) override;
- virtual void drawLine(float startX, float startY, float stopX, float stopY,
- const SkPaint& paint) override {
- float points[4] = {startX, startY, stopX, stopY};
- drawLines(points, 4, paint);
- }
- virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) override;
- virtual void drawRect(float left, float top, float right, float bottom,
- const SkPaint& paint) override;
- virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
- virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
- const SkPaint& paint) override;
- virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
- virtual void drawOval(float left, float top, float right, float bottom,
- const SkPaint& paint) override;
- virtual void drawArc(float left, float top, float right, float bottom, float startAngle,
- float sweepAngle, bool useCenter, const SkPaint& paint) override;
- virtual void drawPath(const SkPath& path, const SkPaint& paint) override;
- virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint)
- override { /* RecordingCanvas does not support drawVertices(); ignore */
- }
-
- virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
-
- // Bitmap-based
- virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override;
- virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override;
- virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
- float srcBottom, float dstLeft, float dstTop, float dstRight,
- float dstBottom, const SkPaint* paint) override;
- virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors,
- const SkPaint* paint) override;
- virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft,
- float dstTop, float dstRight, float dstBottom,
- const SkPaint* paint) override;
- virtual double drawAnimatedImage(AnimatedImageDrawable*) override;
-
- // Text
- virtual bool drawTextAbsolutePos() const override { return false; }
-
-protected:
- 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;
+ RecordingCanvas();
+ void reset(DisplayListData*, const SkIRect& bounds);
+
+ sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
+
+ void willSave() override;
+ SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
+ void willRestore() override;
+
+ void onFlush() override;
+
+ void didConcat(const SkMatrix&) override;
+ void didSetMatrix(const SkMatrix&) override;
+ void didTranslate(SkScalar, SkScalar) override;
+
+ void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
+ void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
+ void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
+ void onClipRegion(const SkRegion&, SkClipOp) override;
+
+ void onDrawPaint(const SkPaint&) override;
+ void onDrawPath(const SkPath&, const SkPaint&) override;
+ void onDrawRect(const SkRect&, const SkPaint&) override;
+ void onDrawRegion(const SkRegion&, const SkPaint&) override;
+ void onDrawOval(const SkRect&, const SkPaint&) override;
+ void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&) override;
+ void onDrawRRect(const SkRRect&, const SkPaint&) override;
+ void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
+
+ void onDrawDrawable(SkDrawable*, const SkMatrix*) override;
+ void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override;
+ void onDrawAnnotation(const SkRect&, const char[], SkData*) override;
+
+ void onDrawText(const void*, size_t, SkScalar x, SkScalar y, const SkPaint&) override;
+ void onDrawPosText(const void*, size_t, const SkPoint[], const SkPaint&) override;
+ void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&) override;
+
+ void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
+ const SkPaint&) override;
+ void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override;
+
+ void onDrawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint*) override;
+ void onDrawBitmapLattice(const SkBitmap&, const Lattice&, const SkRect&,
+ const SkPaint*) override;
+ void onDrawBitmapNine(const SkBitmap&, const SkIRect&, const SkRect&, const SkPaint*) override;
+ void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*,
+ SrcRectConstraint) override;
+
+ void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top, const SkPaint* paint,
+ BitmapPalette pallete);
+
+ void drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
+ const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette);
+ void drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice, const SkRect& dst,
+ const SkPaint* paint, BitmapPalette palette);
+
+ void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override;
+ void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override;
+ void onDrawImageNine(const SkImage*, const SkIRect&, const SkRect&, const SkPaint*) override;
+ void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
+ SrcRectConstraint) override;
+
+ void onDrawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode,
+ const SkPaint&) override;
+ void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
+ void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode, const SkPaint&) override;
+ void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
+ SkBlendMode, const SkRect*, const SkPaint*) override;
+ void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
+
+ void drawVectorDrawable(VectorDrawableRoot* tree);
private:
- const ClipBase* getRecordedClip() {
- return mState.writableSnapshot()->mutateClipArea().serializeClip(alloc());
- }
-
- void drawBitmap(Bitmap& bitmap, const SkPaint* paint);
- void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint);
-
- int addOp(RecordedOp* op);
- // ----------------------------------------------------------------------------
- // lazy object copy
- // ----------------------------------------------------------------------------
- LinearAllocator& alloc() { return mDisplayList->allocator; }
-
- void refBitmapsInShader(const SkShader* shader);
-
- template <class T>
- inline const T* refBuffer(const T* srcBuffer, int32_t count) {
- if (!srcBuffer) return nullptr;
-
- T* dstBuffer = (T*)mDisplayList->allocator.alloc<T>(count * sizeof(T));
- memcpy(dstBuffer, srcBuffer, count * sizeof(T));
- return dstBuffer;
- }
-
- inline const SkPath* refPath(const SkPath* path) {
- if (!path) return nullptr;
-
- // The points/verbs within the path are refcounted so this copy operation
- // is inexpensive and maintains the generationID of the original path.
- const SkPath* cachedPath = new SkPath(*path);
- mDisplayList->pathResources.push_back(cachedPath);
- return cachedPath;
- }
-
- /**
- * Returns a RenderThread-safe, const copy of the SkPaint parameter passed in
- * (with deduping based on paint hash / equality check)
- */
- inline const SkPaint* refPaint(const SkPaint* paint) {
- if (!paint) return nullptr;
-
- // If there is a draw filter apply it here and store the modified paint
- // so that we don't need to modify the paint every time we access it.
- SkTLazy<SkPaint> filteredPaint;
- if (mDrawFilter.get()) {
- filteredPaint.set(*paint);
- mDrawFilter->filter(filteredPaint.get(), SkDrawFilter::kPaint_Type);
- paint = filteredPaint.get();
- }
-
- // compute the hash key for the paint and check the cache.
- const uint32_t key = paint->getHash();
- const SkPaint* cachedPaint = mPaintMap.valueFor(key);
- // In the unlikely event that 2 unique paints have the same hash we do a
- // object equality check to ensure we don't erroneously dedup them.
- if (cachedPaint == nullptr || *cachedPaint != *paint) {
- cachedPaint = new SkPaint(*paint);
- mDisplayList->paints.emplace_back(cachedPaint);
- // replaceValueFor() performs an add if the entry doesn't exist
- mPaintMap.replaceValueFor(key, cachedPaint);
- refBitmapsInShader(cachedPaint->getShader());
- }
-
- return cachedPaint;
- }
-
- inline const SkRegion* refRegion(const SkRegion* region) {
- if (!region) {
- return region;
- }
-
- const SkRegion* cachedRegion = mRegionMap.valueFor(region);
- // TODO: Add generation ID to SkRegion
- if (cachedRegion == nullptr) {
- std::unique_ptr<const SkRegion> copy(new SkRegion(*region));
- cachedRegion = copy.get();
- mDisplayList->regions.push_back(std::move(copy));
-
- // replaceValueFor() performs an add if the entry doesn't exist
- mRegionMap.replaceValueFor(region, cachedRegion);
- }
-
- return cachedRegion;
- }
-
- inline Bitmap* refBitmap(Bitmap& bitmap) {
- // Note that this assumes the bitmap is immutable. There are cases this won't handle
- // correctly, such as creating the bitmap from scratch, drawing with it, changing its
- // contents, and drawing again. The only fix would be to always copy it the first time,
- // which doesn't seem worth the extra cycles for this unlikely case.
-
- // this is required because sk_sp's ctor adopts the pointer,
- // but does not increment the refcount,
- bitmap.ref();
- mDisplayList->bitmapResources.emplace_back(&bitmap);
- return &bitmap;
- }
-
- inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
- mDisplayList->patchResources.push_back(patch);
- mResourceCache.incrementRefcount(patch);
- return patch;
- }
-
- DefaultKeyedVector<uint32_t, const SkPaint*> mPaintMap;
- DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
- DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
-
- CanvasState mState;
- std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy;
- ResourceCache& mResourceCache;
- DeferredBarrierType mDeferredBarrierType = DeferredBarrierType::None;
- const ClipBase* mDeferredBarrierClip = nullptr;
- DisplayList* mDisplayList = nullptr;
- sk_sp<SkDrawFilter> mDrawFilter;
-}; // class RecordingCanvas
+ typedef SkCanvasVirtualEnforcer<SkNoDrawCanvas> INHERITED;
-}; // namespace uirenderer
-}; // namespace android
+ DisplayListData* fDL;
+};
-#endif // ANDROID_HWUI_RECORDING_CANVAS_H
+}; // namespace uirenderer
+}; // namespace android \ No newline at end of file
diff --git a/libs/hwui/RenderBuffer.h b/libs/hwui/RenderBuffer.h
deleted file mode 100644
index 191a66a6d741..000000000000
--- a/libs/hwui/RenderBuffer.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_RENDER_BUFFER_H
-#define ANDROID_HWUI_RENDER_BUFFER_H
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Represents an OpenGL render buffer. Render buffers are attached
- * to layers to perform stencil work.
- */
-struct RenderBuffer {
- /**
- * Creates a new render buffer in the specified format and dimensions.
- * The format must be one of the formats allowed by glRenderbufferStorage().
- */
- RenderBuffer(GLenum format, uint32_t width, uint32_t height)
- : mFormat(format), mWidth(width), mHeight(height), mAllocated(false) {
- glGenRenderbuffers(1, &mName);
- }
-
- ~RenderBuffer() {
- if (mName) {
- glDeleteRenderbuffers(1, &mName);
- }
- }
-
- /**
- * Returns the GL name of this render buffer.
- */
- GLuint getName() const { return mName; }
-
- /**
- * Returns the format of this render buffer.
- */
- GLenum getFormat() const { return mFormat; }
-
- /**
- * Binds this render buffer to the current GL context.
- */
- void bind() const { glBindRenderbuffer(GL_RENDERBUFFER, mName); }
-
- /**
- * Indicates whether this render buffer has allocated its
- * storage. See allocate() and resize().
- */
- bool isAllocated() const { return mAllocated; }
-
- /**
- * Allocates this render buffer's storage if needed.
- * This method doesn't do anything if isAllocated() returns true.
- */
- void allocate() {
- if (!mAllocated) {
- glRenderbufferStorage(GL_RENDERBUFFER, mFormat, mWidth, mHeight);
- mAllocated = true;
- }
- }
-
- /**
- * Resizes this render buffer. If the buffer was previously allocated,
- * the storage is re-allocated wit the new specified dimensions. If the
- * buffer wasn't previously allocated, the buffer remains unallocated.
- */
- void resize(uint32_t width, uint32_t height) {
- if (isAllocated() && (width != mWidth || height != mHeight)) {
- glRenderbufferStorage(GL_RENDERBUFFER, mFormat, width, height);
- }
-
- mWidth = width;
- mHeight = height;
- }
-
- /**
- * Returns the width of the render buffer in pixels.
- */
- uint32_t getWidth() const { return mWidth; }
-
- /**
- * Returns the height of the render buffer in pixels.
- */
- uint32_t getHeight() const { return mHeight; }
-
- /**
- * Returns the size of this render buffer in bytes.
- */
- uint32_t getSize() const {
- // Round to the nearest byte
- return (uint32_t)((mWidth * mHeight * formatSize(mFormat)) / 8.0f + 0.5f);
- }
-
- /**
- * Returns the number of bits per component in the specified format.
- * The format must be one of the formats allowed by glRenderbufferStorage().
- */
- static uint32_t formatSize(GLenum format) {
- switch (format) {
- case GL_STENCIL_INDEX8:
- return 8;
- case GL_STENCIL_INDEX1_OES:
- return 1;
- case GL_STENCIL_INDEX4_OES:
- return 4;
- case GL_DEPTH_COMPONENT16:
- case GL_RGBA4:
- case GL_RGB565:
- case GL_RGB5_A1:
- return 16;
- }
- return 0;
- }
-
- /**
- * Indicates whether the specified format represents a stencil buffer.
- */
- static bool isStencilBuffer(GLenum format) {
- switch (format) {
- case GL_STENCIL_INDEX8:
- case GL_STENCIL_INDEX1_OES:
- case GL_STENCIL_INDEX4_OES:
- return true;
- }
- return false;
- }
-
- /**
- * Returns the name of the specified render buffer format.
- */
- static const char* formatName(GLenum format) {
- switch (format) {
- case GL_STENCIL_INDEX8:
- return "STENCIL_8";
- case GL_STENCIL_INDEX1_OES:
- return "STENCIL_1";
- case GL_STENCIL_INDEX4_OES:
- return "STENCIL_4";
- case GL_DEPTH_COMPONENT16:
- return "DEPTH_16";
- case GL_RGBA4:
- return "RGBA_4444";
- case GL_RGB565:
- return "RGB_565";
- case GL_RGB5_A1:
- return "RGBA_5551";
- }
- return "Unknown";
- }
-
-private:
- GLenum mFormat;
-
- uint32_t mWidth;
- uint32_t mHeight;
-
- bool mAllocated;
-
- GLuint mName;
-}; // struct RenderBuffer
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_RENDER_BUFFER_H
diff --git a/libs/hwui/RenderBufferCache.cpp b/libs/hwui/RenderBufferCache.cpp
deleted file mode 100644
index 98010d8da1bd..000000000000
--- a/libs/hwui/RenderBufferCache.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "RenderBufferCache.h"
-#include "Debug.h"
-#include "DeviceInfo.h"
-#include "Properties.h"
-
-#include <utils/Log.h>
-
-#include <cstdlib>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_RENDER_BUFFERS
-#define RENDER_BUFFER_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-#define RENDER_BUFFER_LOGD(...)
-#endif
-
-static uint32_t calculateRboCacheSize() {
- // TODO: Do we need to use extensions().has4BitStencil() here?
- // The tuning guide recommends it, but all real devices are configured
- // with a larger cache than necessary by 4x, so keep the 2x for now regardless
- return DeviceInfo::multiplyByResolution(2);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-RenderBufferCache::RenderBufferCache() : mSize(0), mMaxSize(calculateRboCacheSize()) {}
-
-RenderBufferCache::~RenderBufferCache() {
- clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Size management
-///////////////////////////////////////////////////////////////////////////////
-
-uint32_t RenderBufferCache::getSize() {
- return mSize;
-}
-
-uint32_t RenderBufferCache::getMaxSize() {
- return mMaxSize;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-int RenderBufferCache::RenderBufferEntry::compare(const RenderBufferCache::RenderBufferEntry& lhs,
- const RenderBufferCache::RenderBufferEntry& rhs) {
- int deltaInt = int(lhs.mWidth) - int(rhs.mWidth);
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = int(lhs.mHeight) - int(rhs.mHeight);
- if (deltaInt != 0) return deltaInt;
-
- return int(lhs.mFormat) - int(rhs.mFormat);
-}
-
-void RenderBufferCache::deleteBuffer(RenderBuffer* buffer) {
- if (buffer) {
- RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d)",
- RenderBuffer::formatName(buffer->getFormat()), buffer->getWidth(),
- buffer->getHeight());
-
- mSize -= buffer->getSize();
- delete buffer;
- }
-}
-
-void RenderBufferCache::clear() {
- for (auto entry : mCache) {
- deleteBuffer(entry.mBuffer);
- }
- mCache.clear();
-}
-
-RenderBuffer* RenderBufferCache::get(GLenum format, const uint32_t width, const uint32_t height) {
- RenderBuffer* buffer = nullptr;
-
- RenderBufferEntry entry(format, width, height);
- auto iter = mCache.find(entry);
-
- if (iter != mCache.end()) {
- entry = *iter;
- mCache.erase(iter);
-
- buffer = entry.mBuffer;
- mSize -= buffer->getSize();
-
- RENDER_BUFFER_LOGD("Found %s render buffer (%dx%d)", RenderBuffer::formatName(format),
- width, height);
- } else {
- buffer = new RenderBuffer(format, width, height);
-
- RENDER_BUFFER_LOGD("Created new %s render buffer (%dx%d)", RenderBuffer::formatName(format),
- width, height);
- }
-
- buffer->bind();
- buffer->allocate();
-
- return buffer;
-}
-
-bool RenderBufferCache::put(RenderBuffer* buffer) {
- if (!buffer) return false;
-
- const uint32_t size = buffer->getSize();
- if (size < mMaxSize) {
- while (mSize + size > mMaxSize) {
- RenderBuffer* victim = mCache.begin()->mBuffer;
- deleteBuffer(victim);
- mCache.erase(mCache.begin());
- }
-
- RenderBufferEntry entry(buffer);
-
- mCache.insert(entry);
- mSize += size;
-
- RENDER_BUFFER_LOGD("Added %s render buffer (%dx%d)",
- RenderBuffer::formatName(buffer->getFormat()), buffer->getWidth(),
- buffer->getHeight());
-
- return true;
- } else {
- RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d) Size=%d, MaxSize=%d",
- RenderBuffer::formatName(buffer->getFormat()), buffer->getWidth(),
- buffer->getHeight(), size, mMaxSize);
- delete buffer;
- }
- return false;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/RenderBufferCache.h b/libs/hwui/RenderBufferCache.h
deleted file mode 100644
index c936a5283965..000000000000
--- a/libs/hwui/RenderBufferCache.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_RENDER_BUFFER_CACHE_H
-#define ANDROID_HWUI_RENDER_BUFFER_CACHE_H
-
-#include <GLES2/gl2.h>
-
-#include "RenderBuffer.h"
-
-#include <set>
-
-namespace android {
-namespace uirenderer {
-
-class RenderBufferCache {
-public:
- RenderBufferCache();
- ~RenderBufferCache();
-
- /**
- * Returns a buffer with the exact specified dimensions. If no suitable
- * buffer can be found, a new one is created and returned. If creating a
- * new buffer fails, NULL is returned.
- *
- * When a buffer is obtained from the cache, it is removed and the total
- * size of the cache goes down.
- *
- * The returned buffer is always allocated and bound
- * (see RenderBuffer::isAllocated()).
- *
- * @param format The desired render buffer format
- * @param width The desired width of the buffer
- * @param height The desired height of the buffer
- */
- RenderBuffer* get(GLenum format, const uint32_t width, const uint32_t height);
-
- /**
- * Adds the buffer to the cache. The buffer will not be added if there is
- * not enough space available. Adding a buffer can cause other buffer to
- * be removed from the cache.
- *
- * @param buffer The render buffer to add to the cache
- *
- * @return True if the buffer was added, false otherwise.
- */
- bool put(RenderBuffer* buffer);
- /**
- * Clears the cache. This causes all layers to be deleted.
- */
- void clear();
-
- /**
- * Returns the maximum size of the cache in bytes.
- */
- uint32_t getMaxSize();
- /**
- * Returns the current size of the cache in bytes.
- */
- uint32_t getSize();
-
-private:
- struct RenderBufferEntry {
- RenderBufferEntry() : mBuffer(nullptr), mWidth(0), mHeight(0) {}
-
- RenderBufferEntry(GLenum format, const uint32_t width, const uint32_t height)
- : mBuffer(nullptr), mFormat(format), mWidth(width), mHeight(height) {}
-
- explicit RenderBufferEntry(RenderBuffer* buffer)
- : mBuffer(buffer)
- , mFormat(buffer->getFormat())
- , mWidth(buffer->getWidth())
- , mHeight(buffer->getHeight()) {}
-
- static int compare(const RenderBufferEntry& lhs, const RenderBufferEntry& rhs);
-
- bool operator==(const RenderBufferEntry& other) const { return compare(*this, other) == 0; }
-
- bool operator!=(const RenderBufferEntry& other) const { return compare(*this, other) != 0; }
-
- bool operator<(const RenderBufferEntry& other) const {
- return RenderBufferEntry::compare(*this, other) < 0;
- }
-
- RenderBuffer* mBuffer;
- GLenum mFormat;
- uint32_t mWidth;
- uint32_t mHeight;
- }; // struct RenderBufferEntry
-
- void deleteBuffer(RenderBuffer* buffer);
-
- std::multiset<RenderBufferEntry> mCache;
-
- uint32_t mSize;
- uint32_t mMaxSize;
-}; // class RenderBufferCache
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_RENDER_BUFFER_CACHE_H
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d93a7578cfd7..d2a8f02cc6a7 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -16,10 +16,8 @@
#include "RenderNode.h"
-#include "BakedOpRenderer.h"
#include "DamageAccumulator.h"
#include "Debug.h"
-#include "RecordedOp.h"
#include "TreeInfo.h"
#include "VectorDrawable.h"
#include "renderstate/RenderState.h"
@@ -29,9 +27,6 @@
#include "utils/StringUtils.h"
#include "utils/TraceUtils.h"
-#include "protos/ProtoHelpers.h"
-#include "protos/hwui.pb.h"
-
#include <SkPathOps.h>
#include <algorithm>
#include <sstream>
@@ -101,77 +96,6 @@ void RenderNode::output(std::ostream& output, uint32_t level) {
output << std::endl;
}
-void RenderNode::copyTo(proto::RenderNode* pnode) {
- pnode->set_id(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this)));
- pnode->set_name(mName.string(), mName.length());
-
- proto::RenderProperties* pprops = pnode->mutable_properties();
- pprops->set_left(properties().getLeft());
- pprops->set_top(properties().getTop());
- pprops->set_right(properties().getRight());
- pprops->set_bottom(properties().getBottom());
- pprops->set_clip_flags(properties().getClippingFlags());
- pprops->set_alpha(properties().getAlpha());
- pprops->set_translation_x(properties().getTranslationX());
- pprops->set_translation_y(properties().getTranslationY());
- pprops->set_translation_z(properties().getTranslationZ());
- pprops->set_elevation(properties().getElevation());
- pprops->set_rotation(properties().getRotation());
- pprops->set_rotation_x(properties().getRotationX());
- pprops->set_rotation_y(properties().getRotationY());
- pprops->set_scale_x(properties().getScaleX());
- pprops->set_scale_y(properties().getScaleY());
- pprops->set_pivot_x(properties().getPivotX());
- pprops->set_pivot_y(properties().getPivotY());
- pprops->set_has_overlapping_rendering(properties().getHasOverlappingRendering());
- pprops->set_pivot_explicitly_set(properties().isPivotExplicitlySet());
- pprops->set_project_backwards(properties().getProjectBackwards());
- pprops->set_projection_receiver(properties().isProjectionReceiver());
- set(pprops->mutable_clip_bounds(), properties().getClipBounds());
-
- const Outline& outline = properties().getOutline();
- if (outline.getType() != Outline::Type::None) {
- proto::Outline* poutline = pprops->mutable_outline();
- poutline->clear_path();
- if (outline.getType() == Outline::Type::Empty) {
- poutline->set_type(proto::Outline_Type_Empty);
- } else if (outline.getType() == Outline::Type::ConvexPath) {
- poutline->set_type(proto::Outline_Type_ConvexPath);
- if (const SkPath* path = outline.getPath()) {
- set(poutline->mutable_path(), *path);
- }
- } else if (outline.getType() == Outline::Type::RoundRect) {
- poutline->set_type(proto::Outline_Type_RoundRect);
- } else {
- ALOGW("Uknown outline type! %d", static_cast<int>(outline.getType()));
- poutline->set_type(proto::Outline_Type_None);
- }
- poutline->set_should_clip(outline.getShouldClip());
- poutline->set_alpha(outline.getAlpha());
- poutline->set_radius(outline.getRadius());
- set(poutline->mutable_bounds(), outline.getBounds());
- } else {
- pprops->clear_outline();
- }
-
- const RevealClip& revealClip = properties().getRevealClip();
- if (revealClip.willClip()) {
- proto::RevealClip* prevealClip = pprops->mutable_reveal_clip();
- prevealClip->set_x(revealClip.getX());
- prevealClip->set_y(revealClip.getY());
- prevealClip->set_radius(revealClip.getRadius());
- } else {
- pprops->clear_reveal_clip();
- }
-
- pnode->clear_children();
- if (mDisplayList) {
- for (auto&& child : mDisplayList->getChildren()) {
- child->renderNode->copyTo(pnode->add_children());
- }
- }
-}
-
int RenderNode::getDebugSize() {
int size = sizeof(RenderNode);
if (mStagingDisplayList) {
@@ -188,11 +112,9 @@ void RenderNode::prepareTree(TreeInfo& info) {
LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
MarkAndSweepRemoved observer(&info);
- // The OpenGL renderer reserves the stencil buffer for overdraw debugging. Functors
- // will need to be drawn in a layer.
- bool functorsNeedLayer = Properties::debugOverdraw && !Properties::isSkiaEnabled();
-
- prepareTreeImpl(observer, info, functorsNeedLayer);
+ const int before = info.disableForceDark;
+ prepareTreeImpl(observer, info, false);
+ LOG_ALWAYS_FATAL_IF(before != info.disableForceDark, "Mis-matched force dark");
}
void RenderNode::addAnimator(const sp<BaseRenderNodeAnimator>& animator) {
@@ -238,7 +160,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0) ||
CC_UNLIKELY(!properties().fitsOnLayer())) {
if (CC_UNLIKELY(hasLayer())) {
- renderthread::CanvasContext::destroyLayer(this);
+ this->setLayerSurface(nullptr);
}
return;
}
@@ -275,6 +197,11 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu
if (info.mode == TreeInfo::MODE_FULL) {
pushStagingPropertiesChanges(info);
}
+
+ if (!mProperties.getAllowForceDark()) {
+ info.disableForceDark++;
+ }
+
uint32_t animatorDirtyMask = 0;
if (CC_LIKELY(info.runAnimations)) {
animatorDirtyMask = mAnimatorManager.animate(info);
@@ -312,6 +239,9 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu
}
pushLayerUpdate(info);
+ if (!mProperties.getAllowForceDark()) {
+ info.disableForceDark--;
+ }
info.damageAccumulator->popTransform();
}
@@ -352,7 +282,45 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {
mStagingDisplayList = nullptr;
if (mDisplayList) {
mDisplayList->syncContents();
+ handleForceDark(info);
+ }
+}
+
+void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
+ if (CC_LIKELY(!info || info->disableForceDark)) {
+ return;
+ }
+ auto usage = usageHint();
+ const auto& children = mDisplayList->mChildNodes;
+ if (mDisplayList->hasText()) {
+ usage = UsageHint::Foreground;
+ }
+ if (usage == UsageHint::Unknown) {
+ if (children.size() > 1) {
+ usage = UsageHint::Background;
+ } else if (children.size() == 1 &&
+ children.front().getRenderNode()->usageHint() !=
+ UsageHint::Background) {
+ usage = UsageHint::Background;
+ }
+ }
+ if (children.size() > 1) {
+ // Crude overlap check
+ SkRect drawn = SkRect::MakeEmpty();
+ for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
+ const auto& child = iter->getRenderNode();
+ // We use stagingProperties here because we haven't yet sync'd the children
+ SkRect bounds = SkRect::MakeXYWH(child->stagingProperties().getX(), child->stagingProperties().getY(),
+ child->stagingProperties().getWidth(), child->stagingProperties().getHeight());
+ if (bounds.contains(drawn)) {
+ // This contains everything drawn after it, so make it a background
+ child->setUsageHint(UsageHint::Background);
+ }
+ drawn.join(bounds);
+ }
}
+ mDisplayList->mDisplayList.applyColorTransform(
+ usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light);
}
void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) {
@@ -379,7 +347,7 @@ void RenderNode::deleteDisplayList(TreeObserver& observer, TreeInfo* info) {
void RenderNode::destroyHardwareResources(TreeInfo* info) {
if (hasLayer()) {
- renderthread::CanvasContext::destroyLayer(this);
+ this->setLayerSurface(nullptr);
}
setStagingDisplayList(nullptr);
@@ -389,7 +357,7 @@ void RenderNode::destroyHardwareResources(TreeInfo* info) {
void RenderNode::destroyLayers() {
if (hasLayer()) {
- renderthread::CanvasContext::destroyLayer(this);
+ this->setLayerSurface(nullptr);
}
if (mDisplayList) {
mDisplayList->updateChildren([](RenderNode* child) { child->destroyLayers(); });
@@ -459,78 +427,6 @@ void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform)
}
}
-/**
- * Organizes the DisplayList hierarchy to prepare for background projection reordering.
- *
- * This should be called before a call to defer() or drawDisplayList()
- *
- * Each DisplayList that serves as a 3d root builds its list of composited children,
- * which are flagged to not draw in the standard draw loop.
- */
-void RenderNode::computeOrdering() {
- ATRACE_CALL();
- mProjectedNodes.clear();
-
- // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
- // transform properties are applied correctly to top level children
- if (mDisplayList == nullptr) return;
- for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
- RenderNodeOp* childOp = mDisplayList->getChildren()[i];
- childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity());
- }
-}
-
-void RenderNode::computeOrderingImpl(
- RenderNodeOp* opState, std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
- const mat4* transformFromProjectionSurface) {
- mProjectedNodes.clear();
- if (mDisplayList == nullptr || mDisplayList->isEmpty()) return;
-
- // TODO: should avoid this calculation in most cases
- // TODO: just calculate single matrix, down to all leaf composited elements
- Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
- localTransformFromProjectionSurface.multiply(opState->localMatrix);
-
- if (properties().getProjectBackwards()) {
- // composited projectee, flag for out of order draw, save matrix, and store in proj surface
- opState->skipInOrderDraw = true;
- opState->transformFromCompositingAncestor = localTransformFromProjectionSurface;
- compositedChildrenOfProjectionSurface->push_back(opState);
- } else {
- // standard in order draw
- opState->skipInOrderDraw = false;
- }
-
- if (mDisplayList->getChildren().size() > 0) {
- const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0;
- bool haveAppliedPropertiesToProjection = false;
- for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
- RenderNodeOp* childOp = mDisplayList->getChildren()[i];
- RenderNode* child = childOp->renderNode;
-
- std::vector<RenderNodeOp*>* projectionChildren = nullptr;
- const mat4* projectionTransform = nullptr;
- if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
- // if receiving projections, collect projecting descendant
-
- // Note that if a direct descendant is projecting backwards, we pass its
- // grandparent projection collection, since it shouldn't project onto its
- // parent, where it will already be drawing.
- projectionChildren = &mProjectedNodes;
- projectionTransform = &mat4::identity();
- } else {
- if (!haveAppliedPropertiesToProjection) {
- applyViewPropertyTransforms(localTransformFromProjectionSurface);
- haveAppliedPropertiesToProjection = true;
- }
- projectionChildren = compositedChildrenOfProjectionSurface;
- projectionTransform = &localTransformFromProjectionSurface;
- }
- child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
- }
- }
-}
-
const SkPath* RenderNode::getClippedOutline(const SkRect& clipRect) const {
const SkPath* outlinePath = properties().getOutline().getPath();
const uint32_t outlineID = outlinePath->getGenerationID();
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 1469a156e2d8..be0b46b1c45f 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -28,6 +28,7 @@
#include <androidfw/ResourceTypes.h>
#include "AnimatorManager.h"
+#include "CanvasTransform.h"
#include "Debug.h"
#include "DisplayList.h"
#include "Matrix.h"
@@ -48,9 +49,6 @@ namespace android {
namespace uirenderer {
class CanvasState;
-class DisplayListOp;
-class FrameBuilder;
-class OffscreenBuffer;
class Rect;
class SkiaShader;
struct RenderNodeOp;
@@ -76,7 +74,6 @@ class RenderNode;
*/
class RenderNode : public VirtualLightRefBase {
friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties
- friend class FrameBuilder;
public:
enum DirtyPropertyMask {
@@ -104,16 +101,13 @@ public:
ANDROID_API void setStagingDisplayList(DisplayList* newData);
- void computeOrdering();
-
ANDROID_API void output();
ANDROID_API int getDebugSize();
- void copyTo(proto::RenderNode* node);
bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); }
bool hasProjectionReceiver() const {
- return mDisplayList && mDisplayList->projectionReceiveIndex >= 0;
+ return mDisplayList && mDisplayList->containsProjectionReceiver();
}
const char* getName() const { return mName.string(); }
@@ -178,9 +172,6 @@ public:
}
const DisplayList* getDisplayList() const { return mDisplayList; }
- OffscreenBuffer* getLayer() const { return mLayer; }
- OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh...
- void setLayer(OffscreenBuffer* layer) { mLayer = layer; }
// Note: The position callbacks are relying on the listener using
// the frameNumber to appropriately batch/synchronize these transactions.
@@ -218,6 +209,10 @@ public:
void output(std::ostream& output, uint32_t level);
+ void setUsageHint(UsageHint usageHint) { mUsageHint = usageHint; }
+
+ UsageHint usageHint() const { return mUsageHint; }
+
private:
void computeOrderingImpl(RenderNodeOp* opState,
std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
@@ -225,6 +220,7 @@ private:
void syncProperties();
void syncDisplayList(TreeObserver& observer, TreeInfo* info);
+ void handleForceDark(TreeInfo* info);
void prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer);
void pushStagingPropertiesChanges(TreeInfo& info);
@@ -256,10 +252,6 @@ private:
friend class AnimatorManager;
AnimatorManager mAnimatorManager;
- // Owned by RT. Lifecycle is managed by prepareTree(), with the exception
- // being in ~RenderNode() which may happen on any thread.
- OffscreenBuffer* mLayer = nullptr;
-
/**
* Draw time state - these properties are only set and used during rendering
*/
@@ -277,6 +269,8 @@ private:
sp<PositionListener> mPositionListener;
+ UsageHint mUsageHint = UsageHint::Unknown;
+
// METHODS & FIELDS ONLY USED BY THE SKIA RENDERER
public:
/**
@@ -298,7 +292,7 @@ public:
* Returns true if an offscreen layer from any renderPipeline is attached
* to this node.
*/
- bool hasLayer() const { return mLayer || mSkiaLayer.get(); }
+ bool hasLayer() const { return mSkiaLayer.get(); }
/**
* Used by the RenderPipeline to attach an offscreen surface to the RenderNode.
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index e49574462e9e..ff9cf45cdc73 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -44,8 +44,8 @@ void LayerProperties::reset() {
}
bool LayerProperties::setColorFilter(SkColorFilter* filter) {
- if (mColorFilter == filter) return false;
- SkRefCnt_SafeAssign(mColorFilter, filter);
+ if (mColorFilter.get() == filter) return false;
+ mColorFilter = sk_ref_sp(filter);
return true;
}
@@ -62,7 +62,7 @@ LayerProperties& LayerProperties::operator=(const LayerProperties& other) {
setOpaque(other.opaque());
setAlpha(other.alpha());
setXferMode(other.xferMode());
- setColorFilter(other.colorFilter());
+ setColorFilter(other.getColorFilter());
return *this;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 6470d4c16130..04379ae68a0d 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -16,7 +16,6 @@
#pragma once
-#include "Caches.h"
#include "DeviceInfo.h"
#include "Outline.h"
#include "Rect.h"
@@ -89,9 +88,7 @@ public:
SkBlendMode xferMode() const { return mMode; }
- bool setColorFilter(SkColorFilter* filter);
-
- SkColorFilter* colorFilter() const { return mColorFilter; }
+ SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
// Sets alpha, xfermode, and colorfilter from an SkPaint
// paint may be NULL, in which case defaults will be set
@@ -105,6 +102,7 @@ private:
LayerProperties();
~LayerProperties();
void reset();
+ bool setColorFilter(SkColorFilter* filter);
// Private since external users should go through properties().effectiveLayerType()
LayerType type() const { return mType; }
@@ -116,7 +114,7 @@ private:
bool mOpaque;
uint8_t mAlpha;
SkBlendMode mMode;
- SkColorFilter* mColorFilter = nullptr;
+ sk_sp<SkColorFilter> mColorFilter;
};
/*
@@ -328,9 +326,7 @@ public:
bool isPivotExplicitlySet() const { return mPrimitiveFields.mPivotExplicitlySet; }
- bool resetPivot() {
- return RP_SET_AND_DIRTY(mPrimitiveFields.mPivotExplicitlySet, false);
- }
+ bool resetPivot() { return RP_SET_AND_DIRTY(mPrimitiveFields.mPivotExplicitlySet, false); }
bool setCameraDistance(float distance) {
if (distance != getCameraDistance()) {
@@ -510,17 +506,13 @@ public:
getOutline().getAlpha() != 0.0f;
}
- SkColor getSpotShadowColor() const {
- return mPrimitiveFields.mSpotShadowColor;
- }
+ SkColor getSpotShadowColor() const { return mPrimitiveFields.mSpotShadowColor; }
bool setSpotShadowColor(SkColor shadowColor) {
return RP_SET(mPrimitiveFields.mSpotShadowColor, shadowColor);
}
- SkColor getAmbientShadowColor() const {
- return mPrimitiveFields.mAmbientShadowColor;
- }
+ SkColor getAmbientShadowColor() const { return mPrimitiveFields.mAmbientShadowColor; }
bool setAmbientShadowColor(SkColor shadowColor) {
return RP_SET(mPrimitiveFields.mAmbientShadowColor, shadowColor);
@@ -543,6 +535,14 @@ public:
return CC_UNLIKELY(promotedToLayer()) ? LayerType::RenderLayer : mLayerProperties.mType;
}
+ bool setAllowForceDark(bool allow) {
+ return RP_SET(mPrimitiveFields.mAllowForceDark, allow);
+ }
+
+ bool getAllowForceDark() const {
+ return mPrimitiveFields.mAllowForceDark;
+ }
+
private:
// Rendering properties
struct PrimitiveFields {
@@ -562,6 +562,7 @@ private:
bool mMatrixOrPivotDirty = false;
bool mProjectBackwards = false;
bool mProjectionReceiver = false;
+ bool mAllowForceDark = true;
Rect mClipBounds;
Outline mOutline;
RevealClip mRevealClip;
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
deleted file mode 100644
index d60b99469368..000000000000
--- a/libs/hwui/ResourceCache.cpp
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ResourceCache.h"
-#include "Caches.h"
-
-namespace android {
-
-using namespace uirenderer;
-ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache);
-
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Resource cache
-///////////////////////////////////////////////////////////////////////////////
-
-void ResourceCache::logCache() {
- ALOGD("ResourceCache: cacheReport:");
- for (size_t i = 0; i < mCache->size(); ++i) {
- ResourceReference* ref = mCache->valueAt(i);
- ALOGD(" ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p", i, mCache->keyAt(i),
- mCache->valueAt(i));
- ALOGD(" ResourceCache: mCache(%zu): refCount, destroyed, type = %d, %d, %d", i,
- ref->refCount, ref->destroyed, ref->resourceType);
- }
-}
-
-ResourceCache::ResourceCache() {
- Mutex::Autolock _l(mLock);
- mCache = new KeyedVector<const void*, ResourceReference*>();
-}
-
-ResourceCache::~ResourceCache() {
- Mutex::Autolock _l(mLock);
- delete mCache;
-}
-
-void ResourceCache::lock() {
- mLock.lock();
-}
-
-void ResourceCache::unlock() {
- mLock.unlock();
-}
-
-void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
- Mutex::Autolock _l(mLock);
- incrementRefcountLocked(resource, resourceType);
-}
-
-void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
- incrementRefcount((void*)patchResource, kNinePatch);
-}
-
-void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) {
- ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
- if (ref == nullptr || mCache->size() == 0) {
- ref = new ResourceReference(resourceType);
- mCache->add(resource, ref);
- }
- ref->refCount++;
-}
-
-void ResourceCache::decrementRefcount(void* resource) {
- Mutex::Autolock _l(mLock);
- decrementRefcountLocked(resource);
-}
-
-void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
- decrementRefcount((void*)patchResource);
-}
-
-void ResourceCache::decrementRefcountLocked(void* resource) {
- ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
- if (ref == nullptr) {
- // Should not get here - shouldn't get a call to decrement if we're not yet tracking it
- return;
- }
- ref->refCount--;
- if (ref->refCount == 0) {
- deleteResourceReferenceLocked(resource, ref);
- }
-}
-
-void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
- decrementRefcountLocked((void*)patchResource);
-}
-
-void ResourceCache::destructor(Res_png_9patch* resource) {
- Mutex::Autolock _l(mLock);
- destructorLocked(resource);
-}
-
-void ResourceCache::destructorLocked(Res_png_9patch* resource) {
- ssize_t index = mCache->indexOfKey(resource);
- ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
- if (ref == nullptr) {
- // If we're not tracking this resource, just delete it
- if (Caches::hasInstance()) {
- Caches::getInstance().patchCache.removeDeferred(resource);
- } else {
- // A Res_png_9patch is actually an array of byte that's larger
- // than sizeof(Res_png_9patch). It must be freed as an array.
- delete[](int8_t*) resource;
- }
- return;
- }
- ref->destroyed = true;
- if (ref->refCount == 0) {
- deleteResourceReferenceLocked(resource, ref);
- }
-}
-
-/**
- * This method should only be called while the mLock mutex is held (that mutex is grabbed
- * by the various destructor() and recycle() methods which call this method).
- */
-void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) {
- if (ref->destroyed) {
- switch (ref->resourceType) {
- case kNinePatch: {
- if (Caches::hasInstance()) {
- Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*)resource);
- } else {
- // A Res_png_9patch is actually an array of byte that's larger
- // than sizeof(Res_png_9patch). It must be freed as an array.
- int8_t* patch = (int8_t*)resource;
- delete[] patch;
- }
- } break;
- }
- }
- mCache->removeItem(resource);
- delete ref;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
deleted file mode 100644
index fd3f9fd05d58..000000000000
--- a/libs/hwui/ResourceCache.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_RESOURCE_CACHE_H
-#define ANDROID_HWUI_RESOURCE_CACHE_H
-
-#include <cutils/compiler.h>
-
-#include <SkBitmap.h>
-#include <SkPixelRef.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/Singleton.h>
-
-#include <androidfw/ResourceTypes.h>
-
-namespace android {
-namespace uirenderer {
-
-class Layer;
-
-/**
- * Type of Resource being cached
- */
-enum ResourceType {
- kNinePatch,
-};
-
-class ResourceReference {
-public:
- explicit ResourceReference(ResourceType type) {
- refCount = 0;
- destroyed = false;
- resourceType = type;
- }
-
- int refCount;
- bool destroyed;
- ResourceType resourceType;
-};
-
-class ANDROID_API ResourceCache : public Singleton<ResourceCache> {
- ResourceCache();
- ~ResourceCache();
-
- friend class Singleton<ResourceCache>;
-
-public:
- /**
- * When using these two methods, make sure to only invoke the *Locked()
- * variants of increment/decrementRefcount(), recyle() and destructor()
- */
- void lock();
- void unlock();
-
- void incrementRefcount(const Res_png_9patch* resource);
-
- void decrementRefcount(const Res_png_9patch* resource);
-
- void decrementRefcountLocked(const Res_png_9patch* resource);
-
- void destructor(Res_png_9patch* resource);
-
- void destructorLocked(Res_png_9patch* resource);
-
-private:
- void deleteResourceReferenceLocked(const void* resource, ResourceReference* ref);
-
- void incrementRefcount(void* resource, ResourceType resourceType);
- void incrementRefcountLocked(void* resource, ResourceType resourceType);
-
- void decrementRefcount(void* resource);
- void decrementRefcountLocked(void* resource);
-
- void logCache();
-
- /**
- * Used to increment, decrement, and destroy. Incrementing is generally accessed on the UI
- * thread, but destroying resources may be called from the GC thread, the finalizer thread,
- * or a reference queue finalization thread.
- */
- mutable Mutex mLock;
-
- KeyedVector<const void*, ResourceReference*>* mCache;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_RESOURCE_CACHE_H
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
deleted file mode 100644
index d0155ee473f7..000000000000
--- a/libs/hwui/ShadowTessellator.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <math.h>
-#include <utils/Log.h>
-#include <utils/MathUtils.h>
-#include <utils/Trace.h>
-
-#include "AmbientShadow.h"
-#include "Properties.h"
-#include "ShadowTessellator.h"
-#include "SpotShadow.h"
-#include "Vector.h"
-
-namespace android {
-namespace uirenderer {
-
-void ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque, const Vector3* casterPolygon,
- int casterVertexCount, const Vector3& centroid3d,
- const Rect& casterBounds, const Rect& localClip,
- float maxZ, VertexBuffer& shadowVertexBuffer) {
- ATRACE_CALL();
-
- // A bunch of parameters to tweak the shadow.
- // TODO: Allow some of these changable by debug settings or APIs.
- float heightFactor = 1.0f / 128;
- const float geomFactor = 64;
-
- if (CC_UNLIKELY(Properties::overrideAmbientRatio > 0.0f)) {
- heightFactor *= Properties::overrideAmbientRatio;
- }
-
- Rect ambientShadowBounds(casterBounds);
- ambientShadowBounds.outset(maxZ * geomFactor * heightFactor);
-
- if (!localClip.intersects(ambientShadowBounds)) {
-#if DEBUG_SHADOW
- ALOGD("Ambient shadow is out of clip rect!");
-#endif
- return;
- }
-
- AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
- heightFactor, geomFactor, shadowVertexBuffer);
-}
-
-void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, const Vector3* casterPolygon,
- int casterVertexCount, const Vector3& casterCentroid,
- const mat4& receiverTransform,
- const Vector3& lightCenter, int lightRadius,
- const Rect& casterBounds, const Rect& localClip,
- VertexBuffer& shadowVertexBuffer) {
- ATRACE_CALL();
-
- Vector3 adjustedLightCenter(lightCenter);
- if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) {
- adjustedLightCenter.y = -Properties::overrideLightPosY; // negated since this shifts up
- }
- if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) {
- adjustedLightCenter.z = Properties::overrideLightPosZ;
- }
-
-#if DEBUG_SHADOW
- ALOGD("light center %f %f %f %d", adjustedLightCenter.x, adjustedLightCenter.y,
- adjustedLightCenter.z, lightRadius);
-#endif
- if (isnan(adjustedLightCenter.x) || isnan(adjustedLightCenter.y) ||
- isnan(adjustedLightCenter.z)) {
- return;
- }
-
- // light position (because it's in local space) needs to compensate for receiver transform
- // TODO: should apply to light orientation, not just position
- Matrix4 reverseReceiverTransform;
- reverseReceiverTransform.loadInverse(receiverTransform);
- reverseReceiverTransform.mapPoint3d(adjustedLightCenter);
-
- if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) {
- lightRadius = Properties::overrideLightRadius;
- }
-
- // Now light and caster are both in local space, we will check whether
- // the shadow is within the clip area.
- Rect lightRect = Rect(adjustedLightCenter.x - lightRadius, adjustedLightCenter.y - lightRadius,
- adjustedLightCenter.x + lightRadius, adjustedLightCenter.y + lightRadius);
- lightRect.unionWith(localClip);
- if (!lightRect.intersects(casterBounds)) {
-#if DEBUG_SHADOW
- ALOGD("Spot shadow is out of clip rect!");
-#endif
- return;
- }
-
- SpotShadow::createSpotShadow(isCasterOpaque, adjustedLightCenter, lightRadius, casterPolygon,
- casterVertexCount, casterCentroid, shadowVertexBuffer);
-
-#if DEBUG_SHADOW
- if (shadowVertexBuffer.getVertexCount() <= 0) {
- ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount());
- }
-#endif
-}
-
-/**
- * Calculate the centroid of a 2d polygon.
- *
- * @param poly The polygon, which is represented in a Vector2 array.
- * @param polyLength The length of the polygon in terms of number of vertices.
- * @return the centroid of the polygon.
- */
-Vector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) {
- double sumx = 0;
- double sumy = 0;
- int p1 = polyLength - 1;
- double area = 0;
- for (int p2 = 0; p2 < polyLength; p2++) {
- double x1 = poly[p1].x;
- double y1 = poly[p1].y;
- double x2 = poly[p2].x;
- double y2 = poly[p2].y;
- double a = (x1 * y2 - x2 * y1);
- sumx += (x1 + x2) * a;
- sumy += (y1 + y2) * a;
- area += a;
- p1 = p2;
- }
-
- Vector2 centroid = poly[0];
- if (area != 0) {
- centroid = (Vector2){static_cast<float>(sumx / (3 * area)),
- static_cast<float>(sumy / (3 * area))};
- } else {
- ALOGW("Area is 0 while computing centroid!");
- }
- return centroid;
-}
-
-// Make sure p1 -> p2 is going CW around the poly.
-Vector2 ShadowTessellator::calculateNormal(const Vector2& p1, const Vector2& p2) {
- Vector2 result = p2 - p1;
- if (result.x != 0 || result.y != 0) {
- result.normalize();
- // Calculate the normal , which is CCW 90 rotate to the delta.
- float tempy = result.y;
- result.y = result.x;
- result.x = -tempy;
- }
- return result;
-}
-
-int ShadowTessellator::getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2,
- float divisor) {
- // When there is no distance difference, there is no need for extra vertices.
- if (vector1.lengthSquared() == 0 || vector2.lengthSquared() == 0) {
- return 0;
- }
- // The formula is :
- // extraNumber = floor(acos(dot(n1, n2)) / (M_PI / EXTRA_VERTEX_PER_PI))
- // The value ranges for each step are:
- // dot( ) --- [-1, 1]
- // acos( ) --- [0, M_PI]
- // floor(...) --- [0, EXTRA_VERTEX_PER_PI]
- float dotProduct = vector1.dot(vector2);
- // make sure that dotProduct value is in acsof input range [-1, 1]
- dotProduct = MathUtils::clamp(dotProduct, -1.0f, 1.0f);
- // TODO: Use look up table for the dotProduct to extraVerticesNumber
- // computation, if needed.
- float angle = acosf(dotProduct);
- return (int)floor(angle / divisor);
-}
-
-void ShadowTessellator::checkOverflow(int used, int total, const char* bufferName) {
- LOG_ALWAYS_FATAL_IF(used > total, "Error: %s overflow!!! used %d, total %d", bufferName, used,
- total);
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
deleted file mode 100644
index 79f46f9e9a06..000000000000
--- a/libs/hwui/ShadowTessellator.h
+++ /dev/null
@@ -1,94 +0,0 @@
-
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_SHADOW_TESSELLATOR_H
-#define ANDROID_HWUI_SHADOW_TESSELLATOR_H
-
-#include <SkPath.h>
-
-#include "Debug.h"
-#include "Matrix.h"
-
-namespace android {
-namespace uirenderer {
-
-class VertexBuffer;
-
-// All SHADOW_* are used to define all the geometry property of shadows.
-// Use a simplified example to illustrate the geometry setup here.
-// Assuming we use 6 rays and only 1 layer, Then we will have 2 hexagons, which
-// are 0 to 5 and 6 to 11. The area between them will be the penumbra area, and
-// the area inside the 2nd hexagon is the umbra.
-// Ambient shadow is using only 1 layer for opaque caster, otherwise, spot
-// shadow and ambient shadow are using 2 layers.
-// Triange strip indices for penumbra area: (0, 6, 1, 7, 2, 8, 3, 9, 4, 10, 5, 11, 0, 6)
-// 0
-//
-// 5 6 1
-// 11 7
-//
-// 10 8
-// 4 9 2
-//
-// 3
-
-// The total number of rays starting from the centroid of shadow area, in order
-// to generate the shadow geometry.
-#define SHADOW_RAY_COUNT 128
-
-// The total number of all the vertices representing the shadow.
-// For the case we only have 1 layer, then we will just fill only 2/3 of it.
-#define SHADOW_VERTEX_COUNT (3 * SHADOW_RAY_COUNT)
-
-// The total number of indices used for drawing the shadow geometry as triangle strips.
-// Depending on the mode we are drawing, we can have 1 layer or 2 layers.
-// Therefore, we only build the longer index buffer.
-#define TWO_POLY_RING_SHADOW_INDEX_COUNT (4 * (SHADOW_RAY_COUNT + 1))
-#define ONE_POLY_RING_SHADOW_INDEX_COUNT (2 * (SHADOW_RAY_COUNT + 1))
-
-#define MAX_SHADOW_INDEX_COUNT TWO_POLY_RING_SHADOW_INDEX_COUNT
-
-#define SHADOW_MIN_CASTER_Z 0.001f
-
-#define MINIMAL_DELTA_THETA (M_PI / 180 / 1000)
-
-class ShadowTessellator {
-public:
- static void tessellateAmbientShadow(bool isCasterOpaque, const Vector3* casterPolygon,
- int casterVertexCount, const Vector3& centroid3d,
- const Rect& casterBounds, const Rect& localClip, float maxZ,
- VertexBuffer& shadowVertexBuffer);
-
- static void tessellateSpotShadow(bool isCasterOpaque, const Vector3* casterPolygon,
- int casterVertexCount, const Vector3& casterCentroid,
- const mat4& receiverTransform, const Vector3& lightCenter,
- int lightRadius, const Rect& casterBounds,
- const Rect& localClip, VertexBuffer& shadowVertexBuffer);
-
- static Vector2 centroid2d(const Vector2* poly, int polyLength);
-
- static Vector2 calculateNormal(const Vector2& p1, const Vector2& p2);
-
- static int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2, float divisor);
-
- static void checkOverflow(int used, int total, const char* bufferName);
-}; // ShadowTessellator
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_SHADOW_TESSELLATOR_H
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 40b811d813fd..9c707bab95f1 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -21,6 +21,7 @@
#include "VectorDrawable.h"
#include "hwui/Bitmap.h"
#include "hwui/MinikinUtils.h"
+#include "hwui/PaintFilter.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include <SkAnimatedImage.h>
@@ -28,7 +29,6 @@
#include <SkColorFilter.h>
#include <SkColorSpaceXformCanvas.h>
#include <SkDeque.h>
-#include <SkDrawFilter.h>
#include <SkDrawable.h>
#include <SkGraphics.h>
#include <SkImage.h>
@@ -40,6 +40,8 @@
#include <SkTextBlob.h>
#include <memory>
+#include <optional>
+#include <utility>
namespace android {
@@ -62,13 +64,7 @@ SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
mCanvasOwned =
std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
if (cs.get() == nullptr || cs->isSRGB()) {
- if (!uirenderer::Properties::isSkiaEnabled()) {
- mCanvasWrapper =
- SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), SkColorSpace::MakeSRGB());
- mCanvas = mCanvasWrapper.get();
- } else {
- mCanvas = mCanvasOwned.get();
- }
+ mCanvas = mCanvasOwned.get();
} else {
/** The wrapper is needed if we are drawing into a non-sRGB destination, since
* we need to transform all colors (not just bitmaps via filters) into the
@@ -101,8 +97,6 @@ void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
std::unique_ptr<SkCanvas> newCanvasWrapper;
if (cs.get() != nullptr && !cs->isSRGB()) {
newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), std::move(cs));
- } else if (!uirenderer::Properties::isSkiaEnabled()) {
- newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), SkColorSpace::MakeSRGB());
}
// deletes the previously owned canvas (if any)
@@ -219,7 +213,7 @@ public:
Clip(const SkRRect& rrect, SkClipOp op, const SkMatrix& m)
: mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {}
Clip(const SkPath& path, SkClipOp op, const SkMatrix& m)
- : mType(Type::Path), mOp(op), mMatrix(m), mPath(&path) {}
+ : mType(Type::Path), mOp(op), mMatrix(m), mPath(std::in_place, path) {}
void apply(SkCanvas* canvas) const {
canvas->setMatrix(mMatrix);
@@ -231,7 +225,7 @@ public:
canvas->clipRRect(mRRect, mOp);
break;
case Type::Path:
- canvas->clipPath(*mPath.get(), mOp);
+ canvas->clipPath(mPath.value(), mOp);
break;
}
}
@@ -248,7 +242,7 @@ private:
SkMatrix mMatrix;
// These are logically a union (tracked separately due to non-POD path).
- SkTLazy<SkPath> mPath;
+ std::optional<SkPath> mPath;
SkRRect mRRect;
};
@@ -408,12 +402,12 @@ bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) {
// Canvas state operations: Filters
// ----------------------------------------------------------------------------
-SkDrawFilter* SkiaCanvas::getDrawFilter() {
- return mCanvas->getDrawFilter();
+PaintFilter* SkiaCanvas::getPaintFilter() {
+ return mPaintFilter.get();
}
-void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) {
- mCanvas->setDrawFilter(drawFilter);
+void SkiaCanvas::setPaintFilter(sk_sp<PaintFilter> paintFilter) {
+ mPaintFilter = std::move(paintFilter);
}
// ----------------------------------------------------------------------------
@@ -447,8 +441,15 @@ void SkiaCanvas::drawColor(int color, SkBlendMode mode) {
mCanvas->drawColor(color, mode);
}
+SkiaCanvas::PaintCoW&& SkiaCanvas::filterPaint(PaintCoW&& paint) const {
+ if (mPaintFilter) {
+ mPaintFilter->filter(&paint.writeable());
+ }
+ return std::move(paint);
+}
+
void SkiaCanvas::drawPaint(const SkPaint& paint) {
- mCanvas->drawPaint(paint);
+ mCanvas->drawPaint(*filterPaint(paint));
}
// ----------------------------------------------------------------------------
@@ -465,53 +466,58 @@ void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint
pts[i].set(points[0], points[1]);
points += 2;
}
- mCanvas->drawPoints(mode, count, pts.get(), paint);
+ mCanvas->drawPoints(mode, count, pts.get(), *filterPaint(paint));
}
void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) {
- mCanvas->drawPoint(x, y, paint);
+ mCanvas->drawPoint(x, y, *filterPaint(paint));
}
void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
- this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode);
+ this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kPoints_PointMode);
}
void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
const SkPaint& paint) {
- mCanvas->drawLine(startX, startY, stopX, stopY, paint);
+ mCanvas->drawLine(startX, startY, stopX, stopY, *filterPaint(paint));
}
void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
if (CC_UNLIKELY(count < 4 || paint.nothingToDraw())) return;
- this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
+ this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kLines_PointMode);
}
void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
- mCanvas->drawRect({left, top, right, bottom}, paint);
+ mCanvas->drawRect({left, top, right, bottom}, *filterPaint(paint));
}
void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
- mCanvas->drawRegion(region, paint);
+ mCanvas->drawRegion(region, *filterPaint(paint));
}
void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
const SkPaint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- mCanvas->drawRoundRect(rect, rx, ry, paint);
+ mCanvas->drawRoundRect(rect, rx, ry, *filterPaint(paint));
+}
+
+void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+ mCanvas->drawDRRect(outer, inner, *filterPaint(paint));
}
void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
- mCanvas->drawCircle(x, y, radius, paint);
+ mCanvas->drawCircle(x, y, radius, *filterPaint(paint));
}
void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
- mCanvas->drawOval(oval, paint);
+ mCanvas->drawOval(oval, *filterPaint(paint));
}
void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle,
@@ -519,9 +525,9 @@ void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float
if (CC_UNLIKELY(paint.nothingToDraw())) return;
SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
if (fabs(sweepAngle) >= 360.0f) {
- mCanvas->drawOval(arc, paint);
+ mCanvas->drawOval(arc, *filterPaint(paint));
} else {
- mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
+ mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, *filterPaint(paint));
}
}
@@ -530,19 +536,19 @@ void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) {
return;
}
- mCanvas->drawPath(path, paint);
+ mCanvas->drawPath(path, *filterPaint(paint));
}
void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
- mCanvas->drawVertices(vertices, mode, paint);
+ mCanvas->drawVertices(vertices, mode, *filterPaint(paint));
}
// ----------------------------------------------------------------------------
// Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
-const SkPaint* SkiaCanvas::addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
- sk_sp<SkColorFilter> colorSpaceFilter) {
+SkiaCanvas::PaintCoW&& SkiaCanvas::filterBitmap(PaintCoW&& paint,
+ sk_sp<SkColorFilter> colorSpaceFilter) const {
/* We don't apply the colorSpace filter if this canvas is already wrapped with
* a SkColorSpaceXformCanvas since it already takes care of converting the
* contents of the bitmap into the appropriate colorspace. The mCanvasWrapper
@@ -550,39 +556,31 @@ const SkPaint* SkiaCanvas::addFilter(const SkPaint* origPaint, SkPaint* tmpPaint
* to have a non-sRGB colorspace.
*/
if (!mCanvasWrapper && colorSpaceFilter) {
- if (origPaint) {
- *tmpPaint = *origPaint;
- }
-
- if (tmpPaint->getColorFilter()) {
- tmpPaint->setColorFilter(
- SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorSpaceFilter));
- LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
+ SkPaint& tmpPaint = paint.writeable();
+ if (tmpPaint.getColorFilter()) {
+ tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(tmpPaint.refColorFilter(),
+ std::move(colorSpaceFilter)));
+ LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
} else {
- tmpPaint->setColorFilter(colorSpaceFilter);
+ tmpPaint.setColorFilter(std::move(colorSpaceFilter));
}
-
- return tmpPaint;
- } else {
- return origPaint;
}
+ return filterPaint(std::move(paint));
}
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImage(image, left, top, addFilter(paint, &tmpPaint, colorFilter));
+ mCanvas->drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
}
void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
SkAutoCanvasRestore acr(mCanvas, true);
mCanvas->concat(matrix);
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImage(image, 0, 0, addFilter(paint, &tmpPaint, colorFilter));
+ mCanvas->drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
}
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
@@ -591,10 +589,9 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float s
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImageRect(image, srcRect, dstRect, addFilter(paint, &tmpPaint, colorFilter),
+ mCanvas->drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
SkCanvas::kFast_SrcRectConstraint);
}
@@ -673,21 +670,20 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
#endif
// cons-up a shader for the bitmap
- SkPaint tmpPaint;
- if (paint) {
- tmpPaint = *paint;
- }
+ PaintCoW paintCoW(paint);
+ SkPaint& tmpPaint = paintCoW.writeable();
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
sk_sp<SkShader> shader =
image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
if (colorFilter) {
- shader = shader->makeWithColorFilter(colorFilter);
+ shader = shader->makeWithColorFilter(std::move(colorFilter));
}
- tmpPaint.setShader(shader);
+ tmpPaint.setShader(std::move(shader));
- mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, tmpPaint);
+ mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate,
+ *filterPaint(std::move(paintCoW)));
}
void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft,
@@ -714,10 +710,10 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImageLattice(image.get(), lattice, dst, addFilter(paint, &tmpPaint, colorFilter));
+ mCanvas->drawImageLattice(image.get(), lattice, dst,
+ filterBitmap(paint, std::move(colorFilter)));
}
double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) {
@@ -736,16 +732,15 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& p
float y, float boundsLeft, float boundsTop, float boundsRight,
float boundsBottom, float totalAdvance) {
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.
SkPaint paintCopy(paint);
- paintCopy.setTextAlign(SkPaint::kLeft_Align);
+ if (mPaintFilter) {
+ mPaintFilter->filter(&paintCopy);
+ }
SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
// Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
// older.
- if (!mCanvasOwned && sApiLevel <= 27 && paintCopy.getStrokeWidth() <= 0
- && paintCopy.getStyle() == SkPaint::kStroke_Style) {
+ if (!mCanvasOwned && sApiLevel <= 27 && paintCopy.getStrokeWidth() <= 0 &&
+ paintCopy.getStyle() == SkPaint::kStroke_Style) {
paintCopy.setStyle(SkPaint::kFill_Style);
}
@@ -764,11 +759,10 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& p
void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
const SkPaint& paint, const SkPath& path, size_t start,
size_t end) {
- // Set align to left for drawing, as we don't want individual
- // glyphs centered or right-aligned; the offsets take care of
- // that portion of the alignment.
SkPaint paintCopy(paint);
- paintCopy.setTextAlign(SkPaint::kLeft_Align);
+ if (mPaintFilter) {
+ mPaintFilter->filter(&paintCopy);
+ }
SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
const int N = end - start;
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 3efc22a03cdf..3a877cf84010 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -22,7 +22,9 @@
#include "hwui/Canvas.h"
#include <SkCanvas.h>
-#include <SkTLazy.h>
+
+#include <cassert>
+#include <optional>
namespace android {
@@ -87,8 +89,8 @@ public:
virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override;
virtual bool clipPath(const SkPath* path, SkClipOp op) override;
- virtual SkDrawFilter* getDrawFilter() override;
- virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
+ virtual PaintFilter* getPaintFilter() override;
+ virtual void setPaintFilter(sk_sp<PaintFilter> paintFilter) override;
virtual SkCanvasState* captureCanvasState() const override;
@@ -105,6 +107,10 @@ public:
virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
const SkPaint& paint) override;
+
+ virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) override;
+
virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
virtual void drawOval(float left, float top, float right, float bottom,
const SkPaint& paint) override;
@@ -158,6 +164,46 @@ protected:
const SkPaint& paint, const SkPath& path, size_t start,
size_t end) override;
+ /** This class acts as a copy on write SkPaint.
+ *
+ * Initially this will be the SkPaint passed to the contructor.
+ * The first time writable() is called this will become a copy of the
+ * initial SkPaint (or a default SkPaint if nullptr).
+ */
+ struct PaintCoW {
+ PaintCoW(const SkPaint& that) : mPtr(&that) {}
+ PaintCoW(const SkPaint* ptr) : mPtr(ptr) {}
+ PaintCoW(const PaintCoW&) = delete;
+ PaintCoW(PaintCoW&&) = delete;
+ PaintCoW& operator=(const PaintCoW&) = delete;
+ PaintCoW& operator=(PaintCoW&&) = delete;
+ SkPaint& writeable() {
+ if (!mStorage) {
+ if (!mPtr) {
+ mStorage.emplace();
+ } else {
+ mStorage.emplace(*mPtr);
+ }
+ mPtr = &*mStorage;
+ }
+ return *mStorage;
+ }
+ operator const SkPaint*() const { return mPtr; }
+ const SkPaint* operator->() const { assert(mPtr); return mPtr; }
+ const SkPaint& operator*() const { assert(mPtr); return *mPtr; }
+ explicit operator bool() { return mPtr != nullptr; }
+ private:
+ const SkPaint* mPtr;
+ std::optional<SkPaint> mStorage;
+ };
+
+ /** Filters the paint using the current paint filter.
+ *
+ * @param paint the paint to filter. Will be initialized with the default
+ * SkPaint before filtering if filtering is required.
+ */
+ PaintCoW&& filterPaint(PaintCoW&& paint) const;
+
private:
struct SaveRec {
int saveCount;
@@ -174,8 +220,15 @@ private:
void drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode);
- const SkPaint* addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
- sk_sp<SkColorFilter> colorSpaceFilter);
+ /** Filters the paint for bitmap drawing.
+ *
+ * After filtering the paint for bitmap drawing,
+ * also calls filterPaint on the paint.
+ *
+ * @param paint the paint to filter. Will be initialized with the default
+ * SkPaint before filtering if filtering is required.
+ */
+ PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter) const;
class Clip;
@@ -185,6 +238,7 @@ private:
// unless it is the same as mCanvasOwned.get()
std::unique_ptr<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
std::vector<Clip> mClipStack; // tracks persistent clips.
+ sk_sp<PaintFilter> mPaintFilter;
};
} // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
deleted file mode 100644
index fc009d871620..000000000000
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SkiaCanvasProxy.h"
-
-#include <memory>
-
-#include <log/log.h>
-
-#include <SkLatticeIter.h>
-#include <SkPaint.h>
-#include <SkPatchUtils.h>
-#include <SkPath.h>
-#include <SkPixelRef.h>
-#include <SkRRect.h>
-#include <SkRSXform.h>
-#include <SkRect.h>
-#include <SkSurface.h>
-#include <SkTextBlobRunIterator.h>
-#include <SkVertices.h>
-#include "hwui/Bitmap.h"
-
-namespace android {
-namespace uirenderer {
-
-SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls)
- : INHERITED(canvas->width(), canvas->height())
- , mCanvas(canvas)
- , mFilterHwuiCalls(filterHwuiCalls) {}
-
-void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) {
- mCanvas->drawPaint(paint);
-}
-
-void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[],
- const SkPaint& paint) {
- if (!pts || count == 0) {
- return;
- }
-
- // convert the SkPoints into floats
- static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
- const size_t floatCount = count << 1;
- const float* floatArray = &pts[0].fX;
-
- switch (pointMode) {
- case kPoints_PointMode: {
- mCanvas->drawPoints(floatArray, floatCount, paint);
- break;
- }
- case kLines_PointMode: {
- mCanvas->drawLines(floatArray, floatCount, paint);
- break;
- }
- case kPolygon_PointMode: {
- SkPaint strokedPaint(paint);
- strokedPaint.setStyle(SkPaint::kStroke_Style);
-
- SkPath path;
- for (size_t i = 0; i < count - 1; i++) {
- path.moveTo(pts[i]);
- path.lineTo(pts[i + 1]);
- this->drawPath(path, strokedPaint);
- path.rewind();
- }
- break;
- }
- default:
- LOG_ALWAYS_FATAL("Unknown point type");
- }
-}
-
-void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) {
- mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) {
- mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) {
- if (!roundRect.isComplex()) {
- const SkRect& rect = roundRect.rect();
- SkVector radii = roundRect.getSimpleRadii();
- mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, radii.fX, radii.fY,
- paint);
- } else {
- SkPath path;
- path.addRRect(roundRect);
- mCanvas->drawPath(path, paint);
- }
-}
-
-void SkiaCanvasProxy::onDrawArc(const SkRect& rect, SkScalar startAngle, SkScalar sweepAngle,
- bool useCenter, const SkPaint& paint) {
- mCanvas->drawArc(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, startAngle, sweepAngle,
- useCenter, paint);
-}
-
-void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
- mCanvas->drawPath(path, paint);
-}
-
-void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
- const SkPaint* paint) {
- sk_sp<Bitmap> hwuiBitmap = Bitmap::createFrom(bitmap.info(), *bitmap.pixelRef());
- // HWUI doesn't support extractSubset(), so convert any subsetted bitmap into
- // a drawBitmapRect(); pass through an un-subsetted bitmap.
- if (hwuiBitmap && bitmap.dimensions() != hwuiBitmap->info().dimensions()) {
- SkIPoint origin = bitmap.pixelRefOrigin();
- mCanvas->drawBitmap(
- *hwuiBitmap, origin.fX, origin.fY, origin.fX + bitmap.dimensions().width(),
- origin.fY + bitmap.dimensions().height(), left, top,
- left + bitmap.dimensions().width(), top + bitmap.dimensions().height(), paint);
- } else {
- mCanvas->drawBitmap(*hwuiBitmap, left, top, paint);
- }
-}
-
-void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& skBitmap, const SkRect* srcPtr,
- const SkRect& dst, const SkPaint* paint, SrcRectConstraint) {
- SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(skBitmap.width(), skBitmap.height());
- // TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
- Bitmap* bitmap = reinterpret_cast<Bitmap*>(skBitmap.pixelRef());
- mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft, dst.fTop,
- dst.fRight, dst.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
- const SkRect& dst, const SkPaint*) {
- // TODO make nine-patch drawing a method on Canvas.h
- SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported");
-}
-
-void SkiaCanvasProxy::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
- const SkPaint* paint) {
- SkBitmap skiaBitmap;
- SkPixmap pixmap;
- if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) {
- onDrawBitmap(skiaBitmap, left, top, paint);
- }
-}
-
-void SkiaCanvasProxy::onDrawImageRect(const SkImage* image, const SkRect* srcPtr, const SkRect& dst,
- const SkPaint* paint, SrcRectConstraint constraint) {
- SkBitmap skiaBitmap;
- SkPixmap pixmap;
- if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) {
- sk_sp<Bitmap> bitmap = Bitmap::createFrom(skiaBitmap.info(), *skiaBitmap.pixelRef());
- SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(image->width(), image->height());
- mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft,
- dst.fTop, dst.fRight, dst.fBottom, paint);
- }
-}
-
-void SkiaCanvasProxy::onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
- const SkPaint*) {
- SkDEBUGFAIL("SkiaCanvasProxy::onDrawImageNine is not yet supported");
-}
-
-void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& lattice,
- const SkRect& dst, const SkPaint* paint) {
- SkLatticeIter iter(lattice, dst);
- SkRect srcR, dstR;
- while (iter.next(&srcR, &dstR)) {
- onDrawImageRect(image, &srcR, dstR, paint, SkCanvas::kFast_SrcRectConstraint);
- }
-}
-
-void SkiaCanvasProxy::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
- const SkPaint& paint) {
- if (mFilterHwuiCalls) {
- return;
- }
- mCanvas->drawVertices(vertices, bmode, paint);
-}
-
-sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
- SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
- return NULL;
-}
-
-void SkiaCanvasProxy::willSave() {
- mCanvas->save(android::SaveFlags::MatrixClip);
-}
-
-static inline SaveFlags::Flags saveFlags(SkCanvas::SaveLayerFlags layerFlags) {
- SaveFlags::Flags saveFlags = 0;
-
- if (!(layerFlags & SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag)) {
- saveFlags |= SaveFlags::ClipToLayer;
- }
-
- return saveFlags;
-}
-
-SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(
- const SaveLayerRec& saveLayerRec) {
- SkRect rect;
- if (saveLayerRec.fBounds) {
- rect = *saveLayerRec.fBounds;
- } else if (!mCanvas->getClipBounds(&rect)) {
- rect = SkRect::MakeEmpty();
- }
- mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint,
- saveFlags(saveLayerRec.fSaveLayerFlags));
- return SkCanvas::kNoLayer_SaveLayerStrategy;
-}
-
-void SkiaCanvasProxy::willRestore() {
- mCanvas->restore();
-}
-
-void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
- mCanvas->concat(matrix);
-}
-
-void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
- mCanvas->setMatrix(matrix);
-}
-
-void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
- const SkPaint& paint) {
- SkPath path;
- path.addRRect(outer);
- path.addRRect(inner);
- path.setFillType(SkPath::kEvenOdd_FillType);
- this->drawPath(path, paint);
-}
-
-/**
- * Utility class that converts the incoming text & paint from the given encoding
- * into glyphIDs.
- */
-class GlyphIDConverter {
-public:
- GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) {
- paint = origPaint;
- if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
- glyphIDs = (uint16_t*)text;
- count = byteLength >> 1;
- } else {
- // ensure space for one glyph per ID given UTF8 encoding.
- storage.reset(new uint16_t[byteLength]);
- glyphIDs = storage.get();
- count = paint.textToGlyphs(text, byteLength, storage.get());
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- }
- }
-
- SkPaint paint;
- uint16_t* glyphIDs;
- int count;
-
-private:
- std::unique_ptr<uint16_t[]> storage;
-};
-
-void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
- const SkPaint& origPaint) {
- // convert to glyphIDs if necessary
- GlyphIDConverter glyphs(text, byteLength, origPaint);
-
- // compute the glyph positions
- std::unique_ptr<SkScalar[]> glyphWidths(new SkScalar[glyphs.count]);
- glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
-
- // compute conservative bounds
- // NOTE: We could call the faster paint.getFontBounds for a less accurate,
- // but even more conservative bounds if this is too slow.
- SkRect bounds;
- glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
-
- // adjust for non-left alignment
- if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) {
- SkScalar stop = 0;
- for (int i = 0; i < glyphs.count; i++) {
- stop += glyphWidths[i];
- }
- if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) {
- stop = SkScalarHalf(stop);
- }
- if (glyphs.paint.isVerticalText()) {
- y -= stop;
- } else {
- x -= stop;
- }
- }
-
- // setup the first glyph position and adjust bounds if needed
- int xBaseline = 0;
- int yBaseline = 0;
- if (mCanvas->drawTextAbsolutePos()) {
- bounds.offset(x, y);
- xBaseline = x;
- yBaseline = y;
- }
-
- static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
- 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[],
- const SkPaint& origPaint) {
- // convert to glyphIDs if necessary
- GlyphIDConverter glyphs(text, byteLength, origPaint);
-
- // convert to relative positions if necessary
- int x, y;
- if (mCanvas->drawTextAbsolutePos()) {
- x = 0;
- y = 0;
- } else {
- x = pos[0].fX;
- y = pos[0].fY;
- }
-
- // Compute conservative bounds. If the content has already been processed
- // by Minikin then it had already computed these bounds. Unfortunately,
- // there is no way to capture those bounds as part of the Skia drawPosText
- // API so we need to do that computation again here.
- SkRect bounds = SkRect::MakeEmpty();
- for (int i = 0; i < glyphs.count; i++) {
- SkRect glyphBounds = SkRect::MakeEmpty();
- glyphs.paint.measureText(&glyphs.glyphIDs[i], sizeof(uint16_t), &glyphBounds);
- glyphBounds.offset(pos[i].fX, pos[i].fY);
- bounds.join(glyphBounds);
- }
-
- static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
- 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[],
- SkScalar constY, const SkPaint& paint) {
- const size_t pointCount = byteLength >> 1;
- std::unique_ptr<SkPoint[]> pts(new SkPoint[pointCount]);
- for (size_t i = 0; i < pointCount; i++) {
- pts[i].set(xpos[i], constY);
- }
- this->onDrawPosText(text, byteLength, pts.get(), paint);
-}
-
-void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
- const SkMatrix* matrix, const SkPaint& origPaint) {
- SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextOnPath is not supported");
-}
-
-void SkiaCanvasProxy::onDrawTextRSXform(const void* text, size_t byteLength,
- const SkRSXform xform[], const SkRect* cullRect,
- const SkPaint& paint) {
- GlyphIDConverter glyphs(text, byteLength, paint); // Just get count
- SkMatrix localM, currM, origM;
- mCanvas->getMatrix(&currM);
- origM = currM;
- for (int i = 0; i < glyphs.count; i++) {
- localM.setRSXform(*xform++);
- currM.setConcat(origM, localM);
- mCanvas->setMatrix(currM);
- this->onDrawText((char*)text + (byteLength / glyphs.count * i), byteLength / glyphs.count,
- 0, 0, paint);
- }
- mCanvas->setMatrix(origM);
-}
-
-void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
- const SkPaint& paint) {
- SkPaint runPaint = paint;
-
- SkTextBlobRunIterator it(blob);
- for (; !it.done(); it.next()) {
- size_t textLen = it.glyphCount() * sizeof(uint16_t);
- const SkPoint& offset = it.offset();
- // applyFontToPaint() always overwrites the exact same attributes,
- // so it is safe to not re-seed the paint for this reason.
- it.applyFontToPaint(&runPaint);
-
- switch (it.positioning()) {
- case SkTextBlob::kDefault_Positioning:
- this->drawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
- break;
- case SkTextBlob::kHorizontal_Positioning: {
- std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
- for (size_t i = 0; i < it.glyphCount(); i++) {
- pts[i].set(x + offset.x() + it.pos()[i], y + offset.y());
- }
- this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
- break;
- }
- case SkTextBlob::kFull_Positioning: {
- std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
- for (size_t i = 0; i < it.glyphCount(); i++) {
- const size_t xIndex = i * 2;
- const size_t yIndex = xIndex + 1;
- pts[i].set(x + offset.x() + it.pos()[xIndex],
- y + offset.y() + it.pos()[yIndex]);
- }
- this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
- break;
- }
- default:
- SK_ABORT("unhandled positioning mode");
- }
- }
-}
-
-void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
- const SkPoint texCoords[4], SkBlendMode bmode,
- const SkPaint& paint) {
- if (mFilterHwuiCalls) {
- return;
- }
- SkMatrix matrix;
- mCanvas->getMatrix(&matrix);
- SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
-
- mCanvas->drawVertices(
- SkPatchUtils::MakeVertices(cubics, colors, texCoords, lod.width(), lod.height()).get(),
- bmode, paint);
-}
-
-void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle) {
- mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
-}
-
-void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkClipOp op, ClipEdgeStyle) {
- SkPath path;
- path.addRRect(roundRect);
- mCanvas->clipPath(&path, op);
-}
-
-void SkiaCanvasProxy::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle) {
- mCanvas->clipPath(&path, op);
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
deleted file mode 100644
index 360d5a08b974..000000000000
--- a/libs/hwui/SkiaCanvasProxy.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SkiaCanvasProxy_DEFINED
-#define SkiaCanvasProxy_DEFINED
-
-#include <SkCanvas.h>
-#include <cutils/compiler.h>
-
-#include "hwui/Canvas.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * This class serves as a proxy between Skia's SkCanvas and Android Framework's
- * Canvas. The class does not maintain any draw-related state and will pass
- * through most requests directly to the Canvas provided in the constructor.
- *
- * Upon construction it is expected that the provided Canvas has already been
- * prepared for recording and will continue to be in the recording state while
- * this proxy class is being used.
- *
- * If filterHwuiCalls is true, the proxy silently ignores away draw calls that
- * aren't supported by HWUI.
- */
-class ANDROID_API SkiaCanvasProxy : public SkCanvas {
-public:
- explicit SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls = false);
- virtual ~SkiaCanvasProxy() {}
-
-protected:
- virtual sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
-
- virtual void willSave() override;
- virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
- virtual void willRestore() override;
-
- virtual void didConcat(const SkMatrix&) override;
- virtual void didSetMatrix(const SkMatrix&) override;
-
- virtual void onDrawPaint(const SkPaint& paint) override;
- virtual void onDrawPoints(PointMode, size_t count, const SkPoint pts[],
- const SkPaint&) override;
- virtual void onDrawOval(const SkRect&, const SkPaint&) override;
- virtual void onDrawRect(const SkRect&, const SkPaint&) override;
- virtual void onDrawRRect(const SkRRect&, const SkPaint&) override;
- virtual void onDrawPath(const SkPath& path, const SkPaint&) override;
- virtual void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
- const SkPaint&) override;
- virtual void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
- const SkPaint*) override;
- virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst,
- const SkPaint* paint, SrcRectConstraint) override;
- virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
- const SkPaint*) override;
- virtual void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*);
- virtual void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
- SrcRectConstraint);
- virtual void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
- const SkPaint*);
- virtual void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
- const SkPaint*);
- virtual void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override;
-
- virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
-
- virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
- const SkPaint&) override;
- virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
- const SkPaint&) override;
- virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
- SkScalar constY, const SkPaint&) override;
- virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
- const SkMatrix* matrix, const SkPaint&) override;
- virtual void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
- const SkRect* cullRect, const SkPaint& paint);
- virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
- const SkPaint& paint) override;
-
- virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
- const SkPoint texCoords[4], SkBlendMode,
- const SkPaint& paint) override;
-
- virtual void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
- virtual void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
- virtual void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
-
-private:
- Canvas* mCanvas;
- bool mFilterHwuiCalls;
-
- typedef SkCanvas INHERITED;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // SkiaCanvasProxy_DEFINED
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
deleted file mode 100644
index df746554f306..000000000000
--- a/libs/hwui/SkiaShader.cpp
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SkiaShader.h"
-
-#include "Caches.h"
-#include "Extensions.h"
-#include "Matrix.h"
-#include "Texture.h"
-#include "hwui/Bitmap.h"
-
-#include <SkMatrix.h>
-#include <utils/Log.h>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Support
-///////////////////////////////////////////////////////////////////////////////
-
-static constexpr GLenum gTileModes[] = {
- GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode
- GL_REPEAT, // == SkShader::kRepeat_Mode
- GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode
-};
-
-static_assert(gTileModes[SkShader::kClamp_TileMode] == GL_CLAMP_TO_EDGE,
- "SkShader TileModes have changed");
-static_assert(gTileModes[SkShader::kRepeat_TileMode] == GL_REPEAT,
- "SkShader TileModes have changed");
-static_assert(gTileModes[SkShader::kMirror_TileMode] == GL_MIRRORED_REPEAT,
- "SkShader TileModes have changed");
-
-/**
- * This function does not work for n == 0.
- */
-static inline bool isPowerOfTwo(unsigned int n) {
- return !(n & (n - 1));
-}
-
-static inline void bindUniformColor(int slot, FloatColor color) {
- glUniform4fv(slot, 1, reinterpret_cast<const float*>(&color));
-}
-
-static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
- caches->textureState().bindTexture(texture->target(), texture->id());
- texture->setWrapST(wrapS, wrapT);
-}
-
-/**
- * Compute the matrix to transform to screen space.
- * @param screenSpace Output param for the computed matrix.
- * @param unitMatrix The unit matrix for gradient shaders, as returned by SkShader::asAGradient,
- * or identity.
- * @param localMatrix Local matrix, as returned by SkShader::getLocalMatrix().
- * @param modelViewMatrix Model view matrix, as supplied by the OpenGLRenderer.
- */
-static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatrix,
- const SkMatrix& localMatrix, const mat4& modelViewMatrix) {
- mat4 shaderMatrix;
- // uses implicit construction
- shaderMatrix.loadInverse(localMatrix);
- // again, uses implicit construction
- screenSpace.loadMultiply(unitMatrix, shaderMatrix);
- screenSpace.multiply(modelViewMatrix);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Gradient shader matrix helpers
-///////////////////////////////////////////////////////////////////////////////
-
-static void toLinearUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
- SkVector vec = pts[1] - pts[0];
- const float mag = vec.length();
- const float inv = mag ? 1.0f / mag : 0;
-
- vec.scale(inv);
- matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
- matrix->postTranslate(-pts[0].fX, -pts[0].fY);
- matrix->postScale(inv, inv);
-}
-
-static void toCircularUnitMatrix(const float x, const float y, const float radius,
- SkMatrix* matrix) {
- const float inv = 1.0f / radius;
- matrix->setTranslate(-x, -y);
- matrix->postScale(inv, inv);
-}
-
-static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
- matrix->setTranslate(-x, -y);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Common gradient code
-///////////////////////////////////////////////////////////////////////////////
-
-static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) {
- return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Store / apply
-///////////////////////////////////////////////////////////////////////////////
-
-bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 modelViewMatrix,
- GLuint* textureUnit, ProgramDescription* description,
- SkiaShaderData::GradientShaderData* outData) {
- SkShader::GradientInfo gradInfo;
- gradInfo.fColorCount = 0;
- gradInfo.fColors = nullptr;
- gradInfo.fColorOffsets = nullptr;
-
- SkMatrix unitMatrix;
- switch (shader.asAGradient(&gradInfo)) {
- case SkShader::kLinear_GradientType:
- description->gradientType = ProgramDescription::kGradientLinear;
-
- toLinearUnitMatrix(gradInfo.fPoint, &unitMatrix);
- break;
- case SkShader::kRadial_GradientType:
- description->gradientType = ProgramDescription::kGradientCircular;
-
- toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, gradInfo.fRadius[0],
- &unitMatrix);
- break;
- case SkShader::kSweep_GradientType:
- description->gradientType = ProgramDescription::kGradientSweep;
-
- toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
- break;
- default:
- // Do nothing. This shader is unsupported.
- return false;
- }
- description->hasGradient = true;
- description->isSimpleGradient = isSimpleGradient(gradInfo);
-
- computeScreenSpaceMatrix(outData->screenSpace, unitMatrix, shader.getLocalMatrix(),
- modelViewMatrix);
-
- // re-query shader to get full color / offset data
- std::unique_ptr<SkColor[]> colorStorage(new SkColor[gradInfo.fColorCount]);
- std::unique_ptr<SkScalar[]> colorOffsets(new SkScalar[gradInfo.fColorCount]);
- gradInfo.fColors = &colorStorage[0];
- gradInfo.fColorOffsets = &colorOffsets[0];
- shader.asAGradient(&gradInfo);
-
- if (CC_UNLIKELY(!description->isSimpleGradient)) {
- outData->gradientSampler = (*textureUnit)++;
-
-#ifndef SK_SCALAR_IS_FLOAT
-#error Need to convert gradInfo.fColorOffsets to float!
-#endif
- outData->gradientTexture = caches.gradientCache.get(
- gradInfo.fColors, gradInfo.fColorOffsets, gradInfo.fColorCount);
- outData->wrapST = gTileModes[gradInfo.fTileMode];
- } else {
- outData->gradientSampler = 0;
- outData->gradientTexture = nullptr;
-
- outData->startColor.set(gradInfo.fColors[0]);
- outData->endColor.set(gradInfo.fColors[1]);
- }
-
- return true;
-}
-
-void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data,
- const GLsizei width, const GLsizei height) {
- if (CC_UNLIKELY(data.gradientTexture)) {
- caches.textureState().activateTexture(data.gradientSampler);
- bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST);
- glUniform1i(caches.program().getUniform("gradientSampler"), data.gradientSampler);
- } else {
- bindUniformColor(caches.program().getUniform("startColor"), data.startColor);
- bindUniformColor(caches.program().getUniform("endColor"), data.endColor);
- }
-
- glUniform2f(caches.program().getUniform("screenSize"), 1.0f / width, 1.0f / height);
- glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1, GL_FALSE,
- &data.screenSpace.data[0]);
-}
-
-bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
- GLuint* textureUnit, ProgramDescription* description,
- SkiaShaderData::BitmapShaderData* outData) {
- SkBitmap bitmap;
- SkShader::TileMode xy[2];
- if (!shader.isABitmap(&bitmap, nullptr, xy)) {
- return false;
- }
-
- // TODO: create hwui-owned BitmapShader.
- Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef());
- outData->bitmapTexture = caches.textureCache.get(hwuiBitmap);
- if (!outData->bitmapTexture) return false;
-
- outData->bitmapSampler = (*textureUnit)++;
-
- const float width = outData->bitmapTexture->width();
- const float height = outData->bitmapTexture->height();
-
- Texture* texture = outData->bitmapTexture;
-
- description->hasBitmap = true;
- description->hasLinearTexture = texture->isLinear();
- description->hasColorSpaceConversion = texture->hasColorSpaceConversion();
- description->transferFunction = texture->getTransferFunctionType();
- description->hasTranslucentConversion = texture->blend;
- description->isShaderBitmapExternal = hwuiBitmap->isHardware();
- // gralloc doesn't support non-clamp modes
- if (hwuiBitmap->isHardware() ||
- (!caches.extensions().hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
- (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode))) {
- // need non-clamp mode, but it's not supported for this draw,
- // so enable custom shader logic to mimic
- description->useShaderBasedWrap = true;
- description->bitmapWrapS = gTileModes[xy[0]];
- description->bitmapWrapT = gTileModes[xy[1]];
-
- outData->wrapS = GL_CLAMP_TO_EDGE;
- outData->wrapT = GL_CLAMP_TO_EDGE;
- } else {
- outData->wrapS = gTileModes[xy[0]];
- outData->wrapT = gTileModes[xy[1]];
- }
-
- computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
- modelViewMatrix);
- outData->textureDimension[0] = 1.0f / width;
- outData->textureDimension[1] = 1.0f / height;
-
- return true;
-}
-
-void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) {
- caches.textureState().activateTexture(data.bitmapSampler);
- bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT);
- data.bitmapTexture->setFilter(GL_LINEAR);
-
- glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
- glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE,
- &data.textureTransform.data[0]);
- glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
-}
-
-SkiaShaderType getComposeSubType(const SkShader& shader) {
- // First check for a gradient shader.
- switch (shader.asAGradient(nullptr)) {
- case SkShader::kNone_GradientType:
- // Not a gradient shader. Fall through to check for other types.
- break;
- case SkShader::kLinear_GradientType:
- case SkShader::kRadial_GradientType:
- case SkShader::kSweep_GradientType:
- return kGradient_SkiaShaderType;
- default:
- // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
- return kNone_SkiaShaderType;
- }
-
- // The shader is not a gradient. Check for a bitmap shader.
- if (shader.isABitmap()) {
- return kBitmap_SkiaShaderType;
- }
- return kNone_SkiaShaderType;
-}
-
-void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader,
- const Matrix4& modelViewMatrix, GLuint* textureUnit,
- ProgramDescription* description, SkiaShaderData* outData) {
- LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix, textureUnit,
- description, &outData->bitmapData),
- "failed storing bitmap shader data");
- LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix, textureUnit,
- description, &outData->gradientData),
- "failing storing gradient shader data");
-}
-
-bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
- GLuint* textureUnit, ProgramDescription* description,
- SkiaShaderData* outData) {
- SkShader::ComposeRec rec;
- if (!shader.asACompose(&rec)) return false;
-
- const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA);
- const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB);
-
- // check that type enum values are the 2 flags that compose the kCompose value
- if ((shaderAType & shaderBType) != 0) return false;
- if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false;
-
- mat4 transform;
- computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix);
- if (shaderAType == kBitmap_SkiaShaderType) {
- description->isBitmapFirst = true;
- storeCompose(caches, *rec.fShaderA, *rec.fShaderB, transform, textureUnit, description,
- outData);
- } else {
- description->isBitmapFirst = false;
- storeCompose(caches, *rec.fShaderB, *rec.fShaderA, transform, textureUnit, description,
- outData);
- }
- description->shadersMode = rec.fBlendMode;
- return true;
-}
-
-void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
- GLuint* textureUnit, ProgramDescription* description,
- SkiaShaderData* outData) {
- if (tryStoreGradient(caches, shader, modelViewMatrix, textureUnit, description,
- &outData->gradientData)) {
- outData->skiaShaderType = kGradient_SkiaShaderType;
- return;
- }
-
- if (tryStoreBitmap(caches, shader, modelViewMatrix, textureUnit, description,
- &outData->bitmapData)) {
- outData->skiaShaderType = kBitmap_SkiaShaderType;
- return;
- }
-
- if (tryStoreCompose(caches, shader, modelViewMatrix, textureUnit, description, outData)) {
- outData->skiaShaderType = kCompose_SkiaShaderType;
- return;
- }
-
- // Unknown/unsupported type, so explicitly ignore shader
- outData->skiaShaderType = kNone_SkiaShaderType;
-}
-
-void SkiaShader::apply(Caches& caches, const SkiaShaderData& data, const GLsizei width,
- const GLsizei height) {
- if (!data.skiaShaderType) return;
-
- if (data.skiaShaderType & kGradient_SkiaShaderType) {
- applyGradient(caches, data.gradientData, width, height);
- }
- if (data.skiaShaderType & kBitmap_SkiaShaderType) {
- applyBitmap(caches, data.bitmapData);
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
deleted file mode 100644
index e8e92bdc1d76..000000000000
--- a/libs/hwui/SkiaShader.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_SKIA_SHADER_H
-#define ANDROID_HWUI_SKIA_SHADER_H
-
-#include "FloatColor.h"
-#include "Matrix.h"
-
-#include <GLES2/gl2.h>
-#include <SkShader.h>
-#include <cutils/compiler.h>
-
-namespace android {
-namespace uirenderer {
-
-class Caches;
-class Extensions;
-class Texture;
-struct ProgramDescription;
-
-/**
- * Type of Skia shader in use.
- *
- * Note that kBitmap | kGradient = kCompose, since Compose implies
- * both its component types are in use simultaneously. No other
- * composition of multiple types is supported.
- */
-enum SkiaShaderType {
- kNone_SkiaShaderType = 0,
- kBitmap_SkiaShaderType = 1,
- kGradient_SkiaShaderType = 2,
- kCompose_SkiaShaderType = kBitmap_SkiaShaderType | kGradient_SkiaShaderType,
-};
-
-struct SkiaShaderData {
- SkiaShaderType skiaShaderType;
- struct BitmapShaderData {
- Texture* bitmapTexture;
- GLuint bitmapSampler;
- GLenum wrapS;
- GLenum wrapT;
-
- Matrix4 textureTransform;
- float textureDimension[2];
- } bitmapData;
- struct GradientShaderData {
- Matrix4 screenSpace;
-
- // simple gradient
- FloatColor startColor;
- FloatColor endColor;
-
- // complex gradient
- Texture* gradientTexture;
- GLuint gradientSampler;
- GLenum wrapST;
- } gradientData;
-};
-
-class SkiaShader {
-public:
- static void store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
- GLuint* textureUnit, ProgramDescription* description,
- SkiaShaderData* outData);
- static void apply(Caches& caches, const SkiaShaderData& data, const GLsizei width,
- const GLsizei height);
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_SKIA_SHADER_H
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
deleted file mode 100644
index f1a1bef7c94e..000000000000
--- a/libs/hwui/Snapshot.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Snapshot.h"
-
-#include "hwui/Canvas.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors
-///////////////////////////////////////////////////////////////////////////////
-
-Snapshot::Snapshot()
- : flags(0)
- , previous(nullptr)
- , layer(nullptr)
- , fbo(0)
- , alpha(1.0f)
- , roundRectClipState(nullptr)
- , projectionPathMask(nullptr)
- , mClipArea(&mClipAreaRoot) {
- transform = &mTransformRoot;
- mRelativeLightCenter.x = mRelativeLightCenter.y = mRelativeLightCenter.z = 0;
-}
-
-/**
- * Copies the specified snapshot/ The specified snapshot is stored as
- * the previous snapshot.
- */
-Snapshot::Snapshot(Snapshot* s, int saveFlags)
- : flags(0)
- , previous(s)
- , layer(s->layer)
- , fbo(s->fbo)
- , alpha(s->alpha)
- , roundRectClipState(s->roundRectClipState)
- , projectionPathMask(s->projectionPathMask)
- , mClipArea(nullptr)
- , mViewportData(s->mViewportData)
- , mRelativeLightCenter(s->mRelativeLightCenter) {
- if (saveFlags & SaveFlags::Matrix) {
- mTransformRoot = *s->transform;
- transform = &mTransformRoot;
- } else {
- transform = s->transform;
- }
-
- if (saveFlags & SaveFlags::Clip) {
- mClipAreaRoot = s->getClipArea();
- mClipArea = &mClipAreaRoot;
- } else {
- mClipArea = s->mClipArea;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Clipping
-///////////////////////////////////////////////////////////////////////////////
-
-void Snapshot::clip(const Rect& localClip, SkClipOp op) {
- flags |= Snapshot::kFlagClipSet;
- mClipArea->clipRectWithTransform(localClip, transform, static_cast<SkRegion::Op>(op));
-}
-
-void Snapshot::clipPath(const SkPath& path, SkClipOp op) {
- flags |= Snapshot::kFlagClipSet;
- mClipArea->clipPathWithTransform(path, transform, static_cast<SkRegion::Op>(op));
-}
-
-void Snapshot::setClip(float left, float top, float right, float bottom) {
- flags |= Snapshot::kFlagClipSet;
- mClipArea->setClip(left, top, right, bottom);
-}
-
-bool Snapshot::hasPerspectiveTransform() const {
- return transform->isPerspective();
-}
-
-const Rect& Snapshot::getLocalClip() {
- mat4 inverse;
- inverse.loadInverse(*transform);
-
- mLocalClip.set(mClipArea->getClipRect());
- inverse.mapRect(mLocalClip);
-
- return mLocalClip;
-}
-
-void Snapshot::resetClip(float left, float top, float right, float bottom) {
- // TODO: This is incorrect, when we start rendering into a new layer,
- // we may have to modify the previous snapshot's clip rect and clip
- // region if the previous restore() call did not restore the clip
- mClipArea = &mClipAreaRoot;
- setClip(left, top, right, bottom);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Clipping round rect
-///////////////////////////////////////////////////////////////////////////////
-
-void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius,
- bool highPriority) {
- if (bounds.isEmpty()) {
- mClipArea->setEmpty();
- return;
- }
-
- if (roundRectClipState && roundRectClipState->highPriority) {
- // ignore, don't replace, already have a high priority clip
- return;
- }
-
- RoundRectClipState* state = new (allocator) RoundRectClipState;
-
- state->highPriority = highPriority;
-
- // store the inverse drawing matrix
- Matrix4 roundRectDrawingMatrix = getOrthoMatrix();
- roundRectDrawingMatrix.multiply(*transform);
- state->matrix.loadInverse(roundRectDrawingMatrix);
-
- // compute area under rounded corners - only draws overlapping these rects need to be clipped
- for (int i = 0; i < 4; i++) {
- state->dangerRects[i] = bounds;
- }
- state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius;
- state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius;
- state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius;
- state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius;
- for (int i = 0; i < 4; i++) {
- transform->mapRect(state->dangerRects[i]);
-
- // round danger rects out as though they are AA geometry (since they essentially are)
- state->dangerRects[i].snapGeometryToPixelBoundaries(true);
- }
-
- // store RR area
- state->innerRect = bounds;
- state->innerRect.inset(radius);
- state->radius = radius;
-
- // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info
- roundRectClipState = state;
-}
-
-void Snapshot::setProjectionPathMask(const SkPath* path) {
- projectionPathMask = path;
-}
-
-static Snapshot* getClipRoot(Snapshot* target) {
- while (target->previous && target->previous->previous) {
- target = target->previous;
- }
- return target;
-}
-
-const ClipBase* Snapshot::serializeIntersectedClip(LinearAllocator& allocator,
- const ClipBase* recordedClip,
- const Matrix4& recordedClipTransform) {
- auto target = this;
- if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) {
- // Clip must be intersected with root, instead of current clip.
- target = getClipRoot(this);
- }
-
- return target->mClipArea->serializeIntersectedClip(allocator, recordedClip,
- recordedClipTransform);
-}
-
-void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) {
- if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) {
- // current clip is being replaced, but must intersect with clip root
- *mClipArea = *(getClipRoot(this)->mClipArea);
- }
- mClipArea->applyClip(recordedClip, transform);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Queries
-///////////////////////////////////////////////////////////////////////////////
-
-void Snapshot::dump() const {
- ALOGD("Snapshot %p, flags %x, prev %p, height %d, hasComplexClip %d", this, flags, previous,
- getViewportHeight(), !mClipArea->isSimple());
- const Rect& clipRect(mClipArea->getClipRect());
- ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d", clipRect.left, clipRect.top,
- clipRect.right, clipRect.bottom, mClipArea->isSimple());
-
- ALOGD(" Transform (at %p):", transform);
- transform->dump();
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
deleted file mode 100644
index 655f819ca41b..000000000000
--- a/libs/hwui/Snapshot.h
+++ /dev/null
@@ -1,279 +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.
- */
-
-#pragma once
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <ui/Region.h>
-#include <utils/LinearAllocator.h>
-#include <utils/RefBase.h>
-
-#include <SkClipOp.h>
-#include <SkRegion.h>
-
-#include "ClipArea.h"
-#include "Layer.h"
-#include "Matrix.h"
-#include "Outline.h"
-#include "Rect.h"
-#include "utils/Macros.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Temporary structure holding information for a single outline clip.
- *
- * These structures are treated as immutable once created, and only exist for a single frame, which
- * is why they may only be allocated with a LinearAllocator.
- */
-class RoundRectClipState {
-public:
- static void* operator new(size_t size) = delete;
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc<RoundRectClipState>(size);
- }
-
- bool areaRequiresRoundRectClip(const Rect& rect) const {
- return rect.intersects(dangerRects[0]) || rect.intersects(dangerRects[1]) ||
- rect.intersects(dangerRects[2]) || rect.intersects(dangerRects[3]);
- }
-
- bool highPriority;
- Matrix4 matrix;
- Rect dangerRects[4];
- Rect innerRect;
- float radius;
-};
-
-/**
- * A snapshot holds information about the current state of the rendering
- * surface. A snapshot is usually created whenever the user calls save()
- * and discarded when the user calls restore(). Once a snapshot is created,
- * it can hold information for deferred rendering.
- *
- * Each snapshot has a link to a previous snapshot, indicating the previous
- * state of the renderer.
- */
-class Snapshot {
-public:
- Snapshot();
- Snapshot(Snapshot* s, int saveFlags);
-
- /**
- * Various flags set on ::flags.
- */
- enum Flags {
- /**
- * Indicates that the clip region was modified. When this
- * snapshot is restored so must the clip.
- */
- kFlagClipSet = 0x1,
- /**
- * Indicates that this snapshot was created when saving
- * a new layer.
- */
- kFlagIsLayer = 0x2,
- /**
- * Indicates that this snapshot is a special type of layer
- * backed by an FBO. This flag only makes sense when the
- * flag kFlagIsLayer is also set.
- *
- * Viewport has been modified to fit the new Fbo, and must be
- * restored when this snapshot is restored.
- */
- kFlagIsFboLayer = 0x4,
- };
-
- /**
- * Modifies the current clip with the new clip rectangle and
- * the specified operation. The specified rectangle is transformed
- * by this snapshot's trasnformation.
- */
- void clip(const Rect& localClip, SkClipOp op);
-
- /**
- * Modifies the current clip with the new clip rectangle and
- * the specified operation. The specified rectangle is considered
- * already transformed.
- */
- void clipTransformed(const Rect& r, SkClipOp op = SkClipOp::kIntersect);
-
- /**
- * Modifies the current clip with the specified path and operation.
- */
- void clipPath(const SkPath& path, SkClipOp op);
-
- /**
- * Sets the current clip.
- */
- void setClip(float left, float top, float right, float bottom);
-
- /**
- * Returns the current clip in local coordinates. The clip rect is
- * transformed by the inverse transform matrix.
- */
- ANDROID_API const Rect& getLocalClip();
-
- /**
- * Returns the current clip in render target coordinates.
- */
- const Rect& getRenderTargetClip() const { return mClipArea->getClipRect(); }
-
- /*
- * Accessor functions so that the clip area can stay private
- */
- bool clipIsEmpty() const { return mClipArea->isEmpty(); }
- const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); }
- bool clipIsSimple() const { return mClipArea->isSimple(); }
- const ClipArea& getClipArea() const { return *mClipArea; }
- ClipArea& mutateClipArea() { return *mClipArea; }
-
- WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(
- LinearAllocator& allocator, const ClipBase* recordedClip,
- const Matrix4& recordedClipTransform);
- void applyClip(const ClipBase* clip, const Matrix4& transform);
-
- /**
- * Resets the clip to the specified rect.
- */
- void resetClip(float left, float top, float right, float bottom);
-
- void initializeViewport(int width, int height) {
- mViewportData.initialize(width, height);
- mClipAreaRoot.setViewportDimensions(width, height);
- }
-
- int getViewportWidth() const { return mViewportData.mWidth; }
- int getViewportHeight() const { return mViewportData.mHeight; }
- const Matrix4& getOrthoMatrix() const { return mViewportData.mOrthoMatrix; }
-
- const Vector3& getRelativeLightCenter() const { return mRelativeLightCenter; }
- void setRelativeLightCenter(const Vector3& lightCenter) { mRelativeLightCenter = lightCenter; }
-
- /**
- * Sets (and replaces) the current clipping outline
- *
- * If the current round rect clip is high priority, the incoming clip is ignored.
- */
- void setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius,
- bool highPriority);
-
- /**
- * Sets (and replaces) the current projection mask
- */
- void setProjectionPathMask(const SkPath* path);
-
- /**
- * Indicates whether the current transform has perspective components.
- */
- bool hasPerspectiveTransform() const;
-
- /**
- * Dirty flags.
- */
- int flags;
-
- /**
- * Previous snapshot.
- */
- Snapshot* previous;
-
- /**
- * A pointer to the currently active layer.
- *
- * This snapshot does not own the layer, this pointer must not be freed.
- */
- Layer* layer;
-
- /**
- * Target FBO used for rendering. Set to 0 when rendering directly
- * into the framebuffer.
- */
- GLuint fbo;
-
- /**
- * Local transformation. Holds the current translation, scale and
- * rotation values.
- *
- * This is a reference to a matrix owned by this snapshot or another
- * snapshot. This pointer must not be freed. See ::mTransformRoot.
- */
- mat4* transform;
-
- /**
- * Current alpha value. This value is 1 by default, but may be set by a DisplayList which
- * has translucent rendering in a non-overlapping View. This value will be used by
- * the renderer to set the alpha in the current color being used for ensuing drawing
- * operations. The value is inherited by child snapshots because the same value should
- * be applied to descendants of the current DisplayList (for example, a TextView contains
- * the base alpha value which should be applied to the child DisplayLists used for drawing
- * the actual text).
- */
- float alpha;
-
- /**
- * Current clipping round rect.
- *
- * Points to data not owned by the snapshot, and may only be replaced by subsequent RR clips,
- * never modified.
- */
- const RoundRectClipState* roundRectClipState;
-
- /**
- * Current projection masking path - used exclusively to mask projected, tessellated circles.
- */
- const SkPath* projectionPathMask;
-
- void dump() const;
-
-private:
- struct ViewportData {
- ViewportData() : mWidth(0), mHeight(0) {}
- void initialize(int width, int height) {
- mWidth = width;
- mHeight = height;
- mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
- }
-
- /*
- * Width and height of current viewport.
- *
- * The viewport is always defined to be (0, 0, width, height).
- */
- int mWidth;
- int mHeight;
- /**
- * Contains the current orthographic, projection matrix.
- */
- mat4 mOrthoMatrix;
- };
-
- mat4 mTransformRoot;
-
- ClipArea mClipAreaRoot;
- ClipArea* mClipArea;
- Rect mLocalClip;
-
- ViewportData mViewportData;
- Vector3 mRelativeLightCenter;
-
-}; // class Snapshot
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
deleted file mode 100644
index e371ac8da1e5..000000000000
--- a/libs/hwui/SpotShadow.cpp
+++ /dev/null
@@ -1,1120 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// The highest z value can't be higher than (CASTER_Z_CAP_RATIO * light.z)
-#define CASTER_Z_CAP_RATIO 0.95f
-
-// When there is no umbra, then just fake the umbra using
-// centroid * (1 - FAKE_UMBRA_SIZE_RATIO) + outline * FAKE_UMBRA_SIZE_RATIO
-#define FAKE_UMBRA_SIZE_RATIO 0.05f
-
-// When the polygon is about 90 vertices, the penumbra + umbra can reach 270 rays.
-// That is consider pretty fine tessllated polygon so far.
-// This is just to prevent using too much some memory when edge slicing is not
-// needed any more.
-#define FINE_TESSELLATED_POLYGON_RAY_NUMBER 270
-/**
- * Extra vertices for the corner for smoother corner.
- * Only for outer loop.
- * Note that we use such extra memory to avoid an extra loop.
- */
-// For half circle, we could add EXTRA_VERTEX_PER_PI vertices.
-// Set to 1 if we don't want to have any.
-#define SPOT_EXTRA_CORNER_VERTEX_PER_PI 18
-
-// For the whole polygon, the sum of all the deltas b/t normals is 2 * M_PI,
-// therefore, the maximum number of extra vertices will be twice bigger.
-#define SPOT_MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * SPOT_EXTRA_CORNER_VERTEX_PER_PI)
-
-// For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals.
-#define SPOT_CORNER_RADIANS_DIVISOR (M_PI / SPOT_EXTRA_CORNER_VERTEX_PER_PI)
-
-#define PENUMBRA_ALPHA 0.0f
-#define UMBRA_ALPHA 1.0f
-
-#include "SpotShadow.h"
-
-#include "ShadowTessellator.h"
-#include "Vertex.h"
-#include "VertexBuffer.h"
-#include "utils/MathUtils.h"
-
-#include <math.h>
-#include <stdlib.h>
-#include <utils/Log.h>
-#include <algorithm>
-
-// TODO: After we settle down the new algorithm, we can remove the old one and
-// its utility functions.
-// Right now, we still need to keep it for comparison purpose and future expansion.
-namespace android {
-namespace uirenderer {
-
-static const float EPSILON = 1e-7;
-
-/**
- * For each polygon's vertex, the light center will project it to the receiver
- * as one of the outline vertex.
- * For each outline vertex, we need to store the position and normal.
- * Normal here is defined against the edge by the current vertex and the next vertex.
- */
-struct OutlineData {
- Vector2 position;
- Vector2 normal;
- float radius;
-};
-
-/**
- * For each vertex, we need to keep track of its angle, whether it is penumbra or
- * umbra, and its corresponding vertex index.
- */
-struct SpotShadow::VertexAngleData {
- // The angle to the vertex from the centroid.
- float mAngle;
- // True is the vertex comes from penumbra, otherwise it comes from umbra.
- bool mIsPenumbra;
- // The index of the vertex described by this data.
- int mVertexIndex;
- void set(float angle, bool isPenumbra, int index) {
- mAngle = angle;
- mIsPenumbra = isPenumbra;
- mVertexIndex = index;
- }
-};
-
-/**
- * Calculate the angle between and x and a y coordinate.
- * The atan2 range from -PI to PI.
- */
-static float angle(const Vector2& point, const Vector2& center) {
- return atan2(point.y - center.y, point.x - center.x);
-}
-
-/**
- * Calculate the intersection of a ray with the line segment defined by two points.
- *
- * Returns a negative value in error conditions.
-
- * @param rayOrigin The start of the ray
- * @param dx The x vector of the ray
- * @param dy The y vector of the ray
- * @param p1 The first point defining the line segment
- * @param p2 The second point defining the line segment
- * @return The distance along the ray if it intersects with the line segment, negative if otherwise
- */
-static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy, const Vector2& p1,
- const Vector2& p2) {
- // The math below is derived from solving this formula, basically the
- // intersection point should stay on both the ray and the edge of (p1, p2).
- // solve([p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2]);
-
- float divisor = (dx * (p1.y - p2.y) + dy * p2.x - dy * p1.x);
- if (divisor == 0) return -1.0f; // error, invalid divisor
-
-#if DEBUG_SHADOW
- float interpVal = (dx * (p1.y - rayOrigin.y) + dy * rayOrigin.x - dy * p1.x) / divisor;
- if (interpVal < 0 || interpVal > 1) {
- ALOGW("rayIntersectPoints is hitting outside the segment %f", interpVal);
- }
-#endif
-
- float distance = (p1.x * (rayOrigin.y - p2.y) + p2.x * (p1.y - rayOrigin.y) +
- rayOrigin.x * (p2.y - p1.y)) /
- divisor;
-
- return distance; // may be negative in error cases
-}
-
-/**
- * Sort points by their X coordinates
- *
- * @param points the points as a Vector2 array.
- * @param pointsLength the number of vertices of the polygon.
- */
-void SpotShadow::xsort(Vector2* points, int pointsLength) {
- auto cmp = [](const Vector2& a, const Vector2& b) -> bool { return a.x < b.x; };
- std::sort(points, points + pointsLength, cmp);
-}
-
-/**
- * compute the convex hull of a collection of Points
- *
- * @param points the points as a Vector2 array.
- * @param pointsLength the number of vertices of the polygon.
- * @param retPoly pre allocated array of floats to put the vertices
- * @return the number of points in the polygon 0 if no intersection
- */
-int SpotShadow::hull(Vector2* points, int pointsLength, Vector2* retPoly) {
- xsort(points, pointsLength);
- int n = pointsLength;
- Vector2 lUpper[n];
- lUpper[0] = points[0];
- lUpper[1] = points[1];
-
- int lUpperSize = 2;
-
- for (int i = 2; i < n; i++) {
- lUpper[lUpperSize] = points[i];
- lUpperSize++;
-
- while (lUpperSize > 2 &&
- !ccw(lUpper[lUpperSize - 3].x, lUpper[lUpperSize - 3].y, lUpper[lUpperSize - 2].x,
- lUpper[lUpperSize - 2].y, lUpper[lUpperSize - 1].x, lUpper[lUpperSize - 1].y)) {
- // Remove the middle point of the three last
- lUpper[lUpperSize - 2].x = lUpper[lUpperSize - 1].x;
- lUpper[lUpperSize - 2].y = lUpper[lUpperSize - 1].y;
- lUpperSize--;
- }
- }
-
- Vector2 lLower[n];
- lLower[0] = points[n - 1];
- lLower[1] = points[n - 2];
-
- int lLowerSize = 2;
-
- for (int i = n - 3; i >= 0; i--) {
- lLower[lLowerSize] = points[i];
- lLowerSize++;
-
- while (lLowerSize > 2 &&
- !ccw(lLower[lLowerSize - 3].x, lLower[lLowerSize - 3].y, lLower[lLowerSize - 2].x,
- lLower[lLowerSize - 2].y, lLower[lLowerSize - 1].x, lLower[lLowerSize - 1].y)) {
- // Remove the middle point of the three last
- lLower[lLowerSize - 2] = lLower[lLowerSize - 1];
- lLowerSize--;
- }
- }
-
- // output points in CW ordering
- const int total = lUpperSize + lLowerSize - 2;
- int outIndex = total - 1;
- for (int i = 0; i < lUpperSize; i++) {
- retPoly[outIndex] = lUpper[i];
- outIndex--;
- }
-
- for (int i = 1; i < lLowerSize - 1; i++) {
- retPoly[outIndex] = lLower[i];
- outIndex--;
- }
- // TODO: Add test harness which verify that all the points are inside the hull.
- return total;
-}
-
-/**
- * Test whether the 3 points form a counter clockwise turn.
- *
- * @return true if a right hand turn
- */
-bool SpotShadow::ccw(float ax, float ay, float bx, float by, float cx, float cy) {
- return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) > EPSILON;
-}
-
-/**
- * Sort points about a center point
- *
- * @param poly The in and out polyogon as a Vector2 array.
- * @param polyLength The number of vertices of the polygon.
- * @param center the center ctr[0] = x , ctr[1] = y to sort around.
- */
-void SpotShadow::sort(Vector2* poly, int polyLength, const Vector2& center) {
- quicksortCirc(poly, 0, polyLength - 1, center);
-}
-
-/**
- * Swap points pointed to by i and j
- */
-void SpotShadow::swap(Vector2* points, int i, int j) {
- Vector2 temp = points[i];
- points[i] = points[j];
- points[j] = temp;
-}
-
-/**
- * quick sort implementation about the center.
- */
-void SpotShadow::quicksortCirc(Vector2* points, int low, int high, const Vector2& center) {
- int i = low, j = high;
- int p = low + (high - low) / 2;
- float pivot = angle(points[p], center);
- while (i <= j) {
- while (angle(points[i], center) > pivot) {
- i++;
- }
- while (angle(points[j], center) < pivot) {
- j--;
- }
-
- if (i <= j) {
- swap(points, i, j);
- i++;
- j--;
- }
- }
- if (low < j) quicksortCirc(points, low, j, center);
- if (i < high) quicksortCirc(points, i, high, center);
-}
-
-/**
- * Test whether a point is inside the polygon.
- *
- * @param testPoint the point to test
- * @param poly the polygon
- * @return true if the testPoint is inside the poly.
- */
-bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len) {
- bool c = false;
- float testx = testPoint.x;
- float testy = testPoint.y;
- for (int i = 0, j = len - 1; i < len; j = i++) {
- float startX = poly[j].x;
- float startY = poly[j].y;
- float endX = poly[i].x;
- float endY = poly[i].y;
-
- if (((endY > testy) != (startY > testy)) &&
- (testx < (startX - endX) * (testy - endY) / (startY - endY) + endX)) {
- c = !c;
- }
- }
- return c;
-}
-
-/**
- * Reverse the polygon
- *
- * @param polygon the polygon as a Vector2 array
- * @param len the number of points of the polygon
- */
-void SpotShadow::reverse(Vector2* polygon, int len) {
- int n = len / 2;
- for (int i = 0; i < n; i++) {
- Vector2 tmp = polygon[i];
- int k = len - 1 - i;
- polygon[i] = polygon[k];
- polygon[k] = tmp;
- }
-}
-
-/**
- * Compute a horizontal circular polygon about point (x , y , height) of radius
- * (size)
- *
- * @param points number of the points of the output polygon.
- * @param lightCenter the center of the light.
- * @param size the light size.
- * @param ret result polygon.
- */
-void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter, float size,
- Vector3* ret) {
- // TODO: Caching all the sin / cos values and store them in a look up table.
- for (int i = 0; i < points; i++) {
- float angle = 2 * i * M_PI / points;
- ret[i].x = cosf(angle) * size + lightCenter.x;
- ret[i].y = sinf(angle) * size + lightCenter.y;
- ret[i].z = lightCenter.z;
- }
-}
-
-/**
- * From light center, project one vertex to the z=0 surface and get the outline.
- *
- * @param outline The result which is the outline position.
- * @param lightCenter The center of light.
- * @param polyVertex The input polygon's vertex.
- *
- * @return float The ratio of (polygon.z / light.z - polygon.z)
- */
-float SpotShadow::projectCasterToOutline(Vector2& outline, const Vector3& lightCenter,
- const Vector3& polyVertex) {
- float lightToPolyZ = lightCenter.z - polyVertex.z;
- float ratioZ = CASTER_Z_CAP_RATIO;
- if (lightToPolyZ != 0) {
- // If any caster's vertex is almost above the light, we just keep it as 95%
- // of the height of the light.
- ratioZ = MathUtils::clamp(polyVertex.z / lightToPolyZ, 0.0f, CASTER_Z_CAP_RATIO);
- }
-
- outline.x = polyVertex.x - ratioZ * (lightCenter.x - polyVertex.x);
- outline.y = polyVertex.y - ratioZ * (lightCenter.y - polyVertex.y);
- return ratioZ;
-}
-
-/**
- * Generate the shadow spot light of shape lightPoly and a object poly
- *
- * @param isCasterOpaque whether the caster is opaque
- * @param lightCenter the center of the light
- * @param lightSize the radius of the light
- * @param poly x,y,z vertexes of a convex polygon that occludes the light source
- * @param polyLength number of vertexes of the occluding polygon
- * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return
- * empty strip if error.
- */
-void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCenter, float lightSize,
- const Vector3* poly, int polyLength, const Vector3& polyCentroid,
- VertexBuffer& shadowTriangleStrip) {
- if (CC_UNLIKELY(lightCenter.z <= 0)) {
- ALOGW("Relative Light Z is not positive. No spot shadow!");
- return;
- }
- if (CC_UNLIKELY(polyLength < 3)) {
-#if DEBUG_SHADOW
- ALOGW("Invalid polygon length. No spot shadow!");
-#endif
- return;
- }
- OutlineData outlineData[polyLength];
- Vector2 outlineCentroid;
- // Calculate the projected outline for each polygon's vertices from the light center.
- //
- // O Light
- // /
- // /
- // . Polygon vertex
- // /
- // /
- // O Outline vertices
- //
- // Ratio = (Poly - Outline) / (Light - Poly)
- // Outline.x = Poly.x - Ratio * (Light.x - Poly.x)
- // Outline's radius / Light's radius = Ratio
-
- // Compute the last outline vertex to make sure we can get the normal and outline
- // in one single loop.
- projectCasterToOutline(outlineData[polyLength - 1].position, lightCenter, poly[polyLength - 1]);
-
- // Take the outline's polygon, calculate the normal for each outline edge.
- int currentNormalIndex = polyLength - 1;
- int nextNormalIndex = 0;
-
- for (int i = 0; i < polyLength; i++) {
- float ratioZ = projectCasterToOutline(outlineData[i].position, lightCenter, poly[i]);
- outlineData[i].radius = ratioZ * lightSize;
-
- outlineData[currentNormalIndex].normal = ShadowTessellator::calculateNormal(
- outlineData[currentNormalIndex].position, outlineData[nextNormalIndex].position);
- currentNormalIndex = (currentNormalIndex + 1) % polyLength;
- nextNormalIndex++;
- }
-
- projectCasterToOutline(outlineCentroid, lightCenter, polyCentroid);
-
- int penumbraIndex = 0;
- // Then each polygon's vertex produce at minmal 2 penumbra vertices.
- // Since the size can be dynamic here, we keep track of the size and update
- // the real size at the end.
- int allocatedPenumbraLength = 2 * polyLength + SPOT_MAX_EXTRA_CORNER_VERTEX_NUMBER;
- Vector2 penumbra[allocatedPenumbraLength];
- int totalExtraCornerSliceNumber = 0;
-
- Vector2 umbra[polyLength];
-
- // When centroid is covered by all circles from outline, then we consider
- // the umbra is invalid, and we will tune down the shadow strength.
- bool hasValidUmbra = true;
- // We need the minimal of RaitoVI to decrease the spot shadow strength accordingly.
- float minRaitoVI = FLT_MAX;
-
- for (int i = 0; i < polyLength; i++) {
- // Generate all the penumbra's vertices only using the (outline vertex + normal * radius)
- // There is no guarantee that the penumbra is still convex, but for
- // each outline vertex, it will connect to all its corresponding penumbra vertices as
- // triangle fans. And for neighber penumbra vertex, it will be a trapezoid.
- //
- // Penumbra Vertices marked as Pi
- // Outline Vertices marked as Vi
- // (P3)
- // (P2) | ' (P4)
- // (P1)' | | '
- // ' | | '
- // (P0) ------------------------------------------------(P5)
- // | (V0) |(V1)
- // | |
- // | |
- // | |
- // | |
- // | |
- // | |
- // | |
- // | |
- // (V3)-----------------------------------(V2)
- int preNormalIndex = (i + polyLength - 1) % polyLength;
-
- const Vector2& previousNormal = outlineData[preNormalIndex].normal;
- const Vector2& currentNormal = outlineData[i].normal;
-
- // Depending on how roundness we want for each corner, we can subdivide
- // further here and/or introduce some heuristic to decide how much the
- // subdivision should be.
- int currentExtraSliceNumber = ShadowTessellator::getExtraVertexNumber(
- previousNormal, currentNormal, SPOT_CORNER_RADIANS_DIVISOR);
-
- int currentCornerSliceNumber = 1 + currentExtraSliceNumber;
- totalExtraCornerSliceNumber += currentExtraSliceNumber;
-#if DEBUG_SHADOW
- ALOGD("currentExtraSliceNumber should be %d", currentExtraSliceNumber);
- ALOGD("currentCornerSliceNumber should be %d", currentCornerSliceNumber);
- ALOGD("totalCornerSliceNumber is %d", totalExtraCornerSliceNumber);
-#endif
- if (CC_UNLIKELY(totalExtraCornerSliceNumber > SPOT_MAX_EXTRA_CORNER_VERTEX_NUMBER)) {
- currentCornerSliceNumber = 1;
- }
- for (int k = 0; k <= currentCornerSliceNumber; k++) {
- Vector2 avgNormal =
- (previousNormal * (currentCornerSliceNumber - k) + currentNormal * k) /
- currentCornerSliceNumber;
- avgNormal.normalize();
- penumbra[penumbraIndex++] = outlineData[i].position + avgNormal * outlineData[i].radius;
- }
-
- // Compute the umbra by the intersection from the outline's centroid!
- //
- // (V) ------------------------------------
- // | ' |
- // | ' |
- // | ' (I) |
- // | ' |
- // | ' (C) |
- // | |
- // | |
- // | |
- // | |
- // ------------------------------------
- //
- // Connect a line b/t the outline vertex (V) and the centroid (C), it will
- // intersect with the outline vertex's circle at point (I).
- // Now, ratioVI = VI / VC, ratioIC = IC / VC
- // Then the intersetion point can be computed as Ixy = Vxy * ratioIC + Cxy * ratioVI;
- //
- // When all of the outline circles cover the the outline centroid, (like I is
- // on the other side of C), there is no real umbra any more, so we just fake
- // a small area around the centroid as the umbra, and tune down the spot
- // shadow's umbra strength to simulate the effect the whole shadow will
- // become lighter in this case.
- // The ratio can be simulated by using the inverse of maximum of ratioVI for
- // all (V).
- float distOutline = (outlineData[i].position - outlineCentroid).length();
- if (CC_UNLIKELY(distOutline == 0)) {
- // If the outline has 0 area, then there is no spot shadow anyway.
- ALOGW("Outline has 0 area, no spot shadow!");
- return;
- }
-
- float ratioVI = outlineData[i].radius / distOutline;
- minRaitoVI = std::min(minRaitoVI, ratioVI);
- if (ratioVI >= (1 - FAKE_UMBRA_SIZE_RATIO)) {
- ratioVI = (1 - FAKE_UMBRA_SIZE_RATIO);
- }
- // When we know we don't have valid umbra, don't bother to compute the
- // values below. But we can't skip the loop yet since we want to know the
- // maximum ratio.
- float ratioIC = 1 - ratioVI;
- umbra[i] = outlineData[i].position * ratioIC + outlineCentroid * ratioVI;
- }
-
- hasValidUmbra = (minRaitoVI <= 1.0);
- float shadowStrengthScale = 1.0;
- if (!hasValidUmbra) {
-#if DEBUG_SHADOW
- ALOGW("The object is too close to the light or too small, no real umbra!");
-#endif
- for (int i = 0; i < polyLength; i++) {
- umbra[i] = outlineData[i].position * FAKE_UMBRA_SIZE_RATIO +
- outlineCentroid * (1 - FAKE_UMBRA_SIZE_RATIO);
- }
- shadowStrengthScale = 1.0 / minRaitoVI;
- }
-
- int penumbraLength = penumbraIndex;
- int umbraLength = polyLength;
-
-#if DEBUG_SHADOW
- ALOGD("penumbraLength is %d , allocatedPenumbraLength %d", penumbraLength,
- allocatedPenumbraLength);
- dumpPolygon(poly, polyLength, "input poly");
- dumpPolygon(penumbra, penumbraLength, "penumbra");
- dumpPolygon(umbra, umbraLength, "umbra");
- ALOGD("hasValidUmbra is %d and shadowStrengthScale is %f", hasValidUmbra, shadowStrengthScale);
-#endif
-
- // The penumbra and umbra needs to be in convex shape to keep consistency
- // and quality.
- // Since we are still shooting rays to penumbra, it needs to be convex.
- // Umbra can be represented as a fan from the centroid, but visually umbra
- // looks nicer when it is convex.
- Vector2 finalUmbra[umbraLength];
- Vector2 finalPenumbra[penumbraLength];
- int finalUmbraLength = hull(umbra, umbraLength, finalUmbra);
- int finalPenumbraLength = hull(penumbra, penumbraLength, finalPenumbra);
-
- generateTriangleStrip(isCasterOpaque, shadowStrengthScale, finalPenumbra, finalPenumbraLength,
- finalUmbra, finalUmbraLength, poly, polyLength, shadowTriangleStrip,
- outlineCentroid);
-}
-
-/**
- * This is only for experimental purpose.
- * After intersections are calculated, we could smooth the polygon if needed.
- * So far, we don't think it is more appealing yet.
- *
- * @param level The level of smoothness.
- * @param rays The total number of rays.
- * @param rayDist (In and Out) The distance for each ray.
- *
- */
-void SpotShadow::smoothPolygon(int level, int rays, float* rayDist) {
- for (int k = 0; k < level; k++) {
- for (int i = 0; i < rays; i++) {
- float p1 = rayDist[(rays - 1 + i) % rays];
- float p2 = rayDist[i];
- float p3 = rayDist[(i + 1) % rays];
- rayDist[i] = (p1 + p2 * 2 + p3) / 4;
- }
- }
-}
-
-// Index pair is meant for storing the tessellation information for the penumbra
-// area. One index must come from exterior tangent of the circles, the other one
-// must come from the interior tangent of the circles.
-struct IndexPair {
- int outerIndex;
- int innerIndex;
-};
-
-// For one penumbra vertex, find the cloest umbra vertex and return its index.
-inline int getClosestUmbraIndex(const Vector2& pivot, const Vector2* polygon, int polygonLength) {
- float minLengthSquared = FLT_MAX;
- int resultIndex = -1;
- bool hasDecreased = false;
- // Starting with some negative offset, assuming both umbra and penumbra are starting
- // at the same angle, this can help to find the result faster.
- // Normally, loop 3 times, we can find the closest point.
- int offset = polygonLength - 2;
- for (int i = 0; i < polygonLength; i++) {
- int currentIndex = (i + offset) % polygonLength;
- float currentLengthSquared = (pivot - polygon[currentIndex]).lengthSquared();
- if (currentLengthSquared < minLengthSquared) {
- if (minLengthSquared != FLT_MAX) {
- hasDecreased = true;
- }
- minLengthSquared = currentLengthSquared;
- resultIndex = currentIndex;
- } else if (currentLengthSquared > minLengthSquared && hasDecreased) {
- // Early break b/c we have found the closet one and now the length
- // is increasing again.
- break;
- }
- }
- if (resultIndex == -1) {
- ALOGE("resultIndex is -1, the polygon must be invalid!");
- resultIndex = 0;
- }
- return resultIndex;
-}
-
-// Allow some epsilon here since the later ray intersection did allow for some small
-// floating point error, when the intersection point is slightly outside the segment.
-inline bool sameDirections(bool isPositiveCross, float a, float b) {
- if (isPositiveCross) {
- return a >= -EPSILON && b >= -EPSILON;
- } else {
- return a <= EPSILON && b <= EPSILON;
- }
-}
-
-// Find the right polygon edge to shoot the ray at.
-inline int findPolyIndex(bool isPositiveCross, int startPolyIndex, const Vector2& umbraDir,
- const Vector2* polyToCentroid, int polyLength) {
- // Make sure we loop with a bound.
- for (int i = 0; i < polyLength; i++) {
- int currentIndex = (i + startPolyIndex) % polyLength;
- const Vector2& currentToCentroid = polyToCentroid[currentIndex];
- const Vector2& nextToCentroid = polyToCentroid[(currentIndex + 1) % polyLength];
-
- float currentCrossUmbra = currentToCentroid.cross(umbraDir);
- float umbraCrossNext = umbraDir.cross(nextToCentroid);
- if (sameDirections(isPositiveCross, currentCrossUmbra, umbraCrossNext)) {
-#if DEBUG_SHADOW
- ALOGD("findPolyIndex loop %d times , index %d", i, currentIndex);
-#endif
- return currentIndex;
- }
- }
- LOG_ALWAYS_FATAL("Can't find the right polygon's edge from startPolyIndex %d", startPolyIndex);
- return -1;
-}
-
-// Generate the index pair for penumbra / umbra vertices, and more penumbra vertices
-// if needed.
-inline void genNewPenumbraAndPairWithUmbra(const Vector2* penumbra, int penumbraLength,
- const Vector2* umbra, int umbraLength,
- Vector2* newPenumbra, int& newPenumbraIndex,
- IndexPair* verticesPair, int& verticesPairIndex) {
- // In order to keep everything in just one loop, we need to pre-compute the
- // closest umbra vertex for the last penumbra vertex.
- int previousClosestUmbraIndex =
- getClosestUmbraIndex(penumbra[penumbraLength - 1], umbra, umbraLength);
- for (int i = 0; i < penumbraLength; i++) {
- const Vector2& currentPenumbraVertex = penumbra[i];
- // For current penumbra vertex, starting from previousClosestUmbraIndex,
- // then check the next one until the distance increase.
- // The last one before the increase is the umbra vertex we need to pair with.
- float currentLengthSquared =
- (currentPenumbraVertex - umbra[previousClosestUmbraIndex]).lengthSquared();
- int currentClosestUmbraIndex = previousClosestUmbraIndex;
- int indexDelta = 0;
- for (int j = 1; j < umbraLength; j++) {
- int newUmbraIndex = (previousClosestUmbraIndex + j) % umbraLength;
- float newLengthSquared = (currentPenumbraVertex - umbra[newUmbraIndex]).lengthSquared();
- if (newLengthSquared > currentLengthSquared) {
- // currentClosestUmbraIndex is the umbra vertex's index which has
- // currently found smallest distance, so we can simply break here.
- break;
- } else {
- currentLengthSquared = newLengthSquared;
- indexDelta++;
- currentClosestUmbraIndex = newUmbraIndex;
- }
- }
-
- if (indexDelta > 1) {
- // For those umbra don't have penumbra, generate new penumbra vertices by
- // interpolation.
- //
- // Assuming Pi for penumbra vertices, and Ui for umbra vertices.
- // In the case like below P1 paired with U1 and P2 paired with U5.
- // U2 to U4 are unpaired umbra vertices.
- //
- // P1 P2
- // | |
- // U1 U2 U3 U4 U5
- //
- // We will need to generate 3 more penumbra vertices P1.1, P1.2, P1.3
- // to pair with U2 to U4.
- //
- // P1 P1.1 P1.2 P1.3 P2
- // | | | | |
- // U1 U2 U3 U4 U5
- //
- // That distance ratio b/t Ui to U1 and Ui to U5 decides its paired penumbra
- // vertex's location.
- int newPenumbraNumber = indexDelta - 1;
-
- float accumulatedDeltaLength[indexDelta];
- float totalDeltaLength = 0;
-
- // To save time, cache the previous umbra vertex info outside the loop
- // and update each loop.
- Vector2 previousClosestUmbra = umbra[previousClosestUmbraIndex];
- Vector2 skippedUmbra;
- // Use umbra data to precompute the length b/t unpaired umbra vertices,
- // and its ratio against the total length.
- for (int k = 0; k < indexDelta; k++) {
- int skippedUmbraIndex = (previousClosestUmbraIndex + k + 1) % umbraLength;
- skippedUmbra = umbra[skippedUmbraIndex];
- float currentDeltaLength = (skippedUmbra - previousClosestUmbra).length();
-
- totalDeltaLength += currentDeltaLength;
- accumulatedDeltaLength[k] = totalDeltaLength;
-
- previousClosestUmbra = skippedUmbra;
- }
-
- const Vector2& previousPenumbra = penumbra[(i + penumbraLength - 1) % penumbraLength];
- // Then for each unpaired umbra vertex, create a new penumbra by the ratio,
- // and pair them togehter.
- for (int k = 0; k < newPenumbraNumber; k++) {
- float weightForCurrentPenumbra = 1.0f;
- if (totalDeltaLength != 0.0f) {
- weightForCurrentPenumbra = accumulatedDeltaLength[k] / totalDeltaLength;
- }
- float weightForPreviousPenumbra = 1.0f - weightForCurrentPenumbra;
-
- Vector2 interpolatedPenumbra = currentPenumbraVertex * weightForCurrentPenumbra +
- previousPenumbra * weightForPreviousPenumbra;
-
- int skippedUmbraIndex = (previousClosestUmbraIndex + k + 1) % umbraLength;
- verticesPair[verticesPairIndex].outerIndex = newPenumbraIndex;
- verticesPair[verticesPairIndex].innerIndex = skippedUmbraIndex;
- verticesPairIndex++;
- newPenumbra[newPenumbraIndex++] = interpolatedPenumbra;
- }
- }
- verticesPair[verticesPairIndex].outerIndex = newPenumbraIndex;
- verticesPair[verticesPairIndex].innerIndex = currentClosestUmbraIndex;
- verticesPairIndex++;
- newPenumbra[newPenumbraIndex++] = currentPenumbraVertex;
-
- previousClosestUmbraIndex = currentClosestUmbraIndex;
- }
-}
-
-// Precompute all the polygon's vector, return true if the reference cross product is positive.
-inline bool genPolyToCentroid(const Vector2* poly2d, int polyLength, const Vector2& centroid,
- Vector2* polyToCentroid) {
- for (int j = 0; j < polyLength; j++) {
- polyToCentroid[j] = poly2d[j] - centroid;
- // Normalize these vectors such that we can use epsilon comparison after
- // computing their cross products with another normalized vector.
- polyToCentroid[j].normalize();
- }
- float refCrossProduct = 0;
- for (int j = 0; j < polyLength; j++) {
- refCrossProduct = polyToCentroid[j].cross(polyToCentroid[(j + 1) % polyLength]);
- if (refCrossProduct != 0) {
- break;
- }
- }
-
- return refCrossProduct > 0;
-}
-
-// For one umbra vertex, shoot an ray from centroid to it.
-// If the ray hit the polygon first, then return the intersection point as the
-// closer vertex.
-inline Vector2 getCloserVertex(const Vector2& umbraVertex, const Vector2& centroid,
- const Vector2* poly2d, int polyLength, const Vector2* polyToCentroid,
- bool isPositiveCross, int& previousPolyIndex) {
- Vector2 umbraToCentroid = umbraVertex - centroid;
- float distanceToUmbra = umbraToCentroid.length();
- umbraToCentroid = umbraToCentroid / distanceToUmbra;
-
- // previousPolyIndex is updated for each item such that we can minimize the
- // looping inside findPolyIndex();
- previousPolyIndex = findPolyIndex(isPositiveCross, previousPolyIndex, umbraToCentroid,
- polyToCentroid, polyLength);
-
- float dx = umbraToCentroid.x;
- float dy = umbraToCentroid.y;
- float distanceToIntersectPoly =
- rayIntersectPoints(centroid, dx, dy, poly2d[previousPolyIndex],
- poly2d[(previousPolyIndex + 1) % polyLength]);
- if (distanceToIntersectPoly < 0) {
- distanceToIntersectPoly = 0;
- }
-
- // Pick the closer one as the occluded area vertex.
- Vector2 closerVertex;
- if (distanceToIntersectPoly < distanceToUmbra) {
- closerVertex.x = centroid.x + dx * distanceToIntersectPoly;
- closerVertex.y = centroid.y + dy * distanceToIntersectPoly;
- } else {
- closerVertex = umbraVertex;
- }
-
- return closerVertex;
-}
-
-/**
- * Generate a triangle strip given two convex polygon
-**/
-void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrengthScale,
- Vector2* penumbra, int penumbraLength, Vector2* umbra,
- int umbraLength, const Vector3* poly, int polyLength,
- VertexBuffer& shadowTriangleStrip, const Vector2& centroid) {
- bool hasOccludedUmbraArea = false;
- Vector2 poly2d[polyLength];
-
- if (isCasterOpaque) {
- for (int i = 0; i < polyLength; i++) {
- poly2d[i].x = poly[i].x;
- poly2d[i].y = poly[i].y;
- }
- // Make sure the centroid is inside the umbra, otherwise, fall back to the
- // approach as if there is no occluded umbra area.
- if (testPointInsidePolygon(centroid, poly2d, polyLength)) {
- hasOccludedUmbraArea = true;
- }
- }
-
- // For each penumbra vertex, find its corresponding closest umbra vertex index.
- //
- // Penumbra Vertices marked as Pi
- // Umbra Vertices marked as Ui
- // (P3)
- // (P2) | ' (P4)
- // (P1)' | | '
- // ' | | '
- // (P0) ------------------------------------------------(P5)
- // | (U0) |(U1)
- // | |
- // | |(U2) (P5.1)
- // | |
- // | |
- // | |
- // | |
- // | |
- // | |
- // (U4)-----------------------------------(U3) (P6)
- //
- // At least, like P0, P1, P2, they will find the matching umbra as U0.
- // If we jump over some umbra vertex without matching penumbra vertex, then
- // we will generate some new penumbra vertex by interpolation. Like P6 is
- // matching U3, but U2 is not matched with any penumbra vertex.
- // So interpolate P5.1 out and match U2.
- // In this way, every umbra vertex will have a matching penumbra vertex.
- //
- // The total pair number can be as high as umbraLength + penumbraLength.
- const int maxNewPenumbraLength = umbraLength + penumbraLength;
- IndexPair verticesPair[maxNewPenumbraLength];
- int verticesPairIndex = 0;
-
- // Cache all the existing penumbra vertices and newly interpolated vertices into a
- // a new array.
- Vector2 newPenumbra[maxNewPenumbraLength];
- int newPenumbraIndex = 0;
-
- // For each penumbra vertex, find its closet umbra vertex by comparing the
- // neighbor umbra vertices.
- genNewPenumbraAndPairWithUmbra(penumbra, penumbraLength, umbra, umbraLength, newPenumbra,
- newPenumbraIndex, verticesPair, verticesPairIndex);
- ShadowTessellator::checkOverflow(verticesPairIndex, maxNewPenumbraLength, "Spot pair");
- ShadowTessellator::checkOverflow(newPenumbraIndex, maxNewPenumbraLength, "Spot new penumbra");
-#if DEBUG_SHADOW
- for (int i = 0; i < umbraLength; i++) {
- ALOGD("umbra i %d, [%f, %f]", i, umbra[i].x, umbra[i].y);
- }
- for (int i = 0; i < newPenumbraIndex; i++) {
- ALOGD("new penumbra i %d, [%f, %f]", i, newPenumbra[i].x, newPenumbra[i].y);
- }
- for (int i = 0; i < verticesPairIndex; i++) {
- ALOGD("index i %d, [%d, %d]", i, verticesPair[i].outerIndex, verticesPair[i].innerIndex);
- }
-#endif
-
- // For the size of vertex buffer, we need 3 rings, one has newPenumbraSize,
- // one has umbraLength, the last one has at most umbraLength.
- //
- // For the size of index buffer, the umbra area needs (2 * umbraLength + 2).
- // The penumbra one can vary a bit, but it is bounded by (2 * verticesPairIndex + 2).
- // And 2 more for jumping between penumbra to umbra.
- const int newPenumbraLength = newPenumbraIndex;
- const int totalVertexCount = newPenumbraLength + umbraLength * 2;
- const int totalIndexCount = 2 * umbraLength + 2 * verticesPairIndex + 6;
- AlphaVertex* shadowVertices = shadowTriangleStrip.alloc<AlphaVertex>(totalVertexCount);
- uint16_t* indexBuffer = shadowTriangleStrip.allocIndices<uint16_t>(totalIndexCount);
- int vertexBufferIndex = 0;
- int indexBufferIndex = 0;
-
- // Fill the IB and VB for the penumbra area.
- for (int i = 0; i < newPenumbraLength; i++) {
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x, newPenumbra[i].y,
- PENUMBRA_ALPHA);
- }
- // Since the umbra can be a faked one when the occluder is too high, the umbra should be lighter
- // in this case.
- float scaledUmbraAlpha = UMBRA_ALPHA * shadowStrengthScale;
-
- for (int i = 0; i < umbraLength; i++) {
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], umbra[i].x, umbra[i].y,
- scaledUmbraAlpha);
- }
-
- for (int i = 0; i < verticesPairIndex; i++) {
- indexBuffer[indexBufferIndex++] = verticesPair[i].outerIndex;
- // All umbra index need to be offseted by newPenumbraSize.
- indexBuffer[indexBufferIndex++] = verticesPair[i].innerIndex + newPenumbraLength;
- }
- indexBuffer[indexBufferIndex++] = verticesPair[0].outerIndex;
- indexBuffer[indexBufferIndex++] = verticesPair[0].innerIndex + newPenumbraLength;
-
- // Now fill the IB and VB for the umbra area.
- // First duplicated the index from previous strip and the first one for the
- // degenerated triangles.
- indexBuffer[indexBufferIndex] = indexBuffer[indexBufferIndex - 1];
- indexBufferIndex++;
- indexBuffer[indexBufferIndex++] = newPenumbraLength + 0;
- // Save the first VB index for umbra area in order to close the loop.
- int savedStartIndex = vertexBufferIndex;
-
- if (hasOccludedUmbraArea) {
- // Precompute all the polygon's vector, and the reference cross product,
- // in order to find the right polygon edge for the ray to intersect.
- Vector2 polyToCentroid[polyLength];
- bool isPositiveCross = genPolyToCentroid(poly2d, polyLength, centroid, polyToCentroid);
-
- // Because both the umbra and polygon are going in the same direction,
- // we can save the previous polygon index to make sure we have less polygon
- // vertex to compute for each ray.
- int previousPolyIndex = 0;
- for (int i = 0; i < umbraLength; i++) {
- // Shoot a ray from centroid to each umbra vertices and pick the one with
- // shorter distance to the centroid, b/t the umbra vertex or the intersection point.
- Vector2 closerVertex =
- getCloserVertex(umbra[i], centroid, poly2d, polyLength, polyToCentroid,
- isPositiveCross, previousPolyIndex);
-
- // We already stored the umbra vertices, just need to add the occlued umbra's ones.
- indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
- indexBuffer[indexBufferIndex++] = vertexBufferIndex;
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], closerVertex.x, closerVertex.y,
- scaledUmbraAlpha);
- }
- } else {
- // If there is no occluded umbra at all, then draw the triangle fan
- // starting from the centroid to all umbra vertices.
- int lastCentroidIndex = vertexBufferIndex;
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid.x, centroid.y,
- scaledUmbraAlpha);
- for (int i = 0; i < umbraLength; i++) {
- indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
- indexBuffer[indexBufferIndex++] = lastCentroidIndex;
- }
- }
- // Closing the umbra area triangle's loop here.
- indexBuffer[indexBufferIndex++] = newPenumbraLength;
- indexBuffer[indexBufferIndex++] = savedStartIndex;
-
- // At the end, update the real index and vertex buffer size.
- shadowTriangleStrip.updateVertexCount(vertexBufferIndex);
- shadowTriangleStrip.updateIndexCount(indexBufferIndex);
- ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Spot Vertex Buffer");
- ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Spot Index Buffer");
-
- shadowTriangleStrip.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices);
- shadowTriangleStrip.computeBounds<AlphaVertex>();
-}
-
-#if DEBUG_SHADOW
-
-#define TEST_POINT_NUMBER 128
-/**
- * Calculate the bounds for generating random test points.
- */
-void SpotShadow::updateBound(const Vector2 inVector, Vector2& lowerBound, Vector2& upperBound) {
- if (inVector.x < lowerBound.x) {
- lowerBound.x = inVector.x;
- }
-
- if (inVector.y < lowerBound.y) {
- lowerBound.y = inVector.y;
- }
-
- if (inVector.x > upperBound.x) {
- upperBound.x = inVector.x;
- }
-
- if (inVector.y > upperBound.y) {
- upperBound.y = inVector.y;
- }
-}
-
-/**
- * For debug purpose, when things go wrong, dump the whole polygon data.
- */
-void SpotShadow::dumpPolygon(const Vector2* poly, int polyLength, const char* polyName) {
- for (int i = 0; i < polyLength; i++) {
- ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y);
- }
-}
-
-/**
- * For debug purpose, when things go wrong, dump the whole polygon data.
- */
-void SpotShadow::dumpPolygon(const Vector3* poly, int polyLength, const char* polyName) {
- for (int i = 0; i < polyLength; i++) {
- ALOGD("polygon %s i %d x %f y %f z %f", polyName, i, poly[i].x, poly[i].y, poly[i].z);
- }
-}
-
-/**
- * Test whether the polygon is convex.
- */
-bool SpotShadow::testConvex(const Vector2* polygon, int polygonLength, const char* name) {
- bool isConvex = true;
- for (int i = 0; i < polygonLength; i++) {
- Vector2 start = polygon[i];
- Vector2 middle = polygon[(i + 1) % polygonLength];
- Vector2 end = polygon[(i + 2) % polygonLength];
-
- float delta = (float(middle.x) - start.x) * (float(end.y) - start.y) -
- (float(middle.y) - start.y) * (float(end.x) - start.x);
- bool isCCWOrCoLinear = (delta >= EPSILON);
-
- if (isCCWOrCoLinear) {
- ALOGW("(Error Type 2): polygon (%s) is not a convex b/c start (x %f, y %f),"
- "middle (x %f, y %f) and end (x %f, y %f) , delta is %f !!!",
- name, start.x, start.y, middle.x, middle.y, end.x, end.y, delta);
- isConvex = false;
- break;
- }
- }
- return isConvex;
-}
-
-/**
- * Test whether or not the polygon (intersection) is within the 2 input polygons.
- * Using Marte Carlo method, we generate a random point, and if it is inside the
- * intersection, then it must be inside both source polygons.
- */
-void SpotShadow::testIntersection(const Vector2* poly1, int poly1Length, const Vector2* poly2,
- int poly2Length, const Vector2* intersection,
- int intersectionLength) {
- // Find the min and max of x and y.
- Vector2 lowerBound = {FLT_MAX, FLT_MAX};
- Vector2 upperBound = {-FLT_MAX, -FLT_MAX};
- for (int i = 0; i < poly1Length; i++) {
- updateBound(poly1[i], lowerBound, upperBound);
- }
- for (int i = 0; i < poly2Length; i++) {
- updateBound(poly2[i], lowerBound, upperBound);
- }
-
- bool dumpPoly = false;
- for (int k = 0; k < TEST_POINT_NUMBER; k++) {
- // Generate a random point between minX, minY and maxX, maxY.
- float randomX = rand() / float(RAND_MAX);
- float randomY = rand() / float(RAND_MAX);
-
- Vector2 testPoint;
- testPoint.x = lowerBound.x + randomX * (upperBound.x - lowerBound.x);
- testPoint.y = lowerBound.y + randomY * (upperBound.y - lowerBound.y);
-
- // If the random point is in both poly 1 and 2, then it must be intersection.
- if (testPointInsidePolygon(testPoint, intersection, intersectionLength)) {
- if (!testPointInsidePolygon(testPoint, poly1, poly1Length)) {
- dumpPoly = true;
- ALOGW("(Error Type 1): one point (%f, %f) in the intersection is"
- " not in the poly1",
- testPoint.x, testPoint.y);
- }
-
- if (!testPointInsidePolygon(testPoint, poly2, poly2Length)) {
- dumpPoly = true;
- ALOGW("(Error Type 1): one point (%f, %f) in the intersection is"
- " not in the poly2",
- testPoint.x, testPoint.y);
- }
- }
- }
-
- if (dumpPoly) {
- dumpPolygon(intersection, intersectionLength, "intersection");
- for (int i = 1; i < intersectionLength; i++) {
- Vector2 delta = intersection[i] - intersection[i - 1];
- ALOGD("Intersetion i, %d Vs i-1 is delta %f", i, delta.lengthSquared());
- }
-
- dumpPolygon(poly1, poly1Length, "poly 1");
- dumpPolygon(poly2, poly2Length, "poly 2");
- }
-}
-#endif
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h
deleted file mode 100644
index 8476be70318b..000000000000
--- a/libs/hwui/SpotShadow.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_SPOT_SHADOW_H
-#define ANDROID_HWUI_SPOT_SHADOW_H
-
-#include "Debug.h"
-#include "Vector.h"
-
-namespace android {
-namespace uirenderer {
-
-class VertexBuffer;
-
-class SpotShadow {
-public:
- static void createSpotShadow(bool isCasterOpaque, const Vector3& lightCenter, float lightSize,
- const Vector3* poly, int polyLength, const Vector3& polyCentroid,
- VertexBuffer& retstrips);
-
-private:
- struct VertexAngleData;
-
- static float projectCasterToOutline(Vector2& outline, const Vector3& lightCenter,
- const Vector3& polyVertex);
-
- static void computeLightPolygon(int points, const Vector3& lightCenter, float size,
- Vector3* ret);
-
- static void smoothPolygon(int level, int rays, float* rayDist);
- static float rayIntersectPoly(const Vector2* poly, int polyLength, const Vector2& point,
- float dx, float dy);
-
- static void xsort(Vector2* points, int pointsLength);
- static int hull(Vector2* points, int pointsLength, Vector2* retPoly);
- static bool ccw(float ax, float ay, float bx, float by, float cx, float cy);
- static void sort(Vector2* poly, int polyLength, const Vector2& center);
-
- static void swap(Vector2* points, int i, int j);
- static void quicksortCirc(Vector2* points, int low, int high, const Vector2& center);
- static void quicksortX(Vector2* points, int low, int high);
-
- static bool testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len);
- static void reverse(Vector2* polygon, int len);
-
- static void generateTriangleStrip(bool isCasterOpaque, float shadowStrengthScale,
- Vector2* penumbra, int penumbraLength, Vector2* umbra,
- int umbraLength, const Vector3* poly, int polyLength,
- VertexBuffer& retstrips, const Vector2& centroid);
-
-#if DEBUG_SHADOW
- static bool testConvex(const Vector2* polygon, int polygonLength, const char* name);
- static void testIntersection(const Vector2* poly1, int poly1Length, const Vector2* poly2,
- int poly2Length, const Vector2* intersection,
- int intersectionLength);
- static void updateBound(const Vector2 inVector, Vector2& lowerBound, Vector2& upperBound);
- static void dumpPolygon(const Vector2* poly, int polyLength, const char* polyName);
- static void dumpPolygon(const Vector3* poly, int polyLength, const char* polyName);
-#endif
-
-}; // SpotShadow
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_SPOT_SHADOW_H
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
deleted file mode 100644
index c7d93da718e7..000000000000
--- a/libs/hwui/TessellationCache.cpp
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/JenkinsHash.h>
-#include <utils/Trace.h>
-
-#include "Caches.h"
-#include "PathTessellator.h"
-#include "ShadowTessellator.h"
-#include "TessellationCache.h"
-
-#include "thread/Signal.h"
-#include "thread/Task.h"
-#include "thread/TaskProcessor.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Cache entries
-///////////////////////////////////////////////////////////////////////////////
-
-TessellationCache::Description::Description()
- : type(Type::None)
- , scaleX(1.0f)
- , scaleY(1.0f)
- , aa(false)
- , cap(SkPaint::kDefault_Cap)
- , style(SkPaint::kFill_Style)
- , strokeWidth(1.0f) {
- // Shape bits should be set to zeroes, because they are used for hash calculation.
- memset(&shape, 0, sizeof(Shape));
-}
-
-TessellationCache::Description::Description(Type type, const Matrix4& transform,
- const SkPaint& paint)
- : type(type)
- , aa(paint.isAntiAlias())
- , cap(paint.getStrokeCap())
- , style(paint.getStyle())
- , strokeWidth(paint.getStrokeWidth()) {
- PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
- // Shape bits should be set to zeroes, because they are used for hash calculation.
- memset(&shape, 0, sizeof(Shape));
-}
-
-bool TessellationCache::Description::operator==(const TessellationCache::Description& rhs) const {
- if (type != rhs.type) return false;
- if (scaleX != rhs.scaleX) return false;
- if (scaleY != rhs.scaleY) return false;
- if (aa != rhs.aa) return false;
- if (cap != rhs.cap) return false;
- if (style != rhs.style) return false;
- if (strokeWidth != rhs.strokeWidth) return false;
- if (type == Type::None) return true;
- const Shape::RoundRect& lRect = shape.roundRect;
- const Shape::RoundRect& rRect = rhs.shape.roundRect;
-
- if (lRect.width != rRect.width) return false;
- if (lRect.height != rRect.height) return false;
- if (lRect.rx != rRect.rx) return false;
- return lRect.ry == rRect.ry;
-}
-
-hash_t TessellationCache::Description::hash() const {
- uint32_t hash = JenkinsHashMix(0, static_cast<int>(type));
- hash = JenkinsHashMix(hash, aa);
- hash = JenkinsHashMix(hash, cap);
- hash = JenkinsHashMix(hash, style);
- hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
- hash = JenkinsHashMix(hash, android::hash_type(scaleX));
- hash = JenkinsHashMix(hash, android::hash_type(scaleY));
- hash = JenkinsHashMixBytes(hash, (uint8_t*)&shape, sizeof(Shape));
- return JenkinsHashWhiten(hash);
-}
-
-void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const {
- matrix->loadScale(scaleX, scaleY, 1.0f);
- paint->setAntiAlias(aa);
- paint->setStrokeCap(cap);
- paint->setStyle(style);
- paint->setStrokeWidth(strokeWidth);
-}
-
-TessellationCache::ShadowDescription::ShadowDescription() : nodeKey(nullptr) {
- memset(&matrixData, 0, sizeof(matrixData));
-}
-
-TessellationCache::ShadowDescription::ShadowDescription(const SkPath* nodeKey,
- const Matrix4* drawTransform)
- : nodeKey(nodeKey) {
- memcpy(&matrixData, drawTransform->data, sizeof(matrixData));
-}
-
-bool TessellationCache::ShadowDescription::operator==(
- const TessellationCache::ShadowDescription& rhs) const {
- return nodeKey == rhs.nodeKey && memcmp(&matrixData, &rhs.matrixData, sizeof(matrixData)) == 0;
-}
-
-hash_t TessellationCache::ShadowDescription::hash() const {
- uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*)&nodeKey, sizeof(const void*));
- hash = JenkinsHashMixBytes(hash, (uint8_t*)&matrixData, sizeof(matrixData));
- return JenkinsHashWhiten(hash);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// General purpose tessellation task processing
-///////////////////////////////////////////////////////////////////////////////
-
-class TessellationCache::TessellationTask : public Task<VertexBuffer*> {
-public:
- TessellationTask(Tessellator tessellator, const Description& description)
- : tessellator(tessellator), description(description) {}
-
- ~TessellationTask() {}
-
- Tessellator tessellator;
- Description description;
-};
-
-class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> {
-public:
- explicit TessellationProcessor(Caches& caches) : TaskProcessor<VertexBuffer*>(&caches.tasks) {}
- ~TessellationProcessor() {}
-
- virtual void onProcess(const sp<Task<VertexBuffer*> >& task) override {
- TessellationTask* t = static_cast<TessellationTask*>(task.get());
- ATRACE_NAME("shape tessellation");
- VertexBuffer* buffer = t->tessellator(t->description);
- t->setResult(buffer);
- }
-};
-
-class TessellationCache::Buffer {
-public:
- explicit Buffer(const sp<Task<VertexBuffer*> >& task) : mTask(task), mBuffer(nullptr) {}
-
- ~Buffer() {
- mTask.clear();
- delete mBuffer;
- }
-
- unsigned int getSize() {
- blockOnPrecache();
- return mBuffer->getSize();
- }
-
- const VertexBuffer* getVertexBuffer() {
- blockOnPrecache();
- return mBuffer;
- }
-
-private:
- void blockOnPrecache() {
- if (mTask != nullptr) {
- mBuffer = mTask->getResult();
- LOG_ALWAYS_FATAL_IF(mBuffer == nullptr, "Failed to precache");
- mTask.clear();
- }
- }
- sp<Task<VertexBuffer*> > mTask;
- VertexBuffer* mBuffer;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Shadow tessellation task processing
-///////////////////////////////////////////////////////////////////////////////
-
-static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* transformZ) {
- // map z coordinate with true 3d matrix
- point.z = transformZ->mapZ(point);
-
- // map x,y coordinates with draw/Skia matrix
- transformXY->mapPoint(point.x, point.y);
-}
-
-static void reverseVertexArray(Vertex* polygon, int len) {
- int n = len / 2;
- for (int i = 0; i < n; i++) {
- Vertex tmp = polygon[i];
- int k = len - 1 - i;
- polygon[i] = polygon[k];
- polygon[k] = tmp;
- }
-}
-
-void tessellateShadows(const Matrix4* drawTransform, const Rect* localClip, bool isCasterOpaque,
- const SkPath* casterPerimeter, const Matrix4* casterTransformXY,
- const Matrix4* casterTransformZ, const Vector3& lightCenter,
- float lightRadius, VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) {
- // tessellate caster outline into a 2d polygon
- std::vector<Vertex> casterVertices2d;
- const float casterRefinementThreshold = 2.0f;
- PathTessellator::approximatePathOutlineVertices(*casterPerimeter, casterRefinementThreshold,
- casterVertices2d);
-
- // Shadow requires CCW for now. TODO: remove potential double-reverse
- reverseVertexArray(&casterVertices2d.front(), casterVertices2d.size());
-
- if (casterVertices2d.size() == 0) return;
-
- // map 2d caster poly into 3d
- const int casterVertexCount = casterVertices2d.size();
- Vector3 casterPolygon[casterVertexCount];
- float minZ = FLT_MAX;
- float maxZ = -FLT_MAX;
- for (int i = 0; i < casterVertexCount; i++) {
- const Vertex& point2d = casterVertices2d[i];
- casterPolygon[i] = (Vector3){point2d.x, point2d.y, 0};
- mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
- minZ = std::min(minZ, casterPolygon[i].z);
- maxZ = std::max(maxZ, casterPolygon[i].z);
- }
-
- // map the centroid of the caster into 3d
- Vector2 centroid = ShadowTessellator::centroid2d(
- reinterpret_cast<const Vector2*>(&casterVertices2d.front()), casterVertexCount);
- Vector3 centroid3d = {centroid.x, centroid.y, 0};
- mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
-
- // if the caster intersects the z=0 plane, lift it in Z so it doesn't
- if (minZ < SHADOW_MIN_CASTER_Z) {
- float casterLift = SHADOW_MIN_CASTER_Z - minZ;
- for (int i = 0; i < casterVertexCount; i++) {
- casterPolygon[i].z += casterLift;
- }
- centroid3d.z += casterLift;
- }
-
- // Check whether we want to draw the shadow at all by checking the caster's bounds against clip.
- // We only have ortho projection, so we can just ignore the Z in caster for
- // simple rejection calculation.
- Rect casterBounds(casterPerimeter->getBounds());
- casterTransformXY->mapRect(casterBounds);
-
- // actual tessellation of both shadows
- ShadowTessellator::tessellateAmbientShadow(isCasterOpaque, casterPolygon, casterVertexCount,
- centroid3d, casterBounds, *localClip, maxZ,
- ambientBuffer);
-
- ShadowTessellator::tessellateSpotShadow(isCasterOpaque, casterPolygon, casterVertexCount,
- centroid3d, *drawTransform, lightCenter, lightRadius,
- casterBounds, *localClip, spotBuffer);
-}
-
-class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t> {
-public:
- explicit ShadowProcessor(Caches& caches)
- : TaskProcessor<TessellationCache::vertexBuffer_pair_t>(&caches.tasks) {}
- ~ShadowProcessor() {}
-
- virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t> >& task) override {
- TessellationCache::ShadowTask* t = static_cast<TessellationCache::ShadowTask*>(task.get());
- ATRACE_NAME("shadow tessellation");
-
- tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter,
- &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius,
- t->ambientBuffer, t->spotBuffer);
-
- t->setResult(TessellationCache::vertexBuffer_pair_t(&t->ambientBuffer, &t->spotBuffer));
- }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Cache constructor/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-TessellationCache::TessellationCache()
- : mMaxSize(MB(1))
- , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity)
- , mShadowCache(
- LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
- mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
- mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener);
- mDebugEnabled = Properties::debugLevel & kDebugCaches;
-}
-
-TessellationCache::~TessellationCache() {
- mCache.clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Size management
-///////////////////////////////////////////////////////////////////////////////
-
-uint32_t TessellationCache::getSize() {
- LruCache<Description, Buffer*>::Iterator iter(mCache);
- uint32_t size = 0;
- while (iter.next()) {
- size += iter.value()->getSize();
- }
- return size;
-}
-
-uint32_t TessellationCache::getMaxSize() {
- return mMaxSize;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-void TessellationCache::trim() {
- uint32_t size = getSize();
- while (size > mMaxSize) {
- size -= mCache.peekOldestValue()->getSize();
- mCache.removeOldest();
- }
- mShadowCache.clear();
-}
-
-void TessellationCache::clear() {
- mCache.clear();
- mShadowCache.clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Callbacks
-///////////////////////////////////////////////////////////////////////////////
-
-void TessellationCache::BufferRemovedListener::operator()(Description& description,
- Buffer*& buffer) {
- delete buffer;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Shadows
-///////////////////////////////////////////////////////////////////////////////
-
-void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
- bool opaque, const SkPath* casterPerimeter,
- const Matrix4* transformXY, const Matrix4* transformZ,
- const Vector3& lightCenter, float lightRadius) {
- ShadowDescription key(casterPerimeter, drawTransform);
-
- if (mShadowCache.get(key)) return;
- sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque, casterPerimeter,
- transformXY, transformZ, lightCenter, lightRadius);
- if (mShadowProcessor == nullptr) {
- mShadowProcessor = new ShadowProcessor(Caches::getInstance());
- }
- mShadowProcessor->add(task);
- task->incStrong(nullptr); // not using sp<>s, so manually ref while in the cache
- mShadowCache.put(key, task.get());
-}
-
-sp<TessellationCache::ShadowTask> TessellationCache::getShadowTask(
- const Matrix4* drawTransform, const Rect& localClip, bool opaque,
- const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ,
- const Vector3& lightCenter, float lightRadius) {
- ShadowDescription key(casterPerimeter, drawTransform);
- ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key));
- if (!task) {
- precacheShadows(drawTransform, localClip, opaque, casterPerimeter, transformXY, transformZ,
- lightCenter, lightRadius);
- task = static_cast<ShadowTask*>(mShadowCache.get(key));
- }
- LOG_ALWAYS_FATAL_IF(task == nullptr, "shadow not precached");
- return task;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Tessellation precaching
-///////////////////////////////////////////////////////////////////////////////
-
-TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(const Description& entry,
- Tessellator tessellator) {
- Buffer* buffer = mCache.get(entry);
- if (!buffer) {
- // not cached, enqueue a task to fill the buffer
- sp<TessellationTask> task = new TessellationTask(tessellator, entry);
- buffer = new Buffer(task);
-
- if (mProcessor == nullptr) {
- mProcessor = new TessellationProcessor(Caches::getInstance());
- }
- mProcessor->add(task);
- bool inserted = mCache.put(entry, buffer);
- // Note to the static analyzer that this insert should always succeed.
- LOG_ALWAYS_FATAL_IF(!inserted, "buffers shouldn't spontaneously appear in the cache");
- }
- return buffer;
-}
-
-static VertexBuffer* tessellatePath(const TessellationCache::Description& description,
- const SkPath& path) {
- Matrix4 matrix;
- SkPaint paint;
- description.setupMatrixAndPaint(&matrix, &paint);
- VertexBuffer* buffer = new VertexBuffer();
- PathTessellator::tessellatePath(path, &paint, matrix, *buffer);
- return buffer;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// RoundRect
-///////////////////////////////////////////////////////////////////////////////
-
-static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) {
- SkRect rect =
- SkRect::MakeWH(description.shape.roundRect.width, description.shape.roundRect.height);
- float rx = description.shape.roundRect.rx;
- float ry = description.shape.roundRect.ry;
- if (description.style == SkPaint::kStrokeAndFill_Style) {
- float outset = description.strokeWidth / 2;
- rect.outset(outset, outset);
- rx += outset;
- ry += outset;
- }
- SkPath path;
- path.addRoundRect(rect, rx, ry);
- return tessellatePath(description, path);
-}
-
-TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform,
- const SkPaint& paint, float width,
- float height, float rx, float ry) {
- Description entry(Description::Type::RoundRect, transform, paint);
- entry.shape.roundRect.width = width;
- entry.shape.roundRect.height = height;
- entry.shape.roundRect.rx = rx;
- entry.shape.roundRect.ry = ry;
- return getOrCreateBuffer(entry, &tessellateRoundRect);
-}
-const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint,
- float width, float height, float rx, float ry) {
- return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer();
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
deleted file mode 100644
index a0f0ed4653e0..000000000000
--- a/libs/hwui/TessellationCache.h
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Debug.h"
-#include "Matrix.h"
-#include "Rect.h"
-#include "Vector.h"
-#include "VertexBuffer.h"
-#include "thread/TaskProcessor.h"
-#include "utils/Macros.h"
-#include "utils/Pair.h"
-
-#include <SkPaint.h>
-#include <SkPath.h>
-
-#include <utils/LruCache.h>
-#include <utils/Mutex.h>
-#include <utils/StrongPointer.h>
-
-class SkBitmap;
-class SkCanvas;
-struct SkRect;
-
-namespace android {
-namespace uirenderer {
-
-class Caches;
-class VertexBuffer;
-
-///////////////////////////////////////////////////////////////////////////////
-// Classes
-///////////////////////////////////////////////////////////////////////////////
-
-class TessellationCache {
-public:
- typedef Pair<VertexBuffer*, VertexBuffer*> vertexBuffer_pair_t;
-
- struct Description {
- HASHABLE_TYPE(Description);
- enum class Type {
- None,
- RoundRect,
- };
-
- Type type;
- float scaleX;
- float scaleY;
- bool aa;
- SkPaint::Cap cap;
- SkPaint::Style style;
- float strokeWidth;
- union Shape {
- struct RoundRect {
- float width;
- float height;
- float rx;
- float ry;
- } roundRect;
- } shape;
-
- Description();
- Description(Type type, const Matrix4& transform, const SkPaint& paint);
- void setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const;
- };
-
- struct ShadowDescription {
- HASHABLE_TYPE(ShadowDescription);
- const SkPath* nodeKey;
- float matrixData[16];
-
- ShadowDescription();
- ShadowDescription(const SkPath* nodeKey, const Matrix4* drawTransform);
- };
-
- class ShadowTask : public Task<vertexBuffer_pair_t> {
- public:
- ShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque,
- const SkPath* casterPerimeter, const Matrix4* transformXY,
- const Matrix4* transformZ, const Vector3& lightCenter, float lightRadius)
- : drawTransform(*drawTransform)
- , localClip(localClip)
- , opaque(opaque)
- , casterPerimeter(*casterPerimeter)
- , transformXY(*transformXY)
- , transformZ(*transformZ)
- , lightCenter(lightCenter)
- , lightRadius(lightRadius) {}
-
- /* Note - we deep copy all task parameters, because *even though* pointers into Allocator
- * controlled objects (like the SkPath and Matrix4s) should be safe for the entire frame,
- * certain Allocators are destroyed before trim() is called to flush incomplete tasks.
- *
- * These deep copies could be avoided, long term, by canceling or flushing outstanding
- * tasks before tearing down single-frame LinearAllocators.
- */
- const Matrix4 drawTransform;
- const Rect localClip;
- bool opaque;
- const SkPath casterPerimeter;
- const Matrix4 transformXY;
- const Matrix4 transformZ;
- const Vector3 lightCenter;
- const float lightRadius;
- VertexBuffer ambientBuffer;
- VertexBuffer spotBuffer;
- };
-
- TessellationCache();
- ~TessellationCache();
-
- /**
- * Clears the cache. This causes all TessellationBuffers to be deleted.
- */
- void clear();
- /**
- * Returns the maximum size of the cache in bytes.
- */
- uint32_t getMaxSize();
- /**
- * Returns the current size of the cache in bytes.
- */
- uint32_t getSize();
-
- /**
- * Trims the contents of the cache, removing items until it's under its
- * specified limit.
- *
- * Trimming is used for caches that support pre-caching from a worker
- * thread. During pre-caching the maximum limit of the cache can be
- * exceeded for the duration of the frame. It is therefore required to
- * trim the cache at the end of the frame to keep the total amount of
- * memory used under control.
- *
- * Also removes transient Shadow VertexBuffers, which aren't cached between frames.
- */
- void trim();
-
- // TODO: precache/get for Oval, Lines, Points, etc.
-
- void precacheRoundRect(const Matrix4& transform, const SkPaint& paint, float width,
- float height, float rx, float ry) {
- getRoundRectBuffer(transform, paint, width, height, rx, ry);
- }
- const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint, float width,
- float height, float rx, float ry);
-
- sp<ShadowTask> getShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque,
- const SkPath* casterPerimeter, const Matrix4* transformXY,
- const Matrix4* transformZ, const Vector3& lightCenter,
- float lightRadius);
-
-private:
- class Buffer;
- class TessellationTask;
- class TessellationProcessor;
-
- typedef VertexBuffer* (*Tessellator)(const Description&);
-
- void precacheShadows(const Matrix4* drawTransform, const Rect& localClip, bool opaque,
- const SkPath* casterPerimeter, const Matrix4* transformXY,
- const Matrix4* transformZ, const Vector3& lightCenter, float lightRadius);
-
- Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint, float width,
- float height);
- Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint, float width,
- float height, float rx, float ry);
-
- Buffer* getOrCreateBuffer(const Description& entry, Tessellator tessellator);
-
- const uint32_t mMaxSize;
-
- bool mDebugEnabled;
-
- mutable Mutex mLock;
-
- ///////////////////////////////////////////////////////////////////////////////
- // General tessellation caching
- ///////////////////////////////////////////////////////////////////////////////
- sp<TaskProcessor<VertexBuffer*> > mProcessor;
- LruCache<Description, Buffer*> mCache;
- class BufferRemovedListener : public OnEntryRemoved<Description, Buffer*> {
- void operator()(Description& description, Buffer*& buffer) override;
- };
- BufferRemovedListener mBufferRemovedListener;
-
- ///////////////////////////////////////////////////////////////////////////////
- // Shadow tessellation caching
- ///////////////////////////////////////////////////////////////////////////////
- sp<TaskProcessor<vertexBuffer_pair_t> > mShadowProcessor;
-
- // holds a pointer, and implicit strong ref to each shadow task of the frame
- LruCache<ShadowDescription, Task<vertexBuffer_pair_t>*> mShadowCache;
- class BufferPairRemovedListener
- : public OnEntryRemoved<ShadowDescription, Task<vertexBuffer_pair_t>*> {
- void operator()(ShadowDescription& description,
- Task<vertexBuffer_pair_t>*& bufferPairTask) override {
- bufferPairTask->decStrong(nullptr);
- }
- };
- BufferPairRemovedListener mBufferPairRemovedListener;
-
-}; // class TessellationCache
-
-void tessellateShadows(const Matrix4* drawTransform, const Rect* localClip, bool isCasterOpaque,
- const SkPath* casterPerimeter, const Matrix4* casterTransformXY,
- const Matrix4* casterTransformZ, const Vector3& lightCenter,
- float lightRadius, VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer);
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
deleted file mode 100644
index c892ceb3e14d..000000000000
--- a/libs/hwui/TextDropShadowCache.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/JenkinsHash.h>
-
-#include "Caches.h"
-#include "Debug.h"
-#include "FontRenderer.h"
-#include "Properties.h"
-#include "TextDropShadowCache.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Cache support
-///////////////////////////////////////////////////////////////////////////////
-
-hash_t ShadowText::hash() const {
- uint32_t hash = JenkinsHashMix(0, glyphCount);
- hash = JenkinsHashMix(hash, android::hash_type(radius));
- hash = JenkinsHashMix(hash, android::hash_type(textSize));
- hash = JenkinsHashMix(hash, android::hash_type(typeface));
- hash = JenkinsHashMix(hash, flags);
- hash = JenkinsHashMix(hash, android::hash_type(italicStyle));
- hash = JenkinsHashMix(hash, android::hash_type(scaleX));
- if (glyphs) {
- hash = JenkinsHashMixShorts(hash, reinterpret_cast<const uint16_t*>(glyphs), glyphCount);
- }
- if (positions) {
- for (uint32_t i = 0; i < glyphCount * 2; i++) {
- hash = JenkinsHashMix(hash, android::hash_type(positions[i]));
- }
- }
- return JenkinsHashWhiten(hash);
-}
-
-int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) {
- int deltaInt = int(lhs.glyphCount) - int(rhs.glyphCount);
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = lhs.flags - rhs.flags;
- if (deltaInt != 0) return deltaInt;
-
- if (lhs.radius < rhs.radius) return -1;
- if (lhs.radius > rhs.radius) return +1;
-
- if (lhs.typeface < rhs.typeface) return -1;
- if (lhs.typeface > rhs.typeface) return +1;
-
- if (lhs.textSize < rhs.textSize) return -1;
- if (lhs.textSize > rhs.textSize) return +1;
-
- if (lhs.italicStyle < rhs.italicStyle) return -1;
- if (lhs.italicStyle > rhs.italicStyle) return +1;
-
- if (lhs.scaleX < rhs.scaleX) return -1;
- if (lhs.scaleX > rhs.scaleX) return +1;
-
- if (lhs.glyphs != rhs.glyphs) {
- if (!lhs.glyphs) return -1;
- if (!rhs.glyphs) return +1;
-
- deltaInt = memcmp(lhs.glyphs, rhs.glyphs, lhs.glyphCount * sizeof(glyph_t));
- if (deltaInt != 0) return deltaInt;
- }
-
- if (lhs.positions != rhs.positions) {
- if (!lhs.positions) return -1;
- if (!rhs.positions) return +1;
-
- return memcmp(lhs.positions, rhs.positions, lhs.glyphCount * sizeof(float) * 2);
- }
-
- return 0;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-TextDropShadowCache::TextDropShadowCache()
- : TextDropShadowCache(DeviceInfo::multiplyByResolution(2)) {}
-
-TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize)
- : mCache(LruCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity)
- , mSize(0)
- , mMaxSize(maxByteSize) {
- mCache.setOnEntryRemovedListener(this);
- mDebugEnabled = Properties::debugLevel & kDebugMoreCaches;
-}
-
-TextDropShadowCache::~TextDropShadowCache() {
- mCache.clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Size management
-///////////////////////////////////////////////////////////////////////////////
-
-uint32_t TextDropShadowCache::getSize() {
- return mSize;
-}
-
-uint32_t TextDropShadowCache::getMaxSize() {
- return mMaxSize;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Callbacks
-///////////////////////////////////////////////////////////////////////////////
-
-void TextDropShadowCache::operator()(ShadowText&, ShadowTexture*& texture) {
- if (texture) {
- mSize -= texture->objectSize();
-
- if (mDebugEnabled) {
- ALOGD("Shadow texture deleted, size = %d", texture->bitmapSize);
- }
-
- texture->deleteTexture();
- delete texture;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-void TextDropShadowCache::clear() {
- mCache.clear();
-}
-
-ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
- float radius, const float* positions) {
- ShadowText entry(paint, radius, numGlyphs, glyphs, positions);
- ShadowTexture* texture = mCache.get(entry);
-
- if (!texture) {
- SkPaint paintCopy(*paint);
- paintCopy.setTextAlign(SkPaint::kLeft_Align);
- FontRenderer::DropShadow shadow =
- mRenderer->renderDropShadow(&paintCopy, glyphs, numGlyphs, radius, positions);
-
- if (!shadow.image) {
- return nullptr;
- }
-
- Caches& caches = Caches::getInstance();
-
- texture = new ShadowTexture(caches);
- texture->left = shadow.penX;
- texture->top = shadow.penY;
- texture->generation = 0;
- texture->blend = true;
-
- const uint32_t size = shadow.width * shadow.height;
-
- // Don't even try to cache a bitmap that's bigger than the cache
- if (size < mMaxSize) {
- while (mSize + size > mMaxSize) {
- LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(),
- "Failed to remove oldest from cache. mSize = %" PRIu32
- ", mCache.size() = %zu",
- mSize, mCache.size());
- }
- }
-
- // Textures are Alpha8
- texture->upload(GL_ALPHA, shadow.width, shadow.height, GL_ALPHA, GL_UNSIGNED_BYTE,
- shadow.image);
- texture->setFilter(GL_LINEAR);
- texture->setWrap(GL_CLAMP_TO_EDGE);
-
- if (size < mMaxSize) {
- if (mDebugEnabled) {
- ALOGD("Shadow texture created, size = %d", texture->bitmapSize);
- }
-
- entry.copyTextLocally();
-
- mSize += texture->objectSize();
- mCache.put(entry, texture);
- } else {
- texture->cleanup = true;
- }
-
- // Cleanup shadow
- free(shadow.image);
- }
-
- return texture;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
deleted file mode 100644
index 86a012970f17..000000000000
--- a/libs/hwui/TextDropShadowCache.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H
-#define ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H
-
-#include <GLES2/gl2.h>
-
-#include <SkPaint.h>
-
-#include <utils/LruCache.h>
-#include <utils/String16.h>
-
-#include "Texture.h"
-#include "font/Font.h"
-
-namespace android {
-namespace uirenderer {
-
-class Caches;
-class FontRenderer;
-
-struct ShadowText {
- ShadowText()
- : glyphCount(0)
- , radius(0.0f)
- , textSize(0.0f)
- , typeface(nullptr)
- , flags(0)
- , italicStyle(0.0f)
- , scaleX(0)
- , glyphs(nullptr)
- , positions(nullptr) {}
-
- // len is the number of bytes in text
- ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const glyph_t* srcGlyphs,
- const float* positions)
- : glyphCount(glyphCount)
- , radius(radius)
- , textSize(paint->getTextSize())
- , typeface(paint->getTypeface())
- , flags(paint->isFakeBoldText() ? Font::kFakeBold : 0)
- , italicStyle(paint->getTextSkewX())
- , scaleX(paint->getTextScaleX())
- , glyphs(srcGlyphs)
- , positions(positions) {}
-
- ~ShadowText() {}
-
- hash_t hash() const;
-
- static int compare(const ShadowText& lhs, const ShadowText& rhs);
-
- bool operator==(const ShadowText& other) const { return compare(*this, other) == 0; }
-
- bool operator!=(const ShadowText& other) const { return compare(*this, other) != 0; }
-
- void copyTextLocally() {
- str.setTo(reinterpret_cast<const char16_t*>(glyphs), glyphCount);
- glyphs = reinterpret_cast<const glyph_t*>(str.string());
- if (positions != nullptr) {
- positionsCopy.clear();
- positionsCopy.appendArray(positions, glyphCount * 2);
- positions = positionsCopy.array();
- }
- }
-
- uint32_t glyphCount;
- float radius;
- float textSize;
- SkTypeface* typeface;
- uint32_t flags;
- float italicStyle;
- float scaleX;
- const glyph_t* glyphs;
- const float* positions;
-
- // Not directly used to compute the cache key
- String16 str;
- Vector<float> positionsCopy;
-
-}; // struct ShadowText
-
-// Caching support
-
-inline int strictly_order_type(const ShadowText& lhs, const ShadowText& rhs) {
- return ShadowText::compare(lhs, rhs) < 0;
-}
-
-inline int compare_type(const ShadowText& lhs, const ShadowText& rhs) {
- return ShadowText::compare(lhs, rhs);
-}
-
-inline hash_t hash_type(const ShadowText& entry) {
- return entry.hash();
-}
-
-/**
- * Alpha texture used to represent a shadow.
- */
-struct ShadowTexture : public Texture {
- explicit ShadowTexture(Caches& caches) : Texture(caches) {}
-
- float left;
- float top;
-}; // struct ShadowTexture
-
-class TextDropShadowCache : public OnEntryRemoved<ShadowText, ShadowTexture*> {
-public:
- TextDropShadowCache();
- explicit TextDropShadowCache(uint32_t maxByteSize);
- ~TextDropShadowCache();
-
- /**
- * Used as a callback when an entry is removed from the cache.
- * Do not invoke directly.
- */
- void operator()(ShadowText& text, ShadowTexture*& texture) override;
-
- ShadowTexture* get(const SkPaint* paint, const glyph_t* text, int numGlyphs, float radius,
- const float* positions);
-
- /**
- * Clears the cache. This causes all textures to be deleted.
- */
- void clear();
-
- void setFontRenderer(FontRenderer& fontRenderer) { mRenderer = &fontRenderer; }
-
- /**
- * Returns the maximum size of the cache in bytes.
- */
- uint32_t getMaxSize();
- /**
- * Returns the current size of the cache in bytes.
- */
- uint32_t getSize();
-
-private:
- LruCache<ShadowText, ShadowTexture*> mCache;
-
- uint32_t mSize;
- const uint32_t mMaxSize;
- FontRenderer* mRenderer = nullptr;
- bool mDebugEnabled;
-}; // class TextDropShadowCache
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
deleted file mode 100644
index 1e90eebe3bb8..000000000000
--- a/libs/hwui/Texture.cpp
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Texture.h"
-#include "Caches.h"
-#include "utils/GLUtils.h"
-#include "utils/MathUtils.h"
-#include "utils/TraceUtils.h"
-
-#include <utils/Log.h>
-
-#include <math/mat4.h>
-
-#include <SkCanvas.h>
-
-namespace android {
-namespace uirenderer {
-
-// Number of bytes used by a texture in the given format
-static int bytesPerPixel(GLint glFormat) {
- switch (glFormat) {
- // The wrapped-texture case, usually means a SurfaceTexture
- case 0:
- return 0;
- case GL_LUMINANCE:
- case GL_ALPHA:
- return 1;
- case GL_SRGB8:
- case GL_RGB:
- return 3;
- case GL_SRGB8_ALPHA8:
- case GL_RGBA:
- return 4;
- case GL_RGBA16F:
- return 8;
- default:
- LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat);
- }
-}
-
-void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) {
- if (force || wrapS != mWrapS || wrapT != mWrapT) {
- mWrapS = wrapS;
- mWrapT = wrapT;
-
- if (bindTexture) {
- mCaches.textureState().bindTexture(mTarget, mId);
- }
-
- glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS);
- glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT);
- }
-}
-
-void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) {
- if (force || min != mMinFilter || mag != mMagFilter) {
- mMinFilter = min;
- mMagFilter = mag;
-
- if (bindTexture) {
- mCaches.textureState().bindTexture(mTarget, mId);
- }
-
- if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
-
- glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min);
- glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag);
- }
-}
-
-void Texture::deleteTexture() {
- mCaches.textureState().deleteTexture(mId);
- mId = 0;
- mTarget = GL_NONE;
- if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
- EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
- eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
- mEglImageHandle = EGL_NO_IMAGE_KHR;
- }
-}
-
-bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format,
- GLenum target) {
- if (mWidth == width && mHeight == height && mFormat == format &&
- mInternalFormat == internalFormat && mTarget == target) {
- return false;
- }
- mWidth = width;
- mHeight = height;
- mFormat = format;
- mInternalFormat = internalFormat;
- mTarget = target;
- notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat));
- return true;
-}
-
-void Texture::resetCachedParams() {
- mWrapS = GL_REPEAT;
- mWrapT = GL_REPEAT;
- mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
- mMagFilter = GL_LINEAR;
-}
-
-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);
- needsAlloc = true;
- resetCachedParams();
- }
- mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId);
- if (needsAlloc) {
- glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels);
- } else if (pixels) {
- glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels);
- }
- GL_CHECKPOINT(MODERATE);
-}
-
-void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) {
- EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
- if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
- eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
- mEglImageHandle = EGL_NO_IMAGE_KHR;
- }
- mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- buffer->getNativeBuffer(), 0);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle);
-}
-
-static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type,
- GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height,
- const GLvoid* data) {
- const bool useStride =
- stride != width && Caches::getInstance().extensions().hasUnpackRowLength();
- if ((stride == width) || useStride) {
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
- }
-
- if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data);
- } else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
- }
-
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- }
- } else {
- // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
- // if the stride doesn't match the width
-
- GLvoid* temp = (GLvoid*)malloc(width * height * bpp);
- if (!temp) return;
-
- uint8_t* pDst = (uint8_t*)temp;
- uint8_t* pSrc = (uint8_t*)data;
- for (GLsizei i = 0; i < height; i++) {
- memcpy(pDst, pSrc, width * bpp);
- pDst += width * bpp;
- pSrc += stride * bpp;
- }
-
- if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp);
- } else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
- }
-
- free(temp);
- }
-}
-
-void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, bool needSRGB,
- GLint* outInternalFormat, GLint* outFormat,
- GLint* outType) {
- switch (colorType) {
- case kAlpha_8_SkColorType:
- *outFormat = GL_ALPHA;
- *outInternalFormat = GL_ALPHA;
- *outType = GL_UNSIGNED_BYTE;
- break;
- case kRGB_565_SkColorType:
- if (needSRGB) {
- // We would ideally use a GL_RGB/GL_SRGB8 texture but the
- // intermediate Skia bitmap needs to be ARGB_8888
- *outFormat = GL_RGBA;
- *outInternalFormat = caches.rgbaInternalFormat();
- *outType = GL_UNSIGNED_BYTE;
- } else {
- *outFormat = GL_RGB;
- *outInternalFormat = GL_RGB;
- *outType = GL_UNSIGNED_SHORT_5_6_5;
- }
- break;
- // ARGB_4444 is upconverted to RGBA_8888
- case kARGB_4444_SkColorType:
- case kN32_SkColorType:
- *outFormat = GL_RGBA;
- *outInternalFormat = caches.rgbaInternalFormat(needSRGB);
- *outType = GL_UNSIGNED_BYTE;
- break;
- case kGray_8_SkColorType:
- *outFormat = GL_LUMINANCE;
- *outInternalFormat = GL_LUMINANCE;
- *outType = GL_UNSIGNED_BYTE;
- break;
- case kRGBA_F16_SkColorType:
- if (caches.extensions().getMajorGlVersion() >= 3) {
- // This format is always linear
- *outFormat = GL_RGBA;
- *outInternalFormat = GL_RGBA16F;
- *outType = GL_HALF_FLOAT;
- } else {
- *outFormat = GL_RGBA;
- *outInternalFormat = caches.rgbaInternalFormat(true);
- *outType = GL_UNSIGNED_BYTE;
- }
- break;
- default:
- LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
- break;
- }
-}
-
-SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending,
- sk_sp<SkColorSpace> sRGB) {
- SkBitmap rgbaBitmap;
- rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
- bitmap.info().alphaType(),
- hasLinearBlending ? sRGB : nullptr));
- rgbaBitmap.eraseColor(0);
-
- if (bitmap.colorType() == kRGBA_F16_SkColorType) {
- // Drawing RGBA_F16 onto ARGB_8888 is not supported
- bitmap.readPixels(rgbaBitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
- rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0);
- } else {
- SkCanvas canvas(rgbaBitmap);
- canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
- }
-
- return rgbaBitmap;
-}
-
-bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending) {
- return info.colorType() == kARGB_4444_SkColorType ||
- (info.colorType() == kRGB_565_SkColorType && hasLinearBlending &&
- info.colorSpace()->isSRGB()) ||
- (info.colorType() == kRGBA_F16_SkColorType &&
- Caches::getInstance().extensions().getMajorGlVersion() < 3);
-}
-
-void Texture::upload(Bitmap& bitmap) {
- ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height());
-
- // We could also enable mipmapping if both bitmap dimensions are powers
- // of 2 but we'd have to deal with size changes. Let's keep this simple
- const bool canMipMap = mCaches.extensions().hasNPot();
-
- // If the texture had mipmap enabled but not anymore,
- // force a glTexImage2D to discard the mipmap levels
- bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap();
- bool setDefaultParams = false;
-
- if (!mId) {
- glGenTextures(1, &mId);
- needsAlloc = true;
- setDefaultParams = true;
- }
-
- bool hasLinearBlending = mCaches.extensions().hasLinearBlending();
- bool needSRGB = transferFunctionCloseToSRGB(bitmap.info().colorSpace());
-
- GLint internalFormat, format, type;
- colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB && hasLinearBlending,
- &internalFormat, &format, &type);
-
- // Some devices don't support GL_RGBA16F, so we need to compare the color type
- // and internal GL format to decide what to do with 16 bit bitmaps
- bool rgba16fNeedsConversion =
- bitmap.colorType() == kRGBA_F16_SkColorType && internalFormat != GL_RGBA16F;
-
- // RGBA16F is always linear extended sRGB
- if (internalFormat == GL_RGBA16F) {
- mIsLinear = true;
- }
-
- mConnector.reset();
-
- // Alpha masks don't have color profiles
- // If an RGBA16F bitmap needs conversion, we know the target will be sRGB
- 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()) {
- SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor);
- if (!colorSpace->toXYZD50(&xyzMatrix)) {
- ALOGW("Incompatible color space!");
- } else {
- SkColorSpaceTransferFn fn;
- if (!colorSpace->isNumericalTransferFn(&fn)) {
- ALOGW("Incompatible color space, no numerical transfer function!");
- } else {
- float data[16];
- xyzMatrix.asColMajorf(data);
-
- ColorSpace::TransferParameters p = {fn.fG, fn.fA, fn.fB, fn.fC,
- fn.fD, fn.fE, fn.fF};
- ColorSpace src("Unnamed", mat4f((const float*)&data[0]).upperLeft(), p);
- mConnector.reset(new ColorSpaceConnector(src, ColorSpace::sRGB()));
-
- // A non-sRGB color space might have a transfer function close enough to sRGB
- // that we can save shader instructions by using an sRGB sampler
- // This is only possible if we have hardware support for sRGB textures
- if (needSRGB && internalFormat == GL_RGBA && mCaches.extensions().hasSRGB() &&
- !bitmap.isHardware()) {
- internalFormat = GL_SRGB8_ALPHA8;
- }
- }
- }
- }
- }
-
- GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
- needsAlloc |= updateLayout(bitmap.width(), bitmap.height(), internalFormat, format, target);
-
- blend = !bitmap.isOpaque();
- mCaches.textureState().bindTexture(mTarget, mId);
-
- // TODO: Handle sRGB gray bitmaps
- if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending))) {
- SkBitmap skBitmap;
- bitmap.getSkBitmap(&skBitmap);
- sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
- SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB));
- uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
- rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), rgbaBitmap.height(),
- rgbaBitmap.getPixels());
- } else if (bitmap.isHardware()) {
- uploadHardwareBitmapToTexture(bitmap.graphicBuffer());
- } else {
- uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(),
- bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(),
- bitmap.pixels());
- }
-
- if (canMipMap) {
- mipMap = bitmap.hasHardwareMipMap();
- if (mipMap) {
- glGenerateMipmap(GL_TEXTURE_2D);
- }
- }
-
- if (setDefaultParams) {
- setFilter(GL_NEAREST);
- setWrap(GL_CLAMP_TO_EDGE);
- }
-}
-
-void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format,
- GLenum target) {
- mId = id;
- mWidth = width;
- mHeight = height;
- mFormat = format;
- mInternalFormat = internalFormat;
- mTarget = target;
- mConnector.reset();
- // We're wrapping an existing texture, so don't double count this memory
- notifySizeChanged(0);
-}
-
-TransferFunctionType Texture::getTransferFunctionType() const {
- if (mConnector.get() != nullptr && mInternalFormat != GL_SRGB8_ALPHA8) {
- const ColorSpace::TransferParameters& p = mConnector->getSource().getTransferParameters();
- if (MathUtils::isZero(p.e) && MathUtils::isZero(p.f)) {
- if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b) &&
- MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) {
- if (MathUtils::areEqual(p.g, 1.0f)) {
- return TransferFunctionType::None;
- }
- return TransferFunctionType::Gamma;
- }
- return TransferFunctionType::Limited;
- }
- return TransferFunctionType::Full;
- }
- return TransferFunctionType::None;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
deleted file mode 100644
index 5b7e4e261f30..000000000000
--- a/libs/hwui/Texture.h
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_TEXTURE_H
-#define ANDROID_HWUI_TEXTURE_H
-
-#include "GpuMemoryTracker.h"
-#include "hwui/Bitmap.h"
-#include "utils/Color.h"
-
-#include <memory>
-
-#include <math/mat3.h>
-
-#include <ui/ColorSpace.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES2/gl2.h>
-#include <GLES3/gl3.h>
-#include <SkBitmap.h>
-
-namespace android {
-
-class GraphicBuffer;
-
-namespace uirenderer {
-
-class Caches;
-class UvMapper;
-class Layer;
-
-/**
- * Represents an OpenGL texture.
- */
-class Texture : public GpuMemoryTracker {
-public:
- static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending,
- sk_sp<SkColorSpace> sRGB);
- static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending);
- static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
- bool needSRGB, GLint* outInternalFormat,
- GLint* outFormat, GLint* outType);
-
- explicit Texture(Caches& caches) : GpuMemoryTracker(GpuObjectType::Texture), mCaches(caches) {}
-
- virtual ~Texture() {}
-
- inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) {
- setWrapST(wrap, wrap, bindTexture, force);
- }
-
- virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
- bool force = false);
-
- inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false) {
- setFilterMinMag(filter, filter, bindTexture, force);
- }
-
- virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
- bool force = false);
-
- /**
- * Convenience method to call glDeleteTextures() on this texture's id.
- */
- void deleteTexture();
-
- /**
- * Sets the width, height, and format of the texture along with allocating
- * the texture ID. Does nothing if the width, height, and format are already
- * the requested values.
- *
- * 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,
- internalFormat == GL_RGBA16F ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr);
- }
-
- /**
- * Updates this Texture with the contents of the provided Bitmap,
- * also setting the appropriate width, height, and format. It is not necessary
- * to call resize() prior to this.
- *
- * Note this does not set the generation from the Bitmap.
- */
- void upload(Bitmap& source);
-
- /**
- * Basically glTexImage2D/glTexSubImage2D.
- */
- void upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, GLenum type,
- const void* pixels);
-
- /**
- * Wraps an existing texture.
- */
- void wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format,
- GLenum target);
-
- GLuint id() const { return mId; }
-
- uint32_t width() const { return mWidth; }
-
- uint32_t height() const { return mHeight; }
-
- GLint format() const { return mFormat; }
-
- GLint internalFormat() const { return mInternalFormat; }
-
- GLenum target() const { return mTarget; }
-
- /**
- * Returns nullptr if this texture does not require color space conversion
- * to sRGB, or a valid pointer to a ColorSpaceConnector if a conversion
- * is required.
- */
- constexpr const ColorSpaceConnector* getColorSpaceConnector() const { return mConnector.get(); }
-
- constexpr bool hasColorSpaceConversion() const { return mConnector.get() != nullptr; }
-
- TransferFunctionType getTransferFunctionType() const;
-
- /**
- * Returns true if this texture uses a linear encoding format.
- */
- constexpr bool isLinear() const { return mIsLinear; }
-
- /**
- * Generation of the backing bitmap,
- */
- uint32_t generation = 0;
- /**
- * Indicates whether the texture requires blending.
- */
- bool blend = false;
- /**
- * Indicates whether this texture should be cleaned up after use.
- */
- bool cleanup = false;
- /**
- * Optional, size of the original bitmap.
- */
- uint32_t bitmapSize = 0;
- /**
- * Indicates whether this texture will use trilinear filtering.
- */
- bool mipMap = false;
-
- /**
- * Optional, pointer to a texture coordinates mapper.
- */
- const UvMapper* uvMapper = nullptr;
-
- /**
- * Whether or not the Texture is marked in use and thus not evictable for
- * the current frame. This is reset at the start of a new frame.
- */
- void* isInUse = nullptr;
-
-private:
- // TODO: Temporarily grant private access to GlLayer, remove once
- // GlLayer can be de-tangled from being a dual-purpose render target
- // and external texture wrapper
- friend class GlLayer;
-
- // Returns true if the texture layout (size, format, etc.) changed, false if it was the same
- bool updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format,
- GLenum target);
- void uploadHardwareBitmapToTexture(GraphicBuffer* buffer);
- void resetCachedParams();
-
- GLuint mId = 0;
- uint32_t mWidth = 0;
- uint32_t mHeight = 0;
- GLint mFormat = 0;
- GLint mInternalFormat = 0;
- GLenum mTarget = GL_NONE;
- EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR;
-
- /* See GLES spec section 3.8.14
- * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is
- * NEAREST_MIPMAP_LINEAR and the value for TEXTURE_MAG_FILTER is LINEAR.
- * s, t, and r wrap modes are all set to REPEAT."
- */
- GLenum mWrapS = GL_REPEAT;
- GLenum mWrapT = GL_REPEAT;
- 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;
-}; // struct Texture
-
-class AutoTexture {
-public:
- explicit AutoTexture(Texture* texture) : texture(texture) {}
- ~AutoTexture() {
- if (texture && texture->cleanup) {
- texture->deleteTexture();
- delete texture;
- }
- }
-
- Texture* const texture;
-}; // class AutoTexture
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_TEXTURE_H
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
deleted file mode 100644
index 9d365fb29ebe..000000000000
--- a/libs/hwui/TextureCache.cpp
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <GLES2/gl2.h>
-
-#include <utils/Mutex.h>
-
-#include "Caches.h"
-#include "DeviceInfo.h"
-#include "Properties.h"
-#include "Texture.h"
-#include "TextureCache.h"
-#include "hwui/Bitmap.h"
-#include "utils/TraceUtils.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-TextureCache::TextureCache()
- : mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity)
- , mSize(0)
- , mMaxSize(DeviceInfo::multiplyByResolution(4 * 6)) // 6 screen-sized RGBA_8888 bitmaps
- , mFlushRate(.4f) {
- mCache.setOnEntryRemovedListener(this);
- mMaxTextureSize = DeviceInfo::get()->maxTextureSize();
- mDebugEnabled = Properties::debugLevel & kDebugCaches;
-}
-
-TextureCache::~TextureCache() {
- this->clear();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Size management
-///////////////////////////////////////////////////////////////////////////////
-
-uint32_t TextureCache::getSize() {
- return mSize;
-}
-
-uint32_t TextureCache::getMaxSize() {
- return mMaxSize;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Callbacks
-///////////////////////////////////////////////////////////////////////////////
-
-void TextureCache::operator()(uint32_t&, Texture*& texture) {
- // This will be called already locked
- if (texture) {
- mSize -= texture->bitmapSize;
- TEXTURE_LOGD("TextureCache::callback: name, removed size, mSize = %d, %d, %d", texture->id,
- texture->bitmapSize, mSize);
- if (mDebugEnabled) {
- ALOGD("Texture deleted, size = %d", texture->bitmapSize);
- }
- texture->deleteTexture();
- delete texture;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-void TextureCache::resetMarkInUse(void* ownerToken) {
- LruCache<uint32_t, Texture*>::Iterator iter(mCache);
- while (iter.next()) {
- if (iter.value()->isInUse == ownerToken) {
- iter.value()->isInUse = nullptr;
- }
- }
-}
-
-bool TextureCache::canMakeTextureFromBitmap(Bitmap* bitmap) {
- if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
- ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)", bitmap->width(),
- bitmap->height(), mMaxTextureSize, mMaxTextureSize);
- return false;
- }
- return true;
-}
-
-Texture* TextureCache::createTexture(Bitmap* bitmap) {
- Texture* texture = new Texture(Caches::getInstance());
- texture->bitmapSize = bitmap->rowBytes() * bitmap->height();
- texture->generation = bitmap->getGenerationID();
- texture->upload(*bitmap);
- return texture;
-}
-
-// Returns a prepared Texture* that either is already in the cache or can fit
-// in the cache (and is thus added to the cache)
-Texture* TextureCache::getCachedTexture(Bitmap* bitmap) {
- if (bitmap->isHardware()) {
- auto textureIterator = mHardwareTextures.find(bitmap->getStableID());
- if (textureIterator == mHardwareTextures.end()) {
- Texture* texture = createTexture(bitmap);
- mHardwareTextures.insert(
- std::make_pair(bitmap->getStableID(), std::unique_ptr<Texture>(texture)));
- if (mDebugEnabled) {
- ALOGD("Texture created for hw bitmap size = %d", texture->bitmapSize);
- }
- return texture;
- }
- return textureIterator->second.get();
- }
-
- Texture* texture = mCache.get(bitmap->getStableID());
-
- if (!texture) {
- if (!canMakeTextureFromBitmap(bitmap)) {
- return nullptr;
- }
-
- const uint32_t size = bitmap->rowBytes() * bitmap->height();
- bool canCache = size < mMaxSize;
- // Don't even try to cache a bitmap that's bigger than the cache
- while (canCache && mSize + size > mMaxSize) {
- Texture* oldest = mCache.peekOldestValue();
- if (oldest && !oldest->isInUse) {
- mCache.removeOldest();
- } else {
- canCache = false;
- }
- }
-
- if (canCache) {
- texture = createTexture(bitmap);
- mSize += size;
- TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
- bitmap, texture->id, size, mSize);
- if (mDebugEnabled) {
- ALOGD("Texture created, size = %d", size);
- }
- mCache.put(bitmap->getStableID(), texture);
- }
- } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
- // Texture was in the cache but is dirty, re-upload
- // TODO: Re-adjust the cache size if the bitmap's dimensions have changed
- texture->upload(*bitmap);
- texture->generation = bitmap->getGenerationID();
- }
-
- return texture;
-}
-
-bool TextureCache::prefetchAndMarkInUse(void* ownerToken, Bitmap* bitmap) {
- Texture* texture = getCachedTexture(bitmap);
- if (texture) {
- texture->isInUse = ownerToken;
- }
- return texture;
-}
-
-bool TextureCache::prefetch(Bitmap* bitmap) {
- return getCachedTexture(bitmap);
-}
-
-Texture* TextureCache::get(Bitmap* bitmap) {
- Texture* texture = getCachedTexture(bitmap);
-
- if (!texture) {
- if (!canMakeTextureFromBitmap(bitmap)) {
- return nullptr;
- }
- texture = createTexture(bitmap);
- texture->cleanup = true;
- }
-
- return texture;
-}
-
-bool TextureCache::destroyTexture(uint32_t pixelRefStableID) {
- auto hardwareIter = mHardwareTextures.find(pixelRefStableID);
- if (hardwareIter != mHardwareTextures.end()) {
- hardwareIter->second->deleteTexture();
- mHardwareTextures.erase(hardwareIter);
- return true;
- }
- return mCache.remove(pixelRefStableID);
-}
-
-void TextureCache::clear() {
- mCache.clear();
- for (auto& iter : mHardwareTextures) {
- iter.second->deleteTexture();
- }
- mHardwareTextures.clear();
- TEXTURE_LOGD("TextureCache:clear(), mSize = %d", mSize);
-}
-
-void TextureCache::flush() {
- if (mFlushRate >= 1.0f || mCache.size() == 0) return;
- if (mFlushRate <= 0.0f) {
- clear();
- return;
- }
-
- uint32_t targetSize = uint32_t(mSize * mFlushRate);
- TEXTURE_LOGD("TextureCache::flush: target size: %d", targetSize);
-
- while (mSize > targetSize) {
- mCache.removeOldest();
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
deleted file mode 100644
index 19e7bea99669..000000000000
--- a/libs/hwui/TextureCache.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_TEXTURE_CACHE_H
-#define ANDROID_HWUI_TEXTURE_CACHE_H
-
-#include <SkBitmap.h>
-
-#include <cutils/compiler.h>
-
-#include <utils/LruCache.h>
-#include <utils/Mutex.h>
-
-#include "Debug.h"
-
-#include <unordered_map>
-#include <vector>
-
-namespace android {
-
-class Bitmap;
-
-namespace uirenderer {
-
-class Texture;
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_TEXTURES
-#define TEXTURE_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-#define TEXTURE_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Classes
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * A simple LRU texture cache. The cache has a maximum size expressed in bytes.
- * Any texture added to the cache causing the cache to grow beyond the maximum
- * allowed size will also cause the oldest texture to be kicked out.
- */
-class TextureCache : public OnEntryRemoved<uint32_t, Texture*> {
-public:
- TextureCache();
- ~TextureCache();
-
- /**
- * Used as a callback when an entry is removed from the cache.
- * Do not invoke directly.
- */
- void operator()(uint32_t&, Texture*& texture) override;
-
- /**
- * Resets all Textures to not be marked as in use
- */
- void resetMarkInUse(void* ownerToken);
-
- /**
- * Attempts to precache the SkBitmap. Returns true if a Texture was successfully
- * acquired for the bitmap, false otherwise. If a Texture was acquired it is
- * marked as in use.
- */
- bool prefetchAndMarkInUse(void* ownerToken, Bitmap* bitmap);
-
- /**
- * Attempts to precache the SkBitmap. Returns true if a Texture was successfully
- * acquired for the bitmap, false otherwise. Does not mark the Texture
- * as in use and won't update currently in-use Textures.
- */
- bool prefetch(Bitmap* bitmap);
-
- /**
- * Returns the texture associated with the specified bitmap from within the cache.
- * If the texture cannot be found in the cache, a new texture is generated.
- */
- Texture* get(Bitmap* bitmap);
-
- /**
- * Removes the texture associated with the specified pixelRef. Must be called from RenderThread
- * Returns true if a texture was destroyed, false if no texture with that id was found
- */
- bool destroyTexture(uint32_t pixelRefStableID);
-
- /**
- * Clears the cache. This causes all textures to be deleted.
- */
- void clear();
-
- /**
- * Returns the maximum size of the cache in bytes.
- */
- uint32_t getMaxSize();
- /**
- * Returns the current size of the cache in bytes.
- */
- uint32_t getSize();
-
- /**
- * Partially flushes the cache. The amount of memory freed by a flush
- * is defined by the flush rate.
- */
- void flush();
-
-private:
- bool canMakeTextureFromBitmap(Bitmap* bitmap);
-
- Texture* getCachedTexture(Bitmap* bitmap);
- Texture* createTexture(Bitmap* bitmap);
-
- LruCache<uint32_t, Texture*> mCache;
-
- uint32_t mSize;
- const uint32_t mMaxSize;
- GLint mMaxTextureSize;
-
- const float mFlushRate;
-
- bool mDebugEnabled;
-
- std::unordered_map<uint32_t, std::unique_ptr<Texture>> mHardwareTextures;
-}; // class TextureCache
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_TEXTURE_CACHE_H
diff --git a/libs/hwui/TreeInfo.cpp b/libs/hwui/TreeInfo.cpp
new file mode 100644
index 000000000000..808a12a311e2
--- /dev/null
+++ b/libs/hwui/TreeInfo.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 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 "TreeInfo.h"
+
+#include "renderthread/CanvasContext.h"
+
+namespace android::uirenderer {
+
+TreeInfo::TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext)
+ : mode(mode)
+ , prepareTextures(mode == MODE_FULL)
+ , canvasContext(canvasContext)
+ , disableForceDark(canvasContext.useForceDark() ? 0 : 1) {}
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index f2766d6a5b6e..a0d960527ca6 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,6 +16,7 @@
#pragma once
+#include "Properties.h"
#include "utils/Macros.h"
#include <utils/Timers.h>
@@ -39,7 +40,7 @@ public:
virtual void onError(const std::string& message) = 0;
protected:
- virtual ~ErrorHandler() {}
+ virtual ~ErrorHandler() = default;
};
class TreeObserver {
@@ -51,7 +52,7 @@ public:
virtual void onMaybeRemovedFromTree(RenderNode* node) = 0;
protected:
- virtual ~TreeObserver() {}
+ virtual ~TreeObserver() = default;
};
// This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
@@ -70,8 +71,7 @@ public:
MODE_RT_ONLY,
};
- TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext)
- : mode(mode), prepareTextures(mode == MODE_FULL), canvasContext(canvasContext) {}
+ TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext);
TraversalMode mode;
// TODO: Remove this? Currently this is used to signal to stop preparing
@@ -93,6 +93,8 @@ public:
bool updateWindowPositions = false;
+ int disableForceDark;
+
struct Out {
bool hasFunctors = false;
// This is only updated if evaluateAnimations is true
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 18358e25fd5b..6cf04bf5f811 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -458,30 +458,22 @@ void Tree::drawStaging(Canvas* outCanvas) {
mStagingCache.dirty = false;
}
- SkPaint tmpPaint;
- SkPaint* paint = updatePaint(&tmpPaint, &mStagingProperties);
+ SkPaint paint;
+ getPaintFor(&paint, mStagingProperties);
outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(),
mStagingCache.bitmap->height(), mStagingProperties.getBounds().left(),
mStagingProperties.getBounds().top(),
mStagingProperties.getBounds().right(),
- mStagingProperties.getBounds().bottom(), paint);
-}
-
-SkPaint* Tree::getPaint() {
- return updatePaint(&mPaint, &mProperties);
+ mStagingProperties.getBounds().bottom(), &paint);
}
-// Update the given paint with alpha and color filter. Return nullptr if no color filter is
-// specified and root alpha is 1. Otherwise, return updated paint.
-SkPaint* Tree::updatePaint(SkPaint* outPaint, TreeProperties* prop) {
- if (prop->getRootAlpha() == 1.0f && prop->getColorFilter() == nullptr) {
- return nullptr;
- } else {
- outPaint->setColorFilter(sk_ref_sp(prop->getColorFilter()));
- outPaint->setFilterQuality(kLow_SkFilterQuality);
- outPaint->setAlpha(prop->getRootAlpha() * 255);
- return outPaint;
+void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties &prop) const {
+ // HWUI always draws VD with bilinear filtering.
+ outPaint->setFilterQuality(kLow_SkFilterQuality);
+ if (prop.getColorFilter() != nullptr) {
+ outPaint->setColorFilter(sk_ref_sp(prop.getColorFilter()));
}
+ outPaint->setAlpha(prop.getRootAlpha() * 255);
}
Bitmap& Tree::getBitmapUpdateIfDirty() {
@@ -554,12 +546,16 @@ void Tree::Cache::clear() {
mAtlasKey = INVALID_ATLAS_KEY;
}
-void Tree::draw(SkCanvas* canvas, const SkRect& bounds) {
+void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) {
+ // Update the paint for any animatable properties
+ SkPaint paint = inPaint;
+ paint.setAlpha(mProperties.getRootAlpha() * 255);
+
SkRect src;
sk_sp<SkSurface> vdSurface = mCache.getSurface(&src);
if (vdSurface) {
- canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src,
- bounds, getPaint(), SkCanvas::kFast_SrcRectConstraint);
+ canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, bounds, &paint,
+ SkCanvas::kFast_SrcRectConstraint);
} else {
// Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure.
// We render the VD into a temporary standalone buffer and mark the frame as dirty. Next
@@ -570,8 +566,8 @@ void Tree::draw(SkCanvas* canvas, const SkRect& bounds) {
int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
- canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight),
- bounds, getPaint(), SkCanvas::kFast_SrcRectConstraint);
+ canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
+ &paint, SkCanvas::kFast_SrcRectConstraint);
mCache.clear();
markDirty();
}
@@ -621,6 +617,80 @@ void Tree::onPropertyChanged(TreeProperties* prop) {
}
}
+class MinMaxAverage {
+public:
+ void add(float sample) {
+ if (mCount == 0) {
+ mMin = sample;
+ mMax = sample;
+ } else {
+ mMin = std::min(mMin, sample);
+ mMax = std::max(mMax, sample);
+ }
+ mTotal += sample;
+ mCount++;
+ }
+
+ float average() { return mTotal / mCount; }
+
+ float min() { return mMin; }
+
+ float max() { return mMax; }
+
+ float delta() { return mMax - mMin; }
+
+private:
+ float mMin = 0.0f;
+ float mMax = 0.0f;
+ float mTotal = 0.0f;
+ int mCount = 0;
+};
+
+BitmapPalette Tree::computePalette() {
+ // TODO Cache this and share the code with Bitmap.cpp
+
+ ATRACE_CALL();
+
+ // TODO: This calculation of converting to HSV & tracking min/max is probably overkill
+ // Experiment with something simpler since we just want to figure out if it's "color-ful"
+ // and then the average perceptual lightness.
+
+ MinMaxAverage hue, saturation, value;
+ int sampledCount = 0;
+
+ // Sample a grid of 100 pixels to get an overall estimation of the colors in play
+ mRootNode->forEachFillColor([&](SkColor color) {
+ if (SkColorGetA(color) < 75) {
+ return;
+ }
+ sampledCount++;
+ float hsv[3];
+ SkColorToHSV(color, hsv);
+ hue.add(hsv[0]);
+ saturation.add(hsv[1]);
+ value.add(hsv[2]);
+ });
+
+ if (sampledCount == 0) {
+ ALOGV("VectorDrawable is mostly translucent");
+ return BitmapPalette::Unknown;
+ }
+
+ ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = "
+ "%f]; value [min = %f, max = %f, avg = %f]",
+ sampledCount, hue.min(), hue.max(), hue.average(), saturation.min(), saturation.max(),
+ saturation.average(), value.min(), value.max(), value.average());
+
+ if (hue.delta() <= 20 && saturation.delta() <= .1f) {
+ if (value.average() >= .5f) {
+ return BitmapPalette::Light;
+ } else {
+ return BitmapPalette::Dark;
+ }
+ }
+ return BitmapPalette::Unknown;
+}
+
}; // namespace VectorDrawable
}; // namespace uirenderer
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index da52a9503377..9c0bb161380c 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -59,12 +59,6 @@ namespace VectorDrawable {
onPropertyChanged(); \
retVal; \
})
-#define UPDATE_SKPROP(field, value) \
- ({ \
- bool retVal = ((field) != (value)); \
- if ((field) != (value)) SkRefCnt_SafeAssign((field), (value)); \
- retVal; \
- })
/* A VectorDrawable is composed of a tree of nodes.
* Each node can be a group node, or a path.
@@ -126,6 +120,8 @@ public:
virtual void syncProperties() = 0;
virtual void setAntiAlias(bool aa) = 0;
+ virtual void forEachFillColor(const std::function<void(SkColor)>& func) const { }
+
protected:
std::string mName;
PropertyChangedListener* mPropertyChangedListener = nullptr;
@@ -223,29 +219,28 @@ public:
int fillType = 0; /* non-zero or kWinding_FillType in Skia */
};
explicit FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {}
- ~FullPathProperties() {
- SkSafeUnref(fillGradient);
- SkSafeUnref(strokeGradient);
- }
+ ~FullPathProperties() {}
void syncProperties(const FullPathProperties& prop) {
mPrimitiveFields = prop.mPrimitiveFields;
mTrimDirty = true;
- UPDATE_SKPROP(fillGradient, prop.fillGradient);
- UPDATE_SKPROP(strokeGradient, prop.strokeGradient);
+ fillGradient = prop.fillGradient;
+ strokeGradient = prop.strokeGradient;
onPropertyChanged();
}
void setFillGradient(SkShader* gradient) {
- if (UPDATE_SKPROP(fillGradient, gradient)) {
+ if (fillGradient.get() != gradient) {
+ fillGradient = sk_ref_sp(gradient);
onPropertyChanged();
}
}
void setStrokeGradient(SkShader* gradient) {
- if (UPDATE_SKPROP(strokeGradient, gradient)) {
+ if (strokeGradient.get() != gradient) {
+ strokeGradient = sk_ref_sp(gradient);
onPropertyChanged();
}
}
- SkShader* getFillGradient() const { return fillGradient; }
- SkShader* getStrokeGradient() const { return strokeGradient; }
+ SkShader* getFillGradient() const { return fillGradient.get(); }
+ SkShader* getStrokeGradient() const { return strokeGradient.get(); }
float getStrokeWidth() const { return mPrimitiveFields.strokeWidth; }
void setStrokeWidth(float strokeWidth) {
VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth);
@@ -325,8 +320,8 @@ public:
count,
};
PrimitiveFields mPrimitiveFields;
- SkShader* fillGradient = nullptr;
- SkShader* strokeGradient = nullptr;
+ sk_sp<SkShader> fillGradient;
+ sk_sp<SkShader> strokeGradient;
};
// Called from UI thread
@@ -356,6 +351,9 @@ public:
}
}
virtual void setAntiAlias(bool aa) { mAntiAlias = aa; }
+ void forEachFillColor(const std::function<void(SkColor)>& func) const override {
+ func(mStagingProperties.getFillColor());
+ }
protected:
const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) override;
@@ -487,6 +485,12 @@ public:
}
}
+ void forEachFillColor(const std::function<void(SkColor)>& func) const override {
+ for (auto& child : mChildren) {
+ child->forEachFillColor(func);
+ }
+ }
+
private:
GroupProperties mProperties = GroupProperties(this);
GroupProperties mStagingProperties = GroupProperties(this);
@@ -502,8 +506,8 @@ public:
// Copy properties from the tree and use the give node as the root node
Tree(const Tree* copy, Group* rootNode) : Tree(rootNode) {
- mStagingProperties.syncAnimatableProperties(*copy->stagingProperties());
- mStagingProperties.syncNonAnimatableProperties(*copy->stagingProperties());
+ mStagingProperties.syncAnimatableProperties(copy->stagingProperties());
+ mStagingProperties.syncNonAnimatableProperties(copy->stagingProperties());
}
// Draws the VD onto a bitmap cache, then the bitmap cache will be rendered onto the input
// canvas. Returns the number of pixels needed for the bitmap cache.
@@ -513,7 +517,6 @@ public:
Bitmap& getBitmapUpdateIfDirty();
void setAllowCaching(bool allowCaching) { mAllowCaching = allowCaching; }
- SkPaint* getPaint();
void syncProperties() {
if (mStagingProperties.mNonAnimatablePropertiesDirty) {
mCache.dirty |= (mProperties.mNonAnimatableProperties.viewportWidth !=
@@ -550,8 +553,7 @@ public:
SkRect bounds;
int scaledWidth = 0;
int scaledHeight = 0;
- SkColorFilter* colorFilter = nullptr;
- ~NonAnimatableProperties() { SkSafeUnref(colorFilter); }
+ sk_sp<SkColorFilter> colorFilter;
} mNonAnimatableProperties;
bool mNonAnimatablePropertiesDirty = true;
@@ -561,8 +563,7 @@ public:
void syncNonAnimatableProperties(const TreeProperties& prop) {
// Copy over the data that can only be changed in UI thread
if (mNonAnimatableProperties.colorFilter != prop.mNonAnimatableProperties.colorFilter) {
- SkRefCnt_SafeAssign(mNonAnimatableProperties.colorFilter,
- prop.mNonAnimatableProperties.colorFilter);
+ mNonAnimatableProperties.colorFilter = prop.mNonAnimatableProperties.colorFilter;
}
mNonAnimatableProperties = prop.mNonAnimatableProperties;
}
@@ -599,12 +600,13 @@ public:
}
}
void setColorFilter(SkColorFilter* filter) {
- if (UPDATE_SKPROP(mNonAnimatableProperties.colorFilter, filter)) {
+ if (mNonAnimatableProperties.colorFilter.get() != filter) {
+ mNonAnimatableProperties.colorFilter = sk_ref_sp(filter);
mNonAnimatablePropertiesDirty = true;
mTree->onPropertyChanged(this);
}
}
- SkColorFilter* getColorFilter() const { return mNonAnimatableProperties.colorFilter; }
+ SkColorFilter* getColorFilter() const { return mNonAnimatableProperties.colorFilter.get(); }
float getViewportWidth() const { return mNonAnimatableProperties.viewportWidth; }
float getViewportHeight() const { return mNonAnimatableProperties.viewportHeight; }
@@ -626,7 +628,7 @@ public:
};
void onPropertyChanged(TreeProperties* prop);
TreeProperties* mutateStagingProperties() { return &mStagingProperties; }
- const TreeProperties* stagingProperties() const { return &mStagingProperties; }
+ const TreeProperties& stagingProperties() const { return mStagingProperties; }
// This should only be called from animations on RT
TreeProperties* mutateProperties() { return &mProperties; }
@@ -644,7 +646,10 @@ public:
* Draws VD cache into a canvas. This should always be called from RT and it works with Skia
* pipelines only.
*/
- void draw(SkCanvas* canvas, const SkRect& bounds);
+ void draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& paint);
+
+ void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const;
+ BitmapPalette computePalette();
/**
* Draws VD into a GPU backed surface.
@@ -688,7 +693,6 @@ private:
skiapipeline::AtlasKey mAtlasKey = INVALID_ATLAS_KEY;
};
- SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
bool allocateBitmapIfNeeded(Cache& cache, int width, int height);
bool canReuseBitmap(Bitmap*, int width, int height);
void updateBitmapCache(Bitmap& outCache, bool useStagingData);
@@ -704,8 +708,6 @@ private:
TreeProperties mProperties = TreeProperties(this);
TreeProperties mStagingProperties = TreeProperties(this);
- SkPaint mPaint;
-
Cache mStagingCache;
Cache mCache;
@@ -718,6 +720,8 @@ private:
} // namespace VectorDrawable
typedef VectorDrawable::Path::Data PathData;
+typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index 186d0a88a33e..f0912777e3d8 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -19,7 +19,6 @@
#include "Vector.h"
-#include "FloatColor.h"
#include "utils/Macros.h"
namespace android {
@@ -74,46 +73,6 @@ struct TextureVertex {
REQUIRE_COMPATIBLE_LAYOUT(TextureVertex);
-/**
- * Simple structure to describe a vertex with a position, texture UV and an
- * sRGB color with alpha. The color is stored pre-multiplied in linear space.
- */
-struct ColorTextureVertex {
- float x, y;
- float u, v;
- float r, g, b, a; // pre-multiplied linear
-
- static inline void set(ColorTextureVertex* vertex, float x, float y, float u, float v,
- uint32_t color) {
- FloatColor c;
- c.set(color);
- *vertex = {x, y, u, v, c.r, c.g, c.b, c.a};
- }
-}; // struct ColorTextureVertex
-
-REQUIRE_COMPATIBLE_LAYOUT(ColorTextureVertex);
-
-/**
- * Simple structure to describe a vertex with a position and an alpha value.
- */
-struct AlphaVertex {
- float x, y;
- float alpha;
-
- static inline void set(AlphaVertex* vertex, float x, float y, float alpha) {
- *vertex = {x, y, alpha};
- }
-
- static inline void copyWithOffset(AlphaVertex* vertex, const AlphaVertex& src, float x,
- float y) {
- AlphaVertex::set(vertex, src.x + x, src.y + y, src.alpha);
- }
-
- static inline void setColor(AlphaVertex* vertex, float alpha) { vertex[0].alpha = alpha; }
-}; // struct AlphaVertex
-
-REQUIRE_COMPATIBLE_LAYOUT(AlphaVertex);
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp
deleted file mode 100644
index 30fba7ae7d9b..000000000000
--- a/libs/hwui/VkLayer.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "VkLayer.h"
-
-#include "renderstate/RenderState.h"
-
-#include <SkCanvas.h>
-#include <SkSurface.h>
-
-namespace android {
-namespace uirenderer {
-
-void VkLayer::updateTexture() {
- sk_sp<SkSurface> surface;
- SkImageInfo info = SkImageInfo::MakeS32(mWidth, mHeight, kPremul_SkAlphaType);
- surface = SkSurface::MakeRenderTarget(mRenderState.getGrContext(), SkBudgeted::kNo, info);
- surface->getCanvas()->clear(SK_ColorBLUE);
- mImage = surface->makeImageSnapshot();
-}
-
-void VkLayer::onVkContextDestroyed() {
- mImage = nullptr;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h
deleted file mode 100644
index e9664d04b7a5..000000000000
--- a/libs/hwui/VkLayer.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Layer.h"
-
-#include <SkImage.h>
-
-namespace android {
-namespace uirenderer {
-/**
- * A layer has dimensions and is backed by a VkImage.
- */
-class VkLayer : public Layer {
-public:
- VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
- : Layer(renderState, Api::Vulkan, colorFilter, alpha, mode)
- , mWidth(layerWidth)
- , mHeight(layerHeight)
- , mBlend(blend) {}
-
- virtual ~VkLayer() {}
-
- uint32_t getWidth() const override { return mWidth; }
-
- uint32_t getHeight() const override { return mHeight; }
-
- void setSize(uint32_t width, uint32_t height) override {
- mWidth = width;
- mHeight = height;
- }
-
- void setBlend(bool blend) override { mBlend = blend; }
-
- bool isBlend() const override { return mBlend; }
-
- sk_sp<SkImage> getImage() { return mImage; }
-
- void updateTexture();
-
- // If we've destroyed the vulkan context (VkInstance, VkDevice, etc.), we must make sure to
- // destroy any VkImages that were made with that context.
- void onVkContextDestroyed();
-
-private:
- int mWidth;
- int mHeight;
- bool mBlend;
-
- sk_sp<SkImage> mImage;
-
-}; // struct VkLayer
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/debug/wrap_gles.h b/libs/hwui/debug/wrap_gles.h
index 27a29aa0a6b2..3da6e802d178 100644
--- a/libs/hwui/debug/wrap_gles.h
+++ b/libs/hwui/debug/wrap_gles.h
@@ -28,6 +28,9 @@
#include <GLES3/gl31.h>
#include <GLES3/gl32.h>
+// constant used by the NULL GPU implementation as well as HWUI's unit tests
+constexpr int NULL_GPU_MAX_TEXTURE_SIZE = 2048;
+
// Generate stubs that route all the calls to our function table
#include "gles_redefine.h"
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
deleted file mode 100644
index 5dd8bb8119e9..000000000000
--- a/libs/hwui/font/CacheTexture.cpp
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <SkGlyph.h>
-
-#include "../Caches.h"
-#include "../Debug.h"
-#include "../Extensions.h"
-#include "../PixelBuffer.h"
-#include "CacheTexture.h"
-#include "FontUtil.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheBlock
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
- * order, except for the final block (the remainder space at the right, since we fill from the
- * left).
- */
-CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock* newBlock) {
-#if DEBUG_FONT_RENDERER
- ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d", newBlock, newBlock->mX,
- newBlock->mY, newBlock->mWidth, newBlock->mHeight);
-#endif
-
- CacheBlock* currBlock = head;
- CacheBlock* prevBlock = nullptr;
-
- while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
- if (newBlock->mWidth < currBlock->mWidth) {
- newBlock->mNext = currBlock;
- newBlock->mPrev = prevBlock;
- currBlock->mPrev = newBlock;
-
- if (prevBlock) {
- prevBlock->mNext = newBlock;
- return head;
- } else {
- return newBlock;
- }
- }
-
- prevBlock = currBlock;
- currBlock = currBlock->mNext;
- }
-
- // new block larger than all others - insert at end (but before the remainder space, if there)
- newBlock->mNext = currBlock;
- newBlock->mPrev = prevBlock;
-
- if (currBlock) {
- currBlock->mPrev = newBlock;
- }
-
- if (prevBlock) {
- prevBlock->mNext = newBlock;
- return head;
- } else {
- return newBlock;
- }
-}
-
-CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove) {
-#if DEBUG_FONT_RENDERER
- ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d", blockToRemove, blockToRemove->mX,
- blockToRemove->mY, blockToRemove->mWidth, blockToRemove->mHeight);
-#endif
-
- CacheBlock* newHead = head;
- CacheBlock* nextBlock = blockToRemove->mNext;
- CacheBlock* prevBlock = blockToRemove->mPrev;
-
- if (prevBlock) {
- // If this doesn't hold, we have a use-after-free below.
- LOG_ALWAYS_FATAL_IF(head == blockToRemove,
- "removeBlock: head should not have a previous block");
- prevBlock->mNext = nextBlock;
- } else {
- newHead = nextBlock;
- }
-
- if (nextBlock) {
- nextBlock->mPrev = prevBlock;
- }
-
- delete blockToRemove;
-
- return newHead;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheTexture
-///////////////////////////////////////////////////////////////////////////////
-
-CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount)
- : mTexture(Caches::getInstance())
- , mWidth(width)
- , mHeight(height)
- , mFormat(format)
- , mMaxQuadCount(maxQuadCount)
- , mCaches(Caches::getInstance()) {
- mTexture.blend = true;
-
- mCacheBlocks =
- new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- getWidth() - TEXTURE_BORDER_SIZE, getHeight() - TEXTURE_BORDER_SIZE);
-
- // OpenGL ES 3.0+ lets us specify the row length for unpack operations such
- // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
- // With OpenGL ES 2.0 we have to upload entire stripes instead.
- mHasUnpackRowLength = mCaches.extensions().hasUnpackRowLength();
-}
-
-CacheTexture::~CacheTexture() {
- releaseMesh();
- releasePixelBuffer();
- reset();
-}
-
-void CacheTexture::reset() {
- // Delete existing cache blocks
- while (mCacheBlocks != nullptr) {
- CacheBlock* tmpBlock = mCacheBlocks;
- mCacheBlocks = mCacheBlocks->mNext;
- delete tmpBlock;
- }
- mNumGlyphs = 0;
- mCurrentQuad = 0;
-}
-
-void CacheTexture::init() {
- // reset, then create a new remainder space to start again
- reset();
- mCacheBlocks =
- new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- getWidth() - TEXTURE_BORDER_SIZE, getHeight() - TEXTURE_BORDER_SIZE);
-}
-
-void CacheTexture::releaseMesh() {
- delete[] mMesh;
-}
-
-void CacheTexture::releasePixelBuffer() {
- if (mPixelBuffer) {
- delete mPixelBuffer;
- mPixelBuffer = nullptr;
- }
- mTexture.deleteTexture();
- mDirty = false;
- mCurrentQuad = 0;
-}
-
-void CacheTexture::setLinearFiltering(bool linearFiltering) {
- mTexture.setFilter(linearFiltering ? GL_LINEAR : GL_NEAREST);
-}
-
-void CacheTexture::allocateMesh() {
- if (!mMesh) {
- mMesh = new TextureVertex[mMaxQuadCount * 4];
- }
-}
-
-void CacheTexture::allocatePixelBuffer() {
- if (!mPixelBuffer) {
- mPixelBuffer = PixelBuffer::create(mFormat, getWidth(), getHeight());
- }
-
- GLint internalFormat = mFormat;
- if (mFormat == GL_RGBA) {
- internalFormat = mCaches.rgbaInternalFormat();
- }
-
- mTexture.resize(mWidth, mHeight, internalFormat, mFormat);
- mTexture.setFilter(getLinearFiltering() ? GL_LINEAR : GL_NEAREST);
- mTexture.setWrap(GL_CLAMP_TO_EDGE);
-}
-
-bool CacheTexture::upload() {
- const Rect& dirtyRect = mDirtyRect;
-
- // align the x direction to 32 and y direction to 4 for better performance
- uint32_t x = (((uint32_t)dirtyRect.left) & (~0x1F));
- uint32_t y = (((uint32_t)dirtyRect.top) & (~0x3));
- uint32_t r = ((((uint32_t)dirtyRect.right) + 0x1F) & (~0x1F)) - x;
- uint32_t b = ((((uint32_t)dirtyRect.bottom) + 0x3) & (~0x3)) - y;
- uint32_t width = (r > getWidth() ? getWidth() : r);
- uint32_t height = (b > getHeight() ? getHeight() : b);
-
- // The unpack row length only needs to be specified when a new
- // texture is bound
- if (mHasUnpackRowLength) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, getWidth());
- } else {
- x = 0;
- width = getWidth();
- }
-
- mPixelBuffer->upload(x, y, width, height);
- setDirty(false);
-
- return mHasUnpackRowLength;
-}
-
-void CacheTexture::setDirty(bool dirty) {
- mDirty = dirty;
- if (!dirty) {
- mDirtyRect.setEmpty();
- }
-}
-
-bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
- switch (glyph.fMaskFormat) {
- case SkMask::kA8_Format:
- case SkMask::kBW_Format:
- if (mFormat != GL_ALPHA) {
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: texture format %x is inappropriate for monochromatic glyphs",
- mFormat);
-#endif
- return false;
- }
- break;
- case SkMask::kARGB32_Format:
- if (mFormat != GL_RGBA) {
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: texture format %x is inappropriate for colour glyphs", mFormat);
-#endif
- return false;
- }
- break;
- default:
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: unknown glyph format %x encountered", glyph.fMaskFormat);
-#endif
- return false;
- }
-
- if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > getHeight()) {
- return false;
- }
-
- uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
- uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
-
- // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
- // This columns for glyphs that are close but not necessarily exactly the same size. It trades
- // off the loss of a few pixels for some glyphs against the ability to store more glyphs
- // of varying sizes in one block.
- uint16_t roundedUpW = (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
-
- CacheBlock* cacheBlock = mCacheBlocks;
- while (cacheBlock) {
- // Store glyph in this block iff: it fits the block's remaining space and:
- // it's the remainder space (mY == 0) or there's only enough height for this one glyph
- // or it's within ROUNDING_SIZE of the block width
- if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
- (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
- (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
- if (cacheBlock->mHeight - glyphH < glyphH) {
- // Only enough space for this glyph - don't bother rounding up the width
- roundedUpW = glyphW;
- }
-
- *retOriginX = cacheBlock->mX;
- *retOriginY = cacheBlock->mY;
-
- // If this is the remainder space, create a new cache block for this column. Otherwise,
- // adjust the info about this column.
- if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
- uint16_t oldX = cacheBlock->mX;
- // Adjust remainder space dimensions
- cacheBlock->mWidth -= roundedUpW;
- cacheBlock->mX += roundedUpW;
-
- if (getHeight() - glyphH >= glyphH) {
- // There's enough height left over to create a new CacheBlock
- CacheBlock* newBlock =
- new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE, roundedUpW,
- getHeight() - glyphH - TEXTURE_BORDER_SIZE);
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
- newBlock, newBlock->mX, newBlock->mY, newBlock->mWidth,
- newBlock->mHeight);
-#endif
- mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
- }
- } else {
- // Insert into current column and adjust column dimensions
- cacheBlock->mY += glyphH;
- cacheBlock->mHeight -= glyphH;
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
- cacheBlock, cacheBlock->mX, cacheBlock->mY, cacheBlock->mWidth,
- cacheBlock->mHeight);
-#endif
- }
-
- if (cacheBlock->mHeight < std::min(glyphH, glyphW)) {
- // If remaining space in this block is too small to be useful, remove it
- mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
- }
-
- mDirty = true;
- const Rect r(*retOriginX - TEXTURE_BORDER_SIZE, *retOriginY - TEXTURE_BORDER_SIZE,
- *retOriginX + glyphW, *retOriginY + glyphH);
- mDirtyRect.unionWith(r);
- mNumGlyphs++;
-
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: current block list:");
- mCacheBlocks->output();
-#endif
-
- return true;
- }
- cacheBlock = cacheBlock->mNext;
- }
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
-#endif
- return false;
-}
-
-uint32_t CacheTexture::calculateFreeMemory() const {
- CacheBlock* cacheBlock = mCacheBlocks;
- uint32_t free = 0;
- // currently only two formats are supported: GL_ALPHA or GL_RGBA;
- uint32_t bpp = mFormat == GL_RGBA ? 4 : 1;
- while (cacheBlock) {
- free += bpp * cacheBlock->mWidth * cacheBlock->mHeight;
- cacheBlock = cacheBlock->mNext;
- }
- return free;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
deleted file mode 100644
index 654378eeb47e..000000000000
--- a/libs/hwui/font/CacheTexture.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_CACHE_TEXTURE_H
-#define ANDROID_HWUI_CACHE_TEXTURE_H
-
-#include "PixelBuffer.h"
-#include "Rect.h"
-#include "Texture.h"
-#include "Vertex.h"
-
-#include <GLES3/gl3.h>
-#include <SkGlyph.h>
-#include <utils/Log.h>
-
-namespace android {
-namespace uirenderer {
-
-class Caches;
-
-/**
- * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
- * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
- * When we add a glyph to the cache, we see if it fits within one of the existing columns that
- * have already been started (this is the case if the glyph fits vertically as well as
- * horizontally, and if its width is sufficiently close to the column width to avoid
- * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
- * glyph fits, we check the final node, which is the remaining space in the cache, creating
- * a new column as appropriate.
- *
- * As columns fill up, we remove their CacheBlock from the list to avoid having to check
- * small blocks in the future.
- */
-struct CacheBlock {
- uint16_t mX;
- uint16_t mY;
- uint16_t mWidth;
- uint16_t mHeight;
- CacheBlock* mNext;
- CacheBlock* mPrev;
-
- CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height)
- : mX(x), mY(y), mWidth(width), mHeight(height), mNext(nullptr), mPrev(nullptr) {}
-
- static CacheBlock* insertBlock(CacheBlock* head, CacheBlock* newBlock);
- static CacheBlock* removeBlock(CacheBlock* head, CacheBlock* blockToRemove);
-
- void output() {
- CacheBlock* currBlock = this;
- while (currBlock) {
- ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d", currBlock, currBlock->mX,
- currBlock->mY, currBlock->mWidth, currBlock->mHeight);
- currBlock = currBlock->mNext;
- }
- }
-};
-
-class CacheTexture {
-public:
- CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount);
- ~CacheTexture();
-
- void reset();
- void init();
-
- void releaseMesh();
- void releasePixelBuffer();
-
- void allocatePixelBuffer();
- void allocateMesh();
-
- // Returns true if glPixelStorei(GL_UNPACK_ROW_LENGTH) must be reset
- // This method will also call setDirty(false)
- bool upload();
-
- bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
-
- inline uint16_t getWidth() const { return mWidth; }
-
- inline uint16_t getHeight() const { return mHeight; }
-
- inline GLenum getFormat() const { return mFormat; }
-
- inline uint32_t getOffset(uint16_t x, uint16_t y) const {
- return (y * getWidth() + x) * PixelBuffer::formatSize(mFormat);
- }
-
- inline const Rect* getDirtyRect() const { return &mDirtyRect; }
-
- inline PixelBuffer* getPixelBuffer() const { return mPixelBuffer; }
-
- Texture& getTexture() {
- allocatePixelBuffer();
- return mTexture;
- }
-
- GLuint getTextureId() {
- allocatePixelBuffer();
- return mTexture.id();
- }
-
- inline bool isDirty() const { return mDirty; }
-
- inline bool getLinearFiltering() const { return mLinearFiltering; }
-
- /**
- * This method assumes that the proper texture unit is active.
- */
- void setLinearFiltering(bool linearFiltering);
-
- inline uint16_t getGlyphCount() const { return mNumGlyphs; }
-
- TextureVertex* mesh() const { return mMesh; }
-
- uint32_t meshElementCount() const { return mCurrentQuad * 6; }
-
- uint16_t* indices() const { return (uint16_t*)nullptr; }
-
- void resetMesh() { mCurrentQuad = 0; }
-
- inline void addQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2,
- float v2, float x3, float y3, float u3, float v3, float x4, float y4,
- float u4, float v4) {
- TextureVertex* mesh = mMesh + mCurrentQuad * 4;
- TextureVertex::set(mesh++, x2, y2, u2, v2);
- TextureVertex::set(mesh++, x3, y3, u3, v3);
- TextureVertex::set(mesh++, x1, y1, u1, v1);
- TextureVertex::set(mesh++, x4, y4, u4, v4);
- mCurrentQuad++;
- }
-
- bool canDraw() const { return mCurrentQuad > 0; }
-
- bool endOfMesh() const { return mCurrentQuad == mMaxQuadCount; }
-
- uint32_t calculateFreeMemory() const;
-
-private:
- void setDirty(bool dirty);
-
- PixelBuffer* mPixelBuffer = nullptr;
- Texture mTexture;
- uint32_t mWidth, mHeight;
- GLenum mFormat;
- bool mLinearFiltering = false;
- bool mDirty = false;
- uint16_t mNumGlyphs = 0;
- TextureVertex* mMesh = nullptr;
- uint32_t mCurrentQuad = 0;
- uint32_t mMaxQuadCount;
- Caches& mCaches;
- CacheBlock* mCacheBlocks;
- bool mHasUnpackRowLength;
- Rect mDirtyRect;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_CACHE_TEXTURE_H
diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h
deleted file mode 100644
index 93bb823db512..000000000000
--- a/libs/hwui/font/CachedGlyphInfo.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_CACHED_GLYPH_INFO_H
-#define ANDROID_HWUI_CACHED_GLYPH_INFO_H
-
-namespace android {
-namespace uirenderer {
-
-class CacheTexture;
-
-struct CachedGlyphInfo {
- // Has the cache been invalidated?
- bool mIsValid;
- // Location of the cached glyph in the bitmap
- // in case we need to resize the texture or
- // render to bitmap
- uint32_t mStartX;
- uint32_t mStartY;
- uint32_t mBitmapWidth;
- uint32_t mBitmapHeight;
- // Also cache texture coords for the quad
- float mBitmapMinU;
- float mBitmapMinV;
- float mBitmapMaxU;
- float mBitmapMaxV;
- // Minimize how much we call freetype
- uint32_t mGlyphIndex;
- float mAdvanceX;
- float mAdvanceY;
- // Values below contain a glyph's origin in the bitmap
- int32_t mBitmapLeft;
- int32_t mBitmapTop;
- // Auto-kerning; represents a 2.6 fixed-point value with range [-1, 1].
- int8_t mLsbDelta;
- int8_t mRsbDelta;
- CacheTexture* mCacheTexture;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_CACHED_GLYPH_INFO_H
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
deleted file mode 100644
index 41a24a809e89..000000000000
--- a/libs/hwui/font/Font.cpp
+++ /dev/null
@@ -1,490 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cutils/compiler.h>
-
-#include <utils/JenkinsHash.h>
-#include <utils/Trace.h>
-
-#include <SkGlyph.h>
-#include <SkGlyphCache.h>
-#include <SkSurfaceProps.h>
-#include <SkUtils.h>
-
-#include "../Debug.h"
-#include "../FontRenderer.h"
-#include "../PixelBuffer.h"
-#include "../Properties.h"
-#include "Font.h"
-#include "FontUtil.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-Font::Font(FontRenderer* state, const Font::FontDescription& desc)
- : mState(state), mDescription(desc) {}
-
-Font::FontDescription::FontDescription(const SkPaint* paint, const SkMatrix& rasterMatrix)
- : mLookupTransform(rasterMatrix) {
- mFontId = SkTypeface::UniqueID(paint->getTypeface());
- mFontSize = paint->getTextSize();
- mFlags = 0;
- if (paint->isFakeBoldText()) {
- mFlags |= Font::kFakeBold;
- }
- mItalicStyle = paint->getTextSkewX();
- mScaleX = paint->getTextScaleX();
- mStyle = paint->getStyle();
- mStrokeWidth = paint->getStrokeWidth();
- mAntiAliasing = paint->isAntiAlias();
- mHinting = paint->getHinting();
- if (!mLookupTransform.invert(&mInverseLookupTransform)) {
- ALOGW("Could not query the inverse lookup transform for this font");
- }
-}
-
-Font::~Font() {
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- delete mCachedGlyphs.valueAt(i);
- }
-}
-
-hash_t Font::FontDescription::hash() const {
- uint32_t hash = JenkinsHashMix(0, mFontId);
- hash = JenkinsHashMix(hash, android::hash_type(mFontSize));
- hash = JenkinsHashMix(hash, android::hash_type(mFlags));
- hash = JenkinsHashMix(hash, android::hash_type(mItalicStyle));
- hash = JenkinsHashMix(hash, android::hash_type(mScaleX));
- hash = JenkinsHashMix(hash, android::hash_type(mStyle));
- hash = JenkinsHashMix(hash, android::hash_type(mStrokeWidth));
- hash = JenkinsHashMix(hash, int(mAntiAliasing));
- hash = JenkinsHashMix(hash, android::hash_type(mHinting));
- hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleX]));
- hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleY]));
- return JenkinsHashWhiten(hash);
-}
-
-int Font::FontDescription::compare(const Font::FontDescription& lhs,
- const Font::FontDescription& rhs) {
- int deltaInt = int(lhs.mFontId) - int(rhs.mFontId);
- if (deltaInt != 0) return deltaInt;
-
- if (lhs.mFontSize < rhs.mFontSize) return -1;
- if (lhs.mFontSize > rhs.mFontSize) return +1;
-
- if (lhs.mItalicStyle < rhs.mItalicStyle) return -1;
- if (lhs.mItalicStyle > rhs.mItalicStyle) return +1;
-
- deltaInt = int(lhs.mFlags) - int(rhs.mFlags);
- if (deltaInt != 0) return deltaInt;
-
- if (lhs.mScaleX < rhs.mScaleX) return -1;
- if (lhs.mScaleX > rhs.mScaleX) return +1;
-
- deltaInt = int(lhs.mStyle) - int(rhs.mStyle);
- if (deltaInt != 0) return deltaInt;
-
- if (lhs.mStrokeWidth < rhs.mStrokeWidth) return -1;
- if (lhs.mStrokeWidth > rhs.mStrokeWidth) return +1;
-
- deltaInt = int(lhs.mAntiAliasing) - int(rhs.mAntiAliasing);
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = int(lhs.mHinting) - int(rhs.mHinting);
- if (deltaInt != 0) return deltaInt;
-
- if (lhs.mLookupTransform[SkMatrix::kMScaleX] < rhs.mLookupTransform[SkMatrix::kMScaleX])
- return -1;
- if (lhs.mLookupTransform[SkMatrix::kMScaleX] > rhs.mLookupTransform[SkMatrix::kMScaleX])
- return +1;
-
- if (lhs.mLookupTransform[SkMatrix::kMScaleY] < rhs.mLookupTransform[SkMatrix::kMScaleY])
- return -1;
- if (lhs.mLookupTransform[SkMatrix::kMScaleY] > rhs.mLookupTransform[SkMatrix::kMScaleY])
- return +1;
-
- return 0;
-}
-
-void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
- if (!cacheTexture || cachedGlyph->mCacheTexture == cacheTexture) {
- cachedGlyph->mIsValid = false;
- }
- }
-}
-
-void Font::measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap,
- uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int width = (int)glyph->mBitmapWidth;
- int height = (int)glyph->mBitmapHeight;
-
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop;
-
- if (bounds->bottom > nPenY) {
- bounds->bottom = nPenY;
- }
- if (bounds->left > nPenX) {
- bounds->left = nPenX;
- }
- if (bounds->right < nPenX + width) {
- bounds->right = nPenX + width;
- }
- if (bounds->top < nPenY + height) {
- bounds->top = nPenY + height;
- }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, uint32_t bitmapW,
- uint32_t bitmapH, Rect* bounds, const float* pos) {
- float width = (float)glyph->mBitmapWidth;
- float height = (float)glyph->mBitmapHeight;
-
- float nPenX = x + glyph->mBitmapLeft;
- float nPenY = y + glyph->mBitmapTop + height;
-
- float u1 = glyph->mBitmapMinU;
- float u2 = glyph->mBitmapMaxU;
- float v1 = glyph->mBitmapMinV;
- float v2 = glyph->mBitmapMaxV;
-
- mState->appendMeshQuad(nPenX, nPenY, u1, v2, nPenX + width, nPenY, u2, v2, nPenX + width,
- nPenY - height, u2, v1, nPenX, nPenY - height, u1, v1,
- glyph->mCacheTexture);
-}
-
-void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap,
- uint32_t bitmapW, uint32_t bitmapH, Rect* bounds,
- const float* pos) {
- float width = (float)glyph->mBitmapWidth;
- float height = (float)glyph->mBitmapHeight;
-
- SkPoint p[4];
- p[0].iset(glyph->mBitmapLeft, glyph->mBitmapTop + height);
- p[1].iset(glyph->mBitmapLeft + width, glyph->mBitmapTop + height);
- p[2].iset(glyph->mBitmapLeft + width, glyph->mBitmapTop);
- p[3].iset(glyph->mBitmapLeft, glyph->mBitmapTop);
-
- mDescription.mInverseLookupTransform.mapPoints(p, 4);
-
- p[0].offset(x, y);
- p[1].offset(x, y);
- p[2].offset(x, y);
- p[3].offset(x, y);
-
- float u1 = glyph->mBitmapMinU;
- float u2 = glyph->mBitmapMaxU;
- float v1 = glyph->mBitmapMinV;
- float v2 = glyph->mBitmapMaxV;
-
- mState->appendRotatedMeshQuad(p[0].x(), p[0].y(), u1, v2, p[1].x(), p[1].y(), u2, v2, p[2].x(),
- p[2].y(), u2, v1, p[3].x(), p[3].y(), u1, v1,
- glyph->mCacheTexture);
-}
-
-void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap,
- uint32_t bitmapWidth, uint32_t bitmapHeight, Rect* bounds,
- const float* pos) {
- int dstX = x + glyph->mBitmapLeft;
- int dstY = y + glyph->mBitmapTop;
-
- CacheTexture* cacheTexture = glyph->mCacheTexture;
- PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer();
-
- uint32_t formatSize = PixelBuffer::formatSize(pixelBuffer->getFormat());
- uint32_t alpha_channel_offset = PixelBuffer::formatAlphaOffset(pixelBuffer->getFormat());
- uint32_t cacheWidth = cacheTexture->getWidth();
- uint32_t srcStride = formatSize * cacheWidth;
- uint32_t startY = glyph->mStartY * srcStride;
- uint32_t endY = startY + (glyph->mBitmapHeight * srcStride);
-
- const uint8_t* cacheBuffer = pixelBuffer->map();
-
- for (uint32_t cacheY = startY, bitmapY = dstY * bitmapWidth; cacheY < endY;
- cacheY += srcStride, bitmapY += bitmapWidth) {
- for (uint32_t i = 0; i < glyph->mBitmapWidth; ++i) {
- uint8_t* dst = &(bitmap[bitmapY + dstX + i]);
- const uint8_t& src =
- cacheBuffer[cacheY + (glyph->mStartX + i) * formatSize + alpha_channel_offset];
- // Add alpha values to a max of 255, full opacity. This is done to handle
- // fonts/strings where glyphs overlap.
- *dst = std::min(*dst + src, 255);
- }
- }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
- const float halfWidth = glyph->mBitmapWidth * 0.5f;
- const float height = glyph->mBitmapHeight;
-
- vOffset += glyph->mBitmapTop + height;
-
- SkPoint destination[4];
- bool ok = measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
- if (!ok) {
- ALOGW("The path for drawTextOnPath is empty or null");
- }
-
- // Move along the tangent and offset by the normal
- destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
- -tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
- tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[2].set(destination[1].fX + tangent->fY * height,
- destination[1].fY - tangent->fX * height);
- destination[3].set(destination[0].fX + tangent->fY * height,
- destination[0].fY - tangent->fX * height);
-
- const float u1 = glyph->mBitmapMinU;
- const float u2 = glyph->mBitmapMaxU;
- const float v1 = glyph->mBitmapMinV;
- const float v2 = glyph->mBitmapMaxV;
-
- mState->appendRotatedMeshQuad(
- position->x() + destination[0].x(), position->y() + destination[0].y(), u1, v2,
- position->x() + destination[1].x(), position->y() + destination[1].y(), u2, v2,
- position->x() + destination[2].x(), position->y() + destination[2].y(), u2, v1,
- position->x() + destination[3].x(), position->y() + destination[3].y(), u1, v1,
- glyph->mCacheTexture);
-}
-
-CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bool precaching) {
- CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(textUnit);
- if (cachedGlyph) {
- // Is the glyph still in texture cache?
- if (!cachedGlyph->mIsValid) {
- SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
- SkAutoGlyphCacheNoGamma autoCache(*paint, &surfaceProps,
- &mDescription.mLookupTransform);
- const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), textUnit);
- updateGlyphCache(paint, skiaGlyph, autoCache.getCache(), cachedGlyph, precaching);
- }
- } else {
- cachedGlyph = cacheGlyph(paint, textUnit, precaching);
- }
-
- return cachedGlyph;
-}
-
-void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y,
- const float* positions) {
- render(paint, glyphs, numGlyphs, x, y, FRAMEBUFFER, nullptr, 0, 0, nullptr, positions);
-}
-
-void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkPath* path,
- float hOffset, float vOffset) {
- if (numGlyphs == 0 || glyphs == nullptr) {
- return;
- }
-
- int glyphsCount = 0;
- int prevRsbDelta = 0;
-
- float penX = 0.0f;
-
- SkPoint position;
- SkVector tangent;
-
- SkPathMeasure measure(*path, false);
- float pathLength = SkScalarToFloat(measure.getLength());
-
- if (paint->getTextAlign() != SkPaint::kLeft_Align) {
- float textWidth = SkScalarToFloat(paint->measureText(glyphs, numGlyphs * 2));
- float pathOffset = pathLength;
- if (paint->getTextAlign() == SkPaint::kCenter_Align) {
- textWidth *= 0.5f;
- pathOffset *= 0.5f;
- }
- penX += pathOffset - textWidth;
- }
-
- while (glyphsCount < numGlyphs && penX < pathLength) {
- glyph_t glyph = *(glyphs++);
-
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta);
- prevRsbDelta = cachedGlyph->mRsbDelta;
-
- if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) {
- drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
- }
-
- penX += cachedGlyph->mAdvanceX;
-
- glyphsCount++;
- }
-}
-
-void Font::measure(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, Rect* bounds,
- const float* positions) {
- if (bounds == nullptr) {
- ALOGE("No return rectangle provided to measure text");
- return;
- }
- bounds->set(1e6, -1e6, -1e6, 1e6);
- render(paint, glyphs, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
-}
-
-void Font::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs) {
- if (numGlyphs == 0 || glyphs == nullptr) {
- return;
- }
-
- int glyphsCount = 0;
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = *(glyphs++);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- getCachedGlyph(paint, glyph, true);
- glyphsCount++;
- }
-}
-
-void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y,
- RenderMode mode, uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* positions) {
- if (numGlyphs == 0 || glyphs == nullptr) {
- return;
- }
-
- static RenderGlyph gRenderGlyph[] = {&android::uirenderer::Font::drawCachedGlyph,
- &android::uirenderer::Font::drawCachedGlyphTransformed,
- &android::uirenderer::Font::drawCachedGlyphBitmap,
- &android::uirenderer::Font::drawCachedGlyphBitmap,
- &android::uirenderer::Font::measureCachedGlyph,
- &android::uirenderer::Font::measureCachedGlyph};
- RenderGlyph render = gRenderGlyph[(mode << 1) + !mIdentityTransform];
-
- int glyphsCount = 0;
-
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = *(glyphs++);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-
- // If it's still not valid, we couldn't cache it, so we shouldn't
- // draw garbage; also skip empty glyphs (spaces)
- if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) {
- int penX = x + (int)roundf(positions[(glyphsCount << 1)]);
- int penY = y + (int)roundf(positions[(glyphsCount << 1) + 1]);
-#ifdef BUGREPORT_FONT_CACHE_USAGE
- mState->historyTracker().glyphRendered(cachedGlyph, penX, penY);
-#endif
- (*this.*render)(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH, bounds, positions);
- } else {
-#ifdef BUGREPORT_FONT_CACHE_USAGE
- mState->historyTracker().glyphRendered(cachedGlyph, -1, -1);
-#endif
- }
-
- glyphsCount++;
- }
-}
-
-void Font::updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph,
- SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching) {
- glyph->mAdvanceX = skiaGlyph.fAdvanceX;
- glyph->mAdvanceY = skiaGlyph.fAdvanceY;
- glyph->mBitmapLeft = skiaGlyph.fLeft;
- glyph->mBitmapTop = skiaGlyph.fTop;
- glyph->mLsbDelta = skiaGlyph.fLsbDelta;
- glyph->mRsbDelta = skiaGlyph.fRsbDelta;
-
- uint32_t startX = 0;
- uint32_t startY = 0;
-
- // Get the bitmap for the glyph
- if (!skiaGlyph.fImage) {
- skiaGlyphCache->findImage(skiaGlyph);
- }
- mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
-
- if (!glyph->mIsValid) {
- return;
- }
-
- uint32_t endX = startX + skiaGlyph.fWidth;
- uint32_t endY = startY + skiaGlyph.fHeight;
-
- glyph->mStartX = startX;
- glyph->mStartY = startY;
- glyph->mBitmapWidth = skiaGlyph.fWidth;
- glyph->mBitmapHeight = skiaGlyph.fHeight;
-
- bool empty = skiaGlyph.fWidth == 0 || skiaGlyph.fHeight == 0;
- if (!empty) {
- uint32_t cacheWidth = glyph->mCacheTexture->getWidth();
- uint32_t cacheHeight = glyph->mCacheTexture->getHeight();
-
- glyph->mBitmapMinU = startX / (float)cacheWidth;
- glyph->mBitmapMinV = startY / (float)cacheHeight;
- glyph->mBitmapMaxU = endX / (float)cacheWidth;
- glyph->mBitmapMaxV = endY / (float)cacheHeight;
-
- mState->setTextureDirty();
- }
-}
-
-CachedGlyphInfo* Font::cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching) {
- CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
- mCachedGlyphs.add(glyph, newGlyph);
-
- SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
- SkAutoGlyphCacheNoGamma autoCache(*paint, &surfaceProps, &mDescription.mLookupTransform);
- const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), glyph);
- newGlyph->mIsValid = false;
- newGlyph->mGlyphIndex = skiaGlyph.fID;
-
- updateGlyphCache(paint, skiaGlyph, autoCache.getCache(), newGlyph, precaching);
-
- return newGlyph;
-}
-
-Font* Font::create(FontRenderer* state, const SkPaint* paint, const SkMatrix& matrix) {
- FontDescription description(paint, matrix);
- Font* font = state->mActiveFonts.get(description);
-
- if (!font) {
- font = new Font(state, description);
- state->mActiveFonts.put(description, font);
- }
- font->mIdentityTransform = matrix.isIdentity();
-
- return font;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
deleted file mode 100644
index 85221bc069f1..000000000000
--- a/libs/hwui/font/Font.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_FONT_H
-#define ANDROID_HWUI_FONT_H
-
-#include <vector>
-
-#include <utils/KeyedVector.h>
-
-#include <SkPaint.h>
-#include <SkPathMeasure.h>
-#include <SkScalar.h>
-#include <SkTypeface.h>
-
-#include "../Matrix.h"
-#include "../Rect.h"
-#include "FontUtil.h"
-
-class SkGlyphCache;
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-struct CachedGlyphInfo;
-class CacheTexture;
-class FontRenderer;
-
-/**
- * Represents a font, defined by a Skia font id and a font size. A font is used
- * to generate glyphs and cache them in the FontState.
- */
-class Font {
-public:
- enum Style { kFakeBold = 1 };
-
- struct FontDescription {
- FontDescription(const SkPaint* paint, const SkMatrix& matrix);
-
- static int compare(const FontDescription& lhs, const FontDescription& rhs);
-
- hash_t hash() const;
-
- bool operator==(const FontDescription& other) const { return compare(*this, other) == 0; }
-
- bool operator!=(const FontDescription& other) const { return compare(*this, other) != 0; }
-
- SkFontID mFontId;
- float mFontSize;
- int mFlags;
- float mItalicStyle;
- float mScaleX;
- uint8_t mStyle;
- float mStrokeWidth;
- bool mAntiAliasing;
- uint8_t mHinting;
- SkMatrix mLookupTransform;
- SkMatrix mInverseLookupTransform;
- };
-
- ~Font();
-
- void render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y,
- const float* positions);
-
- void render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkPath* path,
- float hOffset, float vOffset);
-
- const Font::FontDescription& getDescription() const { return mDescription; }
-
- /**
- * Creates a new font associated with the specified font state.
- */
- static Font* create(FontRenderer* state, const SkPaint* paint, const SkMatrix& matrix);
-
-private:
- friend class FontRenderer;
-
- Font(FontRenderer* state, const Font::FontDescription& desc);
-
- typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*, uint32_t, uint32_t,
- Rect*, const float*);
-
- enum RenderMode {
- FRAMEBUFFER,
- BITMAP,
- MEASURE,
- };
-
- void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs);
-
- void render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y,
- RenderMode mode, uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds,
- const float* positions);
-
- void measure(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, Rect* bounds,
- const float* positions);
-
- void invalidateTextureCache(CacheTexture* cacheTexture = nullptr);
-
- CachedGlyphInfo* cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching);
- void updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph,
- SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching);
-
- void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, uint32_t bitmapW,
- uint32_t bitmapH, Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, uint32_t bitmapW,
- uint32_t bitmapH, Rect* bounds, const float* pos);
- void drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap,
- uint32_t bitmapW, uint32_t bitmapH, Rect* bounds,
- const float* pos);
- void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap,
- uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
-
- CachedGlyphInfo* getCachedGlyph(const SkPaint* paint, glyph_t textUnit,
- bool precaching = false);
-
- FontRenderer* mState;
- FontDescription mDescription;
-
- // Cache of glyphs
- DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
-
- bool mIdentityTransform;
-};
-
-inline int strictly_order_type(const Font::FontDescription& lhs, const Font::FontDescription& rhs) {
- return Font::FontDescription::compare(lhs, rhs) < 0;
-}
-
-inline int compare_type(const Font::FontDescription& lhs, const Font::FontDescription& rhs) {
- return Font::FontDescription::compare(lhs, rhs);
-}
-
-inline hash_t hash_type(const Font::FontDescription& entry) {
- return entry.hash();
-}
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_FONT_H
diff --git a/libs/hwui/font/FontCacheHistoryTracker.cpp b/libs/hwui/font/FontCacheHistoryTracker.cpp
deleted file mode 100644
index 5b61c494524a..000000000000
--- a/libs/hwui/font/FontCacheHistoryTracker.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FontCacheHistoryTracker.h"
-
-#include "CacheTexture.h"
-#include "CachedGlyphInfo.h"
-
-namespace android {
-namespace uirenderer {
-
-void FontCacheHistoryTracker::dumpCachedGlyph(String8& log, const CachedGlyph& glyph) {
- log.appendFormat("glyph (texture %p, position: (%d, %d), size: %dx%d, gen: %d)", glyph.texture,
- glyph.startX, glyph.startY, glyph.bitmapW, glyph.bitmapH, glyph.generation);
-}
-
-void FontCacheHistoryTracker::dumpRenderEntry(String8& log, const RenderEntry& entry) {
- if (entry.penX == -1 && entry.penY == -1) {
- log.appendFormat(" glyph skipped in gen: %d\n", entry.glyph.generation);
- } else {
- log.appendFormat(" rendered ");
- dumpCachedGlyph(log, entry.glyph);
- log.appendFormat(" at (%d, %d)\n", entry.penX, entry.penY);
- }
-}
-
-void FontCacheHistoryTracker::dumpUploadEntry(String8& log, const CachedGlyph& glyph) {
- if (glyph.bitmapW == 0 && glyph.bitmapH == 0) {
- log.appendFormat(" cleared cachetexture %p in gen %d\n", glyph.texture,
- glyph.generation);
- } else {
- log.appendFormat(" uploaded ");
- dumpCachedGlyph(log, glyph);
- log.appendFormat("\n");
- }
-}
-
-void FontCacheHistoryTracker::dump(String8& log) const {
- log.appendFormat("FontCacheHistory: \n");
- log.appendFormat(" Upload history: \n");
- for (size_t i = 0; i < mUploadHistory.size(); i++) {
- dumpUploadEntry(log, mUploadHistory[i]);
- }
- log.appendFormat(" Render history: \n");
- for (size_t i = 0; i < mRenderHistory.size(); i++) {
- dumpRenderEntry(log, mRenderHistory[i]);
- }
-}
-
-void FontCacheHistoryTracker::glyphRendered(CachedGlyphInfo* glyphInfo, int penX, int penY) {
- RenderEntry& entry = mRenderHistory.next();
- entry.glyph.generation = generation;
- entry.glyph.texture = glyphInfo->mCacheTexture;
- entry.glyph.startX = glyphInfo->mStartX;
- entry.glyph.startY = glyphInfo->mStartY;
- entry.glyph.bitmapW = glyphInfo->mBitmapWidth;
- entry.glyph.bitmapH = glyphInfo->mBitmapHeight;
- entry.penX = penX;
- entry.penY = penY;
-}
-
-void FontCacheHistoryTracker::glyphUploaded(CacheTexture* texture, uint32_t x, uint32_t y,
- uint16_t glyphW, uint16_t glyphH) {
- CachedGlyph& glyph = mUploadHistory.next();
- glyph.generation = generation;
- glyph.texture = texture;
- glyph.startX = x;
- glyph.startY = y;
- glyph.bitmapW = glyphW;
- glyph.bitmapH = glyphH;
-}
-
-void FontCacheHistoryTracker::glyphsCleared(CacheTexture* texture) {
- CachedGlyph& glyph = mUploadHistory.next();
- glyph.generation = generation;
- glyph.texture = texture;
- glyph.startX = 0;
- glyph.startY = 0;
- glyph.bitmapW = 0;
- glyph.bitmapH = 0;
-}
-
-void FontCacheHistoryTracker::frameCompleted() {
- generation++;
-}
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/font/FontCacheHistoryTracker.h b/libs/hwui/font/FontCacheHistoryTracker.h
deleted file mode 100644
index 4b9ceccc09c9..000000000000
--- a/libs/hwui/font/FontCacheHistoryTracker.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include "../utils/RingBuffer.h"
-
-#include <utils/String8.h>
-
-namespace android {
-namespace uirenderer {
-
-class CacheTexture;
-struct CachedGlyphInfo;
-
-// Tracks glyph uploads and recent rendered/skipped glyphs, so it can give an idea
-// what a missing character is: skipped glyph, wrong coordinates in cache texture etc.
-class FontCacheHistoryTracker {
-public:
- void glyphRendered(CachedGlyphInfo*, int penX, int penY);
- void glyphUploaded(CacheTexture*, uint32_t x, uint32_t y, uint16_t glyphW, uint16_t glyphH);
- void glyphsCleared(CacheTexture*);
- void frameCompleted();
-
- void dump(String8& log) const;
-
-private:
- struct CachedGlyph {
- void* texture;
- uint16_t generation;
- uint16_t startX;
- uint16_t startY;
- uint16_t bitmapW;
- uint16_t bitmapH;
- };
-
- struct RenderEntry {
- CachedGlyph glyph;
- int penX;
- int penY;
- };
-
- static void dumpCachedGlyph(String8& log, const CachedGlyph& glyph);
- static void dumpRenderEntry(String8& log, const RenderEntry& entry);
- static void dumpUploadEntry(String8& log, const CachedGlyph& glyph);
-
- RingBuffer<RenderEntry, 300> mRenderHistory;
- RingBuffer<CachedGlyph, 120> mUploadHistory;
- uint16_t generation = 0;
-};
-
-}; // namespace uirenderer
-}; // namespace android \ No newline at end of file
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
deleted file mode 100644
index 596e153db6a9..000000000000
--- a/libs/hwui/font/FontUtil.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_FONT_UTIL_H
-#define ANDROID_HWUI_FONT_UTIL_H
-
-#include <SkUtils.h>
-
-#include "Properties.h"
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#ifdef TEXTURE_BORDER_SIZE
-#if TEXTURE_BORDER_SIZE != 1
-#error TEXTURE_BORDER_SIZE other than 1 is not currently supported
-#endif
-#else
-#define TEXTURE_BORDER_SIZE 1
-#endif
-
-#define CACHE_BLOCK_ROUNDING_SIZE 4
-
-typedef uint16_t glyph_t;
-#define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph)
-#define IS_END_OF_STRING(glyph) false
-
-// prev, next are assumed to be signed x.6 fixed-point numbers with range
-// [-1, 1]. Result is an integral float.
-#define AUTO_KERN(prev, next) static_cast<float>(((next) - (prev) + 32) >> 6)
-
-#endif // ANDROID_HWUI_FONT_UTIL_H
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 007961a6bedb..8d4e7e09b458 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -21,7 +21,8 @@
#include <SkPicture.h>
#include <SkRefCnt.h>
-#include <SkTLazy.h>
+
+#include <optional>
namespace android {
@@ -126,13 +127,13 @@ AnimatedImageDrawable::Snapshot AnimatedImageDrawable::reset() {
// Only called on the RenderThread.
void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
- SkTLazy<SkPaint> lazyPaint;
+ std::optional<SkPaint> lazyPaint;
SkAutoCanvasRestore acr(canvas, false);
if (mProperties.mAlpha != SK_AlphaOPAQUE || mProperties.mColorFilter.get()) {
- lazyPaint.init();
- lazyPaint.get()->setAlpha(mProperties.mAlpha);
- lazyPaint.get()->setColorFilter(mProperties.mColorFilter);
- lazyPaint.get()->setFilterQuality(kLow_SkFilterQuality);
+ lazyPaint.emplace();
+ lazyPaint->setAlpha(mProperties.mAlpha);
+ lazyPaint->setColorFilter(mProperties.mColorFilter);
+ lazyPaint->setFilterQuality(kLow_SkFilterQuality);
}
if (mProperties.mMirrored) {
canvas->save();
@@ -147,8 +148,8 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
if (drawDirectly) {
// The image is not animating, and never was. Draw directly from
// mSkAnimatedImage.
- if (lazyPaint.isValid()) {
- canvas->saveLayer(mSkAnimatedImage->getBounds(), lazyPaint.get());
+ if (lazyPaint) {
+ canvas->saveLayer(mSkAnimatedImage->getBounds(), &*lazyPaint);
}
std::unique_lock lock{mImageLock};
@@ -193,7 +194,7 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
if (!drawDirectly) {
// No other thread will modify mCurrentSnap so this should be safe to
// use without locking.
- canvas->drawPicture(mSnapshot.mPic, nullptr, lazyPaint.getMaybeNull());
+ canvas->drawPicture(mSnapshot.mPic, nullptr, lazyPaint ? &*lazyPaint : nullptr);
}
if (finalFrame) {
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 115c45ae4bcb..f0aa35acf71b 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -106,9 +106,7 @@ public:
Snapshot decodeNextFrame();
Snapshot reset();
- size_t byteSize() const {
- return sizeof(this) + mBytesUsed;
- }
+ size_t byteSize() const { return sizeof(*this) + mBytesUsed; }
protected:
virtual void onDraw(SkCanvas* canvas) override;
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index e7ffbee521ff..75a6e722dd8a 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -15,11 +15,11 @@
*/
#include "Bitmap.h"
-#include "Caches.h"
-#include "renderthread/EglManager.h"
+#include "HardwareBitmapUploader.h"
+#include "Properties.h"
#include "renderthread/RenderProxy.h"
-#include "renderthread/RenderThread.h"
#include "utils/Color.h"
+#include <utils/Trace.h>
#include <sys/mman.h>
@@ -34,17 +34,17 @@
#include <SkImagePriv.h>
#include <SkToSRGBColorFilter.h>
+#include <SkHighContrastFilter.h>
+#include <limits>
+
namespace android {
+// returns true if rowBytes * height can be represented by a positive int32_t value
+// and places that value in size.
static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) {
- int32_t rowBytes32 = SkToS32(rowBytes);
- int64_t bigSize = (int64_t)height * rowBytes32;
- if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
- return false; // allocation will be too large
- }
-
- *size = sk_64_asS32(bigSize);
- return true;
+ return 0 <= height && height <= std::numeric_limits<size_t>::max() &&
+ !__builtin_mul_overflow(rowBytes, (size_t)height, size) &&
+ *size <= std::numeric_limits<int32_t>::max();
}
typedef sk_sp<Bitmap> (*AllocPixelRef)(size_t allocSize, const SkImageInfo& info, size_t rowBytes);
@@ -85,7 +85,7 @@ static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, si
}
sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
- return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap);
+ return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
}
sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap) {
@@ -134,14 +134,15 @@ sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef)
}
sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
- PixelFormat format = graphicBuffer->getPixelFormat();
- if (!graphicBuffer.get() ||
- (format != PIXEL_FORMAT_RGBA_8888 && format != PIXEL_FORMAT_RGBA_FP16)) {
- return nullptr;
- }
+ return createFrom(graphicBuffer, SkColorSpace::MakeSRGB());
+}
+
+sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer, sk_sp<SkColorSpace> colorSpace) {
+ // As we will be effectively texture-sampling the buffer (using either EGL or Vulkan), we can
+ // view the colorspace as RGBA8888.
SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
kRGBA_8888_SkColorType, kPremul_SkAlphaType,
- SkColorSpace::MakeSRGB());
+ colorSpace);
return sk_sp<Bitmap>(new Bitmap(graphicBuffer.get(), info));
}
@@ -197,18 +198,18 @@ Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info
mPixelStorage.ashmem.size = mappedSize;
}
-Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info)
+Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette)
: SkPixelRef(info.width(), info.height(), nullptr,
bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride())
, mInfo(validateAlpha(info))
- , mPixelStorageType(PixelStorageType::Hardware) {
+ , mPixelStorageType(PixelStorageType::Hardware)
+ , mPalette(palette)
+ , mPaletteGenerationId(getGenerationID()) {
mPixelStorage.hardware.buffer = buffer;
buffer->incStrong(buffer);
setImmutable(); // HW bitmaps are always immutable
- if (uirenderer::Properties::isSkiaEnabled()) {
- mImage = SkImage::MakeFromAHardwareBuffer(reinterpret_cast<AHardwareBuffer*>(buffer),
- mInfo.alphaType(), mInfo.refColorSpace());
- }
+ mImage = SkImage::MakeFromAHardwareBuffer(reinterpret_cast<AHardwareBuffer*>(buffer),
+ mInfo.alphaType(), mInfo.refColorSpace());
}
Bitmap::~Bitmap() {
@@ -231,8 +232,6 @@ Bitmap::~Bitmap() {
mPixelStorage.hardware.buffer = nullptr;
break;
}
-
- android::uirenderer::renderthread::RenderProxy::onBitmapDestroyed(getStableID());
}
bool Bitmap::hasHardwareMipMap() const {
@@ -287,16 +286,10 @@ void Bitmap::setAlphaType(SkAlphaType alphaType) {
}
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
- outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
if (isHardware()) {
- if (uirenderer::Properties::isSkiaEnabled()) {
- outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
- info().colorType(), info().alphaType(),
- nullptr));
- } else {
- outBitmap->allocPixels(info());
- }
- uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap);
+ outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
+ info().colorType(), info().alphaType(), nullptr));
+ uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap);
if (mInfo.colorSpace()) {
sk_sp<SkPixelRef> pixelRef = sk_ref_sp(outBitmap->pixelRef());
outBitmap->setInfo(mInfo);
@@ -323,21 +316,102 @@ GraphicBuffer* Bitmap::graphicBuffer() {
sk_sp<SkImage> Bitmap::makeImage(sk_sp<SkColorFilter>* outputColorFilter) {
sk_sp<SkImage> image = mImage;
if (!image) {
- SkASSERT(!(isHardware() && uirenderer::Properties::isSkiaEnabled()));
+ SkASSERT(!isHardware());
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);
}
- if (uirenderer::Properties::isSkiaEnabled() && image->colorSpace() != nullptr &&
- !image->colorSpace()->isSRGB()) {
+ if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) {
*outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace());
}
return image;
}
+class MinMaxAverage {
+public:
+ void add(float sample) {
+ if (mCount == 0) {
+ mMin = sample;
+ mMax = sample;
+ } else {
+ mMin = std::min(mMin, sample);
+ mMax = std::max(mMax, sample);
+ }
+ mTotal += sample;
+ mCount++;
+ }
+
+ float average() { return mTotal / mCount; }
+
+ float min() { return mMin; }
+
+ float max() { return mMax; }
+
+ float delta() { return mMax - mMin; }
+
+private:
+ float mMin = 0.0f;
+ float mMax = 0.0f;
+ float mTotal = 0.0f;
+ int mCount = 0;
+};
+
+BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes) {
+ ATRACE_CALL();
+
+ SkPixmap pixmap{info, addr, rowBytes};
+
+ // TODO: This calculation of converting to HSV & tracking min/max is probably overkill
+ // Experiment with something simpler since we just want to figure out if it's "color-ful"
+ // and then the average perceptual lightness.
+
+ MinMaxAverage hue, saturation, value;
+ int sampledCount = 0;
+
+ // Sample a grid of 100 pixels to get an overall estimation of the colors in play
+ const int x_step = std::max(1, pixmap.width() / 10);
+ const int y_step = std::max(1, pixmap.height() / 10);
+ for (int x = 0; x < pixmap.width(); x += x_step) {
+ for (int y = 0; y < pixmap.height(); y += y_step) {
+ SkColor color = pixmap.getColor(x, y);
+ if (!info.isOpaque() && SkColorGetA(color) < 75) {
+ continue;
+ }
+
+ sampledCount++;
+ float hsv[3];
+ SkColorToHSV(color, hsv);
+ hue.add(hsv[0]);
+ saturation.add(hsv[1]);
+ value.add(hsv[2]);
+ }
+ }
+
+ // TODO: Tune the coverage threshold
+ if (sampledCount < 5) {
+ ALOGV("Not enough samples, only found %d for image sized %dx%d, format = %d, alpha = %d",
+ sampledCount, info.width(), info.height(), (int)info.colorType(),
+ (int)info.alphaType());
+ return BitmapPalette::Unknown;
+ }
+
+ ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = "
+ "%f]",
+ sampledCount, hue.min(), hue.max(), hue.average(), saturation.min(), saturation.max(),
+ saturation.average());
+
+ if (hue.delta() <= 20 && saturation.delta() <= .1f) {
+ if (value.average() >= .5f) {
+ return BitmapPalette::Light;
+ } else {
+ return BitmapPalette::Dark;
+ }
+ }
+ return BitmapPalette::Unknown;
+}
+
} // namespace android
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 4f06656e8e6d..238c764cdea6 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -34,6 +34,14 @@ enum class PixelStorageType {
Hardware,
};
+// TODO: Find a better home for this. It's here because hwui/Bitmap is exported and CanvasTransform
+// isn't, but cleanup should be done
+enum class BitmapPalette {
+ Unknown,
+ Light,
+ Dark,
+};
+
namespace uirenderer {
namespace renderthread {
class RenderThread;
@@ -56,6 +64,8 @@ public:
size_t rowBytes);
static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer);
+ static sk_sp<Bitmap> createFrom(sp<GraphicBuffer> graphicBuffer,
+ sk_sp<SkColorSpace> colorSpace);
static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&);
@@ -63,11 +73,10 @@ public:
Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info,
size_t rowBytes);
Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes);
- Bitmap(GraphicBuffer* buffer, const SkImageInfo& info);
+ Bitmap(GraphicBuffer* buffer, const SkImageInfo& info,
+ BitmapPalette palette = BitmapPalette::Unknown);
- int rowBytesAsPixels() const {
- return rowBytes() >> mInfo.shiftPerPixel();
- }
+ int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); }
void reconfigure(const SkImageInfo& info, size_t rowBytes);
void reconfigure(const SkImageInfo& info);
@@ -105,6 +114,20 @@ public:
*/
sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter);
+ static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes);
+
+ static BitmapPalette computePalette(const SkBitmap& bitmap) {
+ return computePalette(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes());
+ }
+
+ BitmapPalette palette() {
+ if (!isHardware() && mPaletteGenerationId != getGenerationID()) {
+ mPalette = computePalette(info(), pixels(), rowBytes());
+ mPaletteGenerationId = getGenerationID();
+ }
+ return mPalette;
+ }
+
private:
virtual ~Bitmap();
void* getStorage() const;
@@ -113,6 +136,9 @@ private:
const PixelStorageType mPixelStorageType;
+ BitmapPalette mPalette = BitmapPalette::Unknown;
+ uint32_t mPaletteGenerationId = -1;
+
bool mHasHardwareMipMap = false;
union {
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 20543df85068..e2ea2bc37375 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -19,20 +19,16 @@
#include "MinikinUtils.h"
#include "Paint.h"
#include "Properties.h"
-#include "RecordingCanvas.h"
#include "RenderNode.h"
#include "Typeface.h"
#include "pipeline/skia/SkiaRecordingCanvas.h"
-#include <SkDrawFilter.h>
+#include "hwui/PaintFilter.h"
namespace android {
Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
- if (uirenderer::Properties::isSkiaEnabled()) {
- return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
- }
- return new uirenderer::RecordingCanvas(width, height);
+ return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
}
static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
@@ -44,10 +40,10 @@ static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkSca
void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
uint32_t flags;
- SkDrawFilter* drawFilter = getDrawFilter();
- if (drawFilter) {
+ PaintFilter* paintFilter = getPaintFilter();
+ if (paintFilter) {
SkPaint paintCopy(paint);
- drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+ paintFilter->filter(&paintCopy);
flags = paintCopy.getFlags();
} else {
flags = paint.getFlags();
@@ -156,15 +152,14 @@ private:
float totalAdvance;
};
-void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount, float x,
- float y, minikin::Bidi bidiFlags, const Paint& origPaint,
- const Typeface* typeface, minikin::MeasuredText* mt) {
+void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, int contextStart,
+ int contextCount, float x, float y, minikin::Bidi bidiFlags,
+ const Paint& origPaint, const Typeface* typeface, minikin::MeasuredText* mt) {
// minikin may modify the original paint
Paint paint(origPaint);
- minikin::Layout layout =
- MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, start, count, contextCount,
- mt);
+ minikin::Layout layout = MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, textSize,
+ start, count, contextStart, contextCount, mt);
x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
@@ -183,6 +178,41 @@ void Canvas::drawText(const uint16_t* text, int start, int count, int contextCou
MinikinUtils::forFontRun(layout, &paint, f);
}
+void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, float outerRx, float outerRy, float innerLeft,
+ float innerTop, float innerRight, float innerBottom, float innerRx,
+ float innerRy, const SkPaint& paint) {
+ if (CC_UNLIKELY(paint.nothingToDraw())) return;
+ SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom);
+ SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom);
+
+ SkRRect outerRRect;
+ outerRRect.setRectXY(outer, outerRx, outerRy);
+
+ SkRRect innerRRect;
+ innerRRect.setRectXY(inner, innerRx, innerRy);
+ drawDoubleRoundRect(outerRRect, innerRRect, paint);
+}
+
+void Canvas::drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, const float* outerRadii, float innerLeft,
+ float innerTop, float innerRight, float innerBottom,
+ const float* innerRadii, const SkPaint& paint) {
+ static_assert(sizeof(SkVector) == sizeof(float) * 2);
+ if (CC_UNLIKELY(paint.nothingToDraw())) return;
+ SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom);
+ SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom);
+
+ SkRRect outerRRect;
+ const SkVector* outerSkVector = reinterpret_cast<const SkVector*>(outerRadii);
+ outerRRect.setRectRadii(outer, outerSkVector);
+
+ SkRRect innerRRect;
+ const SkVector* innerSkVector = reinterpret_cast<const SkVector*>(innerRadii);
+ innerRRect.setRectRadii(inner, innerSkVector);
+ drawDoubleRoundRect(outerRRect, innerRRect, paint);
+}
+
class DrawTextOnPathFunctor {
public:
DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset,
@@ -212,7 +242,10 @@ void Canvas::drawTextOnPath(const uint16_t* text, int count, minikin::Bidi bidiF
const Typeface* typeface) {
Paint paintCopy(paint);
minikin::Layout layout =
- MinikinUtils::doLayout(&paintCopy, bidiFlags, typeface, text, 0, count, count, nullptr);
+ MinikinUtils::doLayout(&paintCopy, bidiFlags, typeface, text, count, // text buffer
+ 0, count, // draw range
+ 0, count, // context range
+ nullptr);
hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
// Set align to left for drawing, as we don't want individual
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index f341cf96120d..e99742bc2eba 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -39,13 +39,22 @@ enum class Bidi : uint8_t;
}
namespace android {
+class PaintFilter;
namespace uirenderer {
class CanvasPropertyPaint;
class CanvasPropertyPrimitive;
class DeferredLayerUpdater;
-class DisplayList;
class RenderNode;
+
+namespace skiapipeline {
+class SkiaDisplayList;
+}
+
+/**
+ * Data structure that holds the list of commands used in display list stream
+ */
+using DisplayList = skiapipeline::SkiaDisplayList;
}
namespace SaveFlags {
@@ -65,7 +74,6 @@ typedef uint32_t Flags;
} // namespace SaveFlags
namespace uirenderer {
-class SkiaCanvasProxy;
namespace VectorDrawable {
class Tree;
};
@@ -205,8 +213,8 @@ public:
virtual bool clipPath(const SkPath* path, SkClipOp op) = 0;
// filters
- virtual SkDrawFilter* getDrawFilter() = 0;
- virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0;
+ virtual PaintFilter* getPaintFilter() = 0;
+ virtual void setPaintFilter(sk_sp<PaintFilter> paintFilter) = 0;
// WebView only
virtual SkCanvasState* captureCanvasState() const { return nullptr; }
@@ -228,6 +236,8 @@ public:
virtual void drawRegion(const SkRegion& region, const SkPaint& paint) = 0;
virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
const SkPaint& paint) = 0;
+ virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) = 0;
virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0;
virtual void drawOval(float left, float top, float right, float bottom,
const SkPaint& paint) = 0;
@@ -268,14 +278,24 @@ public:
* Converts utf16 text to glyphs, calculating position and boundary,
* and delegating the final draw to virtual drawGlyphs method.
*/
- void drawText(const uint16_t* text, int start, int count, int contextCount, float x, float y,
- minikin::Bidi bidiFlags, const Paint& origPaint, const Typeface* typeface,
- minikin::MeasuredText* mt);
+ void drawText(const uint16_t* text, int textSize, int start, int count, int contextStart,
+ int contextCount, float x, float y, minikin::Bidi bidiFlags,
+ const Paint& origPaint, const Typeface* typeface, minikin::MeasuredText* mt);
void drawTextOnPath(const uint16_t* text, int count, minikin::Bidi bidiFlags,
const SkPath& path, float hOffset, float vOffset, const Paint& paint,
const Typeface* typeface);
+ void drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, float outerRx, float outerRy, float innerLeft,
+ float innerTop, float innerRight, float innerBottom, float innerRx,
+ float innerRy, const SkPaint& paint);
+
+ void drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight,
+ float outerBottom, const float* outerRadii, float innerLeft,
+ float innerTop, float innerRight, float innerBottom,
+ const float* innerRadii, const SkPaint& paint);
+
static int GetApiLevel() { return sApiLevel; }
protected:
@@ -296,7 +316,6 @@ protected:
friend class DrawTextFunctor;
friend class DrawTextOnPathFunctor;
- friend class uirenderer::SkiaCanvasProxy;
};
}; // namespace android
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 5d33860bab6b..769fce498a70 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -22,16 +22,23 @@
#include <SkTypeface.h>
#include <log/log.h>
+#include <minikin/Font.h>
+#include <minikin/MinikinExtent.h>
+#include <minikin/MinikinPaint.h>
+#include <minikin/MinikinRect.h>
+
namespace android {
MinikinFontSkia::MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
- int ttcIndex, const std::vector<minikin::FontVariation>& axes)
+ std::string_view filePath, int ttcIndex,
+ const std::vector<minikin::FontVariation>& axes)
: minikin::MinikinFont(typeface->uniqueID())
, mTypeface(std::move(typeface))
, mFontData(fontData)
, mFontSize(fontSize)
, mTtcIndex(ttcIndex)
- , mAxes(axes) {}
+ , mAxes(axes)
+ , mFilePath(filePath) {}
static void MinikinFontSkia_SetSkiaPaint(const minikin::MinikinFont* font, SkPaint* skPaint,
const minikin::MinikinPaint& paint,
@@ -77,11 +84,10 @@ void MinikinFontSkia::GetFontExtent(minikin::MinikinExtent* extent,
const minikin::FontFakery& fakery) const {
SkPaint skPaint;
MinikinFontSkia_SetSkiaPaint(this, &skPaint, paint, fakery);
- SkPaint::FontMetrics metrics;
+ SkFontMetrics metrics;
skPaint.getFontMetrics(&metrics);
extent->ascent = metrics.fAscent;
extent->descent = metrics.fDescent;
- extent->line_gap = metrics.fLeading;
}
SkTypeface* MinikinFontSkia::GetSkTypeface() const {
@@ -127,25 +133,24 @@ std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation(
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), params));
- return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, ttcIndex,
- variations);
+ return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, mFilePath,
+ ttcIndex, variations);
}
uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) {
uint32_t flags = paint->getFlags();
- SkPaint::Hinting hinting = paint->getHinting();
+ unsigned hinting = static_cast<unsigned>(paint->getHinting());
// select only flags that might affect text layout
flags &= (SkPaint::kAntiAlias_Flag | SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag |
- SkPaint::kSubpixelText_Flag | SkPaint::kDevKernText_Flag |
- SkPaint::kEmbeddedBitmapText_Flag | SkPaint::kAutoHinting_Flag |
- SkPaint::kVerticalText_Flag);
+ SkPaint::kSubpixelText_Flag | SkPaint::kEmbeddedBitmapText_Flag |
+ SkPaint::kAutoHinting_Flag);
flags |= (hinting << 16);
return flags;
}
void MinikinFontSkia::unpackPaintFlags(SkPaint* paint, uint32_t paintFlags) {
paint->setFlags(paintFlags & SkPaint::kAllFlags);
- paint->setHinting(static_cast<SkPaint::Hinting>(paintFlags >> 16));
+ paint->setHinting(static_cast<SkFontHinting>(paintFlags >> 16));
}
void MinikinFontSkia::populateSkPaint(SkPaint* paint, const MinikinFont* font,
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index d1565986304f..55576b7bfa4e 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -28,8 +28,9 @@ namespace android {
class ANDROID_API MinikinFontSkia : public minikin::MinikinFont {
public:
- explicit MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
- int ttcIndex, const std::vector<minikin::FontVariation>& axes);
+ MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
+ std::string_view filePath, int ttcIndex,
+ const std::vector<minikin::FontVariation>& axes);
float GetHorizontalAdvance(uint32_t glyph_id, const minikin::MinikinPaint& paint,
const minikin::FontFakery& fakery) const override;
@@ -48,6 +49,7 @@ public:
const void* GetFontData() const;
size_t GetFontSize() const;
int GetFontIndex() const;
+ const std::string& getFilePath() const { return mFilePath; }
const std::vector<minikin::FontVariation>& GetAxes() const;
std::shared_ptr<minikin::MinikinFont> createFontWithVariation(
const std::vector<minikin::FontVariation>&) const;
@@ -68,6 +70,7 @@ private:
size_t mFontSize;
int mTtcIndex;
std::vector<minikin::FontVariation> mAxes;
+ std::string mFilePath;
};
} // namespace android
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 5b69bb78101f..ba240feb4f41 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -48,23 +48,25 @@ minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint,
}
minikin::Layout MinikinUtils::doLayout(const Paint* paint, minikin::Bidi bidiFlags,
- const Typeface* typeface, const uint16_t* buf, size_t start,
- size_t count, size_t bufSize, minikin::MeasuredText* mt) {
+ const Typeface* typeface, const uint16_t* buf,
+ size_t bufSize, size_t start, size_t count,
+ size_t contextStart, size_t contextCount,
+ minikin::MeasuredText* mt) {
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
- minikin::Layout layout;
const minikin::U16StringPiece textBuf(buf, bufSize);
const minikin::Range range(start, start + count);
+ const minikin::Range contextRange(contextStart, contextStart + contextCount);
const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit());
const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit);
const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
if (mt == nullptr) {
- layout.doLayout(textBuf,range, bidiFlags, minikinPaint, startHyphen, endHyphen);
+ return minikin::Layout(textBuf.substr(contextRange), range - contextStart, bidiFlags,
+ minikinPaint, startHyphen, endHyphen);
} else {
- mt->buildLayout(textBuf, range, minikinPaint, bidiFlags, startHyphen, endHyphen, &layout);
+ return mt->buildLayout(textBuf, range, contextRange, minikinPaint, startHyphen, endHyphen);
}
- return layout;
}
float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
@@ -78,8 +80,7 @@ float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen,
- endHyphen, advances, nullptr /* extent */,
- nullptr /* layout pieces */);
+ endHyphen, advances);
}
bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index 77dfbb21f7b5..d27d54454ea0 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -25,11 +25,11 @@
#define _ANDROID_GRAPHICS_MINIKIN_UTILS_H_
#include <cutils/compiler.h>
+#include <log/log.h>
#include <minikin/Layout.h>
#include "MinikinSkia.h"
#include "Paint.h"
#include "Typeface.h"
-#include <log/log.h>
namespace minikin {
class MeasuredText;
@@ -44,7 +44,8 @@ public:
ANDROID_API static minikin::Layout doLayout(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf,
- size_t start, size_t count, size_t bufSize,
+ size_t bufSize, size_t start, size_t count,
+ size_t contextStart, size_t contextCount,
minikin::MeasuredText* mt);
ANDROID_API static float measureText(const Paint* paint, minikin::Bidi bidiFlags,
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 002f75906c35..c1a3b6d1143b 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -25,6 +25,7 @@
#include <string>
#include <minikin/FontFamily.h>
+#include <minikin/FamilyVariant.h>
namespace android {
@@ -73,9 +74,9 @@ public:
uint32_t getMinikinLocaleListId() const { return mMinikinLocaleListId; }
- void setFamilyVariant(minikin::FontFamily::Variant variant) { mFamilyVariant = variant; }
+ void setFamilyVariant(minikin::FamilyVariant variant) { mFamilyVariant = variant; }
- minikin::FontFamily::Variant getFamilyVariant() const { return mFamilyVariant; }
+ minikin::FamilyVariant getFamilyVariant() const { return mFamilyVariant; }
void setHyphenEdit(uint32_t hyphen) { mHyphenEdit = hyphen; }
@@ -85,18 +86,27 @@ public:
const Typeface* getAndroidTypeface() const { return mTypeface; }
+ enum Align {
+ kLeft_Align,
+ kCenter_Align,
+ kRight_Align,
+ };
+ Align getTextAlign() const { return mAlign; }
+ void setTextAlign(Align align) { mAlign = align; }
+
private:
float mLetterSpacing = 0;
float mWordSpacing = 0;
std::string mFontFeatureSettings;
uint32_t mMinikinLocaleListId;
- minikin::FontFamily::Variant mFamilyVariant;
+ minikin::FamilyVariant mFamilyVariant;
uint32_t mHyphenEdit = 0;
// The native Typeface object has the same lifetime of the Java Typeface
// object. The Java Paint object holds a strong reference to the Java Typeface
// object. Thus, following pointer can never be a dangling pointer. Note that
// nullptr is valid: it means the default typeface.
const Typeface* mTypeface = nullptr;
+ Align mAlign = kLeft_Align;
};
} // namespace android
diff --git a/libs/hwui/hwui/PaintFilter.h b/libs/hwui/hwui/PaintFilter.h
new file mode 100644
index 000000000000..bf5627eac229
--- /dev/null
+++ b/libs/hwui/hwui/PaintFilter.h
@@ -0,0 +1,19 @@
+#ifndef ANDROID_GRAPHICS_PAINT_FILTER_H_
+#define ANDROID_GRAPHICS_PAINT_FILTER_H_
+
+class SkPaint;
+
+namespace android {
+
+class PaintFilter : public SkRefCnt {
+public:
+ /**
+ * Called with the paint that will be used to draw.
+ * The implementation may modify the paint as they wish.
+ */
+ virtual void filter(SkPaint*) = 0;
+};
+
+} // namespace android
+
+#endif
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index ae9c475d09d4..bdbf5cacaaf0 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -24,7 +24,7 @@ Paint::Paint()
, mWordSpacing(0)
, mFontFeatureSettings()
, mMinikinLocaleListId(0)
- , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {}
+ , mFamilyVariant(minikin::FamilyVariant::DEFAULT) {}
Paint::Paint(const Paint& paint)
: SkPaint(paint)
@@ -34,7 +34,8 @@ Paint::Paint(const Paint& paint)
, mMinikinLocaleListId(paint.mMinikinLocaleListId)
, mFamilyVariant(paint.mFamilyVariant)
, mHyphenEdit(paint.mHyphenEdit)
- , mTypeface(paint.mTypeface) {}
+ , mTypeface(paint.mTypeface)
+ , mAlign(paint.mAlign) {}
Paint::Paint(const SkPaint& paint)
: SkPaint(paint)
@@ -42,7 +43,7 @@ Paint::Paint(const SkPaint& paint)
, mWordSpacing(0)
, mFontFeatureSettings()
, mMinikinLocaleListId(0)
- , mFamilyVariant(minikin::FontFamily::Variant::DEFAULT) {}
+ , mFamilyVariant(minikin::FamilyVariant::DEFAULT) {}
Paint::~Paint() {}
@@ -55,6 +56,7 @@ Paint& Paint::operator=(const Paint& other) {
mFamilyVariant = other.mFamilyVariant;
mHyphenEdit = other.mHyphenEdit;
mTypeface = other.mTypeface;
+ mAlign = other.mAlign;
return *this;
}
@@ -64,6 +66,6 @@ bool operator==(const Paint& a, const Paint& b) {
a.mFontFeatureSettings == b.mFontFeatureSettings &&
a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
- a.mTypeface == b.mTypeface;
+ a.mTypeface == b.mTypeface && a.mAlign == b.mAlign;
}
} // namespace android
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index dca9ef5559a6..c4d8aa6c8fad 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -132,8 +132,10 @@ Typeface* Typeface::createFromFamilies(std::vector<std::shared_ptr<minikin::Font
bool italicFromFont;
const minikin::FontStyle defaultStyle;
- const minikin::MinikinFont* mf = families.empty() ? nullptr
- : families[0]->getClosestMatch(defaultStyle).font->typeface().get();
+ const minikin::MinikinFont* mf =
+ families.empty()
+ ? nullptr
+ : families[0]->getClosestMatch(defaultStyle).font->typeface().get();
if (mf != nullptr) {
SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(mf)->GetSkTypeface();
const SkFontStyle& style = skTypeface->fontStyle();
@@ -176,12 +178,13 @@ void Typeface::setRobotoTypefaceForTest() {
struct stat st = {};
LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", kRobotoFont);
void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
- std::unique_ptr<SkMemoryStream> fontData(new SkMemoryStream(data, st.st_size));
- sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(fontData.release());
+ std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data, st.st_size));
+ sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(std::move(fontData));
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont);
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
- std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
+ std::move(typeface), data, st.st_size, kRobotoFont, 0,
+ std::vector<minikin::FontVariation>());
std::vector<minikin::Font> fonts;
fonts.push_back(minikin::Font::Builder(font).build());
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 1f83d1a201b0..e4ba13da709c 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -94,11 +94,6 @@ protected:
mOutput << mIdent << "drawPosTextH" << std::endl;
}
- void onDrawTextOnPath(const void*, size_t, const SkPath&, const SkMatrix*,
- const SkPaint&) override {
- mOutput << mIdent << "drawTextOnPath" << std::endl;
- }
-
void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*,
const SkPaint&) override {
mOutput << mIdent << "drawTextRSXform" << std::endl;
@@ -143,7 +138,7 @@ protected:
renderNodeDrawable->getRenderNode()->output(mOutput, mLevel + 1);
return;
}
- auto glFunctorDrawable = getGLFunctorDrawable(drawable);
+ auto glFunctorDrawable = getFunctorDrawable(drawable);
if (nullptr != glFunctorDrawable) {
mOutput << std::string(mLevel * 2, ' ') << "drawGLFunctorDrawable" << std::endl;
return;
@@ -162,10 +157,10 @@ private:
return nullptr;
}
- GLFunctorDrawable* getGLFunctorDrawable(SkDrawable* drawable) {
+ FunctorDrawable* getFunctorDrawable(SkDrawable* drawable) {
for (auto& child : mDisplayList.mChildFunctors) {
- if (drawable == &child) {
- return &child;
+ if (drawable == child) {
+ return child;
}
}
return nullptr;
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
new file mode 100644
index 000000000000..162d13762e1a
--- /dev/null
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "GlFunctorLifecycleListener.h"
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+
+#include <utils/Functor.h>
+
+namespace android {
+namespace uirenderer {
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class FunctorDrawable : public SkDrawable {
+public:
+ FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
+ : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+ virtual ~FunctorDrawable() {}
+
+ virtual void syncFunctor() const = 0;
+
+protected:
+ virtual SkRect onGetBounds() override { return mBounds; }
+
+ Functor* mFunctor;
+ sp<GlFunctorLifecycleListener> mListener;
+ const SkRect mBounds;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 3684bc1e6a1f..90d5e715f8cd 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -25,7 +25,6 @@
#include "GrBackendSurface.h"
#include "GrRenderTarget.h"
#include "GrRenderTargetContext.h"
-#include "GrGLTypes.h"
namespace android {
namespace uirenderer {
@@ -63,15 +62,13 @@ static bool GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSiz
return false;
}
- GrBackendRenderTarget backendTarget = renderTarget->getBackendRenderTarget();
- const GrGLFramebufferInfo* fboInfo = backendTarget.getGLFramebufferInfo();
-
- if (!fboInfo) {
+ GrGLFramebufferInfo fboInfo;
+ if (!renderTarget->getBackendRenderTarget().getGLFramebufferInfo(&fboInfo)) {
ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
return false;
}
- *outFboID = fboInfo->fFBOID;
+ *outFboID = fboInfo.fFBOID;
*outFboSize = SkISize::Make(renderTargetContext->width(), renderTargetContext->height());
return true;
}
@@ -82,11 +79,6 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
return;
}
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- canvas->clear(SK_ColorRED);
- return;
- }
-
GLuint fboID = 0;
SkISize fboSize;
if (!GetFboDetails(canvas, &fboID, &fboSize)) {
@@ -109,14 +101,15 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
surfaceInfo);
tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
- GrBackendObject backendObject;
- if (!tmpSurface->getRenderTargetHandle(&backendObject, SkSurface::kFlushWrite_BackendHandleAccess)) {
+ GrGLFramebufferInfo fboInfo;
+ if (!tmpSurface->getBackendRenderTarget(SkSurface::kFlushWrite_BackendHandleAccess)
+ .getGLFramebufferInfo(&fboInfo)) {
ALOGW("Unable to extract renderTarget info from offscreen canvas; aborting GLFunctor");
return;
}
fboSize = SkISize::Make(surfaceInfo.width(), surfaceInfo.height());
- fboID = (GLuint)backendObject;
+ fboID = fboInfo.fFBOID;
// update the matrix and clip that we pass to the WebView to match the coordinates of
// the offscreen layer
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index af57d7d33c2c..dd6ef25f30c5 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -16,39 +16,29 @@
#pragma once
-#include <SkCanvas.h>
-#include <SkDrawable.h>
+#include "FunctorDrawable.h"
-#include <utils/Functor.h>
#include <utils/RefBase.h>
namespace android {
namespace uirenderer {
-class GlFunctorLifecycleListener;
-
namespace skiapipeline {
/**
* This drawable wraps a OpenGL functor enabling it to be recorded into a list
* of Skia drawing commands.
*/
-class GLFunctorDrawable : public SkDrawable {
+class GLFunctorDrawable : public FunctorDrawable {
public:
GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
- : mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
+ : FunctorDrawable(functor, listener, canvas) {}
virtual ~GLFunctorDrawable();
- void syncFunctor() const;
+ void syncFunctor() const override;
protected:
- virtual SkRect onGetBounds() override { return mBounds; }
virtual void onDraw(SkCanvas* canvas) override;
-
-private:
- Functor* mFunctor;
- sp<GlFunctorLifecycleListener> mListener;
- const SkRect mBounds;
};
}; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index ab1d5c2546ed..13d2dae8e281 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -15,8 +15,6 @@
*/
#include "LayerDrawable.h"
-#include "GlLayer.h"
-#include "VkLayer.h"
#include "GrBackendSurface.h"
#include "SkColorFilter.h"
@@ -30,91 +28,73 @@ namespace skiapipeline {
void LayerDrawable::onDraw(SkCanvas* canvas) {
Layer* layer = mLayerUpdater->backingLayer();
if (layer) {
- DrawLayer(canvas->getGrContext(), canvas, layer);
+ DrawLayer(canvas->getGrContext(), canvas, layer, nullptr, nullptr, true);
}
}
bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
- const SkRect* dstRect) {
+ const SkRect* srcRect, const SkRect* dstRect,
+ bool useLayerTransform) {
if (context == nullptr) {
SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
return false;
}
// transform the matrix based on the layer
- SkMatrix layerTransform;
- layer->getTransform().copyTo(layerTransform);
- sk_sp<SkImage> layerImage;
+ SkMatrix layerTransform = layer->getTransform();
+ sk_sp<SkImage> layerImage = layer->getImage();
const int layerWidth = layer->getWidth();
const int layerHeight = layer->getHeight();
- const int bufferWidth = layer->getBufferWidth();
- const int bufferHeight = layer->getBufferHeight();
- if (bufferWidth <= 0 || bufferHeight <=0) {
- return false;
- }
- if (layer->getApi() == Layer::Api::OpenGL) {
- GlLayer* glLayer = static_cast<GlLayer*>(layer);
- GrGLTextureInfo externalTexture;
- externalTexture.fTarget = glLayer->getRenderTarget();
- externalTexture.fID = glLayer->getTextureId();
- // The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't
- // expose that info we use it as our default. Further, given that we only use this texture
- // as a source this will not impact how Skia uses the texture. The only potential affect
- // this is anticipated to have is that for some format types if we are not bound as an OES
- // texture we may get invalid results for SKP capture if we read back the texture.
- externalTexture.fFormat = GL_RGBA8;
- GrBackendTexture backendTexture(bufferWidth, bufferHeight, GrMipMapped::kNo, externalTexture);
- layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
- kPremul_SkAlphaType, nullptr);
- } else {
- SkASSERT(layer->getApi() == Layer::Api::Vulkan);
- VkLayer* vkLayer = static_cast<VkLayer*>(layer);
- canvas->clear(SK_ColorGREEN);
- layerImage = vkLayer->getImage();
- }
if (layerImage) {
SkMatrix textureMatrixInv;
- layer->getTexTransform().copyTo(textureMatrixInv);
+ textureMatrixInv = layer->getTexTransform();
// TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
// use bottom left origin and remove flipV and invert transformations.
SkMatrix flipV;
flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
textureMatrixInv.preConcat(flipV);
textureMatrixInv.preScale(1.0f / layerWidth, 1.0f / layerHeight);
- textureMatrixInv.postScale(bufferWidth, bufferHeight);
+ textureMatrixInv.postScale(layerImage->width(), layerImage->height());
SkMatrix textureMatrix;
if (!textureMatrixInv.invert(&textureMatrix)) {
textureMatrix = textureMatrixInv;
}
SkMatrix matrix;
- if (dstRect) {
- // Destination rectangle is set only when we are trying to read back the content
- // of the layer. In this case we don't want to apply layer transform.
- matrix = textureMatrix;
- } else {
+ if (useLayerTransform) {
matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+ } else {
+ matrix = textureMatrix;
}
SkPaint paint;
paint.setAlpha(layer->getAlpha());
paint.setBlendMode(layer->getMode());
paint.setColorFilter(layer->getColorSpaceWithFilter());
-
const bool nonIdentityMatrix = !matrix.isIdentity();
if (nonIdentityMatrix) {
canvas->save();
canvas->concat(matrix);
}
const SkMatrix& totalMatrix = canvas->getTotalMatrix();
- if (dstRect) {
+ if (dstRect || srcRect) {
SkMatrix matrixInv;
if (!matrix.invert(&matrixInv)) {
matrixInv = matrix;
}
- SkRect srcRect = SkRect::MakeIWH(layerWidth, layerHeight);
- matrixInv.mapRect(&srcRect);
- SkRect skiaDestRect = *dstRect;
+ SkRect skiaSrcRect;
+ if (srcRect) {
+ skiaSrcRect = *srcRect;
+ } else {
+ skiaSrcRect = SkRect::MakeIWH(layerWidth, layerHeight);
+ }
+ matrixInv.mapRect(&skiaSrcRect);
+ SkRect skiaDestRect;
+ if (dstRect) {
+ skiaDestRect = *dstRect;
+ } else {
+ skiaDestRect = SkRect::MakeIWH(layerWidth, layerHeight);
+ }
matrixInv.mapRect(&skiaDestRect);
// If (matrix is identity or an integer translation) and (src/dst buffers size match),
// then use nearest neighbor, otherwise use bilerp sampling.
@@ -123,13 +103,13 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer
// only for SrcOver blending and without color filter (readback uses Src blending).
bool isIntegerTranslate = totalMatrix.isTranslate()
&& SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
- == SkScalarFraction(srcRect.fLeft)
+ == SkScalarFraction(skiaSrcRect.fLeft)
&& SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
- == SkScalarFraction(srcRect.fTop);
+ == SkScalarFraction(skiaSrcRect.fTop);
if (layer->getForceFilter() || !isIntegerTranslate) {
paint.setFilterQuality(kLow_SkFilterQuality);
}
- canvas->drawImageRect(layerImage.get(), srcRect, skiaDestRect, &paint,
+ canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint,
SkCanvas::kFast_SrcRectConstraint);
} else {
bool isIntegerTranslate = totalMatrix.isTranslate()
@@ -146,7 +126,7 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer
}
}
- return layerImage;
+ return layerImage != nullptr;
}
}; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 18d118405a39..5c125908ffb2 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -33,7 +33,7 @@ public:
explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
- const SkRect* dstRect = nullptr);
+ const SkRect* srcRect, const SkRect* dstRect, bool useLayerTransform);
protected:
virtual SkRect onGetBounds() override {
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 6c04d7862979..ea14d11b7b3e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -21,10 +21,25 @@
#include "SkiaPipeline.h"
#include "utils/TraceUtils.h"
+#include <optional>
+
namespace android {
namespace uirenderer {
namespace skiapipeline {
+RenderNodeDrawable::RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer,
+ bool inReorderingSection)
+ : mRenderNode(node)
+ , mRecordedTransform(canvas->getTotalMatrix())
+ , mComposeLayer(composeLayer)
+ , mInReorderingSection(inReorderingSection) {}
+
+RenderNodeDrawable::~RenderNodeDrawable() {
+ // Just here to move the destructor into the cpp file where we can access RenderNode.
+
+ // TODO: Detangle the header nightmare.
+}
+
void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas,
const SkiaDisplayList& displayList,
int nestLevel) {
@@ -115,7 +130,6 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
return;
}
- SkASSERT(renderNode->getDisplayList()->isSkiaDL());
SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList();
SkAutoCanvasRestore acr(canvas, true);
@@ -130,7 +144,7 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline);
const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath();
SkAutoCanvasRestore acr2(canvas, shouldClip);
- canvas->setMatrix(mProjectedDisplayList->mProjectedReceiverParentMatrix);
+ canvas->setMatrix(mProjectedDisplayList->mParentMatrix);
if (shouldClip) {
clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr);
}
@@ -144,10 +158,10 @@ static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultip
SkPaint* paint) {
paint->setFilterQuality(kLow_SkFilterQuality);
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
- properties.xferMode() != SkBlendMode::kSrcOver || properties.colorFilter() != nullptr) {
+ properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr) {
paint->setAlpha(properties.alpha() * alphaMultiplier);
paint->setBlendMode(properties.xferMode());
- paint->setColorFilter(sk_ref_sp(properties.colorFilter()));
+ paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
return true;
}
return false;
@@ -159,9 +173,9 @@ public:
protected:
bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type t) const override {
- SkTLazy<SkPaint> defaultPaint;
+ std::optional<SkPaint> defaultPaint;
if (!*paint) {
- paint->init(*defaultPaint.init());
+ paint->init(defaultPaint.emplace());
}
paint->writable()->setAlpha((uint8_t)(*paint)->getAlpha() * mAlpha);
return true;
@@ -188,9 +202,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
setViewProperties(properties, canvas, &alphaMultiplier);
}
SkiaDisplayList* displayList = (SkiaDisplayList*)mRenderNode->getDisplayList();
- if (displayList->containsProjectionReceiver()) {
- displayList->mProjectedReceiverParentMatrix = canvas->getTotalMatrix();
- }
+ displayList->mParentMatrix = canvas->getTotalMatrix();
// TODO should we let the bound of the drawable do this for us?
const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index ef21cd8a29b5..d746978b0a61 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -16,6 +16,8 @@
#pragma once
+#include "SkiaUtils.h"
+
#include <SkCanvas.h>
#include <SkDrawable.h>
#include <SkMatrix.h>
@@ -47,11 +49,9 @@ public:
* layer into the canvas.
*/
explicit RenderNodeDrawable(RenderNode* node, SkCanvas* canvas, bool composeLayer = true,
- bool inReorderingSection = false)
- : mRenderNode(node)
- , mRecordedTransform(canvas->getTotalMatrix())
- , mComposeLayer(composeLayer)
- , mInReorderingSection(inReorderingSection) {}
+ bool inReorderingSection = false);
+
+ ~RenderNodeDrawable();
/**
* Draws into the canvas this render node and its children. If the node is marked as a
@@ -91,7 +91,7 @@ protected:
virtual SkRect onGetBounds() override {
// We don't want to enable a record time quick reject because the properties
// of the RenderNode may be updated on subsequent frames.
- return SkRect::MakeLargest();
+ return SkRectMakeLargest();
}
/**
* This function draws into a canvas as forceDraw, but does nothing if the render node has a
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 25c51f2716e6..dba97fe5ef9f 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -19,10 +19,7 @@
#include "SkiaDisplayList.h"
#include "SkiaPipeline.h"
-#include <SkBlurMask.h>
-#include <SkBlurMaskFilter.h>
#include <SkPathOps.h>
-#include <SkRRectsGaussianEdgeMaskFilter.h>
#include <SkShadowUtils.h>
namespace android {
@@ -58,6 +55,11 @@ void StartReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
if (casterZ >= -NON_ZERO_EPSILON) { // draw only children with negative Z
return;
}
+ SkAutoCanvasRestore acr(canvas, true);
+ // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+ // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+ canvas->setMatrix(mDisplayList->mParentMatrix);
+ canvas->concat(childNode->getRecordedMatrix());
childNode->forceDraw(canvas);
drawIndex++;
}
@@ -105,6 +107,11 @@ void EndReorderBarrierDrawable::onDraw(SkCanvas* canvas) {
RenderNodeDrawable* childNode = zChildren[drawIndex];
SkASSERT(childNode);
+ SkAutoCanvasRestore acr(canvas, true);
+ // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+ // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+ canvas->setMatrix(mStartBarrier->mDisplayList->mParentMatrix);
+ canvas->concat(childNode->getRecordedMatrix());
childNode->forceDraw(canvas);
drawIndex++;
@@ -156,10 +163,15 @@ void EndReorderBarrierDrawable::drawShadow(SkCanvas* canvas, RenderNodeDrawable*
}
SkAutoCanvasRestore acr(canvas, true);
+ // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+ // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+ canvas->setMatrix(mStartBarrier->mDisplayList->mParentMatrix);
SkMatrix shadowMatrix;
- mat4 hwuiMatrix;
+ mat4 hwuiMatrix(caster->getRecordedMatrix());
// TODO we don't pass the optional boolean to treat it as a 4x4 matrix
+ // applyViewPropertyTransforms gets the same matrix, which render nodes apply with
+ // RenderNodeDrawable::setViewProperties as a part if their draw.
caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
hwuiMatrix.copyTo(shadowMatrix);
canvas->concat(shadowMatrix);
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
index 3c48d3604864..26cfa908228c 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -17,6 +17,7 @@
#pragma once
#include "RenderNodeDrawable.h"
+#include "SkiaUtils.h"
#include <SkCanvas.h>
#include <SkDrawable.h>
@@ -41,7 +42,7 @@ public:
explicit StartReorderBarrierDrawable(SkiaDisplayList* data);
protected:
- virtual SkRect onGetBounds() override { return SkRect::MakeLargest(); }
+ virtual SkRect onGetBounds() override { return SkRectMakeLargest(); }
virtual void onDraw(SkCanvas* canvas) override;
private:
@@ -65,7 +66,7 @@ public:
explicit EndReorderBarrierDrawable(StartReorderBarrierDrawable* startBarrier);
protected:
- virtual SkRect onGetBounds() override { return SkRect::MakeLargest(); }
+ virtual SkRect onGetBounds() override { return SkRectMakeLargest(); }
virtual void onDraw(SkCanvas* canvas) override;
private:
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 670074871c71..073b4814305e 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -18,6 +18,8 @@
#include <algorithm>
#include <log/log.h>
#include <thread>
+#include <array>
+#include <openssl/sha.h>
#include "FileBlobCache.h"
#include "Properties.h"
#include "utils/TraceUtils.h"
@@ -41,7 +43,40 @@ ShaderCache& ShaderCache::get() {
return sCache;
}
-void ShaderCache::initShaderDiskCache() {
+bool ShaderCache::validateCache(const void* identity, ssize_t size) {
+ if (nullptr == identity && size == 0)
+ return true;
+
+ if (nullptr == identity || size < 0) {
+ if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
+ ALOGW("ShaderCache::validateCache invalid cache identity");
+ }
+ mBlobCache->clear();
+ return false;
+ }
+
+ SHA256_CTX ctx;
+ SHA256_Init(&ctx);
+
+ SHA256_Update(&ctx, identity, size);
+ mIDHash.resize(SHA256_DIGEST_LENGTH);
+ SHA256_Final(mIDHash.data(), &ctx);
+
+ std::array<uint8_t, SHA256_DIGEST_LENGTH> hash;
+ auto key = sIDKey;
+ auto loaded = mBlobCache->get(&key, sizeof(key), hash.data(), hash.size());
+
+ if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin()))
+ return true;
+
+ if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
+ ALOGW("ShaderCache::validateCache cache validation fails");
+ }
+ mBlobCache->clear();
+ return false;
+}
+
+void ShaderCache::initShaderDiskCache(const void* identity, ssize_t size) {
ATRACE_NAME("initShaderDiskCache");
std::lock_guard<std::mutex> lock(mMutex);
@@ -50,6 +85,7 @@ void ShaderCache::initShaderDiskCache() {
// desktop / laptop GPUs. Thus, disable the shader disk cache for emulator builds.
if (!Properties::runningInEmulator && mFilename.length() > 0) {
mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename));
+ validateCache(identity, size);
mInitialized = true;
}
}
@@ -104,6 +140,18 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) {
return SkData::MakeFromMalloc(valueBuffer, valueSize);
}
+void ShaderCache::saveToDiskLocked() {
+ ATRACE_NAME("ShaderCache::saveToDiskLocked");
+ if (mInitialized && mBlobCache && mSavePending) {
+ if (mIDHash.size()) {
+ auto key = sIDKey;
+ mBlobCache->set(&key, sizeof(key), mIDHash.data(), mIDHash.size());
+ }
+ mBlobCache->writeToFile();
+ }
+ mSavePending = false;
+}
+
void ShaderCache::store(const SkData& key, const SkData& data) {
ATRACE_NAME("ShaderCache::store");
std::lock_guard<std::mutex> lock(mMutex);
@@ -129,11 +177,7 @@ void ShaderCache::store(const SkData& key, const SkData& data) {
std::thread deferredSaveThread([this]() {
sleep(mDeferredSaveDelay);
std::lock_guard<std::mutex> lock(mMutex);
- ATRACE_NAME("ShaderCache::saveToDisk");
- if (mInitialized && mBlobCache) {
- mBlobCache->writeToFile();
- }
- mSavePending = false;
+ saveToDiskLocked();
});
deferredSaveThread.detach();
}
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 27473d67bd1a..82804cf93785 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -40,12 +40,21 @@ public:
ANDROID_API static ShaderCache& get();
/**
- * "initShaderDiskCache" loads the serialized cache contents from disk and puts the ShaderCache
- * into an initialized state, such that it is able to insert and retrieve entries from the
- * cache. This should be called when HWUI pipeline is initialized. When not in the initialized
- * state the load and store methods will return without performing any cache operations.
+ * initShaderDiskCache" loads the serialized cache contents from disk,
+ * optionally checks that the on-disk cache matches a provided identity,
+ * and puts the ShaderCache into an initialized state, such that it is
+ * able to insert and retrieve entries from the cache. If identity is
+ * non-null and validation fails, the cache is initialized but contains
+ * no data. If size is less than zero, the cache is initilaized but
+ * contains no data.
+ *
+ * This should be called when HWUI pipeline is initialized. When not in
+ * the initialized state the load and store methods will return without
+ * performing any cache operations.
*/
- virtual void initShaderDiskCache();
+ virtual void initShaderDiskCache(const void *identity, ssize_t size);
+
+ virtual void initShaderDiskCache() { initShaderDiskCache(nullptr, 0); }
/**
* "setFilename" sets the name of the file that should be used to store
@@ -83,6 +92,19 @@ private:
BlobCache* getBlobCacheLocked();
/**
+ * "validateCache" updates the cache to match the given identity. If the
+ * cache currently has the wrong identity, all entries in the cache are cleared.
+ */
+ bool validateCache(const void* identity, ssize_t size);
+
+ /**
+ * "saveToDiskLocked" attemps to save the current contents of the cache to
+ * disk. If the identity hash exists, we will insert the identity hash into
+ * the cache for next validation.
+ */
+ void saveToDiskLocked();
+
+ /**
* "mInitialized" indicates whether the ShaderCache is in the initialized
* state. It is initialized to false at construction time, and gets set to
* true when initialize is called.
@@ -111,6 +133,15 @@ private:
std::string mFilename;
/**
+ * "mIDHash" is the current identity hash for the cache validation. It is
+ * initialized to an empty vector at construction time, and its content is
+ * generated in the call of the validateCache method. An empty vector
+ * indicates that cache validation is not performed, and the hash should
+ * not be stored on disk.
+ */
+ std::vector<uint8_t> mIDHash;
+
+ /**
* "mSavePending" indicates whether or not a deferred save operation is
* pending. Each time a key/value pair is inserted into the cache via
* load, a deferred save is initiated if one is not already pending.
@@ -140,6 +171,11 @@ private:
*/
static ShaderCache sCache;
+ /**
+ * "sIDKey" is the cache key of the identity hash
+ */
+ static constexpr uint8_t sIDKey = 0;
+
friend class ShaderCacheTestUtils; //used for unit testing
};
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 82179a37f5be..38905138e332 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -29,7 +29,7 @@ namespace skiapipeline {
void SkiaDisplayList::syncContents() {
for (auto& functor : mChildFunctors) {
- functor.syncFunctor();
+ functor->syncFunctor();
}
for (auto& animatedImage : mAnimatedImages) {
animatedImage->syncProperties();
@@ -132,7 +132,6 @@ void SkiaDisplayList::reset() {
mChildFunctors.clear();
mChildNodes.clear();
- projectionReceiveIndex = -1;
allocator.~LinearAllocator();
new (&allocator) LinearAllocator();
}
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 818ec114a5b3..ac7bb7b0950c 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -16,20 +16,29 @@
#pragma once
-#include "DisplayList.h"
#include "hwui/AnimatedImageDrawable.h"
-#include "GLFunctorDrawable.h"
+#include "FunctorDrawable.h"
+#include "RecordingCanvas.h"
#include "RenderNodeDrawable.h"
+#include "TreeInfo.h"
+#include "utils/LinearAllocator.h"
-#include <SkLiteDL.h>
-#include <SkLiteRecorder.h>
#include <deque>
namespace android {
namespace uirenderer {
+namespace renderthread {
+class CanvasContext;
+}
+
class Outline;
+namespace VectorDrawable {
+class Tree;
+};
+typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
+
namespace skiapipeline {
/**
@@ -38,10 +47,11 @@ namespace skiapipeline {
* runtime. The downside of this inheritance is that we pay for the overhead
* of the parent class construction/destruction without any real benefit.
*/
-class SkiaDisplayList : public DisplayList {
+class SkiaDisplayList {
public:
- SkiaDisplayList() { SkASSERT(projectionReceiveIndex == -1); }
- virtual ~SkiaDisplayList() {
+ size_t getUsedSize() { return allocator.usedSize(); }
+
+ ~SkiaDisplayList() {
/* Given that we are using a LinearStdAllocator to store some of the
* SkDrawable contents we must ensure that any other object that is
* holding a reference to those drawables is destroyed prior to their
@@ -64,33 +74,33 @@ public:
* that creates them. Allocator dtor invokes all SkDrawable dtors.
*/
template <class T, typename... Params>
- SkDrawable* allocateDrawable(Params&&... params) {
+ T* allocateDrawable(Params&&... params) {
return allocator.create<T>(std::forward<Params>(params)...);
}
- bool isSkiaDL() const override { return true; }
-
/**
* Returns true if the DisplayList does not have any recorded content
*/
- bool isEmpty() const override { return mDisplayList.empty(); }
+ bool isEmpty() const { return mDisplayList.empty(); }
/**
* Returns true if this list directly contains a GLFunctor drawing command.
*/
- bool hasFunctor() const override { return !mChildFunctors.empty(); }
+ bool hasFunctor() const { return !mChildFunctors.empty(); }
/**
* Returns true if this list directly contains a VectorDrawable drawing command.
*/
- bool hasVectorDrawables() const override { return !mVectorDrawables.empty(); }
+ bool hasVectorDrawables() const { return !mVectorDrawables.empty(); }
+
+ bool hasText() const { return mDisplayList.hasText(); }
/**
* Attempts to reset and reuse this DisplayList.
*
* @return true if the displayList will be reused and therefore should not be deleted
*/
- bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) override;
+ bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context);
/**
* ONLY to be called by RenderNode::syncDisplayList so that we can notify any
@@ -99,7 +109,7 @@ public:
* NOTE: This function can be folded into RenderNode when we no longer need
* to subclass from DisplayList
*/
- void syncContents() override;
+ void syncContents();
/**
* ONLY to be called by RenderNode::prepareTree in order to prepare this
@@ -116,25 +126,27 @@ public:
bool prepareListAndChildren(
TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
- std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) override;
+ std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn);
/**
* Calls the provided function once for each child of this DisplayList
*/
- void updateChildren(std::function<void(RenderNode*)> updateFn) override;
+ void updateChildren(std::function<void(RenderNode*)> updateFn);
/**
* Returns true if there is a child render node that is a projection receiver.
*/
inline bool containsProjectionReceiver() const { return mProjectionReceiver; }
- void attachRecorder(SkLiteRecorder* recorder, const SkIRect& bounds) {
+ void attachRecorder(RecordingCanvas* recorder, const SkIRect& bounds) {
recorder->reset(&mDisplayList, bounds);
}
void draw(SkCanvas* canvas) { mDisplayList.draw(canvas); }
- void output(std::ostream& output, uint32_t level) override;
+ void output(std::ostream& output, uint32_t level);
+
+ LinearAllocator allocator;
/**
* We use std::deque here because (1) we need to iterate through these
@@ -142,11 +154,11 @@ public:
* cannot relocate.
*/
std::deque<RenderNodeDrawable> mChildNodes;
- std::deque<GLFunctorDrawable> mChildFunctors;
+ std::deque<FunctorDrawable*> mChildFunctors;
std::vector<SkImage*> mMutableImages;
std::vector<VectorDrawableRoot*> mVectorDrawables;
std::vector<AnimatedImageDrawable*> mAnimatedImages;
- SkLiteDL mDisplayList;
+ DisplayListData mDisplayList;
// mProjectionReceiver points to a child node (stored in mChildNodes) that is as a projection
// receiver. It is set at record time and used at both prepare and draw tree traversals to
@@ -159,12 +171,12 @@ public:
// node is drawn.
const Outline* mProjectedOutline = nullptr;
- // mProjectedReceiverParentMatrix is valid when render node tree is traversed during the draw
- // pass. Render nodes that have a child receiver node, will store their matrix in
- // mProjectedReceiverParentMatrix. Child receiver node will set the matrix and then clip with
- // the
- // outline of their parent.
- SkMatrix mProjectedReceiverParentMatrix;
+ // mParentMatrix is set and valid when render node tree is traversed during the draw
+ // pass. Render nodes, which draw in a order different than recording order (e.g. nodes with a
+ // child receiver node or Z elevation), can use mParentMatrix to calculate the final transform
+ // without replaying the matrix transform OPs from the display list.
+ // Child receiver node will set the matrix and then clip with the outline of their parent.
+ SkMatrix mParentMatrix;
};
}; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 270527d551a9..d401b385075e 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -17,19 +17,22 @@
#include "SkiaOpenGLPipeline.h"
#include "DeferredLayerUpdater.h"
-#include "GlLayer.h"
#include "LayerDrawable.h"
#include "SkiaPipeline.h"
#include "SkiaProfileRenderer.h"
#include "hwui/Bitmap.h"
+#include "private/hwui/DrawGlInfo.h"
#include "renderstate/RenderState.h"
#include "renderthread/EglManager.h"
#include "renderthread/Frame.h"
+#include "utils/GLUtils.h"
#include "utils/TraceUtils.h"
+#include <GLES3/gl3.h>
+
#include <GrBackendSurface.h>
-#include <SkImageInfo.h>
#include <SkBlendMode.h>
+#include <SkImageInfo.h>
#include <cutils/properties.h>
#include <strings.h>
@@ -41,7 +44,13 @@ namespace uirenderer {
namespace skiapipeline {
SkiaOpenGLPipeline::SkiaOpenGLPipeline(RenderThread& thread)
- : SkiaPipeline(thread), mEglManager(thread.eglManager()) {}
+ : SkiaPipeline(thread), mEglManager(thread.eglManager()) {
+ thread.renderState().registerContextCallback(this);
+}
+
+SkiaOpenGLPipeline::~SkiaOpenGLPipeline() {
+ mRenderThread.renderState().removeContextCallback(this);
+}
MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
// TODO: Figure out why this workaround is needed, see b/13913604
@@ -60,32 +69,38 @@ Frame SkiaOpenGLPipeline::getFrame() {
}
bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
- const FrameBuilder::LightGeometry& lightGeometry,
+ const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
- bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo,
+ bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) {
mEglManager.damageFrame(frame, dirty);
+ SkColorType colorType = getSurfaceColorType();
// setup surface for fbo0
GrGLFramebufferInfo fboInfo;
fboInfo.fFBOID = 0;
- GrPixelConfig pixelConfig =
- wideColorGamut ? kRGBA_half_GrPixelConfig : kRGBA_8888_GrPixelConfig;
+ if (colorType == kRGBA_F16_SkColorType) {
+ fboInfo.fFormat = GL_RGBA16F;
+ } else if (colorType == kN32_SkColorType) {
+ // Note: The default preference of pixel format is RGBA_8888, when other
+ // pixel format is available, we should branch out and do more check.
+ fboInfo.fFormat = GL_RGBA8;
+ } else {
+ LOG_ALWAYS_FATAL("Unsupported color type.");
+ }
- GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE,
- pixelConfig, fboInfo);
+ GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo);
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkASSERT(mRenderThread.getGrContext() != nullptr);
sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
- mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, nullptr, &props));
+ mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, colorType,
+ nullptr, &props));
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
- renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds,
- surface);
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
layerUpdateQueue->clear();
// Draw visual debugging features
@@ -122,77 +137,16 @@ bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect
return *requireSwap;
}
-bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
- if (!mRenderThread.getGrContext()) {
- return false;
- }
-
- // acquire most recent buffer for drawing
- deferredLayer->updateTexImage();
- deferredLayer->apply();
-
- // drop the colorSpace as we only support readback into sRGB or extended sRGB
- SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr);
-
- /* This intermediate surface is present to work around a bug in SwiftShader that
- * prevents us from reading the contents of the layer's texture directly. The
- * workaround involves first rendering that texture into an intermediate buffer and
- * then reading from the intermediate buffer into the bitmap.
- */
- sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
- SkBudgeted::kYes, surfaceInfo);
-
- if (!tmpSurface.get()) {
- surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType);
- tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
- SkBudgeted::kYes, surfaceInfo);
- if (!tmpSurface.get()) {
- ALOGW("Unable to readback GPU contents into the provided bitmap");
- return false;
- }
- }
-
- Layer* layer = deferredLayer->backingLayer();
- const SkRect dstRect = SkRect::MakeIWH(bitmap->width(), bitmap->height());
- if (LayerDrawable::DrawLayer(mRenderThread.getGrContext(), tmpSurface->getCanvas(), layer,
- &dstRect)) {
- sk_sp<SkImage> tmpImage = tmpSurface->makeImageSnapshot();
- if (tmpImage->readPixels(surfaceInfo, bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
- bitmap->notifyPixelsChanged();
- return true;
- }
-
- // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into 8888
- // and then draw that into the destination format before giving up.
- SkBitmap tmpBitmap;
- SkImageInfo bitmapInfo = SkImageInfo::MakeN32(bitmap->width(), bitmap->height(),
- bitmap->alphaType());
- if (tmpBitmap.tryAllocPixels(bitmapInfo) &&
- tmpImage->readPixels(bitmapInfo, tmpBitmap.getPixels(),
- tmpBitmap.rowBytes(), 0, 0)) {
- SkCanvas canvas(*bitmap);
- SkPaint paint;
- paint.setBlendMode(SkBlendMode::kSrc);
- canvas.drawBitmap(tmpBitmap, 0, 0, &paint);
- bitmap->notifyPixelsChanged();
- return true;
- }
- }
-
- return false;
-}
-
-static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend) {
- GlLayer* layer =
- new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
- layer->generateTexture();
- return layer;
+DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
+ mRenderThread.requireGlContext();
+ return new DeferredLayerUpdater(mRenderThread.renderState());
}
-DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
- mEglManager.initialize();
- return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL);
+void SkiaOpenGLPipeline::onContextDestroyed() {
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mEglManager.destroySurface(mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+ }
}
void SkiaOpenGLPipeline::onStop() {
@@ -209,8 +163,14 @@ bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
}
if (surface) {
- const bool wideColorGamut = colorMode == ColorMode::WideColorGamut;
- mEglSurface = mEglManager.createSurface(surface, wideColorGamut);
+ mRenderThread.requireGlContext();
+ mEglSurface = mEglManager.createSurface(surface, colorMode);
+ }
+
+ if (colorMode == ColorMode::SRGB) {
+ mSurfaceColorType = SkColorType::kN32_SkColorType;
+ } else if (colorMode == ColorMode::WideColorGamut) {
+ mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
}
if (mEglSurface != EGL_NO_SURFACE) {
@@ -244,203 +204,6 @@ 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;
-};
-
-static bool isFP16Supported(const sk_sp<GrContext>& grContext) {
- static std::once_flag sOnceFlag;
- static bool supported = false;
-
- std::call_once(sOnceFlag, [](const sk_sp<GrContext>& grContext) {
- if (!grContext->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) {
- supported = false;
- return;
- }
-
- sp<GraphicBuffer> buffer = new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_FP16,
- GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
- GraphicBuffer::USAGE_SW_READ_NEVER, "tempFp16Buffer");
- status_t error = buffer->initCheck();
- supported = !error;
- }, grContext);
-
- return supported;
-}
-
-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 is upconverted to RGBA_8888
- case kARGB_4444_SkColorType:
- pixelFormat = PIXEL_FORMAT_RGBA_8888;
- format = GL_RGBA;
- type = GL_UNSIGNED_BYTE;
- break;
- case kRGBA_F16_SkColorType:
- isSupported = isFP16Supported(grContext);
- 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 5e013b6697a7..4ab3541d447b 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -18,6 +18,8 @@
#include "SkiaPipeline.h"
+#include "renderstate/RenderState.h"
+
namespace android {
class Bitmap;
@@ -25,22 +27,20 @@ class Bitmap;
namespace uirenderer {
namespace skiapipeline {
-class SkiaOpenGLPipeline : public SkiaPipeline {
+class SkiaOpenGLPipeline : public SkiaPipeline, public IGpuContextCallback {
public:
SkiaOpenGLPipeline(renderthread::RenderThread& thread);
- virtual ~SkiaOpenGLPipeline() {}
+ virtual ~SkiaOpenGLPipeline();
renderthread::MakeCurrentResult makeCurrent() override;
renderthread::Frame getFrame() override;
bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
- const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo,
+ const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode> >& renderNodes,
FrameInfoVisualizer* profiler) override;
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
- bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
DeferredLayerUpdater* createTextureLayer() override;
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
renderthread::ColorMode colorMode) override;
@@ -49,8 +49,9 @@ public:
bool isContextReady() override;
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
- static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread,
- SkBitmap& skBitmap);
+
+protected:
+ void onContextDestroyed() override;
private:
renderthread::EglManager& mEglManager;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
deleted file mode 100644
index 0760f1610891..000000000000
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SkiaOpenGLReadback.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <GrBackendSurface.h>
-#include <SkCanvas.h>
-#include <SkSurface.h>
-#include <gl/GrGLInterface.h>
-#include <gl/GrGLTypes.h>
-#include "DeviceInfo.h"
-#include "Matrix.h"
-#include "Properties.h"
-#include "utils/MathUtils.h"
-
-using namespace android::uirenderer::renderthread;
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
- int imgWidth, int imgHeight, const Rect& srcRect,
- SkBitmap* bitmap) {
- GLuint sourceTexId;
- glGenTextures(1, &sourceTexId);
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
-
- sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
- LOG_ALWAYS_FATAL_IF(!glInterface.get());
- grContext = GrContext::MakeGL(std::move(glInterface));
- } else {
- grContext->resetContext();
- }
-
- GrGLTextureInfo externalTexture;
- externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
- externalTexture.fID = sourceTexId;
-
- GrPixelConfig pixelConfig;
- switch (bitmap->colorType()) {
- case kRGBA_F16_SkColorType:
- pixelConfig = kRGBA_half_GrPixelConfig;
- break;
- case kN32_SkColorType:
- default:
- pixelConfig = kRGBA_8888_GrPixelConfig;
- break;
- }
-
- if (pixelConfig == kRGBA_half_GrPixelConfig &&
- !grContext->caps()->isConfigRenderable(kRGBA_half_GrPixelConfig, false)) {
- ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
- return CopyResult::DestinationInvalid;
- }
-
- GrBackendTexture backendTexture(imgWidth, imgHeight, pixelConfig, externalTexture);
-
- CopyResult copyResult = CopyResult::UnknownError;
- sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture,
- kTopLeft_GrSurfaceOrigin));
- if (image) {
- int displayedWidth = imgWidth, displayedHeight = imgHeight;
- // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
- // size.
- if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) {
- std::swap(displayedWidth, displayedHeight);
- }
- SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
- SkRect skiaSrcRect = srcRect.toSkRect();
- if (skiaSrcRect.isEmpty()) {
- skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight);
- }
- bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight));
-
- if (srcNotEmpty) {
- SkMatrix textureMatrixInv;
- imgTransform.copyTo(textureMatrixInv);
- // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
- // use bottom left origin and remove flipV and invert transformations.
- SkMatrix flipV;
- flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
- textureMatrixInv.preConcat(flipV);
- textureMatrixInv.preScale(1.0f / displayedWidth, 1.0f / displayedHeight);
- textureMatrixInv.postScale(imgWidth, imgHeight);
- SkMatrix textureMatrix;
- if (!textureMatrixInv.invert(&textureMatrix)) {
- textureMatrix = textureMatrixInv;
- }
-
- textureMatrixInv.mapRect(&skiaSrcRect);
- textureMatrixInv.mapRect(&skiaDestRect);
-
- // 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);
- // Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage
- // is codified by tests using golden images like DecodeAccuracyTest.
- bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width())
- && MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
- if (!disableFilter) {
- paint.setFilterQuality(kLow_SkFilterQuality);
- }
- scaledSurface->getCanvas()->concat(textureMatrix);
- scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint,
- SkCanvas::kFast_SrcRectConstraint);
-
- image = scaledSurface->makeImageSnapshot();
-
- if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
- bitmap->notifyPixelsChanged();
- copyResult = CopyResult::Success;
- }
- }
- }
-
- // make sure that we have deleted the texture (in the SkImage) before we
- // destroy the EGLImage that it was created from
- image.reset();
- return copyResult;
-}
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
deleted file mode 100644
index cc9fb3b0a5e0..000000000000
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "OpenGLReadback.h"
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-class SkiaOpenGLReadback : public OpenGLReadback {
-public:
- SkiaOpenGLReadback(renderthread::RenderThread& thread) : OpenGLReadback(thread) {}
-
-protected:
- virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
- int imgWidth, int imgHeight, const Rect& srcRect,
- SkBitmap* bitmap) override;
-};
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index d66cba12ad99..2dfe7c71ca1b 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -81,18 +81,17 @@ void SkiaPipeline::onPrepareTree() {
mVectorDrawables.clear();
}
-void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
+void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, bool opaque,
- bool wideColorGamut, const BakedOpRenderer::LightInfo& lightInfo) {
+ const LightInfo& lightInfo) {
updateLighting(lightGeometry, lightInfo);
ATRACE_NAME("draw layers");
renderVectorDrawableCache();
- renderLayersImpl(*layerUpdateQueue, opaque, wideColorGamut);
+ renderLayersImpl(*layerUpdateQueue, opaque);
layerUpdateQueue->clear();
}
-void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque,
- bool wideColorGamut) {
+void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
sk_sp<GrContext> cachedContext;
// Render all layers that need to be updated, in order.
@@ -103,7 +102,6 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque,
// as not to lose info on what portion is damaged
if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) {
SkASSERT(layerNode->getLayerSurface());
- SkASSERT(layerNode->getDisplayList()->isSkiaDL());
SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList();
if (!displayList || displayList->isEmpty()) {
SkDEBUGF(("%p drawLayers(%s) : missing drawable", layerNode, layerNode->getName()));
@@ -162,7 +160,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque,
}
bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
- bool wideColorGamut, ErrorHandler* errorHandler) {
+ ErrorHandler* errorHandler) {
// compute the size of the surface (i.e. texture) to be allocated for this layer
const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE;
const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE;
@@ -170,12 +168,8 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator
SkSurface* layer = node->getLayerSurface();
if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
SkImageInfo info;
- if (wideColorGamut) {
- info = SkImageInfo::Make(surfaceWidth, surfaceHeight, kRGBA_F16_SkColorType,
- kPremul_SkAlphaType);
- } else {
- info = SkImageInfo::MakeN32Premul(surfaceWidth, surfaceHeight);
- }
+ info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),
+ kPremul_SkAlphaType);
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
SkASSERT(mRenderThread.getGrContext() != nullptr);
node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
@@ -206,10 +200,6 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator
return false;
}
-void SkiaPipeline::destroyLayer(RenderNode* node) {
- node->setLayerSurface(nullptr);
-}
-
void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
GrContext* context = thread.getGrContext();
if (context) {
@@ -322,19 +312,18 @@ void SkiaPipeline::endCapture(SkSurface* surface) {
void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque,
- bool wideColorGamut, const Rect& contentDrawBounds,
- sk_sp<SkSurface> surface) {
+ const Rect& contentDrawBounds, sk_sp<SkSurface> surface) {
renderVectorDrawableCache();
// draw all layers up front
- renderLayersImpl(layers, opaque, wideColorGamut);
+ renderLayersImpl(layers, opaque);
// initialize the canvas for the current frame, that might be a recording canvas if SKP
// capture is enabled.
std::unique_ptr<SkPictureRecorder> recorder;
SkCanvas* canvas = tryCapture(surface.get());
- renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas);
+ renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas);
endCapture(surface.get());
@@ -355,13 +344,12 @@ static Rect nodeBounds(RenderNode& node) {
void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
const std::vector<sp<RenderNode>>& nodes, bool opaque,
- bool wideColorGamut, const Rect& contentDrawBounds,
- SkCanvas* canvas) {
+ const Rect& contentDrawBounds, SkCanvas* canvas) {
SkAutoCanvasRestore saver(canvas, true);
canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
// STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293
- if (!opaque || wideColorGamut) {
+ if (!opaque || getSurfaceColorType() == kRGBA_F16_SkColorType) {
canvas->clear(SK_ColorTRANSPARENT);
}
@@ -494,7 +482,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, false, contentDrawBounds, &overdrawCanvas);
+ renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas);
sk_sp<SkImage> counts = offscreen->makeImageSnapshot();
// Draw overdraw colors to the canvas. The color filter will convert counts to colors.
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 38ad9c09a8aa..42a411a6808c 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -17,7 +17,7 @@
#pragma once
#include <SkSurface.h>
-#include "FrameBuilder.h"
+#include "Lighting.h"
#include "hwui/AnimatedImageDrawable.h"
#include "renderthread/CanvasContext.h"
#include "renderthread/IRenderPipeline.h"
@@ -42,24 +42,24 @@ public:
void unpinImages() override;
void onPrepareTree() override;
- void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo) override;
+ void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
+ bool opaque, const LightInfo& lightInfo) override;
bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
- bool wideColorGamut, ErrorHandler* errorHandler) override;
+ ErrorHandler* errorHandler) override;
+
+ SkColorType getSurfaceColorType() const { return mSurfaceColorType; }
+ sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; }
void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
+ const std::vector<sp<RenderNode>>& nodes, bool opaque,
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);
- void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut);
+ void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque);
static float getLightRadius() {
if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) {
@@ -97,8 +97,8 @@ public:
return mLightCenter;
}
- static void updateLighting(const FrameBuilder::LightGeometry& lightGeometry,
- const BakedOpRenderer::LightInfo& lightInfo) {
+ static void updateLighting(const LightGeometry& lightGeometry,
+ const LightInfo& lightInfo) {
mLightRadius = lightGeometry.radius;
mAmbientShadowAlpha = lightInfo.ambientShadowAlpha;
mSpotShadowAlpha = lightInfo.spotShadowAlpha;
@@ -109,10 +109,12 @@ protected:
void dumpResourceCacheUsage() const;
renderthread::RenderThread& mRenderThread;
+ SkColorType mSurfaceColorType;
+ sk_sp<SkColorSpace> mSurfaceColorSpace;
private:
void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
- const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
+ const std::vector<sp<RenderNode>>& nodes, bool opaque,
const Rect& contentDrawBounds, SkCanvas* canvas);
/**
diff --git a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
index 5ae7d6b0b607..daa4c1839693 100644
--- a/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
+++ b/libs/hwui/pipeline/skia/SkiaProfileRenderer.h
@@ -16,7 +16,7 @@
#include "IProfileRenderer.h"
-#include "BakedOpRenderer.h"
+#include "SkCanvas.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index f0da660f17b0..45022e733979 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -17,11 +17,14 @@
#include "SkiaRecordingCanvas.h"
#include <SkImagePriv.h>
+#include "CanvasTransform.h"
#include "Layer.h"
#include "LayerDrawable.h"
#include "NinePatchUtils.h"
#include "RenderNode.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#include "pipeline/skia/GLFunctorDrawable.h"
+#include "pipeline/skia/VkInteropFunctorDrawable.h"
namespace android {
namespace uirenderer {
@@ -78,6 +81,11 @@ void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x,
}
void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
+ if (mCurrentBarrier && enableReorder) {
+ // Already in a re-order section, nothing to do
+ return;
+ }
+
if (nullptr != mCurrentBarrier) {
// finish off the existing chunk
SkDrawable* drawable =
@@ -86,9 +94,8 @@ void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
drawDrawable(drawable);
}
if (enableReorder) {
- mCurrentBarrier = (StartReorderBarrierDrawable*)
- mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
- mDisplayList.get());
+ mCurrentBarrier = mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
+ mDisplayList.get());
drawDrawable(mCurrentBarrier);
}
}
@@ -110,37 +117,25 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
// use staging property, since recording on UI thread
if (renderNode->stagingProperties().isProjectionReceiver()) {
mDisplayList->mProjectionReceiver = &renderNodeDrawable;
- // set projectionReceiveIndex so that RenderNode.hasProjectionReceiver returns true
- mDisplayList->projectionReceiveIndex = mDisplayList->mChildNodes.size() - 1;
}
}
void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
uirenderer::GlFunctorLifecycleListener* listener) {
- // Drawable dtor will be invoked when mChildFunctors deque is cleared.
- mDisplayList->mChildFunctors.emplace_back(functor, listener, asSkCanvas());
- drawDrawable(&mDisplayList->mChildFunctors.back());
-}
-
-class VectorDrawable : public SkDrawable {
-public:
- VectorDrawable(VectorDrawableRoot* tree)
- : mRoot(tree)
- , mBounds(tree->stagingProperties()->getBounds()) {}
-
-protected:
- virtual SkRect onGetBounds() override { return mBounds; }
- virtual void onDraw(SkCanvas* canvas) override {
- mRoot->draw(canvas, mBounds);
+ FunctorDrawable* functorDrawable;
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor,
+ listener, asSkCanvas());
+ } else {
+ functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener,
+ asSkCanvas());
}
-
-private:
- sp<VectorDrawableRoot> mRoot;
- SkRect mBounds;
-};
+ mDisplayList->mChildFunctors.push_back(functorDrawable);
+ drawDrawable(functorDrawable);
+}
void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
- drawDrawable(mDisplayList->allocateDrawable<VectorDrawable>(tree));
+ mRecorder.drawVectorDrawable(tree);
mDisplayList->mVectorDrawables.push_back(tree);
}
@@ -148,12 +143,45 @@ void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
// Recording Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
+SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint,
+ sk_sp<SkColorFilter> colorSpaceFilter) {
+ bool fixBlending = false;
+ bool fixAA = false;
+ if (paint) {
+ // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
+ // older.
+ fixBlending = sApiLevel <= 27 && paint->getBlendMode() == SkBlendMode::kClear;
+ fixAA = paint->isAntiAlias();
+ }
+
+ if (fixBlending || fixAA || colorSpaceFilter) {
+ SkPaint& tmpPaint = paint.writeable();
+
+ if (fixBlending) {
+ tmpPaint.setBlendMode(SkBlendMode::kDstOut);
+ }
+
+ if (colorSpaceFilter) {
+ if (tmpPaint.getColorFilter()) {
+ tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(
+ tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
+ } else {
+ tmpPaint.setColorFilter(std::move(colorSpaceFilter));
+ }
+ LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
+ }
+
+ // disabling AA on bitmap draws matches legacy HWUI behavior
+ tmpPaint.setAntiAlias(false);
+ }
+
+ return filterPaint(std::move(paint));
+}
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImage(image, left, top, bitmapPaint(paint, &tmpPaint, colorFilter));
+ mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
// 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.
@@ -166,10 +194,9 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con
SkAutoCanvasRestore acr(&mRecorder, true);
concat(matrix);
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImage(image, 0, 0, bitmapPaint(paint, &tmpPaint, colorFilter));
+ mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique()) {
mDisplayList->mMutableImages.push_back(image.get());
}
@@ -181,11 +208,10 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImageRect(image, srcRect, dstRect, bitmapPaint(paint, &tmpPaint, colorFilter),
- SkCanvas::kFast_SrcRectConstraint);
+ mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
+ SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
!dstRect.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
@@ -216,23 +242,16 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- SkPaint tmpPaint;
+ PaintCoW filteredPaint(paint);
+ // HWUI always draws 9-patches with bilinear filtering, regardless of what is set in the Paint.
+ if (!filteredPaint || filteredPaint->getFilterQuality() != kLow_SkFilterQuality) {
+ filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
+ }
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- const SkPaint* filteredPaint = bitmapPaint(paint, &tmpPaint, colorFilter);
- // Besides kNone, the other three SkFilterQualities are treated the same. And Android's
- // Java API only supports kLow and kNone anyway.
- if (!filteredPaint || filteredPaint->getFilterQuality() == kNone_SkFilterQuality) {
- if (filteredPaint != &tmpPaint) {
- if (paint) {
- tmpPaint = *paint;
- }
- filteredPaint = &tmpPaint;
- }
- tmpPaint.setFilterQuality(kLow_SkFilterQuality);
- }
-
- mRecorder.drawImageLattice(image.get(), lattice, dst, filteredPaint);
+ mRecorder.drawImageLattice(image, lattice, dst,
+ filterBitmap(std::move(filteredPaint), std::move(colorFilter)),
+ bitmap.palette());
if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
}
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 93807a5476e6..988728dfe23e 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -15,7 +15,7 @@
*/
#pragma once
-#include <SkLiteRecorder.h>
+#include "RecordingCanvas.h"
#include "ReorderBarrierDrawables.h"
#include "SkiaCanvas.h"
#include "SkiaDisplayList.h"
@@ -76,7 +76,7 @@ public:
uirenderer::GlFunctorLifecycleListener* listener) override;
private:
- SkLiteRecorder mRecorder;
+ RecordingCanvas mRecorder;
std::unique_ptr<SkiaDisplayList> mDisplayList;
StartReorderBarrierDrawable* mCurrentBarrier;
@@ -89,44 +89,7 @@ private:
*/
void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height);
- inline static const SkPaint* bitmapPaint(const SkPaint* origPaint, SkPaint* tmpPaint,
- sk_sp<SkColorFilter> colorSpaceFilter) {
- bool fixBlending = false;
- bool fixAA = false;
- if (origPaint) {
- // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
- // older.
- fixBlending = sApiLevel <= 27 && origPaint->getBlendMode() == SkBlendMode::kClear;
- fixAA = origPaint->isAntiAlias();
- }
-
- if (fixBlending || fixAA || colorSpaceFilter) {
- if (origPaint) {
- *tmpPaint = *origPaint;
- }
-
- if (fixBlending) {
- tmpPaint->setBlendMode(SkBlendMode::kDstOut);
- }
-
- if (colorSpaceFilter) {
- if (tmpPaint->getColorFilter()) {
- tmpPaint->setColorFilter(SkColorFilter::MakeComposeFilter(
- tmpPaint->refColorFilter(), colorSpaceFilter));
- } else {
- tmpPaint->setColorFilter(colorSpaceFilter);
- }
- LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
- }
-
- // disabling AA on bitmap draws matches legacy HWUI behavior
- tmpPaint->setAntiAlias(false);
- return tmpPaint;
- } else {
- return origPaint;
- }
- }
-
+ PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter);
};
}; // namespace skiapipeline
diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/pipeline/skia/SkiaUtils.h
index f7ae6c575f6a..834446905216 100644
--- a/libs/hwui/renderstate/PixelBufferState.h
+++ b/libs/hwui/pipeline/skia/SkiaUtils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -13,26 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef RENDERSTATE_PIXELBUFFERSTATE_H
-#define RENDERSTATE_PIXELBUFFERSTATE_H
-#include <GLES3/gl3.h>
+#pragma once
-namespace android {
-namespace uirenderer {
+#include <SkRect.h>
-class PixelBufferState {
- friend class Caches; // TODO: move to RenderState
-public:
- bool bind(GLuint buffer);
- bool unbind();
+namespace android {
-private:
- PixelBufferState();
- GLuint mCurrentPixelBuffer;
+static inline SkRect SkRectMakeLargest() {
+ const SkScalar v = SK_ScalarMax;
+ return { -v, -v, v, v };
};
-} /* namespace uirenderer */
} /* namespace android */
-
-#endif // RENDERSTATE_PIXELBUFFERSTATE_H
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 5825060f902a..7fc41acefe8a 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -20,9 +20,9 @@
#include "Readback.h"
#include "SkiaPipeline.h"
#include "SkiaProfileRenderer.h"
-#include "VkLayer.h"
#include "renderstate/RenderState.h"
#include "renderthread/Frame.h"
+#include "VkInteropFunctorDrawable.h"
#include <SkSurface.h>
#include <SkTypes.h>
@@ -62,10 +62,9 @@ Frame SkiaVulkanPipeline::getFrame() {
}
bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
- const FrameBuilder::LightGeometry& lightGeometry,
+ const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
- bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo,
+ bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) {
sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface();
@@ -73,8 +72,7 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con
return false;
}
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
- renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds,
- backBuffer);
+ renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer);
layerUpdateQueue->clear();
// Draw visual debugging features
@@ -109,21 +107,10 @@ bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect
return *requireSwap;
}
-bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
- // TODO: implement copyLayerInto for vulkan.
- return false;
-}
-
-static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode,
- bool blend) {
- return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
-}
-
DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
mVkManager.initialize();
- return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::Vulkan);
+ return new DeferredLayerUpdater(mRenderThread.renderState());
}
void SkiaVulkanPipeline::onStop() {}
@@ -136,8 +123,13 @@ bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
}
if (surface) {
- // TODO: handle color mode
- mVkSurface = mVkManager.createSurface(surface);
+ mVkSurface = mVkManager.createSurface(surface, colorMode);
+ }
+
+ if (colorMode == ColorMode::SRGB) {
+ mSurfaceColorType = SkColorType::kN32_SkColorType;
+ } else if (colorMode == ColorMode::WideColorGamut) {
+ mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
}
return mVkSurface != nullptr;
@@ -152,9 +144,7 @@ bool SkiaVulkanPipeline::isContextReady() {
}
void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
- // TODO: we currently don't support OpenGL WebView's
- DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
- (*functor)(mode, nullptr);
+ VkInteropFunctorDrawable::vkInvokeFunctor(functor);
}
sk_sp<Bitmap> SkiaVulkanPipeline::allocateHardwareBitmap(renderthread::RenderThread& renderThread,
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 03b4c79f2beb..14c0d69dba33 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -31,14 +31,12 @@ public:
renderthread::MakeCurrentResult makeCurrent() override;
renderthread::Frame getFrame() override;
bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
- const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo,
+ const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
+ const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode> >& renderNodes,
FrameInfoVisualizer* profiler) override;
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
- bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
DeferredLayerUpdater* createTextureLayer() override;
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
renderthread::ColorMode colorMode) override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanReadback.h b/libs/hwui/pipeline/skia/SkiaVulkanReadback.h
deleted file mode 100644
index 65b89d617f7b..000000000000
--- a/libs/hwui/pipeline/skia/SkiaVulkanReadback.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Readback.h"
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-class SkiaVulkanReadback : public Readback {
-public:
- SkiaVulkanReadback(renderthread::RenderThread& thread) : Readback(thread) {}
-
- virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
- SkBitmap* bitmap) override {
- //TODO: implement Vulkan readback.
- return CopyResult::UnknownError;
- }
-
- virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
- SkBitmap* bitmap) override {
- //TODO: implement Vulkan readback.
- return CopyResult::UnknownError;
- }
-};
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
new file mode 100644
index 000000000000..a594206a2dd9
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2018 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 "VkInteropFunctorDrawable.h"
+#include <private/hwui/DrawGlInfo.h>
+
+#include "renderthread/EglManager.h"
+#include "thread/ThreadBase.h"
+#include "utils/TimeUtils.h"
+#include <thread>
+#include <utils/Color.h>
+#include <utils/Trace.h>
+#include <utils/TraceUtils.h>
+
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include <utils/GLUtils.h>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+static std::mutex sLock{};
+static ThreadBase* sGLDrawThread = nullptr;
+static renderthread::EglManager sEglManager;
+
+// ScopedDrawRequest makes sure a GL thread is started and EGL context is initialized on it.
+class ScopedDrawRequest {
+public:
+ ScopedDrawRequest() { beginDraw(); }
+private:
+ void beginDraw() {
+ std::lock_guard{sLock};
+
+ if (!sGLDrawThread) {
+ sGLDrawThread = new ThreadBase{};
+ }
+
+ if (!sGLDrawThread->isRunning()) {
+ sGLDrawThread->start("GLFunctorThread");
+ }
+
+ if (!sEglManager.hasEglContext()) {
+ sGLDrawThread->queue().runSync([]() {
+ sEglManager.initialize();
+ });
+ }
+ }
+};
+
+void VkInteropFunctorDrawable::vkInvokeFunctor(Functor* functor) {
+ ScopedDrawRequest _drawRequest{};
+ sGLDrawThread->queue().runSync([&]() {
+ EGLDisplay display = sEglManager.eglDisplay();
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (display != EGL_NO_DISPLAY) {
+ mode = DrawGlInfo::kModeProcess;
+ }
+ (*functor)(mode, nullptr);
+ });
+}
+
+#define FENCE_TIMEOUT 2000000000
+
+void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
+ ATRACE_CALL();
+
+ if (canvas->getGrContext() == nullptr) {
+ SkDEBUGF(("Attempting to draw VkInteropFunctor into an unsupported surface"));
+ return;
+ }
+
+ ScopedDrawRequest _drawRequest{};
+
+ SkImageInfo surfaceInfo = canvas->imageInfo();
+
+ if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
+ // Buffer will be used as an OpenGL ES render target.
+ mFrameBuffer = new GraphicBuffer(
+ //TODO: try to reduce the size of the buffer: possibly by using clip bounds.
+ static_cast<uint32_t>(surfaceInfo.width()),
+ static_cast<uint32_t>(surfaceInfo.height()),
+ ColorTypeToPixelFormat(surfaceInfo.colorType()),
+ GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
+ std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
+ "]");
+ status_t error = mFrameBuffer->initCheck();
+ if (error < 0) {
+ ALOGW("VkInteropFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
+ return;
+ }
+
+ mFBInfo = surfaceInfo;
+ }
+
+ //TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
+ //TODO: draw command has completed.
+ //TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
+ //TODO: GrVkGpu::destroyResources() for example.
+ bool success = sGLDrawThread->queue().runSync([&]() -> bool {
+ ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
+ EGLDisplay display = sEglManager.eglDisplay();
+ 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)mFrameBuffer->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;
+ }
+
+ AutoSkiaGlTexture glTexture;
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
+ GL_CHECKPOINT(MODERATE);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ DrawGlInfo info;
+ SkMatrix44 mat4(canvas->getTotalMatrix());
+ SkIRect clipBounds = canvas->getDeviceClipBounds();
+
+ info.clipLeft = clipBounds.fLeft;
+ info.clipTop = clipBounds.fTop;
+ info.clipRight = clipBounds.fRight;
+ info.clipBottom = clipBounds.fBottom;
+ info.isLayer = true;
+ info.width = mFBInfo.width();
+ info.height = mFBInfo.height();
+ mat4.asColMajorf(&info.transform[0]);
+
+ glViewport(0, 0, info.width, info.height);
+
+ AutoGLFramebuffer glFb;
+ // Bind texture to the frame buffer.
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ glTexture.mTexture, 0);
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Failed framebuffer check for created target buffer: %s",
+ GLUtils::getGLFramebufferError());
+ return false;
+ }
+
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_SCISSOR_TEST);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ (*mFunctor)(DrawGlInfo::kModeDraw, &info);
+
+ EGLSyncKHR glDrawFinishedFence =
+ eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
+ LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
+ "Could not create sync fence %#x", eglGetError());
+ glFlush();
+ // TODO: export EGLSyncKHR in file descr
+ // TODO: import file desc in Vulkan Semaphore
+ // TODO: instead block the GPU: probably by using external Vulkan semaphore.
+ // Block the CPU until the glFlush finish.
+ EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
+ FENCE_TIMEOUT);
+ LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
+ "Failed to wait for the fence %#x", eglGetError());
+ eglDestroySyncKHR(display, glDrawFinishedFence);
+ return true;
+ });
+
+ if (!success) {
+ return;
+ }
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrcOver);
+ canvas->save();
+ // The size of the image matches the size of the canvas. We've used the matrix already, while
+ // drawing into the offscreen surface, so we need to reset it here.
+ canvas->resetMatrix();
+
+ auto functorImage = SkImage::MakeFromAHardwareBuffer(
+ reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
+ nullptr, kBottomLeft_GrSurfaceOrigin);
+ canvas->drawImage(functorImage, 0, 0, &paint);
+ canvas->restore();
+}
+
+VkInteropFunctorDrawable::~VkInteropFunctorDrawable() {
+ if (mListener.get() != nullptr) {
+ ScopedDrawRequest _drawRequest{};
+ sGLDrawThread->queue().runSync([&]() {
+ mListener->onGlFunctorReleased(mFunctor);
+ });
+ }
+}
+
+void VkInteropFunctorDrawable::syncFunctor() const {
+ ScopedDrawRequest _drawRequest{};
+ sGLDrawThread->queue().runSync([&]() {
+ (*mFunctor)(DrawGlInfo::kModeSync, nullptr);
+ });
+}
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
new file mode 100644
index 000000000000..3269cfbb8fe3
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FunctorDrawable.h"
+
+#include <utils/RefBase.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+namespace skiapipeline {
+
+/**
+ * This drawable wraps a Vulkan functor enabling it to be recorded into a list
+ * of Skia drawing commands.
+ */
+class VkInteropFunctorDrawable : public FunctorDrawable {
+public:
+ VkInteropFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
+ SkCanvas* canvas)
+ : FunctorDrawable(functor, listener, canvas) {}
+ virtual ~VkInteropFunctorDrawable();
+
+ void syncFunctor() const override;
+
+ static void vkInvokeFunctor(Functor* functor);
+
+protected:
+ virtual void onDraw(SkCanvas* canvas) override;
+
+private:
+
+ // Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
+ sp<GraphicBuffer> mFrameBuffer;
+ SkImageInfo mFBInfo;
+};
+
+}; // namespace skiapipeline
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/protos/ProtoHelpers.h b/libs/hwui/protos/ProtoHelpers.h
deleted file mode 100644
index 833c77f2b8cb..000000000000
--- a/libs/hwui/protos/ProtoHelpers.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef PROTOHELPERS_H
-#define PROTOHELPERS_H
-
-#include "Rect.h"
-#include "protos/hwui.pb.h"
-
-namespace android {
-namespace uirenderer {
-
-void set(proto::RectF* dest, const Rect& src) {
- dest->set_left(src.left);
- dest->set_top(src.top);
- dest->set_right(src.right);
- dest->set_bottom(src.bottom);
-}
-
-void set(std::string* dest, const SkPath& src) {
- size_t size = src.writeToMemory(nullptr);
- dest->resize(size);
- src.writeToMemory(&*dest->begin());
-}
-
-} // namespace uirenderer
-} // namespace android
-
-#endif // PROTOHELPERS_H
diff --git a/libs/hwui/protos/hwui.proto b/libs/hwui/protos/hwui.proto
deleted file mode 100644
index dcff80a24974..000000000000
--- a/libs/hwui/protos/hwui.proto
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-package android.uirenderer.proto;
-
-option optimize_for = LITE_RUNTIME;
-
-message RenderNode {
- required uint64 id = 1;
- required string name = 2;
- required RenderProperties properties = 3;
- optional DisplayList display_list = 4;
- repeated RenderNode children = 5;
-};
-
-message RenderProperties {
- required int32 left = 1;
- required int32 right = 2;
- required int32 top = 3;
- required int32 bottom = 4;
- required int32 clip_flags = 5;
- required float alpha = 6;
- required float translation_x = 7;
- required float translation_y = 8;
- required float translation_z = 9;
- required float elevation = 10;
- required float rotation = 11;
- required float rotation_x = 12;
- required float rotation_y = 13;
- required float scale_x = 14;
- required float scale_y = 15;
- required float pivot_x = 16;
- required float pivot_y = 17;
- required bool has_overlapping_rendering = 18;
- required bool pivot_explicitly_set = 19;
- required bool project_backwards = 20;
- required bool projection_receiver = 21;
- required RectF clip_bounds = 22;
- optional Outline outline = 23;
- optional RevealClip reveal_clip = 24;
-};
-
-message RectF {
- required float left = 1;
- required float right = 2;
- required float top = 3;
- required float bottom = 4;
-}
-
-message Outline {
- required bool should_clip = 1;
- enum Type {
- None = 0;
- Empty = 1;
- ConvexPath = 2;
- RoundRect = 3;
- }
- required Type type = 2;
- required RectF bounds = 3;
- required float radius = 4;
- required float alpha = 5;
- optional bytes path = 6;
-}
-
-message RevealClip {
- required float x = 1;
- required float y = 2;
- required float radius = 3;
-}
-
-message DisplayList {
- optional int32 projection_receive_index = 1;
- repeated DrawOp draw_ops = 2;
-}
-
-message DrawOp {
- oneof drawop {
- DrawOp_RenderNode render_node = 1;
- }
-}
-
-message DrawOp_RenderNode {
- optional RenderNode node = 1;
-}
diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp
deleted file mode 100644
index 5bef01c0ec3c..000000000000
--- a/libs/hwui/renderstate/Blend.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <renderstate/Blend.h>
-#include "Program.h"
-
-#include "ShadowTessellator.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Structure mapping Skia xfermodes to OpenGL blending factors.
- */
-struct Blender {
- SkBlendMode mode;
- GLenum src;
- GLenum dst;
-};
-
-// assumptions made by lookup tables in either this file or ProgramCache
-static_assert(0 == static_cast<int>(SkBlendMode::kClear), "SkBlendMode enums have changed");
-static_assert(1 == static_cast<int>(SkBlendMode::kSrc), "SkBlendMode enums have changed");
-static_assert(2 == static_cast<int>(SkBlendMode::kDst), "SkBlendMode enums have changed");
-static_assert(3 == static_cast<int>(SkBlendMode::kSrcOver), "SkBlendMode enums have changed");
-static_assert(4 == static_cast<int>(SkBlendMode::kDstOver), "SkBlendMode enums have changed");
-static_assert(5 == static_cast<int>(SkBlendMode::kSrcIn), "SkBlendMode enums have changed");
-static_assert(6 == static_cast<int>(SkBlendMode::kDstIn), "SkBlendMode enums have changed");
-static_assert(7 == static_cast<int>(SkBlendMode::kSrcOut), "SkBlendMode enums have changed");
-static_assert(8 == static_cast<int>(SkBlendMode::kDstOut), "SkBlendMode enums have changed");
-static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "SkBlendMode enums have changed");
-static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "SkBlendMode enums have changed");
-static_assert(11 == static_cast<int>(SkBlendMode::kXor), "SkBlendMode enums have changed");
-static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "SkBlendMode enums have changed");
-static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "SkBlendMode enums have changed");
-static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "SkBlendMode enums have changed");
-static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "SkBlendMode enums have changed");
-static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "SkBlendMode enums have changed");
-static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "SkBlendMode enums have changed");
-
-// In this array, the index of each Blender equals the value of the first
-// entry. For instance, gBlends[1] == gBlends[SkBlendMode::kSrc]
-const Blender kBlends[] = {{SkBlendMode::kClear, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA},
- {SkBlendMode::kSrc, GL_ONE, GL_ZERO},
- {SkBlendMode::kDst, GL_ZERO, GL_ONE},
- {SkBlendMode::kSrcOver, GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
- {SkBlendMode::kDstOver, GL_ONE_MINUS_DST_ALPHA, GL_ONE},
- {SkBlendMode::kSrcIn, GL_DST_ALPHA, GL_ZERO},
- {SkBlendMode::kDstIn, GL_ZERO, GL_SRC_ALPHA},
- {SkBlendMode::kSrcOut, GL_ONE_MINUS_DST_ALPHA, GL_ZERO},
- {SkBlendMode::kDstOut, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA},
- {SkBlendMode::kSrcATop, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA},
- {SkBlendMode::kDstATop, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA},
- {SkBlendMode::kXor, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA},
- {SkBlendMode::kPlus, GL_ONE, GL_ONE},
- {SkBlendMode::kModulate, GL_ZERO, GL_SRC_COLOR},
- {SkBlendMode::kScreen, GL_ONE, GL_ONE_MINUS_SRC_COLOR}};
-
-// This array contains the swapped version of each SkBlendMode. For instance
-// this array's SrcOver blending mode is actually DstOver. You can refer to
-// createLayer() for more information on the purpose of this array.
-const Blender kBlendsSwap[] = {{SkBlendMode::kClear, GL_ONE_MINUS_DST_ALPHA, GL_ZERO},
- {SkBlendMode::kSrc, GL_ZERO, GL_ONE},
- {SkBlendMode::kDst, GL_ONE, GL_ZERO},
- {SkBlendMode::kSrcOver, GL_ONE_MINUS_DST_ALPHA, GL_ONE},
- {SkBlendMode::kDstOver, GL_ONE, GL_ONE_MINUS_SRC_ALPHA},
- {SkBlendMode::kSrcIn, GL_ZERO, GL_SRC_ALPHA},
- {SkBlendMode::kDstIn, GL_DST_ALPHA, GL_ZERO},
- {SkBlendMode::kSrcOut, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA},
- {SkBlendMode::kDstOut, GL_ONE_MINUS_DST_ALPHA, GL_ZERO},
- {SkBlendMode::kSrcATop, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA},
- {SkBlendMode::kDstATop, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA},
- {SkBlendMode::kXor, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA},
- {SkBlendMode::kPlus, GL_ONE, GL_ONE},
- {SkBlendMode::kModulate, GL_DST_COLOR, GL_ZERO},
- {SkBlendMode::kScreen, GL_ONE_MINUS_DST_COLOR, GL_ONE}};
-
-Blend::Blend() : mEnabled(false), mSrcMode(GL_ZERO), mDstMode(GL_ZERO) {
- // gl blending off by default
-}
-
-void Blend::invalidate() {
- syncEnabled();
- mSrcMode = mDstMode = GL_ZERO;
-}
-
-void Blend::syncEnabled() {
- if (mEnabled) {
- glEnable(GL_BLEND);
- } else {
- glDisable(GL_BLEND);
- }
-}
-
-void Blend::getFactors(SkBlendMode mode, ModeOrderSwap modeUsage, GLenum* outSrc, GLenum* outDst) {
- int index = static_cast<int>(mode);
- *outSrc = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[index].src : kBlends[index].src;
- *outDst = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[index].dst : kBlends[index].dst;
-}
-
-void Blend::setFactors(GLenum srcMode, GLenum dstMode) {
- if ((srcMode == GL_ZERO || srcMode == GL_ONE) && dstMode == GL_ZERO) {
- // disable blending
- if (mEnabled) {
- glDisable(GL_BLEND);
- mEnabled = false;
- }
- } else {
- // enable blending
- if (!mEnabled) {
- glEnable(GL_BLEND);
- mEnabled = true;
- }
-
- if (srcMode != mSrcMode || dstMode != mDstMode) {
- glBlendFunc(srcMode, dstMode);
- mSrcMode = srcMode;
- mDstMode = dstMode;
- }
- }
-}
-
-void Blend::dump() {
- ALOGD("Blend: enabled %d, func src %d, dst %d", mEnabled, mSrcMode, mDstMode);
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h
deleted file mode 100644
index 7e559bace3f2..000000000000
--- a/libs/hwui/renderstate/Blend.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef RENDERSTATE_BLEND_H
-#define RENDERSTATE_BLEND_H
-
-#include "Vertex.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <SkBlendMode.h>
-#include <memory>
-
-namespace android {
-namespace uirenderer {
-
-class Blend {
- friend class RenderState;
-
-public:
- // dictates whether to swap src/dst
- enum class ModeOrderSwap {
- NoSwap,
- Swap,
- };
- void syncEnabled();
-
- static void getFactors(SkBlendMode mode, ModeOrderSwap modeUsage, GLenum* outSrc,
- GLenum* outDst);
- void setFactors(GLenum src, GLenum dst);
-
- bool getEnabled() { return mEnabled; }
- void getFactors(GLenum* src, GLenum* dst) {
- *src = mSrcMode;
- *dst = mDstMode;
- }
-
- void dump();
-
-private:
- Blend();
- void invalidate();
- bool mEnabled;
- GLenum mSrcMode;
- GLenum mDstMode;
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif // RENDERSTATE_BLEND_H
diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp
deleted file mode 100644
index 4f6c49e67b99..000000000000
--- a/libs/hwui/renderstate/MeshState.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "renderstate/MeshState.h"
-
-#include "Program.h"
-
-namespace android {
-namespace uirenderer {
-
-MeshState::MeshState()
- : mCurrentIndicesBuffer(0)
- , mCurrentPixelBuffer(0)
- , mCurrentPositionPointer(this)
- , mCurrentPositionStride(0)
- , mCurrentTexCoordsPointer(this)
- , mCurrentTexCoordsStride(0)
- , mTexCoordsArrayEnabled(false)
- , mQuadListIndices(0) {
- glGenBuffers(1, &mUnitQuadBuffer);
- glBindBuffer(GL_ARRAY_BUFFER, mUnitQuadBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(kUnitQuadVertices), kUnitQuadVertices, GL_STATIC_DRAW);
- mCurrentBuffer = mUnitQuadBuffer;
-
- uint16_t regionIndices[kMaxNumberOfQuads * 6];
- for (uint32_t i = 0; i < kMaxNumberOfQuads; i++) {
- uint16_t quad = i * 4;
- int index = i * 6;
- regionIndices[index] = quad; // top-left
- regionIndices[index + 1] = quad + 1; // top-right
- regionIndices[index + 2] = quad + 2; // bottom-left
- regionIndices[index + 3] = quad + 2; // bottom-left
- regionIndices[index + 4] = quad + 1; // top-right
- regionIndices[index + 5] = quad + 3; // bottom-right
- }
- glGenBuffers(1, &mQuadListIndices);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadListIndices);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(regionIndices), regionIndices, GL_STATIC_DRAW);
- mCurrentIndicesBuffer = mQuadListIndices;
-
- // position attribute always enabled
- glEnableVertexAttribArray(Program::kBindingPosition);
-}
-
-MeshState::~MeshState() {
- glDeleteBuffers(1, &mUnitQuadBuffer);
- mCurrentBuffer = 0;
-
- glDeleteBuffers(1, &mQuadListIndices);
- mQuadListIndices = 0;
-}
-
-void MeshState::dump() {
- ALOGD("MeshState VBOs: unitQuad %d, current %d", mUnitQuadBuffer, mCurrentBuffer);
- ALOGD("MeshState IBOs: quadList %d, current %d", mQuadListIndices, mCurrentIndicesBuffer);
- ALOGD("MeshState vertices: vertex data %p, stride %d", mCurrentPositionPointer,
- mCurrentPositionStride);
- ALOGD("MeshState texCoord: data %p, stride %d", mCurrentTexCoordsPointer,
- mCurrentTexCoordsStride);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Buffer Objects
-///////////////////////////////////////////////////////////////////////////////
-
-void MeshState::bindMeshBuffer(GLuint buffer) {
- if (mCurrentBuffer != buffer) {
- glBindBuffer(GL_ARRAY_BUFFER, buffer);
- mCurrentBuffer = buffer;
-
- // buffer has changed, so invalidate cached vertex pos/texcoord pointers
- resetVertexPointers();
- }
-}
-
-void MeshState::unbindMeshBuffer() {
- return bindMeshBuffer(0);
-}
-
-void MeshState::genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size, const void* data,
- GLenum usage) {
- if (!*buffer) {
- glGenBuffers(1, buffer);
- }
- bindMeshBuffer(*buffer);
- glBufferData(GL_ARRAY_BUFFER, size, data, usage);
-}
-
-void MeshState::updateMeshBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size,
- const void* data) {
- bindMeshBuffer(buffer);
- glBufferSubData(GL_ARRAY_BUFFER, offset, size, data);
-}
-
-void MeshState::deleteMeshBuffer(GLuint buffer) {
- if (buffer == mCurrentBuffer) {
- // GL defines that deleting the currently bound VBO rebinds to 0 (no VBO).
- // Reflect this in our cached value.
- mCurrentBuffer = 0;
- }
- glDeleteBuffers(1, &buffer);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Vertices
-///////////////////////////////////////////////////////////////////////////////
-
-void MeshState::bindPositionVertexPointer(const GLvoid* vertices, GLsizei stride) {
- // update pos coords if !current vbo, since vertices may point into mutable memory (e.g. stack)
- if (mCurrentBuffer == 0 || vertices != mCurrentPositionPointer ||
- stride != mCurrentPositionStride) {
- glVertexAttribPointer(Program::kBindingPosition, 2, GL_FLOAT, GL_FALSE, stride, vertices);
- mCurrentPositionPointer = vertices;
- mCurrentPositionStride = stride;
- }
-}
-
-void MeshState::bindTexCoordsVertexPointer(const GLvoid* vertices, GLsizei stride) {
- // update tex coords if !current vbo, since vertices may point into mutable memory (e.g. stack)
- if (mCurrentBuffer == 0 || vertices != mCurrentTexCoordsPointer ||
- stride != mCurrentTexCoordsStride) {
- glVertexAttribPointer(Program::kBindingTexCoords, 2, GL_FLOAT, GL_FALSE, stride, vertices);
- mCurrentTexCoordsPointer = vertices;
- mCurrentTexCoordsStride = stride;
- }
-}
-
-void MeshState::resetVertexPointers() {
- mCurrentPositionPointer = this;
- mCurrentTexCoordsPointer = this;
-}
-
-void MeshState::enableTexCoordsVertexArray() {
- if (!mTexCoordsArrayEnabled) {
- glEnableVertexAttribArray(Program::kBindingTexCoords);
- mCurrentTexCoordsPointer = this;
- mTexCoordsArrayEnabled = true;
- }
-}
-
-void MeshState::disableTexCoordsVertexArray() {
- if (mTexCoordsArrayEnabled) {
- glDisableVertexAttribArray(Program::kBindingTexCoords);
- mTexCoordsArrayEnabled = false;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Indices
-///////////////////////////////////////////////////////////////////////////////
-
-void MeshState::bindIndicesBuffer(const GLuint buffer) {
- if (mCurrentIndicesBuffer != buffer) {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
- mCurrentIndicesBuffer = buffer;
- }
-}
-
-void MeshState::unbindIndicesBuffer() {
- if (mCurrentIndicesBuffer) {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- mCurrentIndicesBuffer = 0;
- }
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h
deleted file mode 100644
index 95faf1ebfb02..000000000000
--- a/libs/hwui/renderstate/MeshState.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef RENDERSTATE_MESHSTATE_H
-#define RENDERSTATE_MESHSTATE_H
-
-#include "Vertex.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <memory>
-
-namespace android {
-namespace uirenderer {
-
-class Program;
-
-// Maximum number of quads that pre-allocated meshes can draw
-const uint32_t kMaxNumberOfQuads = 2048;
-
-// This array is never used directly but used as a memcpy source in the
-// OpenGLRenderer constructor
-const TextureVertex kUnitQuadVertices[] = {
- {0, 0, 0, 0}, {1, 0, 1, 0}, {0, 1, 0, 1}, {1, 1, 1, 1},
-};
-
-const GLsizei kVertexStride = sizeof(Vertex);
-const GLsizei kAlphaVertexStride = sizeof(AlphaVertex);
-const GLsizei kTextureVertexStride = sizeof(TextureVertex);
-const GLsizei kColorTextureVertexStride = sizeof(ColorTextureVertex);
-
-const GLsizei kMeshTextureOffset = 2 * sizeof(float);
-const GLsizei kVertexAlphaOffset = 2 * sizeof(float);
-const GLsizei kVertexAAWidthOffset = 2 * sizeof(float);
-const GLsizei kVertexAALengthOffset = 3 * sizeof(float);
-const GLsizei kUnitQuadCount = 4;
-
-class MeshState {
-private:
- friend class RenderState;
-
-public:
- ~MeshState();
- void dump();
- ///////////////////////////////////////////////////////////////////////////////
- // Buffer objects
- ///////////////////////////////////////////////////////////////////////////////
-
- /**
- * Binds the specified VBO if needed. If buffer == 0, binds default simple textured quad.
- */
- void bindMeshBuffer(GLuint buffer);
-
- /**
- * Unbinds the current VBO if active.
- */
- void unbindMeshBuffer();
-
- void genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size, const void* data, GLenum usage);
- void updateMeshBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data);
- void deleteMeshBuffer(GLuint);
-
- ///////////////////////////////////////////////////////////////////////////////
- // Vertices
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * Binds an attrib to the specified float vertex pointer.
- * Assumes a stride of gTextureVertexStride and a size of 2.
- */
- void bindPositionVertexPointer(const GLvoid* vertices, GLsizei stride = kTextureVertexStride);
-
- /**
- * Binds an attrib to the specified float vertex pointer.
- * Assumes a stride of gTextureVertexStride and a size of 2.
- */
- void bindTexCoordsVertexPointer(const GLvoid* vertices, GLsizei stride = kTextureVertexStride);
-
- /**
- * Resets the vertex pointers.
- */
- void resetVertexPointers();
-
- void enableTexCoordsVertexArray();
- void disableTexCoordsVertexArray();
-
- ///////////////////////////////////////////////////////////////////////////////
- // Indices
- ///////////////////////////////////////////////////////////////////////////////
- void bindIndicesBuffer(const GLuint buffer);
- void unbindIndicesBuffer();
-
- ///////////////////////////////////////////////////////////////////////////////
- // Getters - for use in Glop building
- ///////////////////////////////////////////////////////////////////////////////
- GLuint getUnitQuadVBO() { return mUnitQuadBuffer; }
- GLuint getQuadListIBO() { return mQuadListIndices; }
-
-private:
- MeshState();
-
- GLuint mUnitQuadBuffer;
-
- GLuint mCurrentBuffer;
- GLuint mCurrentIndicesBuffer;
- GLuint mCurrentPixelBuffer;
-
- const void* mCurrentPositionPointer;
- GLsizei mCurrentPositionStride;
- const void* mCurrentTexCoordsPointer;
- GLsizei mCurrentTexCoordsStride;
-
- bool mTexCoordsArrayEnabled;
-
- // Global index buffer
- GLuint mQuadListIndices;
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif // RENDERSTATE_MESHSTATE_H
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp
deleted file mode 100644
index a0f5cb9d4e09..000000000000
--- a/libs/hwui/renderstate/OffscreenBufferPool.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "OffscreenBufferPool.h"
-
-#include "Caches.h"
-#include "renderstate/RenderState.h"
-#include "utils/FatVector.h"
-#include "utils/TraceUtils.h"
-
-#include <utils/Color.h>
-#include <utils/Log.h>
-
-#include <GLES2/gl2.h>
-
-namespace android {
-namespace uirenderer {
-
-////////////////////////////////////////////////////////////////////////////////
-// OffscreenBuffer
-////////////////////////////////////////////////////////////////////////////////
-
-OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches, uint32_t viewportWidth,
- uint32_t viewportHeight, bool wideColorGamut)
- : GpuMemoryTracker(GpuObjectType::OffscreenBuffer)
- , renderState(renderState)
- , viewportWidth(viewportWidth)
- , viewportHeight(viewportHeight)
- , 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, 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
-}
-
-Rect OffscreenBuffer::getTextureCoordinates() {
- const float texX = 1.0f / static_cast<float>(texture.width());
- const float texY = 1.0f / static_cast<float>(texture.height());
- return Rect(0, viewportHeight * texY, viewportWidth * texX, 0);
-}
-
-void OffscreenBuffer::dirty(Rect dirtyArea) {
- dirtyArea.doIntersect(0, 0, viewportWidth, viewportHeight);
- if (!dirtyArea.isEmpty()) {
- region.orSelf(
- android::Rect(dirtyArea.left, dirtyArea.top, dirtyArea.right, dirtyArea.bottom));
- }
-}
-
-void OffscreenBuffer::updateMeshFromRegion() {
- // avoid T-junctions as they cause artifacts in between the resultant
- // geometry when complex transforms occur.
- // TODO: generate the safeRegion only if necessary based on drawing transform
- Region safeRegion = Region::createTJunctionFreeRegion(region);
-
- size_t count;
- const android::Rect* rects = safeRegion.getArray(&count);
-
- const float texX = 1.0f / float(texture.width());
- const float texY = 1.0f / float(texture.height());
-
- FatVector<TextureVertex, 64> meshVector(count *
- 4); // uses heap if more than 64 vertices needed
- TextureVertex* mesh = &meshVector[0];
- for (size_t i = 0; i < count; i++) {
- const android::Rect* r = &rects[i];
-
- const float u1 = r->left * texX;
- const float v1 = (viewportHeight - r->top) * texY;
- const float u2 = r->right * texX;
- const float v2 = (viewportHeight - r->bottom) * texY;
-
- TextureVertex::set(mesh++, r->left, r->top, u1, v1);
- TextureVertex::set(mesh++, r->right, r->top, u2, v1);
- TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
- TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
- }
- elementCount = count * 6;
- renderState.meshState().genOrUpdateMeshBuffer(
- &vbo, sizeof(TextureVertex) * count * 4, &meshVector[0],
- GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer
-}
-
-uint32_t OffscreenBuffer::computeIdealDimension(uint32_t dimension) {
- return uint32_t(ceilf(dimension / float(LAYER_SIZE)) * LAYER_SIZE);
-}
-
-OffscreenBuffer::~OffscreenBuffer() {
- ATRACE_FORMAT("Destroy %ux%u HW Layer", texture.width(), texture.height());
- texture.deleteTexture();
- renderState.meshState().deleteMeshBuffer(vbo);
- elementCount = 0;
- vbo = 0;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// OffscreenBufferPool
-///////////////////////////////////////////////////////////////////////////////
-
-OffscreenBufferPool::OffscreenBufferPool()
- // 4 screen-sized RGBA_8888 textures
- : mMaxSize(DeviceInfo::multiplyByResolution(4 * 4)) {}
-
-OffscreenBufferPool::~OffscreenBufferPool() {
- clear(); // TODO: unique_ptr?
-}
-
-int OffscreenBufferPool::Entry::compare(const Entry& lhs, const Entry& rhs) {
- int deltaInt = int(lhs.width) - int(rhs.width);
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = int(lhs.height) - int(rhs.height);
- if (deltaInt != 0) return deltaInt;
-
- return int(lhs.wideColorGamut) - int(rhs.wideColorGamut);
-}
-
-void OffscreenBufferPool::clear() {
- for (auto& entry : mPool) {
- delete entry.layer;
- }
- mPool.clear();
- mSize = 0;
-}
-
-OffscreenBuffer* OffscreenBufferPool::get(RenderState& renderState, const uint32_t width,
- const uint32_t height, bool wideColorGamut) {
- OffscreenBuffer* layer = nullptr;
-
- Entry entry(width, height, wideColorGamut);
- auto iter = mPool.find(entry);
-
- if (iter != mPool.end()) {
- entry = *iter;
- mPool.erase(iter);
-
- layer = entry.layer;
- layer->viewportWidth = width;
- layer->viewportHeight = height;
- mSize -= layer->getSizeInBytes();
- } else {
- layer = new OffscreenBuffer(renderState, Caches::getInstance(), width, height,
- wideColorGamut);
- }
-
- return layer;
-}
-
-OffscreenBuffer* OffscreenBufferPool::resize(OffscreenBuffer* layer, const uint32_t width,
- const uint32_t height) {
- RenderState& renderState = layer->renderState;
- if (layer->texture.width() == OffscreenBuffer::computeIdealDimension(width) &&
- layer->texture.height() == OffscreenBuffer::computeIdealDimension(height)) {
- // resize in place
- layer->viewportWidth = width;
- layer->viewportHeight = height;
-
- // entire area will be repainted (and may be smaller) so clear usage region
- layer->region.clear();
- return layer;
- }
- bool wideColorGamut = layer->wideColorGamut;
- putOrDelete(layer);
- return get(renderState, width, height, wideColorGamut);
-}
-
-void OffscreenBufferPool::dump() {
- for (auto entry : mPool) {
- ALOGD(" Layer size %dx%d", entry.width, entry.height);
- }
-}
-
-void OffscreenBufferPool::putOrDelete(OffscreenBuffer* layer) {
- const uint32_t size = layer->getSizeInBytes();
- // Don't even try to cache a layer that's bigger than the cache
- if (size < mMaxSize) {
- // TODO: Use an LRU
- while (mSize + size > mMaxSize) {
- OffscreenBuffer* victim = mPool.begin()->layer;
- mSize -= victim->getSizeInBytes();
- delete victim;
- mPool.erase(mPool.begin());
- }
-
- // clear region, since it's no longer valid
- layer->region.clear();
-
- Entry entry(layer);
-
- mPool.insert(entry);
- mSize += size;
- } else {
- delete layer;
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h
deleted file mode 100644
index 08ae052da391..000000000000
--- a/libs/hwui/renderstate/OffscreenBufferPool.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
-#define ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
-
-#include <GpuMemoryTracker.h>
-#include <ui/Region.h>
-#include "Caches.h"
-#include "Texture.h"
-#include "utils/Macros.h"
-
-#include <set>
-
-namespace android {
-namespace uirenderer {
-
-class RenderState;
-
-/**
- * Lightweight alternative to Layer. Owns the persistent state of an offscreen render target, and
- * encompasses enough information to draw it back on screen (minus paint properties, which are held
- * by LayerOp).
- *
- * Has two distinct sizes - viewportWidth/viewportHeight describe content area,
- * texture.width/.height are actual allocated texture size. Texture will tend to be larger than the
- * viewport bounds, since textures are always allocated with width / height as a multiple of 64, for
- * the purpose of improving reuse.
- */
-class OffscreenBuffer : GpuMemoryTracker {
-public:
- OffscreenBuffer(RenderState& renderState, Caches& caches, uint32_t viewportWidth,
- uint32_t viewportHeight, bool wideColorGamut = false);
- ~OffscreenBuffer();
-
- Rect getTextureCoordinates();
-
- void dirty(Rect dirtyArea);
-
- // must be called prior to rendering, to construct/update vertex buffer
- void updateMeshFromRegion();
-
- // Set by RenderNode for HW layers, TODO for clipped saveLayers
- void setWindowTransform(const Matrix4& transform) {
- inverseTransformInWindow.loadInverse(transform);
- }
-
- static uint32_t computeIdealDimension(uint32_t dimension);
-
- uint32_t getSizeInBytes() { return texture.objectSize(); }
-
- RenderState& renderState;
-
- uint32_t viewportWidth;
- 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;
-
- Matrix4 inverseTransformInWindow;
-
- // vbo / size of mesh
- GLsizei elementCount = 0;
- GLuint vbo = 0;
-
- bool hasRenderedSinceRepaint;
-};
-
-/**
- * Pool of OffscreenBuffers allocated, but not currently in use.
- */
-class OffscreenBufferPool {
-public:
- OffscreenBufferPool();
- ~OffscreenBufferPool();
-
- WARN_UNUSED_RESULT OffscreenBuffer* get(RenderState& renderState, 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);
-
- void putOrDelete(OffscreenBuffer* layer);
-
- /**
- * Clears the pool. This causes all layers to be deleted.
- */
- void clear();
-
- /**
- * Returns the maximum size of the pool in bytes.
- */
- uint32_t getMaxSize() { return mMaxSize; }
-
- /**
- * Returns the current size of the pool in bytes.
- */
- uint32_t getSize() { return mSize; }
-
- size_t getCount() { return mPool.size(); }
-
- /**
- * Prints out the content of the pool.
- */
- void dump();
-
-private:
- struct Entry {
- Entry() {}
-
- Entry(const uint32_t layerWidth, const uint32_t layerHeight, bool wideColorGamut)
- : width(OffscreenBuffer::computeIdealDimension(layerWidth))
- , height(OffscreenBuffer::computeIdealDimension(layerHeight))
- , wideColorGamut(wideColorGamut) {}
-
- explicit Entry(OffscreenBuffer* layer)
- : layer(layer)
- , width(layer->texture.width())
- , height(layer->texture.height())
- , wideColorGamut(layer->wideColorGamut) {}
-
- static int compare(const Entry& lhs, const Entry& rhs);
-
- bool operator==(const Entry& other) const { return compare(*this, other) == 0; }
-
- bool operator!=(const Entry& other) const { return compare(*this, other) != 0; }
-
- bool operator<(const Entry& other) const { return Entry::compare(*this, other) < 0; }
-
- OffscreenBuffer* layer = nullptr;
- uint32_t width = 0;
- uint32_t height = 0;
- bool wideColorGamut = false;
- }; // struct Entry
-
- std::multiset<Entry> mPool;
-
- uint32_t mSize = 0;
- uint32_t mMaxSize;
-}; // class OffscreenBufferCache
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp
deleted file mode 100644
index 3a6efb833c47..000000000000
--- a/libs/hwui/renderstate/PixelBufferState.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "renderstate/PixelBufferState.h"
-
-namespace android {
-namespace uirenderer {
-
-PixelBufferState::PixelBufferState() : mCurrentPixelBuffer(0) {}
-
-bool PixelBufferState::bind(GLuint buffer) {
- if (mCurrentPixelBuffer != buffer) {
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
- mCurrentPixelBuffer = buffer;
- return true;
- }
- return false;
-}
-
-bool PixelBufferState::unbind() {
- if (mCurrentPixelBuffer) {
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
- mCurrentPixelBuffer = 0;
- return true;
- }
- return false;
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 5e33353c3ac6..fad9440be73f 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -14,220 +14,26 @@
* limitations under the License.
*/
#include "renderstate/RenderState.h"
-#include <GpuMemoryTracker.h>
-#include "DeferredLayerUpdater.h"
-#include "GlLayer.h"
-#include "VkLayer.h"
-#include "renderthread/CanvasContext.h"
-#include "renderthread/EglManager.h"
-#include "utils/GLUtils.h"
-
-#include <algorithm>
-
-#include <ui/ColorSpace.h>
+#include "renderthread/RenderThread.h"
+#include "GpuMemoryTracker.h"
namespace android {
namespace uirenderer {
-RenderState::RenderState(renderthread::RenderThread& thread)
- : mRenderThread(thread), mViewportWidth(0), mViewportHeight(0), mFramebuffer(0) {
+RenderState::RenderState(renderthread::RenderThread& thread) : mRenderThread(thread) {
mThreadId = pthread_self();
}
-RenderState::~RenderState() {
- LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
- "State object lifecycle not managed correctly");
-}
-
-void RenderState::onGLContextCreated() {
- LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
- "State object lifecycle not managed correctly");
- GpuMemoryTracker::onGpuContextCreated();
-
- mBlend = new Blend();
- mMeshState = new MeshState();
- mScissor = new Scissor();
- mStencil = new Stencil();
-
- // Deferred because creation needs GL context for texture limits
- if (!mLayerPool) {
- mLayerPool = new OffscreenBufferPool();
- }
-
- // This is delayed because the first access of Caches makes GL calls
- if (!mCaches) {
- mCaches = &Caches::createInstance(*this);
- }
- mCaches->init();
-}
-
-static void layerLostGlContext(Layer* layer) {
- LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL,
- "layerLostGlContext on non GL layer");
- static_cast<GlLayer*>(layer)->onGlContextLost();
-}
-
-void RenderState::onGLContextDestroyed() {
- mLayerPool->clear();
-
- // TODO: reset all cached state in state objects
- std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext);
-
- mCaches->terminate();
-
- delete mBlend;
- mBlend = nullptr;
- delete mMeshState;
- mMeshState = nullptr;
- delete mScissor;
- mScissor = nullptr;
- delete mStencil;
- mStencil = nullptr;
-
- destroyLayersInUpdater();
- GpuMemoryTracker::onGpuContextDestroyed();
-}
-
-void RenderState::onVkContextCreated() {
- LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
- "State object lifecycle not managed correctly");
+void RenderState::onContextCreated() {
GpuMemoryTracker::onGpuContextCreated();
}
-static void layerDestroyedVkContext(Layer* layer) {
- LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::Vulkan,
- "layerLostVkContext on non Vulkan layer");
- static_cast<VkLayer*>(layer)->onVkContextDestroyed();
-}
-
-void RenderState::onVkContextDestroyed() {
- std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext);
- destroyLayersInUpdater();
- GpuMemoryTracker::onGpuContextDestroyed();
-}
-
-GrContext* RenderState::getGrContext() const {
- return mRenderThread.getGrContext();
-}
-
-void RenderState::flush(Caches::FlushMode mode) {
- switch (mode) {
- case Caches::FlushMode::Full:
- // fall through
- case Caches::FlushMode::Moderate:
- // fall through
- case Caches::FlushMode::Layers:
- if (mLayerPool) mLayerPool->clear();
- break;
- }
- if (mCaches) mCaches->flush(mode);
-}
-
-void RenderState::onBitmapDestroyed(uint32_t pixelRefId) {
- if (mCaches && mCaches->textureCache.destroyTexture(pixelRefId)) {
- glFlush();
- GL_CHECKPOINT(MODERATE);
- }
-}
-
-void RenderState::setViewport(GLsizei width, GLsizei height) {
- mViewportWidth = width;
- mViewportHeight = height;
- glViewport(0, 0, mViewportWidth, mViewportHeight);
-}
-
-void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) {
- *outWidth = mViewportWidth;
- *outHeight = mViewportHeight;
-}
-
-void RenderState::bindFramebuffer(GLuint fbo) {
- if (mFramebuffer != fbo) {
- mFramebuffer = fbo;
- glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
- }
-}
-
-GLuint RenderState::createFramebuffer() {
- GLuint ret;
- glGenFramebuffers(1, &ret);
- return ret;
-}
-
-void RenderState::deleteFramebuffer(GLuint fbo) {
- if (mFramebuffer == fbo) {
- // GL defines that deleting the currently bound FBO rebinds FBO 0.
- // Reflect this in our cached value.
- mFramebuffer = 0;
- }
- glDeleteFramebuffers(1, &fbo);
-}
-
-void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) {
- if (mode == DrawGlInfo::kModeProcessNoContext) {
- // If there's no context we don't need to interrupt as there's
- // no gl state to save/restore
- (*functor)(mode, info);
- } else {
- interruptForFunctorInvoke();
- (*functor)(mode, info);
- resumeFromFunctorInvoke();
- }
-}
-
-void RenderState::interruptForFunctorInvoke() {
- mCaches->setProgram(nullptr);
- mCaches->textureState().resetActiveTexture();
- meshState().unbindMeshBuffer();
- meshState().unbindIndicesBuffer();
- meshState().resetVertexPointers();
- meshState().disableTexCoordsVertexArray();
- debugOverdraw(false, false);
- // TODO: We need a way to know whether the functor is sRGB aware (b/32072673)
- if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) {
- glDisable(GL_FRAMEBUFFER_SRGB_EXT);
- }
-}
-
-void RenderState::resumeFromFunctorInvoke() {
- if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) {
- glEnable(GL_FRAMEBUFFER_SRGB_EXT);
- }
-
- glViewport(0, 0, mViewportWidth, mViewportHeight);
- glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
- debugOverdraw(false, false);
-
- glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
- scissor().invalidate();
- blend().invalidate();
-
- mCaches->textureState().activateTexture(0);
- mCaches->textureState().resetBoundTextures();
-}
-
-void RenderState::debugOverdraw(bool enable, bool clear) {
- if (Properties::debugOverdraw && mFramebuffer == 0) {
- if (clear) {
- scissor().setEnabled(false);
- stencil().clear();
- }
- if (enable) {
- stencil().enableDebugWrite();
- } else {
- stencil().disable();
- }
+void RenderState::onContextDestroyed() {
+ for(auto callback : mContextCallbacks) {
+ callback->onContextDestroyed();
}
-}
-
-static void destroyLayerInUpdater(DeferredLayerUpdater* layerUpdater) {
- layerUpdater->destroyLayer();
-}
-
-void RenderState::destroyLayersInUpdater() {
- std::for_each(mActiveLayerUpdaters.begin(), mActiveLayerUpdaters.end(), destroyLayerInUpdater);
+ GpuMemoryTracker::onGpuContextDestroyed();
}
void RenderState::postDecStrong(VirtualLightRefBase* object) {
@@ -242,230 +48,5 @@ void RenderState::postDecStrong(VirtualLightRefBase* object) {
// Render
///////////////////////////////////////////////////////////////////////////////
-void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix,
- bool overrideDisableBlending) {
- const Glop::Mesh& mesh = glop.mesh;
- const Glop::Mesh::Vertices& vertices = mesh.vertices;
- const Glop::Mesh::Indices& indices = mesh.indices;
- const Glop::Fill& fill = glop.fill;
-
- GL_CHECKPOINT(MODERATE);
-
- // ---------------------------------------------
- // ---------- Program + uniform setup ----------
- // ---------------------------------------------
- mCaches->setProgram(fill.program);
-
- if (fill.colorEnabled) {
- fill.program->setColor(fill.color);
- }
-
- fill.program->set(orthoMatrix, glop.transform.modelView, glop.transform.meshTransform(),
- glop.transform.transformFlags & TransformFlags::OffsetByFudgeFactor);
-
- // Color filter uniforms
- if (fill.filterMode == ProgramDescription::ColorFilterMode::Blend) {
- const FloatColor& color = fill.filter.color;
- glUniform4f(mCaches->program().getUniform("colorBlend"), color.r, color.g, color.b,
- color.a);
- } else if (fill.filterMode == ProgramDescription::ColorFilterMode::Matrix) {
- glUniformMatrix4fv(mCaches->program().getUniform("colorMatrix"), 1, GL_FALSE,
- fill.filter.matrix.matrix);
- glUniform4fv(mCaches->program().getUniform("colorMatrixVector"), 1,
- fill.filter.matrix.vector);
- }
-
- // Round rect clipping uniforms
- if (glop.roundRectClipState) {
- // TODO: avoid query, and cache values (or RRCS ptr) in program
- const RoundRectClipState* state = glop.roundRectClipState;
- const Rect& innerRect = state->innerRect;
-
- // 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);
- }
-
- GL_CHECKPOINT(MODERATE);
-
- // --------------------------------
- // ---------- Mesh setup ----------
- // --------------------------------
- // vertices
- meshState().bindMeshBuffer(vertices.bufferObject);
- meshState().bindPositionVertexPointer(vertices.position, vertices.stride);
-
- // indices
- meshState().bindIndicesBuffer(indices.bufferObject);
-
- // texture
- if (fill.texture.texture != nullptr) {
- const Glop::Fill::TextureData& texture = fill.texture;
- // texture always takes slot 0, shader samplers increment from there
- mCaches->textureState().activateTexture(0);
-
- mCaches->textureState().bindTexture(texture.texture->target(), texture.texture->id());
- if (texture.clamp != GL_INVALID_ENUM) {
- texture.texture->setWrap(texture.clamp, false, false);
- }
- if (texture.filter != GL_INVALID_ENUM) {
- texture.texture->setFilter(texture.filter, false, false);
- }
-
- if (texture.textureTransform) {
- glUniformMatrix4fv(fill.program->getUniform("mainTextureTransform"), 1, GL_FALSE,
- &texture.textureTransform->data[0]);
- }
- }
-
- // vertex attributes (tex coord, color, alpha)
- if (vertices.attribFlags & VertexAttribFlags::TextureCoord) {
- meshState().enableTexCoordsVertexArray();
- meshState().bindTexCoordsVertexPointer(vertices.texCoord, vertices.stride);
- } else {
- meshState().disableTexCoordsVertexArray();
- }
- int colorLocation = -1;
- if (vertices.attribFlags & VertexAttribFlags::Color) {
- colorLocation = fill.program->getAttrib("colors");
- glEnableVertexAttribArray(colorLocation);
- glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, vertices.stride,
- vertices.color);
- }
- int alphaLocation = -1;
- if (vertices.attribFlags & VertexAttribFlags::Alpha) {
- // NOTE: alpha vertex position is computed assuming no VBO
- const void* alphaCoords = ((const GLbyte*)vertices.position) + kVertexAlphaOffset;
- alphaLocation = fill.program->getAttrib("vtxAlpha");
- glEnableVertexAttribArray(alphaLocation);
- glVertexAttribPointer(alphaLocation, 1, GL_FLOAT, GL_FALSE, vertices.stride, alphaCoords);
- }
- // Shader uniforms
- SkiaShader::apply(*mCaches, fill.skiaShaderData, mViewportWidth, mViewportHeight);
-
- GL_CHECKPOINT(MODERATE);
- Texture* texture = (fill.skiaShaderData.skiaShaderType & kBitmap_SkiaShaderType)
- ? fill.skiaShaderData.bitmapData.bitmapTexture
- : nullptr;
- const AutoTexture autoCleanup(texture);
-
- // If we have a shader and a base texture, the base texture is assumed to be an alpha mask
- // which means the color space conversion applies to the shader's bitmap
- Texture* colorSpaceTexture = texture != nullptr ? texture : fill.texture.texture;
- if (colorSpaceTexture != nullptr) {
- if (colorSpaceTexture->hasColorSpaceConversion()) {
- const ColorSpaceConnector* connector = colorSpaceTexture->getColorSpaceConnector();
- glUniformMatrix3fv(fill.program->getUniform("colorSpaceMatrix"), 1, GL_FALSE,
- connector->getTransform().asArray());
- }
-
- TransferFunctionType transferFunction = colorSpaceTexture->getTransferFunctionType();
- if (transferFunction != TransferFunctionType::None) {
- const ColorSpaceConnector* connector = colorSpaceTexture->getColorSpaceConnector();
- const ColorSpace& source = connector->getSource();
-
- switch (transferFunction) {
- case TransferFunctionType::None:
- break;
- case TransferFunctionType::Full:
- glUniform1fv(fill.program->getUniform("transferFunction"), 7,
- reinterpret_cast<const float*>(&source.getTransferParameters().g));
- break;
- case TransferFunctionType::Limited:
- glUniform1fv(fill.program->getUniform("transferFunction"), 5,
- reinterpret_cast<const float*>(&source.getTransferParameters().g));
- break;
- case TransferFunctionType::Gamma:
- glUniform1f(fill.program->getUniform("transferFunctionGamma"),
- source.getTransferParameters().g);
- break;
- }
- }
- }
-
- // ------------------------------------
- // ---------- GL state setup ----------
- // ------------------------------------
- if (CC_UNLIKELY(overrideDisableBlending)) {
- blend().setFactors(GL_ZERO, GL_ZERO);
- } else {
- blend().setFactors(glop.blend.src, glop.blend.dst);
- }
-
- GL_CHECKPOINT(MODERATE);
-
- // ------------------------------------
- // ---------- Actual drawing ----------
- // ------------------------------------
- if (indices.bufferObject == meshState().getQuadListIBO()) {
- // Since the indexed quad list is of limited length, we loop over
- // the glDrawXXX method while updating the vertex pointer
- GLsizei elementsCount = mesh.elementCount;
- 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);
- }
-
- 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 += vertexCount * vertices.stride;
- }
- } else if (indices.bufferObject || 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);
- }
-
- GL_CHECKPOINT(MODERATE);
-
- // -----------------------------------
- // ---------- Mesh teardown ----------
- // -----------------------------------
- if (vertices.attribFlags & VertexAttribFlags::Alpha) {
- glDisableVertexAttribArray(alphaLocation);
- }
- if (vertices.attribFlags & VertexAttribFlags::Color) {
- glDisableVertexAttribArray(colorLocation);
- }
-
- GL_CHECKPOINT(MODERATE);
-}
-
-void RenderState::dump() {
- blend().dump();
- meshState().dump();
- scissor().dump();
- stencil().dump();
-}
-
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index e033cf2c5046..ff5d02fe359a 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -16,132 +16,59 @@
#ifndef RENDERSTATE_H
#define RENDERSTATE_H
-#include "Caches.h"
-#include "Glop.h"
-#include "renderstate/Blend.h"
-#include "renderstate/MeshState.h"
-#include "renderstate/OffscreenBufferPool.h"
-#include "renderstate/PixelBufferState.h"
-#include "renderstate/Scissor.h"
-#include "renderstate/Stencil.h"
#include "utils/Macros.h"
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <private/hwui/DrawGlInfo.h>
-#include <ui/Region.h>
-#include <utils/Functor.h>
-#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <set>
-class GrContext;
-
namespace android {
namespace uirenderer {
-class Caches;
class Layer;
-class DeferredLayerUpdater;
namespace renderthread {
class CacheManager;
-class CanvasContext;
class RenderThread;
}
-// TODO: Replace Cache's GL state tracking with this. For now it's more a thin
+class IGpuContextCallback {
+public:
+ virtual void onContextDestroyed() = 0;
+protected:
+ virtual ~IGpuContextCallback() {}
+};
+
// wrapper of Caches for users to migrate to.
class RenderState {
PREVENT_COPY_AND_ASSIGN(RenderState);
friend class renderthread::RenderThread;
- friend class Caches;
friend class renderthread::CacheManager;
public:
- void onGLContextCreated();
- void onGLContextDestroyed();
-
- void onVkContextCreated();
- void onVkContextDestroyed();
-
- void flush(Caches::FlushMode flushMode);
- void onBitmapDestroyed(uint32_t pixelRefId);
-
- void setViewport(GLsizei width, GLsizei height);
- void getViewport(GLsizei* outWidth, GLsizei* outHeight);
-
- void bindFramebuffer(GLuint fbo);
- GLuint getFramebuffer() { return mFramebuffer; }
- GLuint createFramebuffer();
- void deleteFramebuffer(GLuint fbo);
-
- void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info);
-
- void debugOverdraw(bool enable, bool clear);
+ void registerContextCallback(IGpuContextCallback* cb) { mContextCallbacks.insert(cb); }
+ void removeContextCallback(IGpuContextCallback* cb) { mContextCallbacks.erase(cb); }
void registerLayer(Layer* layer) { mActiveLayers.insert(layer); }
void unregisterLayer(Layer* layer) { mActiveLayers.erase(layer); }
- void registerCanvasContext(renderthread::CanvasContext* context) {
- mRegisteredContexts.insert(context);
- }
-
- void unregisterCanvasContext(renderthread::CanvasContext* context) {
- mRegisteredContexts.erase(context);
- }
-
- void registerDeferredLayerUpdater(DeferredLayerUpdater* layerUpdater) {
- mActiveLayerUpdaters.insert(layerUpdater);
- }
-
- void unregisterDeferredLayerUpdater(DeferredLayerUpdater* layerUpdater) {
- mActiveLayerUpdaters.erase(layerUpdater);
- }
-
// TODO: This system is a little clunky feeling, this could use some
// more thinking...
void postDecStrong(VirtualLightRefBase* object);
- void render(const Glop& glop, const Matrix4& orthoMatrix, bool overrideDisableBlending);
-
- Blend& blend() { return *mBlend; }
- MeshState& meshState() { return *mMeshState; }
- Scissor& scissor() { return *mScissor; }
- Stencil& stencil() { return *mStencil; }
-
- OffscreenBufferPool& layerPool() { return *mLayerPool; }
-
- GrContext* getGrContext() const;
-
- void dump();
+ renderthread::RenderThread& getRenderThread() const { return mRenderThread; }
private:
- void interruptForFunctorInvoke();
- void resumeFromFunctorInvoke();
- void destroyLayersInUpdater();
-
explicit RenderState(renderthread::RenderThread& thread);
- ~RenderState();
-
- renderthread::RenderThread& mRenderThread;
- Caches* mCaches = nullptr;
+ ~RenderState() {}
- Blend* mBlend = nullptr;
- MeshState* mMeshState = nullptr;
- Scissor* mScissor = nullptr;
- Stencil* mStencil = nullptr;
-
- OffscreenBufferPool* mLayerPool = nullptr;
+ // Context notifications are only to be triggered by renderthread::RenderThread
+ void onContextCreated();
+ void onContextDestroyed();
+ std::set<IGpuContextCallback*> mContextCallbacks;
std::set<Layer*> mActiveLayers;
- std::set<DeferredLayerUpdater*> mActiveLayerUpdaters;
- std::set<renderthread::CanvasContext*> mRegisteredContexts;
-
- GLsizei mViewportWidth;
- GLsizei mViewportHeight;
- GLuint mFramebuffer;
+ renderthread::RenderThread& mRenderThread;
pthread_t mThreadId;
};
diff --git a/libs/hwui/renderstate/Scissor.cpp b/libs/hwui/renderstate/Scissor.cpp
deleted file mode 100644
index e37ed029542b..000000000000
--- a/libs/hwui/renderstate/Scissor.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "renderstate/Scissor.h"
-
-#include "Rect.h"
-
-#include <utils/Log.h>
-
-namespace android {
-namespace uirenderer {
-
-Scissor::Scissor()
- : mEnabled(false), mScissorX(0), mScissorY(0), mScissorWidth(0), mScissorHeight(0) {}
-
-bool Scissor::setEnabled(bool enabled) {
- if (mEnabled != enabled) {
- if (enabled) {
- glEnable(GL_SCISSOR_TEST);
- } else {
- glDisable(GL_SCISSOR_TEST);
- }
- mEnabled = enabled;
- return true;
- }
- return false;
-}
-
-bool Scissor::set(GLint x, GLint y, GLint width, GLint height) {
- if (mEnabled &&
- (x != mScissorX || y != mScissorY || width != mScissorWidth || height != mScissorHeight)) {
- if (x < 0) {
- width += x;
- x = 0;
- }
- if (y < 0) {
- height += y;
- y = 0;
- }
- if (width < 0) {
- width = 0;
- }
- if (height < 0) {
- height = 0;
- }
- glScissor(x, y, width, height);
-
- mScissorX = x;
- mScissorY = y;
- mScissorWidth = width;
- mScissorHeight = height;
-
- return true;
- }
- return false;
-}
-
-void Scissor::set(int viewportHeight, const Rect& clip) {
- // transform to Y-flipped GL space, and prevent negatives
- GLint x = std::max(0, (int)clip.left);
- GLint y = std::max(0, viewportHeight - (int)clip.bottom);
- GLint width = std::max(0, ((int)clip.right) - x);
- GLint height = std::max(0, (viewportHeight - (int)clip.top) - y);
-
- if (x != mScissorX || y != mScissorY || width != mScissorWidth || height != mScissorHeight) {
- glScissor(x, y, width, height);
-
- mScissorX = x;
- mScissorY = y;
- mScissorWidth = width;
- mScissorHeight = height;
- }
-}
-
-void Scissor::reset() {
- mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
-}
-
-void Scissor::invalidate() {
- mEnabled = glIsEnabled(GL_SCISSOR_TEST);
- setEnabled(true);
- reset();
-}
-
-void Scissor::dump() {
- ALOGD("Scissor: enabled %d, %d %d %d %d", mEnabled, mScissorX, mScissorY, mScissorWidth,
- mScissorHeight);
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderstate/Scissor.h b/libs/hwui/renderstate/Scissor.h
deleted file mode 100644
index 2b04f4e1384a..000000000000
--- a/libs/hwui/renderstate/Scissor.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef RENDERSTATE_SCISSOR_H
-#define RENDERSTATE_SCISSOR_H
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-namespace android {
-namespace uirenderer {
-
-class Rect;
-
-class Scissor {
- friend class RenderState;
-
-public:
- bool setEnabled(bool enabled);
- bool set(GLint x, GLint y, GLint width, GLint height);
- void set(int viewportHeight, const Rect& clip);
- void reset();
- bool isEnabled() { return mEnabled; }
- void dump();
-
-private:
- Scissor();
- void invalidate();
- bool mEnabled;
- GLint mScissorX;
- GLint mScissorY;
- GLint mScissorWidth;
- GLint mScissorHeight;
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif // RENDERSTATE_SCISSOR_H
diff --git a/libs/hwui/renderstate/Stencil.cpp b/libs/hwui/renderstate/Stencil.cpp
deleted file mode 100644
index dc465fc7a6f8..000000000000
--- a/libs/hwui/renderstate/Stencil.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "renderstate/Stencil.h"
-
-#include "Caches.h"
-#include "Debug.h"
-#include "Extensions.h"
-#include "Properties.h"
-
-#include <GLES2/gl2ext.h>
-
-namespace android {
-namespace uirenderer {
-
-#if DEBUG_STENCIL
-#define STENCIL_WRITE_VALUE 0xff
-#define STENCIL_MASK_VALUE 0xff
-#else
-#define STENCIL_WRITE_VALUE 0x1
-#define STENCIL_MASK_VALUE 0x1
-#endif
-
-uint8_t Stencil::getStencilSize() {
- return STENCIL_BUFFER_SIZE;
-}
-
-/**
- * This method will return either GL_STENCIL_INDEX4_OES if supported,
- * GL_STENCIL_INDEX8 if not.
- *
- * Layers can't use a single bit stencil because multi-rect ClipArea needs a high enough
- * stencil resolution to represent the summation of multiple intersecting rect geometries.
- */
-GLenum Stencil::getLayerStencilFormat() {
-#if !DEBUG_STENCIL
- const Extensions& extensions = DeviceInfo::get()->extensions();
- if (extensions.has4BitStencil()) {
- return GL_STENCIL_INDEX4_OES;
- }
-#endif
- return GL_STENCIL_INDEX8;
-}
-
-void Stencil::clear() {
- glStencilMask(0xff);
- glClearStencil(0);
- glClear(GL_STENCIL_BUFFER_BIT);
-
- if (mState == StencilState::Test) {
- // reset to test state, with immutable stencil
- glStencilMask(0);
- }
-}
-
-void Stencil::enableTest(int incrementThreshold) {
- if (mState != StencilState::Test) {
- enable();
- if (incrementThreshold > 0) {
- glStencilFunc(GL_EQUAL, incrementThreshold, 0xff);
- } else {
- glStencilFunc(GL_EQUAL, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE);
- }
- // We only want to test, let's keep everything
- glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- glStencilMask(0);
- mState = StencilState::Test;
- }
-}
-
-void Stencil::enableWrite(int incrementThreshold) {
- if (mState != StencilState::Write) {
- enable();
- if (incrementThreshold > 0) {
- glStencilFunc(GL_ALWAYS, 1, 0xff);
- // The test always passes so the first two values are meaningless
- glStencilOp(GL_INCR, GL_INCR, GL_INCR);
- } else {
- glStencilFunc(GL_ALWAYS, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE);
- // The test always passes so the first two values are meaningless
- glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
- }
- glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
- glStencilMask(0xff);
- mState = StencilState::Write;
- }
-}
-
-void Stencil::enableDebugTest(GLint value, bool greater) {
- enable();
- glStencilFunc(greater ? GL_LESS : GL_EQUAL, value, 0xffffffff);
- // We only want to test, let's keep everything
- glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
- mState = StencilState::Test;
- glStencilMask(0);
-}
-
-void Stencil::enableDebugWrite() {
- enable();
- glStencilFunc(GL_ALWAYS, 0x1, 0xffffffff);
- // The test always passes so the first two values are meaningless
- glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
- glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- mState = StencilState::Write;
- glStencilMask(0xff);
-}
-
-void Stencil::enable() {
- if (mState == StencilState::Disabled) {
- glEnable(GL_STENCIL_TEST);
- }
-}
-
-void Stencil::disable() {
- if (mState != StencilState::Disabled) {
- glDisable(GL_STENCIL_TEST);
- mState = StencilState::Disabled;
- }
-}
-
-void Stencil::dump() {
- ALOGD("Stencil: state %d", mState);
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/renderstate/Stencil.h b/libs/hwui/renderstate/Stencil.h
deleted file mode 100644
index 95f372344ee4..000000000000
--- a/libs/hwui/renderstate/Stencil.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_STENCIL_H
-#define ANDROID_HWUI_STENCIL_H
-
-#include <GLES2/gl2.h>
-
-#include <cutils/compiler.h>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Stencil buffer management
-///////////////////////////////////////////////////////////////////////////////
-
-class ANDROID_API Stencil {
-public:
- /**
- * Returns the desired size for the stencil buffer. If the returned value
- * is 0, then no stencil buffer is required.
- */
- ANDROID_API static uint8_t getStencilSize();
-
- static GLenum getLayerStencilFormat();
-
- /**
- * Clears the stencil buffer.
- */
- void clear();
-
- /**
- * Enables stencil test. When the stencil test is enabled the stencil buffer is not written
- * into. An increment threshold of zero causes the stencil to use a constant reference value
- * and GL_EQUAL for the test. A non-zero increment threshold causes the stencil to use that
- * value as the reference value and GL_EQUAL for the test.
- */
- void enableTest(int incrementThreshold);
-
- /**
- * Enables stencil write. When stencil write is enabled, the stencil
- * test always succeeds and the value 0x1 is written in the stencil
- * buffer for each fragment. An increment threshold of zero causes the stencil to use a constant
- * reference value and GL_EQUAL for the test. A non-zero increment threshold causes the stencil
- * to use that value as the reference value and GL_EQUAL for the test.
- */
- void enableWrite(int incrementThreshold);
-
- /**
- * The test passes only when equal to the specified value.
- */
- void enableDebugTest(GLint value, bool greater = false);
-
- /**
- * Used for debugging. The stencil test always passes and increments.
- */
- void enableDebugWrite();
-
- /**
- * Disables stencil test and write.
- */
- void disable();
-
- /**
- * Indicates whether either test or write is enabled.
- */
- bool isEnabled() { return mState != StencilState::Disabled; }
-
- /**
- * Indicates whether testing only is enabled.
- */
- bool isTestEnabled() { return mState == StencilState::Test; }
-
- bool isWriteEnabled() { return mState == StencilState::Write; }
-
- void dump();
-
-private:
- enum class StencilState { Disabled, Test, Write };
-
- void enable();
- StencilState mState = StencilState::Disabled;
-
-}; // class Stencil
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_STENCIL_H
diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp
deleted file mode 100644
index 470b4f5de97f..000000000000
--- a/libs/hwui/renderstate/TextureState.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "renderstate/TextureState.h"
-
-#include "Caches.h"
-#include "utils/TraceUtils.h"
-
-#include <GLES3/gl3.h>
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <memory>
-
-namespace android {
-namespace uirenderer {
-
-// Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is
-static const int SHADOW_LUT_SIZE = 128;
-
-// Must define as many texture units as specified by kTextureUnitsCount
-const GLenum kTextureUnits[] = {GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_TEXTURE3};
-
-TextureState::TextureState() : mTextureUnit(0) {
- glActiveTexture(kTextureUnits[0]);
- resetBoundTextures();
-
- GLint maxTextureUnits;
- glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
- LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount,
- "At least %d texture units are required!", kTextureUnitsCount);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-}
-
-TextureState::~TextureState() {
- if (mShadowLutTexture != nullptr) {
- mShadowLutTexture->deleteTexture();
- }
-}
-
-/**
- * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
- * darkness at that spot. Input values of 0->1 should be mapped within the same
- * range, but can affect the curve for a different visual falloff.
- *
- * This is used to populate the shadow LUT texture for quick lookup in the
- * shadow shader.
- */
-static float computeShadowOpacity(float ratio) {
- // exponential falloff function provided by UX
- float val = 1 - ratio;
- return exp(-val * val * 4.0) - 0.018;
-}
-
-void TextureState::constructTexture(Caches& caches) {
- if (mShadowLutTexture == nullptr) {
- mShadowLutTexture.reset(new Texture(caches));
-
- unsigned char bytes[SHADOW_LUT_SIZE];
- for (int i = 0; i < SHADOW_LUT_SIZE; i++) {
- float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f);
- bytes[i] = computeShadowOpacity(inputRatio) * 255;
- }
- mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes);
- mShadowLutTexture->setFilter(GL_LINEAR);
- mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE);
- }
-}
-
-void TextureState::activateTexture(GLuint textureUnit) {
- LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount,
- "Tried to use texture unit index %d, only %d exist", textureUnit,
- kTextureUnitsCount);
- if (mTextureUnit != textureUnit) {
- glActiveTexture(kTextureUnits[textureUnit]);
- mTextureUnit = textureUnit;
- }
-}
-
-void TextureState::resetActiveTexture() {
- mTextureUnit = -1;
-}
-
-void TextureState::bindTexture(GLuint texture) {
- if (mBoundTextures[mTextureUnit] != texture) {
- glBindTexture(GL_TEXTURE_2D, texture);
- mBoundTextures[mTextureUnit] = texture;
- }
-}
-
-void TextureState::bindTexture(GLenum target, GLuint texture) {
- if (target == GL_TEXTURE_2D) {
- bindTexture(texture);
- } else {
- // GLConsumer directly calls glBindTexture() with
- // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target
- // since the cached state could be stale
- glBindTexture(target, texture);
- }
-}
-
-void TextureState::deleteTexture(GLuint texture) {
- // When glDeleteTextures() is called on a currently bound texture,
- // OpenGL ES specifies that the texture is then considered unbound
- // Consider the following series of calls:
- //
- // glGenTextures -> creates texture name 2
- // glBindTexture(2)
- // glDeleteTextures(2) -> 2 is now unbound
- // glGenTextures -> can return 2 again
- //
- // If we don't call glBindTexture(2) after the second glGenTextures
- // call, any texture operation will be performed on the default
- // texture (name=0)
-
- unbindTexture(texture);
-
- glDeleteTextures(1, &texture);
-}
-
-void TextureState::resetBoundTextures() {
- for (int i = 0; i < kTextureUnitsCount; i++) {
- mBoundTextures[i] = 0;
- }
-}
-
-void TextureState::unbindTexture(GLuint texture) {
- for (int i = 0; i < kTextureUnitsCount; i++) {
- if (mBoundTextures[i] == texture) {
- mBoundTextures[i] = 0;
- }
- }
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h
deleted file mode 100644
index f1996d431fa2..000000000000
--- a/libs/hwui/renderstate/TextureState.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef RENDERSTATE_TEXTURESTATE_H
-#define RENDERSTATE_TEXTURESTATE_H
-
-#include "Texture.h"
-#include "Vertex.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <memory>
-
-namespace android {
-namespace uirenderer {
-
-class Texture;
-
-class TextureState {
- friend class Caches; // TODO: move to RenderState
-public:
- void constructTexture(Caches& caches);
-
- /**
- * Activate the specified texture unit. The texture unit must
- * be specified using an integer number (0 for GL_TEXTURE0 etc.)
- */
- void activateTexture(GLuint textureUnit);
-
- /**
- * Invalidate the cached value of the active texture unit.
- */
- void resetActiveTexture();
-
- /**
- * Binds the specified texture as a GL_TEXTURE_2D texture.
- * All texture bindings must be performed with this method or
- * bindTexture(GLenum, GLuint).
- */
- void bindTexture(GLuint texture);
-
- /**
- * Binds the specified texture with the specified render target.
- * All texture bindings must be performed with this method or
- * bindTexture(GLuint).
- */
- void bindTexture(GLenum target, GLuint texture);
-
- /**
- * Deletes the specified texture and clears it from the cache
- * of bound textures.
- * All textures must be deleted using this method.
- */
- void deleteTexture(GLuint texture);
-
- /**
- * Signals that the cache of bound textures should be cleared.
- * Other users of the context may have altered which textures are bound.
- */
- void resetBoundTextures();
-
- /**
- * Clear the cache of bound textures.
- */
- void unbindTexture(GLuint texture);
-
- Texture* getShadowLutTexture() { return mShadowLutTexture.get(); }
-
-private:
- // total number of texture units available for use
- static const int kTextureUnitsCount = 4;
-
- TextureState();
- ~TextureState();
- GLuint mTextureUnit;
-
- // Caches texture bindings for the GL_TEXTURE_2D target
- GLuint mBoundTextures[kTextureUnitsCount];
-
- std::unique_ptr<Texture> mShadowLutTexture;
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif // RENDERSTATE_BLEND_H
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index f510a2055309..7acc44ca65b7 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -21,6 +21,7 @@
#include "RenderThread.h"
#include "pipeline/skia/ShaderCache.h"
#include "pipeline/skia/SkiaMemoryTracer.h"
+#include "Properties.h"
#include "renderstate/RenderState.h"
#include <GrContextOptions.h>
@@ -50,9 +51,6 @@ CacheManager::CacheManager(const DisplayInfo& display) : mMaxSurfaceArea(display
mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(
mMaxSurfaceArea / 2,
skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface);
- if (Properties::isSkiaEnabled()) {
- skiapipeline::ShaderCache::get().initShaderDiskCache();
- }
}
void CacheManager::reset(sk_sp<GrContext> context) {
@@ -105,7 +103,7 @@ public:
}
};
-void CacheManager::configureContext(GrContextOptions* contextOptions) {
+void CacheManager::configureContext(GrContextOptions* contextOptions, const void* identity, ssize_t size) {
contextOptions->fAllowPathMaskCaching = true;
float screenMP = mMaxSurfaceArea / 1024.0f / 1024.0f;
@@ -135,7 +133,10 @@ void CacheManager::configureContext(GrContextOptions* contextOptions) {
contextOptions->fExecutor = mTaskProcessor.get();
}
- contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get();
+ auto& cache = skiapipeline::ShaderCache::get();
+ cache.initShaderDiskCache(identity, size);
+ contextOptions->fPersistentCache = &cache;
+ contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
}
void CacheManager::trimMemory(TrimMemoryMode mode) {
@@ -215,11 +216,12 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState)
log.appendFormat(" Layer Info:\n");
}
+ const char* layerType = Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL
+ ? "GlLayer" : "VkLayer";
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;
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 7d733525194f..35fc91a42510 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -44,7 +44,7 @@ class CacheManager {
public:
enum class TrimMemoryMode { Complete, UiHidden };
- void configureContext(GrContextOptions* context);
+ void configureContext(GrContextOptions* context, const void* identity, ssize_t size);
void trimMemory(TrimMemoryMode mode);
void trimStaleResources();
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index bfa2ae7e72ff..f1a522ecd588 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -18,26 +18,20 @@
#include <GpuMemoryTracker.h>
#include "AnimationContext.h"
-#include "Caches.h"
#include "EglManager.h"
#include "Frame.h"
#include "LayerUpdateQueue.h"
-#include "OpenGLPipeline.h"
#include "Properties.h"
#include "RenderThread.h"
#include "hwui/Canvas.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaPipeline.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
-#include "protos/hwui.pb.h"
-#include "renderstate/RenderState.h"
-#include "renderstate/Stencil.h"
#include "utils/GLUtils.h"
#include "utils/TimeUtils.h"
#include "../Properties.h"
#include <cutils/properties.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <private/hwui/DrawGlInfo.h>
#include <strings.h>
@@ -51,8 +45,6 @@
#define TRIM_MEMORY_COMPLETE 80
#define TRIM_MEMORY_UI_HIDDEN 20
-#define ENABLE_RENDERNODE_SERIALIZATION false
-
#define LOG_FRAMETIME_MMA 0
#if LOG_FRAMETIME_MMA
@@ -70,9 +62,6 @@ CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
- case RenderPipelineType::OpenGL:
- return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
- std::make_unique<OpenGLPipeline>(thread));
case RenderPipelineType::SkiaGL:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
@@ -86,29 +75,10 @@ CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
return nullptr;
}
-void CanvasContext::destroyLayer(RenderNode* node) {
- auto renderType = Properties::getRenderPipelineType();
- switch (renderType) {
- case RenderPipelineType::OpenGL:
- OpenGLPipeline::destroyLayer(node);
- break;
- case RenderPipelineType::SkiaGL:
- case RenderPipelineType::SkiaVulkan:
- skiapipeline::SkiaPipeline::destroyLayer(node);
- break;
- default:
- LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
- break;
- }
-}
-
void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor) {
ATRACE_CALL();
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
- case RenderPipelineType::OpenGL:
- OpenGLPipeline::invokeFunctor(thread, functor);
- break;
case RenderPipelineType::SkiaGL:
skiapipeline::SkiaOpenGLPipeline::invokeFunctor(thread, functor);
break;
@@ -122,19 +92,7 @@ void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor)
}
void CanvasContext::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
- auto renderType = Properties::getRenderPipelineType();
- switch (renderType) {
- case RenderPipelineType::OpenGL:
- OpenGLPipeline::prepareToDraw(thread, bitmap);
- break;
- case RenderPipelineType::SkiaGL:
- case RenderPipelineType::SkiaVulkan:
- skiapipeline::SkiaPipeline::prepareToDraw(thread, bitmap);
- break;
- default:
- LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
- break;
- }
+ skiapipeline::SkiaPipeline::prepareToDraw(thread, bitmap);
}
CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
@@ -150,13 +108,11 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
, mRenderPipeline(std::move(renderPipeline)) {
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
- mRenderThread.renderState().registerCanvasContext(this);
mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
}
CanvasContext::~CanvasContext() {
destroy();
- mRenderThread.renderState().unregisterCanvasContext(this);
for (auto& node : mRenderNodes) {
node->clearRoot();
}
@@ -188,7 +144,7 @@ void CanvasContext::setSurface(sp<Surface>&& surface) {
mNativeSurface = std::move(surface);
- ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Srgb;
+ ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode);
mFrameNumber = -1;
@@ -224,14 +180,20 @@ void CanvasContext::setStopped(bool stopped) {
}
}
-void CanvasContext::setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
- mLightGeometry.radius = lightRadius;
+void CanvasContext::allocateBuffers() {
+ if (mNativeSurface) {
+ mNativeSurface->allocateBuffers();
+ }
+}
+
+void CanvasContext::setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
mLightInfo.ambientShadowAlpha = ambientShadowAlpha;
mLightInfo.spotShadowAlpha = spotShadowAlpha;
}
-void CanvasContext::setLightCenter(const Vector3& lightCenter) {
+void CanvasContext::setLightGeometry(const Vector3& lightCenter, float lightRadius) {
mLightGeometry.center = lightCenter;
+ mLightGeometry.radius = lightRadius;
}
void CanvasContext::setOpaque(bool opaque) {
@@ -453,7 +415,7 @@ void CanvasContext::draw() {
SkRect windowDirty = computeDirtyRect(frame, &dirty);
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
- mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo,
+ mContentDrawBounds, mOpaque, mLightInfo,
mRenderNodes, &(profiler()));
int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1;
@@ -531,13 +493,6 @@ void CanvasContext::draw() {
}
GpuMemoryTracker::onFrameCompleted();
-#ifdef BUGREPORT_FONT_CACHE_USAGE
- auto renderType = Properties::getRenderPipelineType();
- if (RenderPipelineType::OpenGL == renderType) {
- Caches& caches = Caches::getInstance();
- caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted();
- }
-#endif
}
// Called by choreographer to do an RT-driven animation
@@ -599,17 +554,12 @@ void CanvasContext::buildLayer(RenderNode* node) {
// purposes when the frame is actually drawn
node->setPropertyFieldsDirty(RenderNode::GENERIC);
- mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mWideColorGamut,
- mLightInfo);
+ mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo);
node->incStrong(nullptr);
mPrefetchedLayers.insert(node);
}
-bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
- return mRenderPipeline->copyLayerInto(layer, bitmap);
-}
-
void CanvasContext::destroyHardwareResources() {
stopDrawing();
if (mRenderPipeline->isContextReady()) {
@@ -622,37 +572,15 @@ void CanvasContext::destroyHardwareResources() {
}
void CanvasContext::trimMemory(RenderThread& thread, int level) {
- 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;
+ ATRACE_CALL();
+ if (!thread.getGrContext()) return;
+ ATRACE_CALL();
+ if (level >= TRIM_MEMORY_COMPLETE) {
+ thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ thread.destroyGlContext();
+ thread.vulkanManager().destroy();
+ } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
+ thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
}
}
@@ -673,39 +601,6 @@ void CanvasContext::setName(const std::string&& name) {
mJankTracker.setDescription(JankTrackerType::Window, std::move(name));
}
-void CanvasContext::serializeDisplayListTree() {
-#if ENABLE_RENDERNODE_SERIALIZATION
- using namespace google::protobuf::io;
- char package[128];
- // Check whether tracing is enabled for this process.
- FILE* file = fopen("/proc/self/cmdline", "r");
- if (file) {
- if (!fgets(package, 128, file)) {
- ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
- fclose(file);
- return;
- }
- fclose(file);
- } else {
- ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno), errno);
- return;
- }
- char path[1024];
- snprintf(path, 1024, "/data/data/%s/cache/rendertree_dump", package);
- int fd = open(path, O_CREAT | O_WRONLY, S_IRWXU | S_IRGRP | S_IROTH);
- if (fd == -1) {
- ALOGD("Failed to open '%s'", path);
- return;
- }
- proto::RenderNode tree;
- // TODO: Streaming writes?
- mRootRenderNode->copyTo(&tree);
- std::string data = tree.SerializeAsString();
- write(fd, data.c_str(), data.length());
- close(fd);
-#endif
-}
-
void CanvasContext::waitOnFences() {
if (mFrameFences.size()) {
ATRACE_CALL();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 1c4e02d7df70..70be4a6d7730 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -16,10 +16,8 @@
#pragma once
-#include "BakedOpDispatcher.h"
-#include "BakedOpRenderer.h"
#include "DamageAccumulator.h"
-#include "FrameBuilder.h"
+#include "Lighting.h"
#include "FrameInfo.h"
#include "FrameInfoVisualizer.h"
#include "FrameMetricsReporter.h"
@@ -77,8 +75,7 @@ public:
*/
bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator,
ErrorHandler* errorHandler) {
- return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, mWideColorGamut,
- errorHandler);
+ return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, errorHandler);
}
/**
@@ -99,12 +96,6 @@ public:
*/
void unpinImages() { mRenderPipeline->unpinImages(); }
- /**
- * Destroy any layers that have been attached to the provided RenderNode removing
- * any state that may have been set during createOrUpdateLayer().
- */
- static void destroyLayer(RenderNode* node);
-
static void invokeFunctor(const RenderThread& thread, Functor* functor);
static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap);
@@ -122,9 +113,10 @@ public:
bool pauseSurface();
void setStopped(bool stopped);
bool hasSurface() { return mNativeSurface.get(); }
+ void allocateBuffers();
- void setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
- void setLightCenter(const Vector3& lightCenter);
+ void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
+ void setLightGeometry(const Vector3& lightCenter, float lightRadius);
void setOpaque(bool opaque);
void setWideGamut(bool wideGamut);
bool makeCurrent();
@@ -137,7 +129,6 @@ public:
void prepareAndDraw(RenderNode* node);
void buildLayer(RenderNode* node);
- bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
void markLayerInUse(RenderNode* node);
void destroyHardwareResources();
@@ -155,8 +146,6 @@ public:
void setName(const std::string&& name);
- void serializeDisplayListTree();
-
void addRenderNode(RenderNode* node, bool placeFront);
void removeRenderNode(RenderNode* node);
@@ -194,6 +183,23 @@ public:
mFrameCompleteCallbacks.push_back(std::move(func));
}
+ void setForceDark(bool enable) {
+ mUseForceDark = enable;
+ }
+
+ bool useForceDark() {
+ // The force-dark override has the highest priority, followed by the disable setting
+ // for the feature as a whole, followed last by whether or not this context has had
+ // force dark set (typically automatically done via UIMode)
+ if (Properties::forceDarkMode) {
+ return true;
+ }
+ if (!Properties::enableForceDarkSupport) {
+ return false;
+ }
+ return mUseForceDark;
+ }
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
@@ -240,8 +246,9 @@ private:
bool mOpaque;
bool mWideColorGamut = false;
- BakedOpRenderer::LightInfo mLightInfo;
- FrameBuilder::LightGeometry mLightGeometry = {{0, 0, 0}, 0};
+ bool mUseForceDark = false;
+ LightInfo mLightInfo;
+ LightGeometry mLightGeometry = {{0, 0, 0}, 0};
bool mHaveNewSurface = false;
DamageAccumulator mDamageAccumulator;
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 2c46762fee5c..696cfaef3cd7 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -32,7 +32,6 @@ namespace android {
namespace uirenderer {
class DeferredLayerUpdater;
-class DisplayList;
class RenderNode;
namespace renderthread {
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 6e239e357cf6..d4ffddde8def 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -16,27 +16,21 @@
#include "EglManager.h"
-#include <string>
-
#include <cutils/properties.h>
#include <log/log.h>
+#include <private/gui/SyncFeatures.h>
+#include <utils/Trace.h>
+#include "utils/Color.h"
#include "utils/StringUtils.h"
-#include "Caches.h"
-#include "DeviceInfo.h"
#include "Frame.h"
#include "Properties.h"
-#include "RenderThread.h"
-#include "Texture.h"
-#include "renderstate/RenderState.h"
#include <EGL/eglext.h>
-#include <GrContextOptions.h>
-#include <gl/GrGLInterface.h>
+#include <GLES/gl.h>
-#ifdef HWUI_GLES_WRAP_ENABLED
-#include "debug/GlesDriver.h"
-#endif
+#include <string>
+#include <vector>
#define GLES_VERSION 2
@@ -82,18 +76,23 @@ static struct {
bool pixelFormatFloat = false;
bool glColorSpace = false;
bool scRGB = false;
+ bool displayP3 = false;
bool contextPriority = false;
+ bool surfacelessContext = false;
} EglExtensions;
-EglManager::EglManager(RenderThread& thread)
- : mRenderThread(thread)
- , mEglDisplay(EGL_NO_DISPLAY)
+EglManager::EglManager()
+ : mEglDisplay(EGL_NO_DISPLAY)
, mEglConfig(nullptr)
, mEglConfigWideGamut(nullptr)
, mEglContext(EGL_NO_CONTEXT)
, mPBufferSurface(EGL_NO_SURFACE)
, mCurrentSurface(EGL_NO_SURFACE) {}
+EglManager::~EglManager() {
+ destroy();
+}
+
void EglManager::initialize() {
if (hasEglContext()) return;
@@ -126,26 +125,7 @@ void EglManager::initialize() {
loadConfigs();
createContext();
createPBufferSurface();
- makeCurrent(mPBufferSurface);
- DeviceInfo::initialize();
- mRenderThread.renderState().onGLContextCreated();
-
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
-#ifdef HWUI_GLES_WRAP_ENABLED
- debug::GlesDriver* driver = debug::GlesDriver::get();
- sk_sp<const GrGLInterface> glInterface(driver->getSkiaInterface());
-#else
- sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
-#endif
- LOG_ALWAYS_FATAL_IF(!glInterface.get());
-
- GrContextOptions options;
- options.fDisableDistanceFieldPaths = true;
- mRenderThread.cacheManager().configureContext(&options);
- sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options));
- LOG_ALWAYS_FATAL_IF(!grContext.get());
- mRenderThread.setGrContext(grContext);
- }
+ makeCurrent(mPBufferSurface, nullptr, /* force */ true);
}
void EglManager::initExtensions() {
@@ -169,7 +149,9 @@ void EglManager::initExtensions() {
#else
EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb");
#endif
+ EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3");
EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
+ EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
}
bool EglManager::hasEglContext() {
@@ -180,6 +162,10 @@ void EglManager::loadConfigs() {
ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
EGLint swapBehavior =
(mSwapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+
+ // Note: The default pixel format is RGBA_8888, when other formats are
+ // available, we should check the target pixel format and configure the
+ // attributes list properly.
EGLint attribs[] = {EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE,
@@ -195,7 +181,7 @@ void EglManager::loadConfigs() {
EGL_CONFIG_CAVEAT,
EGL_NONE,
EGL_STENCIL_SIZE,
- Stencil::getStencilSize(),
+ STENCIL_BUFFER_SIZE,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT | swapBehavior,
EGL_NONE};
@@ -232,7 +218,7 @@ void EglManager::loadConfigs() {
EGL_DEPTH_SIZE,
0,
EGL_STENCIL_SIZE,
- Stencil::getStencilSize(),
+ STENCIL_BUFFER_SIZE,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT | swapBehavior,
EGL_NONE};
@@ -269,17 +255,18 @@ void EglManager::createPBufferSurface() {
LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
"usePBufferSurface() called on uninitialized GlobalContext!");
- if (mPBufferSurface == EGL_NO_SURFACE) {
+ if (mPBufferSurface == EGL_NO_SURFACE && !EglExtensions.surfacelessContext) {
EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
}
}
-EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorGamut) {
- initialize();
+EGLSurface EglManager::createSurface(EGLNativeWindowType window, ColorMode colorMode) {
+ LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
- wideColorGamut = wideColorGamut && EglExtensions.glColorSpace && EglExtensions.scRGB &&
- EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext;
+ bool wideColorGamut = colorMode == ColorMode::WideColorGamut && EglExtensions.glColorSpace &&
+ EglExtensions.scRGB && 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
@@ -289,9 +276,9 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorG
// 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)
+ // - If linear blending is on, we must use the non-linear 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
@@ -299,7 +286,7 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorG
// - 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
+ // Not all Android targets support the EGL_GL_COLORSPACE_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
@@ -350,10 +337,10 @@ void EglManager::destroySurface(EGLSurface surface) {
void EglManager::destroy() {
if (mEglDisplay == EGL_NO_DISPLAY) return;
- mRenderThread.setGrContext(nullptr);
- mRenderThread.renderState().onGLContextDestroyed();
eglDestroyContext(mEglDisplay, mEglContext);
- eglDestroySurface(mEglDisplay, mPBufferSurface);
+ if (mPBufferSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, mPBufferSurface);
+ }
eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mEglDisplay);
eglReleaseThread();
@@ -364,8 +351,8 @@ void EglManager::destroy() {
mCurrentSurface = EGL_NO_SURFACE;
}
-bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
- if (isCurrent(surface)) return false;
+bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut, bool force) {
+ if (!force && isCurrent(surface)) return false;
if (surface == EGL_NO_SURFACE) {
// Ensure we always have a valid surface & context
@@ -485,6 +472,109 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) {
return preserved;
}
+status_t EglManager::fenceWait(sp<Fence>& fence) {
+ if (!hasEglContext()) {
+ ALOGE("EglManager::fenceWait: EGLDisplay not initialized");
+ return INVALID_OPERATION;
+ }
+
+ if (SyncFeatures::getInstance().useWaitSync() &&
+ SyncFeatures::getInstance().useNativeFenceSync()) {
+ // Block GPU on the fence.
+ // Create an EGLSyncKHR from the current fence.
+ int fenceFd = fence->dup();
+ if (fenceFd == -1) {
+ ALOGE("EglManager::fenceWait: error dup'ing fence fd: %d", errno);
+ return -errno;
+ }
+ EGLint attribs[] = {
+ EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd,
+ EGL_NONE
+ };
+ EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay,
+ EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ close(fenceFd);
+ ALOGE("EglManager::fenceWait: error creating EGL fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ // XXX: The spec draft is inconsistent as to whether this should
+ // return an EGLint or void. Ignore the return value for now, as
+ // it's not strictly needed.
+ eglWaitSyncKHR(mEglDisplay, sync, 0);
+ EGLint eglErr = eglGetError();
+ eglDestroySyncKHR(mEglDisplay, sync);
+ if (eglErr != EGL_SUCCESS) {
+ ALOGE("EglManager::fenceWait: error waiting for EGL fence: %#x", eglErr);
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ // Block CPU on the fence.
+ status_t err = fence->waitForever("EglManager::fenceWait");
+ if (err != NO_ERROR) {
+ ALOGE("EglManager::fenceWait: error waiting for fence: %d", err);
+ return err;
+ }
+ }
+ return OK;
+}
+
+status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence,
+ sp<Fence>& nativeFence) {
+ if (!hasEglContext()) {
+ ALOGE("EglManager::createReleaseFence: EGLDisplay not initialized");
+ return INVALID_OPERATION;
+ }
+
+ if (SyncFeatures::getInstance().useNativeFenceSync()) {
+ EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay,
+ EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ int fenceFd = eglDupNativeFenceFDANDROID(mEglDisplay, sync);
+ eglDestroySyncKHR(mEglDisplay, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ ALOGE("EglManager::createReleaseFence: error dup'ing native fence "
+ "fd: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ nativeFence = new Fence(fenceFd);
+ *eglFence = EGL_NO_SYNC_KHR;
+ } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) {
+ if (*eglFence != EGL_NO_SYNC_KHR) {
+ // There is already a fence for the current slot. We need to
+ // wait on that before replacing it with another fence to
+ // ensure that all outstanding buffer accesses have completed
+ // before the producer accesses it.
+ EGLint result = eglClientWaitSyncKHR(mEglDisplay, *eglFence, 0, 1000000000);
+ if (result == EGL_FALSE) {
+ ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence");
+ return TIMED_OUT;
+ }
+ eglDestroySyncKHR(mEglDisplay, *eglFence);
+ }
+
+ // Create a fence for the outstanding accesses in the current
+ // OpenGL ES context.
+ *eglFence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, nullptr);
+ if (*eglFence == EGL_NO_SYNC_KHR) {
+ ALOGE("EglManager::createReleaseFence: error creating fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ }
+ return OK;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index ef9effbf9953..55c81d42d8a0 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -17,10 +17,14 @@
#define EGLMANAGER_H
#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <SkImageInfo.h>
#include <SkRect.h>
#include <cutils/compiler.h>
+#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
+#include "IRenderPipeline.h"
namespace android {
namespace uirenderer {
@@ -33,20 +37,24 @@ class RenderThread;
// and EGLConfig, which are re-used by CanvasContext
class EglManager {
public:
+ explicit EglManager();
+
+ ~EglManager();
+
static const char* eglErrorString();
- // Returns true on success, false on failure
+
void initialize();
bool hasEglContext();
- EGLSurface createSurface(EGLNativeWindowType window, bool wideColorGamut);
+ EGLSurface createSurface(EGLNativeWindowType window, ColorMode colorMode);
void destroySurface(EGLSurface surface);
void destroy();
bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
// Returns true if the current surface changed, false if it was already current
- bool makeCurrent(EGLSurface surface, EGLint* errOut = nullptr);
+ bool makeCurrent(EGLSurface surface, EGLint* errOut = nullptr, bool force = false);
Frame beginFrame(EGLSurface surface);
void damageFrame(const Frame& frame, const SkRect& dirty);
// If this returns true it is mandatory that swapBuffers is called
@@ -60,11 +68,17 @@ public:
void fence();
+ EGLDisplay eglDisplay() const { return mEglDisplay; }
+
+ // Inserts a wait on fence command into the OpenGL ES command stream. If EGL extension
+ // support is missing, block the CPU on the fence.
+ status_t fenceWait(sp<Fence>& fence);
+
+ // Creates a fence that is signaled, when all the pending GL commands are flushed.
+ // Depending on installed extensions, the result is either Android native fence or EGL fence.
+ status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp<Fence>& nativeFence);
+
private:
- friend class RenderThread;
- explicit EglManager(RenderThread& thread);
- // EglContext is never destroyed, method is purposely not implemented
- ~EglManager();
void initExtensions();
void createPBufferSurface();
@@ -72,14 +86,11 @@ private:
void createContext();
EGLint queryBufferAge(EGLSurface surface);
- RenderThread& mRenderThread;
-
EGLDisplay mEglDisplay;
EGLConfig mEglConfig;
EGLConfig mEglConfigWideGamut;
EGLContext mEglContext;
EGLSurface mPBufferSurface;
-
EGLSurface mCurrentSurface;
enum class SwapBehavior {
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index b1de49733c09..4972554c65cc 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -16,8 +16,12 @@
#pragma once
+#include "DamageAccumulator.h"
#include "FrameInfoVisualizer.h"
+#include "LayerUpdateQueue.h"
+#include "Lighting.h"
#include "SwapBehavior.h"
+#include "hwui/Bitmap.h"
#include <SkRect.h>
#include <utils/RefBase.h>
@@ -32,13 +36,18 @@ namespace uirenderer {
class DeferredLayerUpdater;
class ErrorHandler;
+class TaskManager;
namespace renderthread {
enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded };
enum class ColorMode {
- Srgb,
+ // SRGB means HWUI will produce buffer in SRGB color space.
+ SRGB,
+ // WideColorGamut means HWUI would support rendering scRGB non-linear into
+ // a signed buffer with enough range to support the wide color gamut of the
+ // display.
WideColorGamut,
// Hdr
};
@@ -50,30 +59,31 @@ public:
virtual MakeCurrentResult makeCurrent() = 0;
virtual Frame getFrame() = 0;
virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
- const FrameBuilder::LightGeometry& lightGeometry,
+ const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
- bool opaque, bool wideColorGamut, const BakedOpRenderer::LightInfo& lightInfo,
+ bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler) = 0;
virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
- virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0;
virtual DeferredLayerUpdater* createTextureLayer() = 0;
virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, 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, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo) = 0;
+ virtual void renderLayers(const LightGeometry& lightGeometry,
+ LayerUpdateQueue* layerUpdateQueue, bool opaque,
+ const LightInfo& lightInfo) = 0;
virtual TaskManager* getTaskManager() = 0;
virtual bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
- bool wideColorGamut, ErrorHandler* errorHandler) = 0;
+ ErrorHandler* errorHandler) = 0;
virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0;
virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0;
virtual void unpinImages() = 0;
virtual void onPrepareTree() = 0;
+ virtual SkColorType getSurfaceColorType() const = 0;
+ virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0;
virtual ~IRenderPipeline() {}
};
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
deleted file mode 100644
index f96001ebdd57..000000000000
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "OpenGLPipeline.h"
-
-#include "DeferredLayerUpdater.h"
-#include "EglManager.h"
-#include "Frame.h"
-#include "GlLayer.h"
-#include "OpenGLReadback.h"
-#include "ProfileRenderer.h"
-#include "renderstate/RenderState.h"
-#include "TreeInfo.h"
-
-#include <cutils/properties.h>
-#include <strings.h>
-
-namespace android {
-namespace uirenderer {
-namespace renderthread {
-
-OpenGLPipeline::OpenGLPipeline(RenderThread& thread)
- : mEglManager(thread.eglManager()), mRenderThread(thread) {}
-
-MakeCurrentResult OpenGLPipeline::makeCurrent() {
- // TODO: Figure out why this workaround is needed, see b/13913604
- // In the meantime this matches the behavior of GLRenderer, so it is not a regression
- EGLint error = 0;
- bool haveNewSurface = mEglManager.makeCurrent(mEglSurface, &error);
-
- Caches::getInstance().textureCache.resetMarkInUse(this);
- if (!haveNewSurface) {
- return MakeCurrentResult::AlreadyCurrent;
- }
- return error ? MakeCurrentResult::Failed : MakeCurrentResult::Succeeded;
-}
-
-Frame OpenGLPipeline::getFrame() {
- LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
- "drawRenderNode called on a context with no surface!");
- return mEglManager.beginFrame(mEglSurface);
-}
-
-bool OpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
- const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
- bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo,
- const std::vector<sp<RenderNode>>& renderNodes,
- FrameInfoVisualizer* profiler) {
- mEglManager.damageFrame(frame, dirty);
-
- bool drew = false;
-
- auto& caches = Caches::getInstance();
- FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), lightGeometry, caches);
-
- frameBuilder.deferLayers(*layerUpdateQueue);
- layerUpdateQueue->clear();
-
- frameBuilder.deferRenderNodeScene(renderNodes, contentDrawBounds);
-
- BakedOpRenderer renderer(caches, mRenderThread.renderState(), opaque, wideColorGamut,
- lightInfo);
- frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
- ProfileRenderer profileRenderer(renderer);
- profiler->draw(profileRenderer);
- drew = renderer.didDraw();
-
- // post frame cleanup
- caches.clearGarbage();
- caches.pathCache.trim();
- caches.tessellationCache.trim();
-
-#if DEBUG_MEMORY_USAGE
- caches.dumpMemoryUsage();
-#else
- if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) {
- caches.dumpMemoryUsage();
- }
-#endif
-
- return drew;
-}
-
-bool OpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) {
- GL_CHECKPOINT(LOW);
-
- // Even if we decided to cancel the frame, from the perspective of jank
- // metrics the frame was swapped at this point
- currentFrameInfo->markSwapBuffers();
-
- *requireSwap = drew || mEglManager.damageRequiresSwap();
-
- if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
- return false;
- }
-
- return *requireSwap;
-}
-
-bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
- ATRACE_CALL();
- // acquire most recent buffer for drawing
- layer->updateTexImage();
- layer->apply();
- return OpenGLReadbackImpl::copyLayerInto(mRenderThread,
- static_cast<GlLayer&>(*layer->backingLayer()), bitmap);
-}
-
-static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode,
- bool blend) {
- GlLayer* layer =
- new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
- Caches::getInstance().textureState().activateTexture(0);
- layer->generateTexture();
- return layer;
-}
-
-DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() {
- mEglManager.initialize();
- return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL);
-}
-
-void OpenGLPipeline::onStop() {
- if (mEglManager.isCurrent(mEglSurface)) {
- mEglManager.makeCurrent(EGL_NO_SURFACE);
- }
-}
-
-bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, ColorMode colorMode) {
- if (mEglSurface != EGL_NO_SURFACE) {
- mEglManager.destroySurface(mEglSurface);
- mEglSurface = EGL_NO_SURFACE;
- }
-
- if (surface) {
- const bool wideColorGamut = colorMode == ColorMode::WideColorGamut;
- mEglSurface = mEglManager.createSurface(surface, wideColorGamut);
- }
-
- if (mEglSurface != EGL_NO_SURFACE) {
- const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
- mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
- return true;
- }
-
- return false;
-}
-
-bool OpenGLPipeline::isSurfaceReady() {
- return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE);
-}
-
-bool OpenGLPipeline::isContextReady() {
- return CC_LIKELY(mEglManager.hasEglContext());
-}
-
-void OpenGLPipeline::onDestroyHardwareResources() {
- Caches& caches = Caches::getInstance();
- // Make sure to release all the textures we were owning as there won't
- // be another draw
- caches.textureCache.resetMarkInUse(this);
- mRenderThread.renderState().flush(Caches::FlushMode::Layers);
-}
-
-void OpenGLPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
- LayerUpdateQueue* layerUpdateQueue, bool opaque,
- bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo) {
- static const std::vector<sp<RenderNode>> emptyNodeList;
- auto& caches = Caches::getInstance();
- FrameBuilder frameBuilder(*layerUpdateQueue, lightGeometry, caches);
- layerUpdateQueue->clear();
- // 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);
-}
-
-TaskManager* OpenGLPipeline::getTaskManager() {
- return &Caches::getInstance().tasks;
-}
-
-static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) {
- return layer->viewportWidth == (uint32_t)width && layer->viewportHeight == (uint32_t)height;
-}
-
-bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
- const DamageAccumulator& damageAccumulator,
- bool wideColorGamut,
- ErrorHandler* errorHandler) {
- RenderState& renderState = mRenderThread.renderState();
- OffscreenBufferPool& layerPool = renderState.layerPool();
- bool transformUpdateNeeded = false;
- if (node->getLayer() == nullptr) {
- 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)
- // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
- if (node->properties().fitsOnLayer()) {
- node->setLayer(layerPool.resize(node->getLayer(), node->getWidth(), node->getHeight()));
- } else {
- destroyLayer(node);
- }
- transformUpdateNeeded = true;
- }
-
- if (transformUpdateNeeded && node->getLayer()) {
- // update the transform in window of the layer to reset its origin wrt light source position
- Matrix4 windowTransform;
- damageAccumulator.computeCurrentTransform(&windowTransform);
- node->getLayer()->setWindowTransform(windowTransform);
- }
-
- if (!node->hasLayer()) {
- Caches::getInstance().dumpMemoryUsage();
- if (errorHandler) {
- std::ostringstream err;
- err << "Unable to create layer for " << node->getName();
- const int maxTextureSize = Caches::getInstance().maxTextureSize;
- if (node->getWidth() > maxTextureSize || node->getHeight() > maxTextureSize) {
- err << ", size " << node->getWidth() << "x" << node->getHeight()
- << " exceeds max size " << maxTextureSize;
- } else {
- err << ", see logcat for more info";
- }
- errorHandler->onError(err.str());
- }
- }
-
- return transformUpdateNeeded;
-}
-
-bool OpenGLPipeline::pinImages(LsaVector<sk_sp<Bitmap>>& images) {
- TextureCache& cache = Caches::getInstance().textureCache;
- bool prefetchSucceeded = true;
- for (auto& bitmapResource : images) {
- prefetchSucceeded &= cache.prefetchAndMarkInUse(this, bitmapResource.get());
- }
- return prefetchSucceeded;
-}
-
-void OpenGLPipeline::unpinImages() {
- Caches::getInstance().textureCache.resetMarkInUse(this);
-}
-
-void OpenGLPipeline::destroyLayer(RenderNode* node) {
- if (OffscreenBuffer* layer = node->getLayer()) {
- layer->renderState.layerPool().putOrDelete(layer);
- node->setLayer(nullptr);
- }
-}
-
-void OpenGLPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
- if (Caches::hasInstance() && thread.eglManager().hasEglContext()) {
- ATRACE_NAME("Bitmap#prepareToDraw task");
- Caches::getInstance().textureCache.prefetch(bitmap);
- }
-}
-
-void OpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
- DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
- if (thread.eglManager().hasEglContext()) {
- mode = DrawGlInfo::kModeProcess;
- }
- thread.renderState().invokeFunctor(functor, mode, nullptr);
-}
-
-#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
deleted file mode 100644
index 9859e931fd85..000000000000
--- a/libs/hwui/renderthread/OpenGLPipeline.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "BakedOpDispatcher.h"
-#include "BakedOpRenderer.h"
-#include "CanvasContext.h"
-#include "FrameBuilder.h"
-#include "IRenderPipeline.h"
-
-namespace android {
-namespace uirenderer {
-namespace renderthread {
-
-class OpenGLPipeline : public IRenderPipeline {
-public:
- OpenGLPipeline(RenderThread& thread);
- virtual ~OpenGLPipeline() {}
-
- MakeCurrentResult makeCurrent() override;
- Frame getFrame() override;
- bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
- const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
- const Rect& contentDrawBounds, bool opaque, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo,
- const std::vector<sp<RenderNode>>& renderNodes,
- FrameInfoVisualizer* profiler) override;
- bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) override;
- bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
- DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(Surface* window, SwapBehavior swapBehavior, 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, bool wideColorGamut,
- const BakedOpRenderer::LightInfo& lightInfo) override;
- TaskManager* getTaskManager() override;
- bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
- bool wideColorGamut, ErrorHandler* errorHandler) override;
- bool pinImages(std::vector<SkImage*>& mutableImages) override { return false; }
- bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override;
- void unpinImages() override;
- void onPrepareTree() 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;
- EGLSurface mEglSurface = EGL_NO_SURFACE;
- bool mBufferPreserved = false;
- RenderThread& mRenderThread;
-};
-
-} /* namespace renderthread */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 020761110ef0..085812a00f71 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -21,6 +21,7 @@
#include "Properties.h"
#include "Readback.h"
#include "Rect.h"
+#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/VectorDrawableAtlas.h"
#include "renderstate/RenderState.h"
#include "renderthread/CanvasContext.h"
@@ -65,10 +66,7 @@ void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) {
bool RenderProxy::loadSystemProperties() {
return mRenderThread.queue().runSync([this]() -> bool {
- bool needsRedraw = false;
- if (Caches::hasInstance()) {
- needsRedraw = Properties::load();
- }
+ bool needsRedraw = Properties::load();
if (mContext->profiler().consumeProperties()) {
needsRedraw = true;
}
@@ -82,22 +80,16 @@ void RenderProxy::setName(const char* name) {
mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); });
}
-void RenderProxy::initialize(const sp<Surface>& surface) {
+void RenderProxy::setSurface(const sp<Surface>& surface) {
mRenderThread.queue().post(
[ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); });
}
-void RenderProxy::allocateBuffers(const sp<Surface>& surface) {
- mRenderThread.queue().post(
- [ surf = surface ]() mutable { surf->allocateBuffers(); });
-}
-
-void RenderProxy::updateSurface(const sp<Surface>& surface) {
- mRenderThread.queue().post(
- [ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); });
+void RenderProxy::allocateBuffers() {
+ mRenderThread.queue().post([=]() { mContext->allocateBuffers(); });
}
-bool RenderProxy::pauseSurface(const sp<Surface>& surface) {
+bool RenderProxy::pause() {
return mRenderThread.queue().runSync([this]() -> bool { return mContext->pauseSurface(); });
}
@@ -105,13 +97,13 @@ void RenderProxy::setStopped(bool stopped) {
mRenderThread.queue().runSync([this, stopped]() { mContext->setStopped(stopped); });
}
-void RenderProxy::setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
+void RenderProxy::setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
mRenderThread.queue().post(
- [=]() { mContext->setup(lightRadius, ambientShadowAlpha, spotShadowAlpha); });
+ [=]() { mContext->setLightAlpha(ambientShadowAlpha, spotShadowAlpha); });
}
-void RenderProxy::setLightCenter(const Vector3& lightCenter) {
- mRenderThread.queue().post([=]() { mContext->setLightCenter(lightCenter); });
+void RenderProxy::setLightGeometry(const Vector3& lightCenter, float lightRadius) {
+ mRenderThread.queue().post([=]() { mContext->setLightGeometry(lightCenter, lightRadius); });
}
void RenderProxy::setOpaque(bool opaque) {
@@ -162,8 +154,10 @@ void RenderProxy::buildLayer(RenderNode* node) {
}
bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) {
- return mRenderThread.queue().runSync(
- [&]() -> bool { return mContext->copyLayerInto(layer, &bitmap); });
+ auto& thread = RenderThread::getInstance();
+ return thread.queue().runSync(
+ [&]() -> bool { return thread.readback().copyLayerInto(layer, &bitmap)
+ == CopyResult::Success; });
}
void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -200,8 +194,11 @@ void RenderProxy::fence() {
mRenderThread.queue().runSync([]() {});
}
-void RenderProxy::staticFence() {
- RenderThread::getInstance().queue().runSync([]() {});
+int RenderProxy::maxTextureSize() {
+ static int maxTextureSize = RenderThread::getInstance().queue().runSync([]() {
+ return DeviceInfo::get()->maxTextureSize();
+ });
+ return maxTextureSize;
}
void RenderProxy::stopDrawing() {
@@ -283,10 +280,6 @@ void RenderProxy::setFrameCompleteCallback(std::function<void(int64_t)>&& callba
mDrawFrameTask.setFrameCompleteCallback(std::move(callback));
}
-void RenderProxy::serializeDisplayListTree() {
- mRenderThread.queue().post([=]() { mContext->serializeDisplayListTree(); });
-}
-
void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observerPtr) {
mRenderThread.queue().post([ this, observer = sp{observerPtr} ]() {
mContext->addFrameMetricsObserver(observer.get());
@@ -299,6 +292,12 @@ void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observerPtr)
});
}
+void RenderProxy::setForceDark(bool enable) {
+ mRenderThread.queue().post([this, enable]() {
+ mContext->setForceDark(enable);
+ });
+}
+
int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom,
SkBitmap* bitmap) {
auto& thread = RenderThread::getInstance();
@@ -334,30 +333,18 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) {
}
}
-sk_sp<Bitmap> RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) {
- auto& thread = RenderThread::getInstance();
- return thread.queue().runSync([&]() -> auto { return thread.allocateHardwareBitmap(bitmap); });
-}
-
-int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) {
+int RenderProxy::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
RenderThread& thread = RenderThread::getInstance();
- if (Properties::isSkiaEnabled() && gettid() == thread.getTid()) {
+ if (gettid() == thread.getTid()) {
// TODO: fix everything that hits this. We should never be triggering a readback ourselves.
- return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap);
+ return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap);
} else {
return thread.queue().runSync([&]() -> int {
- return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap);
+ return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap);
});
}
}
-void RenderProxy::onBitmapDestroyed(uint32_t pixelRefId) {
- if (!RenderThread::hasInstance()) return;
- RenderThread& thread = RenderThread::getInstance();
- thread.queue().post(
- [&thread, pixelRefId]() { thread.renderState().onBitmapDestroyed(pixelRefId); });
-}
-
void RenderProxy::disableVsync() {
Properties::disableVsync = true;
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 0d29b4bcc317..6668c5840c3e 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -69,13 +69,12 @@ public:
ANDROID_API bool loadSystemProperties();
ANDROID_API void setName(const char* name);
- ANDROID_API void initialize(const sp<Surface>& surface);
- ANDROID_API void allocateBuffers(const sp<Surface>& surface);
- ANDROID_API void updateSurface(const sp<Surface>& surface);
- ANDROID_API bool pauseSurface(const sp<Surface>& surface);
+ ANDROID_API void setSurface(const sp<Surface>& surface);
+ ANDROID_API void allocateBuffers();
+ ANDROID_API bool pause();
ANDROID_API void setStopped(bool stopped);
- ANDROID_API void setup(float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
- ANDROID_API void setLightCenter(const Vector3& lightCenter);
+ ANDROID_API void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
+ ANDROID_API void setLightGeometry(const Vector3& lightCenter, float lightRadius);
ANDROID_API void setOpaque(bool opaque);
ANDROID_API void setWideGamut(bool wideGamut);
ANDROID_API int64_t* frameInfo();
@@ -96,7 +95,7 @@ public:
ANDROID_API static void overrideProperty(const char* name, const char* value);
ANDROID_API void fence();
- ANDROID_API static void staticFence();
+ ANDROID_API static int maxTextureSize();
ANDROID_API void stopDrawing();
ANDROID_API void notifyFramePending();
@@ -110,8 +109,6 @@ public:
ANDROID_API static void setProcessStatsBuffer(int fd);
ANDROID_API int getRenderThreadTid();
- ANDROID_API void serializeDisplayListTree();
-
ANDROID_API void addRenderNode(RenderNode* node, bool placeFront);
ANDROID_API void removeRenderNode(RenderNode* node);
ANDROID_API void drawRenderNode(RenderNode* node);
@@ -121,17 +118,13 @@ public:
ANDROID_API void addFrameMetricsObserver(FrameMetricsObserver* observer);
ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
- ANDROID_API long getDroppedFrameReportCount();
+ ANDROID_API void setForceDark(bool enable);
ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right,
int bottom, SkBitmap* bitmap);
ANDROID_API static void prepareToDraw(Bitmap& bitmap);
- static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
-
- static int copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap);
-
- static void onBitmapDestroyed(uint32_t pixelRefId);
+ static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
ANDROID_API static void disableVsync();
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 6a2a025da121..207673c1c8dd 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -19,19 +19,23 @@
#include "CanvasContext.h"
#include "DeviceInfo.h"
#include "EglManager.h"
-#include "OpenGLReadback.h"
+#include "Readback.h"
#include "RenderProxy.h"
#include "VulkanManager.h"
#include "hwui/Bitmap.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
-#include "pipeline/skia/SkiaOpenGLReadback.h"
-#include "pipeline/skia/SkiaVulkanReadback.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
#include "renderstate/RenderState.h"
-#include "renderthread/OpenGLPipeline.h"
#include "utils/FatVector.h"
#include "utils/TimeUtils.h"
+#ifdef HWUI_GLES_WRAP_ENABLED
+#include "debug/GlesDriver.h"
+#endif
+
+#include <GrContextOptions.h>
+#include <gl/GrGLInterface.h>
+
#include <gui/DisplayEventReceiver.h>
#include <sys/resource.h>
#include <utils/Condition.h>
@@ -93,14 +97,11 @@ public:
DummyVsyncSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
virtual void requestNextVsync() override {
- mRenderThread->queue().postDelayed(16_ms, [this]() {
- mRenderThread->drainDisplayEventQueue();
- });
+ mRenderThread->queue().postDelayed(16_ms,
+ [this]() { mRenderThread->drainDisplayEventQueue(); });
}
- virtual nsecs_t latestVsyncEvent() override {
- return systemTime(CLOCK_MONOTONIC);
- }
+ virtual nsecs_t latestVsyncEvent() override { return systemTime(CLOCK_MONOTONIC); }
private:
RenderThread* mRenderThread;
@@ -147,13 +148,13 @@ void RenderThread::initializeDisplayEventReceiver() {
auto receiver = std::make_unique<DisplayEventReceiver>();
status_t status = receiver->initCheck();
LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "Initialization of DisplayEventReceiver "
- "failed with status: %d",
- status);
+ "Initialization of DisplayEventReceiver "
+ "failed with status: %d",
+ status);
// Register the FD
mLooper->addFd(receiver->getFd(), 0, Looper::EVENT_INPUT,
- RenderThread::displayEventReceiverCallback, this);
+ RenderThread::displayEventReceiverCallback, this);
mVsyncSource = new DisplayEventReceiverWrapper(std::move(receiver));
} else {
mVsyncSource = new DummyVsyncSource(this);
@@ -161,16 +162,48 @@ void RenderThread::initializeDisplayEventReceiver() {
}
void RenderThread::initThreadLocals() {
- mDisplayInfo = DeviceInfo::queryDisplayInfo();
+ mDisplayInfo = DeviceInfo::get()->displayInfo();
nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1000000000 / mDisplayInfo.fps);
mTimeLord.setFrameInterval(frameIntervalNanos);
initializeDisplayEventReceiver();
- mEglManager = new EglManager(*this);
+ mEglManager = new EglManager();
mRenderState = new RenderState(*this);
mVkManager = new VulkanManager(*this);
mCacheManager = new CacheManager(mDisplayInfo);
}
+void RenderThread::requireGlContext() {
+ if (mEglManager->hasEglContext()) {
+ return;
+ }
+ mEglManager->initialize();
+
+#ifdef HWUI_GLES_WRAP_ENABLED
+ debug::GlesDriver* driver = debug::GlesDriver::get();
+ sk_sp<const GrGLInterface> glInterface(driver->getSkiaInterface());
+#else
+ sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
+#endif
+ LOG_ALWAYS_FATAL_IF(!glInterface.get());
+
+ GrContextOptions options;
+ options.fPreferExternalImagesOverES3 = true;
+ options.fDisableDistanceFieldPaths = true;
+ auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
+ auto size = glesVersion ? strlen(glesVersion) : -1;
+ cacheManager().configureContext(&options, glesVersion, size);
+ sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options));
+ LOG_ALWAYS_FATAL_IF(!grContext.get());
+ setGrContext(grContext);
+}
+
+void RenderThread::destroyGlContext() {
+ if (mEglManager->hasEglContext()) {
+ setGrContext(nullptr);
+ mEglManager->destroy();
+ }
+}
+
void RenderThread::dumpGraphicsMemory(int fd) {
globalProfileData()->dump(fd);
@@ -178,16 +211,6 @@ void RenderThread::dumpGraphicsMemory(int fd) {
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)");
@@ -209,21 +232,7 @@ void RenderThread::dumpGraphicsMemory(int fd) {
Readback& RenderThread::readback() {
if (!mReadback) {
- auto renderType = Properties::getRenderPipelineType();
- switch (renderType) {
- case RenderPipelineType::OpenGL:
- mReadback = new OpenGLReadbackImpl(*this);
- break;
- case RenderPipelineType::SkiaGL:
- mReadback = new skiapipeline::SkiaOpenGLReadback(*this);
- break;
- case RenderPipelineType::SkiaVulkan:
- mReadback = new skiapipeline::SkiaVulkanReadback(*this);
- break;
- default:
- LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
- break;
- }
+ mReadback = new Readback(*this);
}
return *mReadback;
@@ -232,9 +241,14 @@ Readback& RenderThread::readback() {
void RenderThread::setGrContext(sk_sp<GrContext> context) {
mCacheManager->reset(context);
if (mGrContext) {
+ mRenderState->onContextDestroyed();
mGrContext->releaseResourcesAndAbandonContext();
}
mGrContext = std::move(context);
+ if (mGrContext) {
+ mRenderState->onContextCreated();
+ DeviceInfo::setMaxTextureSize(mGrContext->maxRenderTargetSize());
+ }
}
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
@@ -347,10 +361,6 @@ void RenderThread::pushBackFrameCallback(IFrameCallback* callback) {
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:
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index e9c264917905..2384f9541ec0 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -74,7 +74,7 @@ class RenderThread : private ThreadBase {
PREVENT_COPY_AND_ASSIGN(RenderThread);
public:
- // Sets a callback that fires before any RenderThread setup has occured.
+ // Sets a callback that fires before any RenderThread setup has occurred.
ANDROID_API static void setOnStartHook(void (*onStartHook)());
WorkQueue& queue() { return ThreadBase::queue(); }
@@ -103,6 +103,9 @@ public:
sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap);
void dumpGraphicsMemory(int fd);
+ void requireGlContext();
+ void destroyGlContext();
+
/**
* isCurrent provides a way to query, if the caller is running on
* the render thread.
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 21c91a26745b..f96b1f897921 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -16,7 +16,6 @@
#include "VulkanManager.h"
-#include "DeviceInfo.h"
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
@@ -25,50 +24,293 @@
#include <GrBackendSurface.h>
#include <GrContext.h>
#include <GrTypes.h>
+#include <GrTypes.h>
+#include <vk/GrVkExtensions.h>
#include <vk/GrVkTypes.h>
namespace android {
namespace uirenderer {
namespace renderthread {
-#define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F)
-#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F)
+#define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F)
+#define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F)
+#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F)
VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
void VulkanManager::destroy() {
- if (!hasVkContext()) return;
-
- mRenderThread.renderState().onVkContextDestroyed();
mRenderThread.setGrContext(nullptr);
+ // We don't need to explicitly free the command buffer since it automatically gets freed when we
+ // delete the VkCommandPool below.
+ mDummyCB = VK_NULL_HANDLE;
+
if (VK_NULL_HANDLE != mCommandPool) {
- mDestroyCommandPool(mBackendContext->fDevice, mCommandPool, nullptr);
+ mDestroyCommandPool(mDevice, mCommandPool, nullptr);
mCommandPool = VK_NULL_HANDLE;
}
- mBackendContext.reset();
+
+ if (mDevice != VK_NULL_HANDLE) {
+ mDeviceWaitIdle(mDevice);
+ mDestroyDevice(mDevice, nullptr);
+ }
+
+ if (mInstance != VK_NULL_HANDLE) {
+ mDestroyInstance(mInstance, nullptr);
+ }
+
+ mGraphicsQueue = VK_NULL_HANDLE;
+ mPresentQueue = VK_NULL_HANDLE;
+ mDevice = VK_NULL_HANDLE;
+ mPhysicalDevice = VK_NULL_HANDLE;
+ mInstance = VK_NULL_HANDLE;
}
-void VulkanManager::initialize() {
- if (hasVkContext()) {
- return;
+bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFeatures2& features) {
+ VkResult err;
+
+ constexpr VkApplicationInfo app_info = {
+ VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType
+ nullptr, // pNext
+ "android framework", // pApplicationName
+ 0, // applicationVersion
+ "android framework", // pEngineName
+ 0, // engineVerison
+ VK_MAKE_VERSION(1, 1, 0), // apiVersion
+ };
+
+ std::vector<const char*> instanceExtensions;
+ {
+ GET_PROC(EnumerateInstanceExtensionProperties);
+
+ uint32_t extensionCount = 0;
+ err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
+ if (VK_SUCCESS != err) {
+ return false;
+ }
+ std::unique_ptr<VkExtensionProperties[]> extensions(
+ new VkExtensionProperties[extensionCount]);
+ err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.get());
+ if (VK_SUCCESS != err) {
+ return false;
+ }
+ bool hasKHRSurfaceExtension = false;
+ bool hasKHRAndroidSurfaceExtension = false;
+ for (uint32_t i = 0; i < extensionCount; ++i) {
+ instanceExtensions.push_back(extensions[i].extensionName);
+ if (!strcmp(extensions[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) {
+ hasKHRSurfaceExtension = true;
+ }
+ if (!strcmp(extensions[i].extensionName,VK_KHR_ANDROID_SURFACE_EXTENSION_NAME)) {
+ hasKHRAndroidSurfaceExtension = true;
+ }
+ }
+ if (!hasKHRSurfaceExtension || !hasKHRAndroidSurfaceExtension) {
+ this->destroy();
+ return false;
+ }
+ }
+
+ const VkInstanceCreateInfo instance_create = {
+ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType
+ nullptr, // pNext
+ 0, // flags
+ &app_info, // pApplicationInfo
+ 0, // enabledLayerNameCount
+ nullptr, // ppEnabledLayerNames
+ (uint32_t) instanceExtensions.size(), // enabledExtensionNameCount
+ instanceExtensions.data(), // ppEnabledExtensionNames
+ };
+
+ GET_PROC(CreateInstance);
+ err = mCreateInstance(&instance_create, nullptr, &mInstance);
+ if (err < 0) {
+ this->destroy();
+ return false;
}
- auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; };
+ GET_INST_PROC(DestroyInstance);
+ GET_INST_PROC(EnumeratePhysicalDevices);
+ GET_INST_PROC(GetPhysicalDeviceProperties);
+ GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
+ GET_INST_PROC(GetPhysicalDeviceFeatures2);
+ GET_INST_PROC(CreateDevice);
+ GET_INST_PROC(EnumerateDeviceExtensionProperties);
+ GET_INST_PROC(CreateAndroidSurfaceKHR);
+ GET_INST_PROC(DestroySurfaceKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfaceSupportKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
+
+ uint32_t gpuCount;
+ err = mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr);
+ if (err) {
+ this->destroy();
+ return false;
+ }
+ if (!gpuCount) {
+ this->destroy();
+ return false;
+ }
+ // Just returning the first physical device instead of getting the whole array. Since there
+ // should only be one device on android.
+ gpuCount = 1;
+ err = mEnumeratePhysicalDevices(mInstance, &gpuCount, &mPhysicalDevice);
+ // VK_INCOMPLETE is returned when the count we provide is less than the total device count.
+ if (err && VK_INCOMPLETE != err) {
+ this->destroy();
+ return false;
+ }
- mBackendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, vkGetDeviceProcAddr,
- &mPresentQueueIndex, canPresent));
- LOG_ALWAYS_FATAL_IF(!mBackendContext.get());
+ VkPhysicalDeviceProperties physDeviceProperties;
+ mGetPhysicalDeviceProperties(mPhysicalDevice, &physDeviceProperties);
+ if (physDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
+ this->destroy();
+ return false;
+ }
- // Get all the addresses of needed vulkan functions
- VkInstance instance = mBackendContext->fInstance;
- VkDevice device = mBackendContext->fDevice;
- GET_PROC(CreateAndroidSurfaceKHR);
- GET_PROC(DestroySurfaceKHR);
- GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
- GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
- GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
- GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
+ // query to get the initial queue props size
+ uint32_t queueCount;
+ mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
+ if (!queueCount) {
+ this->destroy();
+ return false;
+ }
+
+ // now get the actual queue props
+ std::unique_ptr<VkQueueFamilyProperties[]> queueProps(new VkQueueFamilyProperties[queueCount]);
+ mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, queueProps.get());
+
+ // iterate to find the graphics queue
+ mGraphicsQueueIndex = queueCount;
+ for (uint32_t i = 0; i < queueCount; i++) {
+ if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ mGraphicsQueueIndex = i;
+ break;
+ }
+ }
+ if (mGraphicsQueueIndex == queueCount) {
+ this->destroy();
+ return false;
+ }
+
+ // All physical devices and queue families on Android must be capable of
+ // presentation with any native window. So just use the first one.
+ mPresentQueueIndex = 0;
+
+ std::vector<const char*> deviceExtensions;
+ {
+ uint32_t extensionCount = 0;
+ err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
+ nullptr);
+ if (VK_SUCCESS != err) {
+ this->destroy();
+ return false;
+ }
+ std::unique_ptr<VkExtensionProperties[]> extensions(
+ new VkExtensionProperties[extensionCount]);
+ err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
+ extensions.get());
+ if (VK_SUCCESS != err) {
+ this->destroy();
+ return false;
+ }
+ bool hasKHRSwapchainExtension = false;
+ for (uint32_t i = 0; i < extensionCount; ++i) {
+ deviceExtensions.push_back(extensions[i].extensionName);
+ if (!strcmp(extensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
+ hasKHRSwapchainExtension = true;
+ }
+ }
+ if (!hasKHRSwapchainExtension) {
+ this->destroy();
+ return false;
+ }
+ }
+
+ auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
+ if (device != VK_NULL_HANDLE) {
+ return vkGetDeviceProcAddr(device, proc_name);
+ }
+ return vkGetInstanceProcAddr(instance, proc_name);
+ };
+ grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(),
+ instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data());
+
+ if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
+ this->destroy();
+ return false;
+ }
+
+ memset(&features, 0, sizeof(VkPhysicalDeviceFeatures2));
+ features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ features.pNext = nullptr;
+
+ // Setup all extension feature structs we may want to use.
+ void** tailPNext = &features.pNext;
+
+ if (grExtensions.hasExtension(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME, 2)) {
+ VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT* blend;
+ blend = (VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT*) malloc(
+ sizeof(VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT));
+ LOG_ALWAYS_FATAL_IF(!blend);
+ blend->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT;
+ blend->pNext = nullptr;
+ *tailPNext = blend;
+ tailPNext = &blend->pNext;
+ }
+
+ // query to get the physical device features
+ mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features);
+ // this looks like it would slow things down,
+ // and we can't depend on it on all platforms
+ features.features.robustBufferAccess = VK_FALSE;
+
+ float queuePriorities[1] = { 0.0 };
+
+ const VkDeviceQueueCreateInfo queueInfo[2] = {
+ {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
+ nullptr, // pNext
+ 0, // VkDeviceQueueCreateFlags
+ mGraphicsQueueIndex, // queueFamilyIndex
+ 1, // queueCount
+ queuePriorities, // pQueuePriorities
+ },
+ {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
+ nullptr, // pNext
+ 0, // VkDeviceQueueCreateFlags
+ mPresentQueueIndex, // queueFamilyIndex
+ 1, // queueCount
+ queuePriorities, // pQueuePriorities
+ }
+ };
+ uint32_t queueInfoCount = (mPresentQueueIndex != mGraphicsQueueIndex) ? 2 : 1;
+
+ const VkDeviceCreateInfo deviceInfo = {
+ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType
+ &features, // pNext
+ 0, // VkDeviceCreateFlags
+ queueInfoCount, // queueCreateInfoCount
+ queueInfo, // pQueueCreateInfos
+ 0, // layerCount
+ nullptr, // ppEnabledLayerNames
+ (uint32_t) deviceExtensions.size(), // extensionCount
+ deviceExtensions.data(), // ppEnabledExtensionNames
+ nullptr, // ppEnabledFeatures
+ };
+
+ err = mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice);
+ if (err) {
+ this->destroy();
+ return false;
+ }
+
+ GET_DEV_PROC(GetDeviceQueue);
+ GET_DEV_PROC(DeviceWaitIdle);
+ GET_DEV_PROC(DestroyDevice);
GET_DEV_PROC(CreateSwapchainKHR);
GET_DEV_PROC(DestroySwapchainKHR);
GET_DEV_PROC(GetSwapchainImagesKHR);
@@ -88,39 +330,103 @@ void VulkanManager::initialize() {
GET_DEV_PROC(DeviceWaitIdle);
GET_DEV_PROC(CreateSemaphore);
GET_DEV_PROC(DestroySemaphore);
+ GET_DEV_PROC(ImportSemaphoreFdKHR);
+ GET_DEV_PROC(GetSemaphoreFdKHR);
GET_DEV_PROC(CreateFence);
GET_DEV_PROC(DestroyFence);
GET_DEV_PROC(WaitForFences);
GET_DEV_PROC(ResetFences);
+ return true;
+}
+
+static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) {
+ // All Vulkan structs that could be part of the features chain will start with the
+ // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
+ // so we can get access to the pNext for the next struct.
+ struct CommonVulkanHeader {
+ VkStructureType sType;
+ void* pNext;
+ };
+
+ void* pNext = features.pNext;
+ while (pNext) {
+ void* current = pNext;
+ pNext = static_cast<CommonVulkanHeader*>(current)->pNext;
+ free(current);
+ }
+}
+
+void VulkanManager::initialize() {
+ if (mDevice != VK_NULL_HANDLE) {
+ return;
+ }
+
+ GET_PROC(EnumerateInstanceVersion);
+ uint32_t instanceVersion = 0;
+ LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
+ LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
+
+ GrVkExtensions extensions;
+ VkPhysicalDeviceFeatures2 features;
+ LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, features));
+
+ mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
+
+ auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
+ if (device != VK_NULL_HANDLE) {
+ return vkGetDeviceProcAddr(device, proc_name);
+ }
+ return vkGetInstanceProcAddr(instance, proc_name);
+ };
+
+ GrVkBackendContext backendContext;
+ backendContext.fInstance = mInstance;
+ backendContext.fPhysicalDevice = mPhysicalDevice;
+ backendContext.fDevice = mDevice;
+ backendContext.fQueue = mGraphicsQueue;
+ backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
+ backendContext.fInstanceVersion = instanceVersion;
+ backendContext.fVkExtensions = &extensions;
+ backendContext.fDeviceFeatures2 = &features;
+ backendContext.fGetProc = std::move(getProc);
+
// create the command pool for the command buffers
if (VK_NULL_HANDLE == mCommandPool) {
VkCommandPoolCreateInfo commandPoolInfo;
memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo));
commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
// this needs to be on the render queue
- commandPoolInfo.queueFamilyIndex = mBackendContext->fGraphicsQueueIndex;
+ commandPoolInfo.queueFamilyIndex = mGraphicsQueueIndex;
commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
- SkDEBUGCODE(VkResult res =) mCreateCommandPool(mBackendContext->fDevice, &commandPoolInfo,
- nullptr, &mCommandPool);
+ SkDEBUGCODE(VkResult res =) mCreateCommandPool(mDevice, &commandPoolInfo, nullptr,
+ &mCommandPool);
SkASSERT(VK_SUCCESS == res);
}
+ LOG_ALWAYS_FATAL_IF(mCommandPool == VK_NULL_HANDLE);
+
+ if (!setupDummyCommandBuffer()) {
+ this->destroy();
+ return;
+ }
+ LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
+
- mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue);
+ mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
GrContextOptions options;
options.fDisableDistanceFieldPaths = true;
- mRenderThread.cacheManager().configureContext(&options);
- sk_sp<GrContext> grContext(GrContext::MakeVulkan(mBackendContext, options));
+ // TODO: get a string describing the SPIR-V compiler version and use it here
+ mRenderThread.cacheManager().configureContext(&options, nullptr, 0);
+ sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options));
LOG_ALWAYS_FATAL_IF(!grContext.get());
mRenderThread.setGrContext(grContext);
- DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
+
+ free_features_extensions_structs(features);
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
mSwapBehavior = SwapBehavior::BufferAge;
}
-
- mRenderThread.renderState().onVkContextCreated();
}
// Returns the next BackbufferInfo to use for the next draw. The function will make sure all
@@ -138,8 +444,7 @@ VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurfa
// Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely
// reuse its commands buffers.
- VkResult res =
- mWaitForFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX);
+ VkResult res = mWaitForFences(mDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX);
if (res != VK_SUCCESS) {
return nullptr;
}
@@ -153,12 +458,12 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) {
VkResult res;
- res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
+ res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
SkASSERT(VK_SUCCESS == res);
// The acquire will signal the attached mAcquireSemaphore. We use this to know the image has
// finished presenting and that it is safe to begin sending new commands to the returned image.
- res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
+ res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
&backbuffer->mImageIndex);
@@ -173,11 +478,11 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) {
return nullptr;
}
backbuffer = getAvailableBackbuffer(surface);
- res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
+ res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
SkASSERT(VK_SUCCESS == res);
// acquire the image
- res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
+ res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
&backbuffer->mImageIndex);
@@ -189,13 +494,11 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) {
// set up layout transfer from initial to color attachment
VkImageLayout layout = surface->mImageInfos[backbuffer->mImageIndex].mImageLayout;
SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout);
- VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout)
- ? VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
- : VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
- VkAccessFlags srcAccessMask =
- (VK_IMAGE_LAYOUT_UNDEFINED == layout) ? 0 : VK_ACCESS_MEMORY_READ_BIT;
- VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ VkAccessFlags srcAccessMask = 0;
+ VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkImageMemoryBarrier imageMemoryBarrier = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
@@ -205,7 +508,7 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) {
layout, // oldLayout
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout
mPresentQueueIndex, // srcQueueFamilyIndex
- mBackendContext->fGraphicsQueueIndex, // dstQueueFamilyIndex
+ mGraphicsQueueIndex, // dstQueueFamilyIndex
surface->mImages[backbuffer->mImageIndex], // image
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange
};
@@ -236,14 +539,17 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) {
submitInfo.signalSemaphoreCount = 0;
// Attach first fence to submission here so we can track when the command buffer finishes.
- mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
// We need to notify Skia that we changed the layout of the wrapped VkImage
- GrVkImageInfo* imageInfo;
sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface;
- skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
- SkSurface::kFlushRead_BackendHandleAccess);
- imageInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ GrBackendRenderTarget backendRT = skSurface->getBackendRenderTarget(
+ SkSurface::kFlushRead_BackendHandleAccess);
+ if (!backendRT.isValid()) {
+ SkASSERT(backendRT.isValid());
+ return nullptr;
+ }
+ backendRT.setVkImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
surface->mBackbuffer = std::move(skSurface);
return surface->mBackbuffer.get();
@@ -252,17 +558,14 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) {
void VulkanManager::destroyBuffers(VulkanSurface* surface) {
if (surface->mBackbuffers) {
for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
- mWaitForFences(mBackendContext->fDevice, 2, surface->mBackbuffers[i].mUsageFences, true,
- UINT64_MAX);
+ mWaitForFences(mDevice, 2, surface->mBackbuffers[i].mUsageFences, true, UINT64_MAX);
surface->mBackbuffers[i].mImageIndex = -1;
- mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mAcquireSemaphore,
- nullptr);
- mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mRenderSemaphore,
- nullptr);
- mFreeCommandBuffers(mBackendContext->fDevice, mCommandPool, 2,
- surface->mBackbuffers[i].mTransitionCmdBuffers);
- mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
- mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
+ mDestroySemaphore(mDevice, surface->mBackbuffers[i].mAcquireSemaphore, nullptr);
+ mDestroySemaphore(mDevice, surface->mBackbuffers[i].mRenderSemaphore, nullptr);
+ mFreeCommandBuffers(mDevice, mCommandPool, 2,
+ surface->mBackbuffers[i].mTransitionCmdBuffers);
+ mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
+ mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
}
}
@@ -279,29 +582,27 @@ void VulkanManager::destroySurface(VulkanSurface* surface) {
if (VK_NULL_HANDLE != mPresentQueue) {
mQueueWaitIdle(mPresentQueue);
}
- mDeviceWaitIdle(mBackendContext->fDevice);
+ mDeviceWaitIdle(mDevice);
destroyBuffers(surface);
if (VK_NULL_HANDLE != surface->mSwapchain) {
- mDestroySwapchainKHR(mBackendContext->fDevice, surface->mSwapchain, nullptr);
+ mDestroySwapchainKHR(mDevice, surface->mSwapchain, nullptr);
surface->mSwapchain = VK_NULL_HANDLE;
}
if (VK_NULL_HANDLE != surface->mVkSurface) {
- mDestroySurfaceKHR(mBackendContext->fInstance, surface->mVkSurface, nullptr);
+ mDestroySurfaceKHR(mInstance, surface->mVkSurface, nullptr);
surface->mVkSurface = VK_NULL_HANDLE;
}
delete surface;
}
void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) {
- mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount,
- nullptr);
+ mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, nullptr);
SkASSERT(surface->mImageCount);
surface->mImages = new VkImage[surface->mImageCount];
- mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount,
- surface->mImages);
+ mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, surface->mImages);
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
@@ -320,7 +621,9 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt
VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
- mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin, nullptr, &props);
+ mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin,
+ surface->mColorMode == ColorMode::WideColorGamut ? kRGBA_F16_SkColorType
+ : kRGBA_8888_SkColorType, nullptr, &props);
}
SkASSERT(mCommandPool != VK_NULL_HANDLE);
@@ -350,15 +653,15 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt
for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
SkDEBUGCODE(VkResult res);
surface->mBackbuffers[i].mImageIndex = -1;
- SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
&surface->mBackbuffers[i].mAcquireSemaphore);
- SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
&surface->mBackbuffers[i].mRenderSemaphore);
- SkDEBUGCODE(res =) mAllocateCommandBuffers(mBackendContext->fDevice, &commandBuffersInfo,
+ SkDEBUGCODE(res =) mAllocateCommandBuffers(mDevice, &commandBuffersInfo,
surface->mBackbuffers[i].mTransitionCmdBuffers);
- SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
&surface->mBackbuffers[i].mUsageFences[0]);
- SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
&surface->mBackbuffers[i].mUsageFences[1]);
SkASSERT(VK_SUCCESS == res);
}
@@ -368,35 +671,35 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt
bool VulkanManager::createSwapchain(VulkanSurface* surface) {
// check for capabilities
VkSurfaceCapabilitiesKHR caps;
- VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mBackendContext->fPhysicalDevice,
+ VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice,
surface->mVkSurface, &caps);
if (VK_SUCCESS != res) {
return false;
}
uint32_t surfaceFormatCount;
- res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
+ res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
&surfaceFormatCount, nullptr);
if (VK_SUCCESS != res) {
return false;
}
FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount);
- res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
+ res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
&surfaceFormatCount, surfaceFormats.data());
if (VK_SUCCESS != res) {
return false;
}
uint32_t presentModeCount;
- res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+ res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
surface->mVkSurface, &presentModeCount, nullptr);
if (VK_SUCCESS != res) {
return false;
}
FatVector<VkPresentModeKHR, VK_PRESENT_MODE_RANGE_SIZE_KHR> presentModes(presentModeCount);
- res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+ res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
surface->mVkSurface, &presentModeCount,
presentModes.data());
if (VK_SUCCESS != res) {
@@ -435,37 +738,27 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) {
? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
- // Pick our surface format. For now, just make sure it matches our sRGB request:
- VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
+ VkFormat surfaceFormat = VK_FORMAT_R8G8B8A8_UNORM;
VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
-
- bool wantSRGB = false;
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
- wantSRGB = true;
-#endif
+ if (surface->mColorMode == ColorMode::WideColorGamut) {
+ surfaceFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
+ colorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT;
+ }
+ bool foundSurfaceFormat = false;
for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
- // We are assuming we can get either R8G8B8A8_UNORM or R8G8B8A8_SRGB
- VkFormat desiredFormat = wantSRGB ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
- if (desiredFormat == surfaceFormats[i].format) {
- surfaceFormat = surfaceFormats[i].format;
- colorSpace = surfaceFormats[i].colorSpace;
+ if (surfaceFormat == surfaceFormats[i].format
+ && colorSpace == surfaceFormats[i].colorSpace) {
+ foundSurfaceFormat = true;
+ break;
}
}
- if (VK_FORMAT_UNDEFINED == surfaceFormat) {
+ if (!foundSurfaceFormat) {
return false;
}
- // If mailbox mode is available, use it, as it is the lowest-latency non-
- // tearing mode. If not, fall back to FIFO which is always available.
+ // FIFO is always available and will match what we do on GL so just pick that here.
VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
- for (uint32_t i = 0; i < presentModeCount; ++i) {
- // use mailbox
- if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) {
- mode = presentModes[i];
- break;
- }
- }
VkSwapchainCreateInfoKHR swapchainCreateInfo;
memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
@@ -478,8 +771,8 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) {
swapchainCreateInfo.imageArrayLayers = 1;
swapchainCreateInfo.imageUsage = usageFlags;
- uint32_t queueFamilies[] = {mBackendContext->fGraphicsQueueIndex, mPresentQueueIndex};
- if (mBackendContext->fGraphicsQueueIndex != mPresentQueueIndex) {
+ uint32_t queueFamilies[] = {mGraphicsQueueIndex, mPresentQueueIndex};
+ if (mGraphicsQueueIndex != mPresentQueueIndex) {
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
swapchainCreateInfo.queueFamilyIndexCount = 2;
swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
@@ -495,19 +788,18 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) {
swapchainCreateInfo.clipped = true;
swapchainCreateInfo.oldSwapchain = surface->mSwapchain;
- res = mCreateSwapchainKHR(mBackendContext->fDevice, &swapchainCreateInfo, nullptr,
- &surface->mSwapchain);
+ res = mCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &surface->mSwapchain);
if (VK_SUCCESS != res) {
return false;
}
// destroy the old swapchain
if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
- mDeviceWaitIdle(mBackendContext->fDevice);
+ mDeviceWaitIdle(mDevice);
destroyBuffers(surface);
- mDestroySwapchainKHR(mBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
+ mDestroySwapchainKHR(mDevice, swapchainCreateInfo.oldSwapchain, nullptr);
}
createBuffers(surface, surfaceFormat, extent);
@@ -515,14 +807,14 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) {
return true;
}
-VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) {
+VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode) {
initialize();
if (!window) {
return nullptr;
}
- VulkanSurface* surface = new VulkanSurface();
+ VulkanSurface* surface = new VulkanSurface(colorMode);
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
@@ -531,20 +823,18 @@ VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) {
surfaceCreateInfo.flags = 0;
surfaceCreateInfo.window = window;
- VkResult res = mCreateAndroidSurfaceKHR(mBackendContext->fInstance, &surfaceCreateInfo, nullptr,
- &surface->mVkSurface);
+ VkResult res = mCreateAndroidSurfaceKHR(mInstance, &surfaceCreateInfo, nullptr,
+ &surface->mVkSurface);
if (VK_SUCCESS != res) {
delete surface;
return nullptr;
}
SkDEBUGCODE(VkBool32 supported; res = mGetPhysicalDeviceSurfaceSupportKHR(
- mBackendContext->fPhysicalDevice, mPresentQueueIndex,
- surface->mVkSurface, &supported);
- // All physical devices and queue families on Android must be capable of
- // presentation with any
- // native window.
- SkASSERT(VK_SUCCESS == res && supported););
+ mPhysicalDevice, mPresentQueueIndex, surface->mVkSurface, &supported);
+ // All physical devices and queue families on Android must be capable of
+ // presentation with any native window.
+ SkASSERT(VK_SUCCESS == res && supported););
if (!createSwapchain(surface)) {
destroySurface(surface);
@@ -555,17 +845,19 @@ VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) {
}
// Helper to know which src stage flags we need to set when transitioning to the present layout
-static VkPipelineStageFlags layoutToPipelineStageFlags(const VkImageLayout layout) {
+static VkPipelineStageFlags layoutToPipelineSrcStageFlags(const VkImageLayout layout) {
if (VK_IMAGE_LAYOUT_GENERAL == layout) {
return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
} else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout ||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
return VK_PIPELINE_STAGE_TRANSFER_BIT;
- } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout ||
- VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout ||
- VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout ||
- VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
- return VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
+ } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) {
+ return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout ||
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout) {
+ return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+ } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
+ return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
return VK_PIPELINE_STAGE_HOST_BIT;
}
@@ -601,26 +893,31 @@ static VkAccessFlags layoutToSrcAccessMask(const VkImageLayout layout) {
void VulkanManager::swapBuffers(VulkanSurface* surface) {
if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
ATRACE_NAME("Finishing GPU work");
- mDeviceWaitIdle(mBackendContext->fDevice);
+ mDeviceWaitIdle(mDevice);
}
SkASSERT(surface->mBackbuffers);
VulkanSurface::BackbufferInfo* backbuffer =
surface->mBackbuffers + surface->mCurrentBackbufferIndex;
- GrVkImageInfo* imageInfo;
+
SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get();
- skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
- SkSurface::kFlushRead_BackendHandleAccess);
+ GrBackendRenderTarget backendRT = skSurface->getBackendRenderTarget(
+ SkSurface::kFlushRead_BackendHandleAccess);
+ SkASSERT(backendRT.isValid());
+
+ GrVkImageInfo imageInfo;
+ SkAssertResult(backendRT.getVkImageInfo(&imageInfo));
+
// Check to make sure we never change the actually wrapped image
- SkASSERT(imageInfo->fImage == surface->mImages[backbuffer->mImageIndex]);
+ SkASSERT(imageInfo.fImage == surface->mImages[backbuffer->mImageIndex]);
// We need to transition the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR and make sure that all
// previous work is complete for before presenting. So we first add the necessary barrier here.
- VkImageLayout layout = imageInfo->fImageLayout;
- VkPipelineStageFlags srcStageMask = layoutToPipelineStageFlags(layout);
+ VkImageLayout layout = imageInfo.fImageLayout;
+ VkPipelineStageFlags srcStageMask = layoutToPipelineSrcStageFlags(layout);
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
VkAccessFlags srcAccessMask = layoutToSrcAccessMask(layout);
- VkAccessFlags dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
+ VkAccessFlags dstAccessMask = 0;
VkImageMemoryBarrier imageMemoryBarrier = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
@@ -629,7 +926,7 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) {
dstAccessMask, // inputMask
layout, // oldLayout
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout
- mBackendContext->fGraphicsQueueIndex, // srcQueueFamilyIndex
+ mGraphicsQueueIndex, // srcQueueFamilyIndex
mPresentQueueIndex, // dstQueueFamilyIndex
surface->mImages[backbuffer->mImageIndex], // image
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange
@@ -661,7 +958,7 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) {
submitInfo.pSignalSemaphores = &backbuffer->mRenderSemaphore;
// Attach second fence to submission here so we can track when the command buffer finishes.
- mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
// Submit present operation to present queue. We use a semaphore here to make sure all rendering
// to the image is complete and that the layout has been change to present on the graphics
@@ -697,6 +994,160 @@ int VulkanManager::getAge(VulkanSurface* surface) {
return surface->mCurrentTime - lastUsed;
}
+bool VulkanManager::setupDummyCommandBuffer() {
+ if (mDummyCB != VK_NULL_HANDLE) {
+ return true;
+ }
+
+ VkCommandBufferAllocateInfo commandBuffersInfo;
+ memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo));
+ commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ commandBuffersInfo.pNext = nullptr;
+ commandBuffersInfo.commandPool = mCommandPool;
+ commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ commandBuffersInfo.commandBufferCount = 1;
+
+ VkResult err = mAllocateCommandBuffers(mDevice, &commandBuffersInfo, &mDummyCB);
+ if (err != VK_SUCCESS) {
+ // It is probably unnecessary to set this back to VK_NULL_HANDLE, but we set it anyways to
+ // make sure the driver didn't set a value and then return a failure.
+ mDummyCB = VK_NULL_HANDLE;
+ return false;
+ }
+
+ VkCommandBufferBeginInfo beginInfo;
+ memset(&beginInfo, 0, sizeof(VkCommandBufferBeginInfo));
+ beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
+
+ mBeginCommandBuffer(mDummyCB, &beginInfo);
+ mEndCommandBuffer(mDummyCB);
+ return true;
+}
+
+status_t VulkanManager::fenceWait(sp<Fence>& fence) {
+ if (!hasVkContext()) {
+ ALOGE("VulkanManager::fenceWait: VkDevice not initialized");
+ return INVALID_OPERATION;
+ }
+
+ // Block GPU on the fence.
+ int fenceFd = fence->dup();
+ if (fenceFd == -1) {
+ ALOGE("VulkanManager::fenceWait: error dup'ing fence fd: %d", errno);
+ return -errno;
+ }
+
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = nullptr;
+ semaphoreInfo.flags = 0;
+ VkSemaphore semaphore;
+ VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+ if (VK_SUCCESS != err) {
+ ALOGE("Failed to create import semaphore, err: %d", err);
+ return UNKNOWN_ERROR;
+ }
+ VkImportSemaphoreFdInfoKHR importInfo;
+ importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
+ importInfo.pNext = nullptr;
+ importInfo.semaphore = semaphore;
+ importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
+ importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+ importInfo.fd = fenceFd;
+
+ err = mImportSemaphoreFdKHR(mDevice, &importInfo);
+ if (VK_SUCCESS != err) {
+ ALOGE("Failed to import semaphore, err: %d", err);
+ return UNKNOWN_ERROR;
+ }
+
+ LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
+
+ VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.waitSemaphoreCount = 1;
+ // Wait to make sure aquire semaphore set above has signaled.
+ submitInfo.pWaitSemaphores = &semaphore;
+ submitInfo.pWaitDstStageMask = &waitDstStageFlags;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &mDummyCB;
+ submitInfo.signalSemaphoreCount = 0;
+
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
+
+ // On Android when we import a semaphore, it is imported using temporary permanence. That
+ // means as soon as we queue the semaphore for a wait it reverts to its previous permanent
+ // state before importing. This means it will now be in an idle state with no pending
+ // signal or wait operations, so it is safe to immediately delete it.
+ mDestroySemaphore(mDevice, semaphore, nullptr);
+ return OK;
+}
+
+status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence) {
+ if (!hasVkContext()) {
+ ALOGE("VulkanManager::createReleaseFence: VkDevice not initialized");
+ return INVALID_OPERATION;
+ }
+
+ VkExportSemaphoreCreateInfo exportInfo;
+ exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
+ exportInfo.pNext = nullptr;
+ exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = &exportInfo;
+ semaphoreInfo.flags = 0;
+ VkSemaphore semaphore;
+ VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+ if (VK_SUCCESS != err) {
+ ALOGE("VulkanManager::createReleaseFence: Failed to create semaphore");
+ return INVALID_OPERATION;
+ }
+
+ LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
+
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.waitSemaphoreCount = 0;
+ submitInfo.pWaitSemaphores = nullptr;
+ submitInfo.pWaitDstStageMask = nullptr;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &mDummyCB;
+ submitInfo.signalSemaphoreCount = 1;
+ submitInfo.pSignalSemaphores = &semaphore;
+
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
+
+ VkSemaphoreGetFdInfoKHR getFdInfo;
+ getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
+ getFdInfo.pNext = nullptr;
+ getFdInfo.semaphore = semaphore;
+ getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ int fenceFd = 0;
+
+ err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
+ if (VK_SUCCESS != err) {
+ ALOGE("VulkanManager::createReleaseFence: Failed to get semaphore Fd");
+ return INVALID_OPERATION;
+ }
+ nativeFence = new Fence(fenceFd);
+
+ // Exporting a semaphore with copy transference via vkGetSemahporeFdKHR, has the same effect of
+ // destroying the semaphore and creating a new one with the same handle, and the payloads
+ // ownership is move to the Fd we created. Thus the semahpore is in a state that we can delete
+ // it and we don't need to wait on the command buffer we submitted to finish.
+ mDestroySemaphore(mDevice, semaphore, nullptr);
+
+ return OK;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index c319c9ec209f..6702649402e6 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -17,10 +17,18 @@
#ifndef VULKANMANAGER_H
#define VULKANMANAGER_H
+#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
+# define VK_USE_PLATFORM_ANDROID_KHR
+#endif
+#include <vulkan/vulkan.h>
+
#include <SkSurface.h>
+#include <ui/Fence.h>
+#include <utils/StrongPointer.h>
#include <vk/GrVkBackendContext.h>
+#include "IRenderPipeline.h"
-#include <vulkan/vulkan.h>
+class GrVkExtensions;
namespace android {
namespace uirenderer {
@@ -30,7 +38,7 @@ class RenderThread;
class VulkanSurface {
public:
- VulkanSurface() {}
+ VulkanSurface(ColorMode colorMode) : mColorMode(colorMode) {}
sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
@@ -66,6 +74,7 @@ private:
VkImage* mImages = nullptr;
ImageInfo* mImageInfos;
uint16_t mCurrentTime = 0;
+ ColorMode mColorMode;
};
// This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -79,11 +88,11 @@ public:
void initialize();
// Quick check to see if the VulkanManager has been initialized.
- bool hasVkContext() { return mBackendContext.get() != nullptr; }
+ bool hasVkContext() { return mDevice != VK_NULL_HANDLE; }
// Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
// VulkanSurface object which is returned.
- VulkanSurface* createSurface(ANativeWindow* window);
+ VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode);
// Destroy the VulkanSurface and all associated vulkan objects.
void destroySurface(VulkanSurface* surface);
@@ -105,12 +114,22 @@ public:
// Presents the current VkImage.
void swapBuffers(VulkanSurface* surface);
+ // Inserts a wait on fence command into the Vulkan command buffer.
+ status_t fenceWait(sp<Fence>& fence);
+
+ // Creates a fence that is signaled, when all the pending Vulkan commands are flushed.
+ status_t createReleaseFence(sp<Fence>& nativeFence);
+
private:
friend class RenderThread;
explicit VulkanManager(RenderThread& thread);
~VulkanManager() { destroy(); }
+ // Sets up the VkInstance and VkDevice objects. Also fills out the passed in
+ // VkPhysicalDeviceFeatures struct.
+ bool setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&);
+
void destroyBuffers(VulkanSurface* surface);
bool createSwapchain(VulkanSurface* surface);
@@ -118,6 +137,8 @@ private:
VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface);
+ bool setupDummyCommandBuffer();
+
// simple wrapper class that exists only to initialize a pointer to NULL
template <typename FNPTR_TYPE>
class VkPtr {
@@ -148,7 +169,23 @@ private:
VkPtr<PFN_vkQueuePresentKHR> mQueuePresentKHR;
VkPtr<PFN_vkCreateSharedSwapchainsKHR> mCreateSharedSwapchainsKHR;
- // Additional vulkan functions
+ // Instance Functions
+ VkPtr<PFN_vkEnumerateInstanceVersion> mEnumerateInstanceVersion;
+ VkPtr<PFN_vkEnumerateInstanceExtensionProperties> mEnumerateInstanceExtensionProperties;
+ VkPtr<PFN_vkCreateInstance> mCreateInstance;
+
+ VkPtr<PFN_vkDestroyInstance> mDestroyInstance;
+ VkPtr<PFN_vkEnumeratePhysicalDevices> mEnumeratePhysicalDevices;
+ VkPtr<PFN_vkGetPhysicalDeviceProperties> mGetPhysicalDeviceProperties;
+ VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties;
+ VkPtr<PFN_vkGetPhysicalDeviceFeatures2> mGetPhysicalDeviceFeatures2;
+ VkPtr<PFN_vkCreateDevice> mCreateDevice;
+ VkPtr<PFN_vkEnumerateDeviceExtensionProperties> mEnumerateDeviceExtensionProperties;
+
+ // Device Functions
+ VkPtr<PFN_vkGetDeviceQueue> mGetDeviceQueue;
+ VkPtr<PFN_vkDeviceWaitIdle> mDeviceWaitIdle;
+ VkPtr<PFN_vkDestroyDevice> mDestroyDevice;
VkPtr<PFN_vkCreateCommandPool> mCreateCommandPool;
VkPtr<PFN_vkDestroyCommandPool> mDestroyCommandPool;
VkPtr<PFN_vkAllocateCommandBuffers> mAllocateCommandBuffers;
@@ -158,13 +195,13 @@ private:
VkPtr<PFN_vkEndCommandBuffer> mEndCommandBuffer;
VkPtr<PFN_vkCmdPipelineBarrier> mCmdPipelineBarrier;
- VkPtr<PFN_vkGetDeviceQueue> mGetDeviceQueue;
VkPtr<PFN_vkQueueSubmit> mQueueSubmit;
VkPtr<PFN_vkQueueWaitIdle> mQueueWaitIdle;
- VkPtr<PFN_vkDeviceWaitIdle> mDeviceWaitIdle;
VkPtr<PFN_vkCreateSemaphore> mCreateSemaphore;
VkPtr<PFN_vkDestroySemaphore> mDestroySemaphore;
+ VkPtr<PFN_vkImportSemaphoreFdKHR> mImportSemaphoreFdKHR;
+ VkPtr<PFN_vkGetSemaphoreFdKHR> mGetSemaphoreFdKHR;
VkPtr<PFN_vkCreateFence> mCreateFence;
VkPtr<PFN_vkDestroyFence> mDestroyFence;
VkPtr<PFN_vkWaitForFences> mWaitForFences;
@@ -172,11 +209,18 @@ private:
RenderThread& mRenderThread;
- sk_sp<const GrVkBackendContext> mBackendContext;
+ VkInstance mInstance = VK_NULL_HANDLE;
+ VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
+ VkDevice mDevice = VK_NULL_HANDLE;
+
+ uint32_t mGraphicsQueueIndex;
+ VkQueue mGraphicsQueue = VK_NULL_HANDLE;
uint32_t mPresentQueueIndex;
VkQueue mPresentQueue = VK_NULL_HANDLE;
VkCommandPool mCommandPool = VK_NULL_HANDLE;
+ VkCommandBuffer mDummyCB = VK_NULL_HANDLE;
+
enum class SwapBehavior {
Discard,
BufferAge,
diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp
new file mode 100644
index 000000000000..c8220c6cb0d4
--- /dev/null
+++ b/libs/hwui/surfacetexture/EGLConsumer.cpp
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2018 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 <inttypes.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
+#include <private/gui/SyncFeatures.h>
+#include "EGLConsumer.h"
+#include "SurfaceTexture.h"
+
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
+#define EGL_PROTECTED_CONTENT_EXT 0x32C0
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+static const struct {
+ uint32_t width, height;
+ char const* bits;
+} kDebugData = {15, 12,
+ "_______________"
+ "_______________"
+ "_____XX_XX_____"
+ "__X_X_____X_X__"
+ "__X_XXXXXXX_X__"
+ "__XXXXXXXXXXX__"
+ "___XX_XXX_XX___"
+ "____XXXXXXX____"
+ "_____X___X_____"
+ "____X_____X____"
+ "_______________"
+ "_______________"};
+
+Mutex EGLConsumer::sStaticInitLock;
+sp<GraphicBuffer> EGLConsumer::sReleasedTexImageBuffer;
+
+static bool hasEglProtectedContentImpl() {
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+ size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR);
+ size_t extsLen = strlen(exts);
+ bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts);
+ bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1);
+ bool atEnd = (cropExtLen + 1) < extsLen &&
+ !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1));
+ bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " ");
+ return equal || atStart || atEnd || inMiddle;
+}
+
+static bool hasEglProtectedContent() {
+ // Only compute whether the extension is present once the first time this
+ // function is called.
+ static bool hasIt = hasEglProtectedContentImpl();
+ return hasIt;
+}
+
+EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {}
+
+status_t EGLConsumer::updateTexImage(SurfaceTexture& st) {
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = checkAndUpdateEglStateLocked(st);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ BufferItem item;
+
+ // Acquire the next buffer.
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ err = st.acquireBufferLocked(&item, 0);
+ if (err != NO_ERROR) {
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ // We always bind the texture even if we don't update its contents.
+ EGC_LOGV("updateTexImage: no buffers were available");
+ glBindTexture(st.mTexTarget, st.mTexName);
+ err = NO_ERROR;
+ } else {
+ EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
+ }
+ return err;
+ }
+
+ // Release the previous buffer.
+ err = updateAndReleaseLocked(item, nullptr, st);
+ if (err != NO_ERROR) {
+ // We always bind the texture.
+ glBindTexture(st.mTexTarget, st.mTexName);
+ return err;
+ }
+
+ // Bind the new buffer to the GL texture, and wait until it's ready.
+ return bindTextureImageLocked(st);
+}
+
+status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) {
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = NO_ERROR;
+
+ // if we're detached, no need to validate EGL's state -- we won't use it.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ err = checkAndUpdateEglStateLocked(st, true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ // Update the EGLConsumer state.
+ int buf = st.mCurrentTexture;
+ if (buf != BufferQueue::INVALID_BUFFER_SLOT) {
+ EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode);
+
+ // if we're detached, we just use the fence that was created in detachFromContext()
+ // so... basically, nothing more to do here.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ // Do whatever sync ops we need to do before releasing the slot.
+ err = syncForReleaseLocked(mEglDisplay, st);
+ if (err != NO_ERROR) {
+ EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err);
+ return err;
+ }
+ }
+
+ err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay,
+ EGL_NO_SYNC_KHR);
+ if (err < NO_ERROR) {
+ EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ if (mReleasedTexImage == nullptr) {
+ mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+ }
+
+ st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+ mCurrentTextureImage = mReleasedTexImage;
+ st.mCurrentCrop.makeInvalid();
+ st.mCurrentTransform = 0;
+ st.mCurrentTimestamp = 0;
+ st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN;
+ st.mCurrentFence = Fence::NO_FENCE;
+ st.mCurrentFenceTime = FenceTime::NO_FENCE;
+
+ // detached, don't touch the texture (and we may not even have an
+ // EGLDisplay here.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ // This binds a dummy buffer (mReleasedTexImage).
+ status_t result = bindTextureImageLocked(st);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+sp<GraphicBuffer> EGLConsumer::getDebugTexImageBuffer() {
+ Mutex::Autolock _l(sStaticInitLock);
+ if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) {
+ // The first time, create the debug texture in case the application
+ // continues to use it.
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
+ GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]");
+ uint32_t* bits;
+ buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
+ uint32_t stride = buffer->getStride();
+ uint32_t height = buffer->getHeight();
+ memset(bits, 0, stride * height * 4);
+ for (uint32_t y = 0; y < kDebugData.height; y++) {
+ for (uint32_t x = 0; x < kDebugData.width; x++) {
+ bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000
+ : 0xFFFFFFFF;
+ }
+ bits += stride;
+ }
+ buffer->unlock();
+ sReleasedTexImageBuffer = buffer;
+ }
+ return sReleasedTexImageBuffer;
+}
+
+void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) {
+ // If item->mGraphicBuffer is not null, this buffer has not been acquired
+ // before, so any prior EglImage created is using a stale buffer. This
+ // replaces any old EglImage with a new one (using the new buffer).
+ int slot = item->mSlot;
+ if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) {
+ mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+ }
+}
+
+void EGLConsumer::onReleaseBufferLocked(int buf) {
+ mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
+}
+
+status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease,
+ SurfaceTexture& st) {
+ status_t err = NO_ERROR;
+
+ int slot = item.mSlot;
+
+ if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) {
+ EGC_LOGE(
+ "updateAndRelease: EGLConsumer is not attached to an OpenGL "
+ "ES context");
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return INVALID_OPERATION;
+ }
+
+ // Confirm state.
+ err = checkAndUpdateEglStateLocked(st);
+ if (err != NO_ERROR) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return err;
+ }
+
+ // Ensure we have a valid EglImageKHR for the slot, creating an EglImage
+ // if nessessary, for the gralloc buffer currently in the slot in
+ // ConsumerBase.
+ // We may have to do this even when item.mGraphicBuffer == NULL (which
+ // means the buffer was previously acquired).
+ err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay);
+ if (err != NO_ERROR) {
+ EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay,
+ slot);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return UNKNOWN_ERROR;
+ }
+
+ // Do whatever sync ops we need to do before releasing the old slot.
+ if (slot != st.mCurrentTexture) {
+ err = syncForReleaseLocked(mEglDisplay, st);
+ if (err != NO_ERROR) {
+ // Release the buffer we just acquired. It's not safe to
+ // release the old buffer, so instead we just drop the new frame.
+ // As we are still under lock since acquireBuffer, it is safe to
+ // release by slot.
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay,
+ EGL_NO_SYNC_KHR);
+ return err;
+ }
+ }
+
+ EGC_LOGV(
+ "updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture,
+ mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : nullptr,
+ slot, st.mSlots[slot].mGraphicBuffer->handle);
+
+ // Hang onto the pointer so that it isn't freed in the call to
+ // releaseBufferLocked() if we're in shared buffer mode and both buffers are
+ // the same.
+ sp<EglImage> nextTextureImage = mEglSlots[slot].mEglImage;
+
+ // release old buffer
+ if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ if (pendingRelease == nullptr) {
+ status_t status = st.releaseBufferLocked(
+ st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay,
+ mEglSlots[st.mCurrentTexture].mEglFence);
+ if (status < NO_ERROR) {
+ EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
+ status);
+ err = status;
+ // keep going, with error raised [?]
+ }
+ } else {
+ pendingRelease->currentTexture = st.mCurrentTexture;
+ pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer();
+ pendingRelease->display = mEglDisplay;
+ pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence;
+ pendingRelease->isPending = true;
+ }
+ }
+
+ // Update the EGLConsumer state.
+ st.mCurrentTexture = slot;
+ mCurrentTextureImage = nextTextureImage;
+ st.mCurrentCrop = item.mCrop;
+ st.mCurrentTransform = item.mTransform;
+ st.mCurrentScalingMode = item.mScalingMode;
+ st.mCurrentTimestamp = item.mTimestamp;
+ st.mCurrentDataSpace = item.mDataSpace;
+ st.mCurrentFence = item.mFence;
+ st.mCurrentFenceTime = item.mFenceTime;
+ st.mCurrentFrameNumber = item.mFrameNumber;
+
+ st.computeCurrentTransformMatrixLocked();
+
+ return err;
+}
+
+status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) {
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ ALOGE("bindTextureImage: invalid display");
+ return INVALID_OPERATION;
+ }
+
+ GLenum error;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error);
+ }
+
+ glBindTexture(st.mTexTarget, st.mTexName);
+ if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
+ EGC_LOGE("bindTextureImage: no currently-bound texture");
+ return NO_INIT;
+ }
+
+ status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay);
+ if (err != NO_ERROR) {
+ EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+ st.mCurrentTexture);
+ return UNKNOWN_ERROR;
+ }
+ mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+
+ // In the rare case that the display is terminated and then initialized
+ // again, we can't detect that the display changed (it didn't), but the
+ // image is invalid. In this case, repeat the exact same steps while
+ // forcing the creation of a new image.
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ glBindTexture(st.mTexTarget, st.mTexName);
+ status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true);
+ if (result != NO_ERROR) {
+ EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+ st.mCurrentTexture);
+ return UNKNOWN_ERROR;
+ }
+ mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ EGC_LOGE("bindTextureImage: error binding external image: %#04x", error);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ // Wait for the new buffer to be ready.
+ return doGLFenceWaitLocked(st);
+}
+
+status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (!contextCheck) {
+ // if this is the first time we're called, mEglDisplay/mEglContext have
+ // never been set, so don't error out (below).
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ mEglDisplay = dpy;
+ }
+ if (mEglContext == EGL_NO_CONTEXT) {
+ mEglContext = ctx;
+ }
+ }
+
+ if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) {
+ EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) {
+ EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ mEglDisplay = dpy;
+ mEglContext = ctx;
+ return NO_ERROR;
+}
+
+status_t EGLConsumer::detachFromContext(SurfaceTexture& st) {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) {
+ EGC_LOGE("detachFromContext: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) {
+ EGC_LOGE("detachFromContext: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) {
+ status_t err = syncForReleaseLocked(dpy, st);
+ if (err != OK) {
+ return err;
+ }
+
+ glDeleteTextures(1, &st.mTexName);
+ }
+
+ mEglDisplay = EGL_NO_DISPLAY;
+ mEglContext = EGL_NO_CONTEXT;
+
+ return OK;
+}
+
+status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) {
+ // Initialize mCurrentTextureImage if there is a current buffer from past attached state.
+ int slot = st.mCurrentTexture;
+ if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+ if (!mEglSlots[slot].mEglImage.get()) {
+ mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+ }
+ mCurrentTextureImage = mEglSlots[slot].mEglImage;
+ }
+
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (dpy == EGL_NO_DISPLAY) {
+ EGC_LOGE("attachToContext: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (ctx == EGL_NO_CONTEXT) {
+ EGC_LOGE("attachToContext: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ // We need to bind the texture regardless of whether there's a current
+ // buffer.
+ glBindTexture(st.mTexTarget, GLuint(tex));
+
+ mEglDisplay = dpy;
+ mEglContext = ctx;
+ st.mTexName = tex;
+ st.mOpMode = SurfaceTexture::OpMode::attachedToGL;
+
+ if (mCurrentTextureImage != nullptr) {
+ // This may wait for a buffer a second time. This is likely required if
+ // this is a different context, since otherwise the wait could be skipped
+ // by bouncing through another context. For the same context the extra
+ // wait is redundant.
+ status_t err = bindTextureImageLocked(st);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) {
+ EGC_LOGV("syncForReleaseLocked");
+
+ if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ if (SyncFeatures::getInstance().useNativeFenceSync()) {
+ EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync);
+ eglDestroySyncKHR(dpy, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ EGC_LOGE(
+ "syncForReleaseLocked: error dup'ing native fence "
+ "fd: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ sp<Fence> fence(new Fence(fenceFd));
+ status_t err = st.addReleaseFenceLocked(st.mCurrentTexture,
+ mCurrentTextureImage->graphicBuffer(), fence);
+ if (err != OK) {
+ EGC_LOGE(
+ "syncForReleaseLocked: error adding release fence: "
+ "%s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+ } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) {
+ EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence;
+ if (fence != EGL_NO_SYNC_KHR) {
+ // There is already a fence for the current slot. We need to
+ // wait on that before replacing it with another fence to
+ // ensure that all outstanding buffer accesses have completed
+ // before the producer accesses it.
+ EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
+ if (result == EGL_FALSE) {
+ EGC_LOGE(
+ "syncForReleaseLocked: error waiting for previous "
+ "fence: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ EGC_LOGE(
+ "syncForReleaseLocked: timeout waiting for previous "
+ "fence");
+ return TIMED_OUT;
+ }
+ eglDestroySyncKHR(dpy, fence);
+ }
+
+ // Create a fence for the outstanding accesses in the current
+ // OpenGL ES context.
+ fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
+ if (fence == EGL_NO_SYNC_KHR) {
+ EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ mEglSlots[st.mCurrentTexture].mEglFence = fence;
+ }
+ }
+
+ return OK;
+}
+
+status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) {
+ EGC_LOGE("doGLFenceWait: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) {
+ EGC_LOGE("doGLFenceWait: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ if (st.mCurrentFence->isValid()) {
+ if (SyncFeatures::getInstance().useWaitSync() &&
+ SyncFeatures::getInstance().useNativeFenceSync()) {
+ // Create an EGLSyncKHR from the current fence.
+ int fenceFd = st.mCurrentFence->dup();
+ if (fenceFd == -1) {
+ EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno);
+ return -errno;
+ }
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+ EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ close(fenceFd);
+ EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ // XXX: The spec draft is inconsistent as to whether this should
+ // return an EGLint or void. Ignore the return value for now, as
+ // it's not strictly needed.
+ eglWaitSyncKHR(dpy, sync, 0);
+ EGLint eglErr = eglGetError();
+ eglDestroySyncKHR(dpy, sync);
+ if (eglErr != EGL_SUCCESS) {
+ EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr);
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked");
+ if (err != NO_ERROR) {
+ EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err);
+ return err;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+void EGLConsumer::onFreeBufferLocked(int slotIndex) {
+ mEglSlots[slotIndex].mEglImage.clear();
+}
+
+void EGLConsumer::onAbandonLocked() {
+ mCurrentTextureImage.clear();
+}
+
+EGLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer)
+ : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {}
+
+EGLConsumer::EglImage::~EglImage() {
+ if (mEglImage != EGL_NO_IMAGE_KHR) {
+ if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+ ALOGE("~EglImage: eglDestroyImageKHR failed");
+ }
+ eglTerminate(mEglDisplay);
+ }
+}
+
+status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) {
+ // If there's an image and it's no longer valid, destroy it.
+ bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
+ bool displayInvalid = mEglDisplay != eglDisplay;
+ if (haveImage && (displayInvalid || forceCreation)) {
+ if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+ ALOGE("createIfNeeded: eglDestroyImageKHR failed");
+ }
+ eglTerminate(mEglDisplay);
+ mEglImage = EGL_NO_IMAGE_KHR;
+ mEglDisplay = EGL_NO_DISPLAY;
+ }
+
+ // If there's no image, create one.
+ if (mEglImage == EGL_NO_IMAGE_KHR) {
+ mEglDisplay = eglDisplay;
+ mEglImage = createImage(mEglDisplay, mGraphicBuffer);
+ }
+
+ // Fail if we can't create a valid image.
+ if (mEglImage == EGL_NO_IMAGE_KHR) {
+ mEglDisplay = EGL_NO_DISPLAY;
+ const sp<GraphicBuffer>& buffer = mGraphicBuffer;
+ ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+ buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
+ buffer->getPixelFormat());
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) {
+ glEGLImageTargetTexture2DOES(texTarget, static_cast<GLeglImageOES>(mEglImage));
+}
+
+EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy,
+ const sp<GraphicBuffer>& graphicBuffer) {
+ EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer());
+ const bool createProtectedImage =
+ (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent();
+ EGLint attrs[] = {
+ EGL_IMAGE_PRESERVED_KHR,
+ EGL_TRUE,
+ createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
+ createProtectedImage ? EGL_TRUE : EGL_NONE,
+ EGL_NONE,
+ };
+ eglInitialize(dpy, nullptr, nullptr);
+ EGLImageKHR image =
+ eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
+ if (image == EGL_NO_IMAGE_KHR) {
+ EGLint error = eglGetError();
+ ALOGE("error creating EGLImage: %#x", error);
+ eglTerminate(dpy);
+ }
+ return image;
+}
+
+}; // namespace android
diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h
new file mode 100644
index 000000000000..eccb08298f6f
--- /dev/null
+++ b/libs/hwui/surfacetexture/EGLConsumer.h
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <gui/BufferQueueDefs.h>
+
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+class SurfaceTexture;
+
+/*
+ * EGLConsumer implements the parts of SurfaceTexture that deal with
+ * textures attached to an GL context.
+ */
+class EGLConsumer {
+public:
+ EGLConsumer();
+
+ /**
+ * updateTexImage acquires the most recently queued buffer, and sets the
+ * image contents of the target texture to it.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ *
+ * This calls doGLFenceWait to ensure proper synchronization.
+ */
+ status_t updateTexImage(SurfaceTexture& st);
+
+ /*
+ * releaseTexImage releases the texture acquired in updateTexImage().
+ * This is intended to be used in single buffer mode.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ */
+ status_t releaseTexImage(SurfaceTexture& st);
+
+ /**
+ * detachFromContext detaches the EGLConsumer from the calling thread's
+ * current OpenGL ES context. This context must be the same as the context
+ * that was current for previous calls to updateTexImage.
+ *
+ * Detaching a EGLConsumer from an OpenGL ES context will result in the
+ * deletion of the OpenGL ES texture object into which the images were being
+ * streamed. After a EGLConsumer has been detached from the OpenGL ES
+ * context calls to updateTexImage will fail returning INVALID_OPERATION
+ * until the EGLConsumer is attached to a new OpenGL ES context using the
+ * attachToContext method.
+ */
+ status_t detachFromContext(SurfaceTexture& st);
+
+ /**
+ * attachToContext attaches a EGLConsumer that is currently in the
+ * 'detached' state to the current OpenGL ES context. A EGLConsumer is
+ * in the 'detached' state iff detachFromContext has successfully been
+ * called and no calls to attachToContext have succeeded since the last
+ * detachFromContext call. Calls to attachToContext made on a
+ * EGLConsumer that is not in the 'detached' state will result in an
+ * INVALID_OPERATION error.
+ *
+ * The tex argument specifies the OpenGL ES texture object name in the
+ * new context into which the image contents will be streamed. A successful
+ * call to attachToContext will result in this texture object being bound to
+ * the texture target and populated with the image contents that were
+ * current at the time of the last call to detachFromContext.
+ */
+ status_t attachToContext(uint32_t tex, SurfaceTexture& st);
+
+ /**
+ * onAcquireBufferLocked amends the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase behavior.
+ */
+ void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st);
+
+ /**
+ * onReleaseBufferLocked amends the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase.
+ */
+ void onReleaseBufferLocked(int slot);
+
+ /**
+ * onFreeBufferLocked frees up the given buffer slot. If the slot has been
+ * initialized this will release the reference to the GraphicBuffer in that
+ * slot and destroy the EGLImage in that slot. Otherwise it has no effect.
+ */
+ void onFreeBufferLocked(int slotIndex);
+
+ /**
+ * onAbandonLocked amends the ConsumerBase method to clear
+ * mCurrentTextureImage in addition to the ConsumerBase behavior.
+ */
+ void onAbandonLocked();
+
+protected:
+ struct PendingRelease {
+ PendingRelease()
+ : isPending(false)
+ , currentTexture(-1)
+ , graphicBuffer()
+ , display(nullptr)
+ , fence(nullptr) {}
+
+ bool isPending;
+ int currentTexture;
+ sp<GraphicBuffer> graphicBuffer;
+ EGLDisplay display;
+ EGLSyncKHR fence;
+ };
+
+ /**
+ * This releases the buffer in the slot referenced by mCurrentTexture,
+ * then updates state to refer to the BufferItem, which must be a
+ * newly-acquired buffer. If pendingRelease is not null, the parameters
+ * which would have been passed to releaseBufferLocked upon the successful
+ * completion of the method will instead be returned to the caller, so that
+ * it may call releaseBufferLocked itself later.
+ */
+ status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease,
+ SurfaceTexture& st);
+
+ /**
+ * Binds mTexName and the current buffer to mTexTarget. Uses
+ * mCurrentTexture if it's set, mCurrentTextureImage if not. If the
+ * bind succeeds, this calls doGLFenceWait.
+ */
+ status_t bindTextureImageLocked(SurfaceTexture& st);
+
+ /**
+ * Gets the current EGLDisplay and EGLContext values, and compares them
+ * to mEglDisplay and mEglContext. If the fields have been previously
+ * set, the values must match; if not, the fields are set to the current
+ * values.
+ * The contextCheck argument is used to ensure that a GL context is
+ * properly set; when set to false, the check is not performed.
+ */
+ status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false);
+
+ /**
+ * EglImage is a utility class for tracking and creating EGLImageKHRs. There
+ * is primarily just one image per slot, but there is also special cases:
+ * - For releaseTexImage, we use a debug image (mReleasedTexImage)
+ * - After freeBuffer, we must still keep the current image/buffer
+ * Reference counting EGLImages lets us handle all these cases easily while
+ * also only creating new EGLImages from buffers when required.
+ */
+ class EglImage : public LightRefBase<EglImage> {
+ public:
+ EglImage(sp<GraphicBuffer> graphicBuffer);
+
+ /**
+ * createIfNeeded creates an EGLImage if required (we haven't created
+ * one yet, or the EGLDisplay or crop-rect has changed).
+ */
+ status_t createIfNeeded(EGLDisplay display, bool forceCreate = false);
+
+ /**
+ * This calls glEGLImageTargetTexture2DOES to bind the image to the
+ * texture in the specified texture target.
+ */
+ void bindToTextureTarget(uint32_t texTarget);
+
+ const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
+ const native_handle* graphicBufferHandle() {
+ return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
+ }
+
+ private:
+ // Only allow instantiation using ref counting.
+ friend class LightRefBase<EglImage>;
+ virtual ~EglImage();
+
+ // createImage creates a new EGLImage from a GraphicBuffer.
+ EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer);
+
+ // Disallow copying
+ EglImage(const EglImage& rhs);
+ void operator=(const EglImage& rhs);
+
+ // mGraphicBuffer is the buffer that was used to create this image.
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // mEglImage is the EGLImage created from mGraphicBuffer.
+ EGLImageKHR mEglImage;
+
+ // mEGLDisplay is the EGLDisplay that was used to create mEglImage.
+ EGLDisplay mEglDisplay;
+
+ // mCropRect is the crop rectangle passed to EGL when mEglImage
+ // was created.
+ Rect mCropRect;
+ };
+
+ /**
+ * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
+ * stream to ensure that it is safe for future OpenGL ES commands to
+ * access the current texture buffer.
+ */
+ status_t doGLFenceWaitLocked(SurfaceTexture& st) const;
+
+ /**
+ * syncForReleaseLocked performs the synchronization needed to release the
+ * current slot from an OpenGL ES context. If needed it will set the
+ * current slot's fence to guard against a producer accessing the buffer
+ * before the outstanding accesses have completed.
+ */
+ status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st);
+
+ /**
+ * returns a graphic buffer used when the texture image has been released
+ */
+ static sp<GraphicBuffer> getDebugTexImageBuffer();
+
+ /**
+ * The default consumer usage flags that EGLConsumer always sets on its
+ * BufferQueue instance; these will be OR:d with any additional flags passed
+ * from the EGLConsumer user. In particular, EGLConsumer will always
+ * consume buffers as hardware textures.
+ */
+ static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+ /**
+ * mCurrentTextureImage is the EglImage/buffer of the current texture. It's
+ * possible that this buffer is not associated with any buffer slot, so we
+ * must track it separately in order to support the getCurrentBuffer method.
+ */
+ sp<EglImage> mCurrentTextureImage;
+
+ /**
+ * EGLSlot contains the information and object references that
+ * EGLConsumer maintains about a BufferQueue buffer slot.
+ */
+ struct EglSlot {
+ EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+ /**
+ * mEglImage is the EGLImage created from mGraphicBuffer.
+ */
+ sp<EglImage> mEglImage;
+
+ /**
+ * mFence is the EGL sync object that must signal before the buffer
+ * associated with this buffer slot may be dequeued. It is initialized
+ * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
+ * on a compile-time option) set to a new sync object in updateTexImage.
+ */
+ EGLSyncKHR mEglFence;
+ };
+
+ /**
+ * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently
+ * associated. It is intialized to EGL_NO_DISPLAY and gets set to the
+ * current display when updateTexImage is called for the first time and when
+ * attachToContext is called.
+ */
+ EGLDisplay mEglDisplay;
+
+ /**
+ * mEglContext is the OpenGL ES context with which this EGLConsumer is
+ * currently associated. It is initialized to EGL_NO_CONTEXT and gets set
+ * to the current GL context when updateTexImage is called for the first
+ * time and when attachToContext is called.
+ */
+ EGLContext mEglContext;
+
+ /**
+ * mEGLSlots stores the buffers that have been allocated by the BufferQueue
+ * for each buffer slot. It is initialized to null pointers, and gets
+ * filled in with the result of BufferQueue::acquire when the
+ * client dequeues a buffer from a
+ * slot that has not yet been used. The buffer allocated to a slot will also
+ * be replaced if the requested buffer usage or geometry differs from that
+ * of the buffer allocated to a slot.
+ */
+ EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
+ /**
+ * protects static initialization
+ */
+ static Mutex sStaticInitLock;
+
+ /**
+ * mReleasedTexImageBuffer is a dummy buffer used when in single buffer
+ * mode and releaseTexImage() has been called
+ */
+ static sp<GraphicBuffer> sReleasedTexImageBuffer;
+ sp<EglImage> mReleasedTexImage;
+};
+
+}; // namespace android
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
new file mode 100644
index 000000000000..9ffccfb4d340
--- /dev/null
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 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 "ImageConsumer.h"
+#include <gui/BufferQueue.h>
+#include "Properties.h"
+#include "SurfaceTexture.h"
+#include "renderstate/RenderState.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/RenderThread.h"
+#include "renderthread/VulkanManager.h"
+
+// Macro for including the SurfaceTexture name in log messages
+#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+namespace android {
+
+void ImageConsumer::onFreeBufferLocked(int slotIndex) {
+ mImageSlots[slotIndex].mImage.reset();
+}
+
+void ImageConsumer::onAcquireBufferLocked(BufferItem* item) {
+ // If item->mGraphicBuffer is not null, this buffer has not been acquired
+ // before, so any prior SkImage is created with a stale buffer. This resets the stale SkImage.
+ if (item->mGraphicBuffer != nullptr) {
+ mImageSlots[item->mSlot].mImage.reset();
+ }
+}
+
+void ImageConsumer::onReleaseBufferLocked(int buf) {
+ mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
+}
+
+void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer) {
+ if (!mImage.get()) {
+ mImage = graphicBuffer.get()
+ ? SkImage::MakeFromAHardwareBuffer(
+ reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()),
+ kPremul_SkAlphaType)
+ : nullptr;
+ }
+}
+
+sk_sp<SkImage> ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st,
+ uirenderer::RenderState& renderState) {
+ BufferItem item;
+ status_t err;
+ err = st.acquireBufferLocked(&item, 0);
+ if (err != OK) {
+ if (err != BufferQueue::NO_BUFFER_AVAILABLE) {
+ IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
+ } else {
+ int slot = st.mCurrentTexture;
+ if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+ *queueEmpty = true;
+ mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+ return mImageSlots[slot].mImage;
+ }
+ }
+ return nullptr;
+ }
+
+ int slot = item.mSlot;
+ if (item.mFence->isValid()) {
+ // Wait on the producer fence for the buffer to be ready.
+ if (uirenderer::Properties::getRenderPipelineType() ==
+ uirenderer::RenderPipelineType::SkiaGL) {
+ err = renderState.getRenderThread().eglManager().fenceWait(item.mFence);
+ } else {
+ err = renderState.getRenderThread().vulkanManager().fenceWait(item.mFence);
+ }
+ if (err != OK) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+ }
+
+ // Release old buffer.
+ if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) {
+ // If needed, set the released slot's fence to guard against a producer accessing the
+ // buffer before the outstanding accesses have completed.
+ sp<Fence> releaseFence;
+ EGLDisplay display = EGL_NO_DISPLAY;
+ if (uirenderer::Properties::getRenderPipelineType() ==
+ uirenderer::RenderPipelineType::SkiaGL) {
+ auto& eglManager = renderState.getRenderThread().eglManager();
+ display = eglManager.eglDisplay();
+ err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].mEglFence,
+ releaseFence);
+ } else {
+ err = renderState.getRenderThread().vulkanManager().createReleaseFence(releaseFence);
+ }
+ if (OK != err) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+
+ if (releaseFence.get()) {
+ status_t err = st.addReleaseFenceLocked(
+ st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence);
+ if (err != OK) {
+ IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+ }
+
+ // Finally release the old buffer.
+ status_t status = st.releaseBufferLocked(
+ st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display,
+ mImageSlots[st.mCurrentTexture].mEglFence);
+ if (status < NO_ERROR) {
+ IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status);
+ err = status;
+ // Keep going, with error raised.
+ }
+ }
+
+ // Update the state.
+ st.mCurrentTexture = slot;
+ st.mCurrentCrop = item.mCrop;
+ st.mCurrentTransform = item.mTransform;
+ st.mCurrentScalingMode = item.mScalingMode;
+ st.mCurrentTimestamp = item.mTimestamp;
+ st.mCurrentDataSpace = item.mDataSpace;
+ st.mCurrentFence = item.mFence;
+ st.mCurrentFenceTime = item.mFenceTime;
+ st.mCurrentFrameNumber = item.mFrameNumber;
+ st.computeCurrentTransformMatrixLocked();
+
+ *queueEmpty = false;
+ mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+ return mImageSlots[slot].mImage;
+}
+
+} /* namespace android */
diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h
new file mode 100644
index 000000000000..31ee8db52874
--- /dev/null
+++ b/libs/hwui/surfacetexture/ImageConsumer.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <gui/BufferQueueDefs.h>
+
+#include <SkImage.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <system/graphics.h>
+
+namespace android {
+
+namespace uirenderer {
+class RenderState;
+}
+
+class SurfaceTexture;
+
+/*
+ * ImageConsumer implements the parts of SurfaceTexture that deal with
+ * images consumed by HWUI view system.
+ */
+class ImageConsumer {
+public:
+ sk_sp<SkImage> dequeueImage(bool* queueEmpty, SurfaceTexture& cb,
+ uirenderer::RenderState& renderState);
+
+ /**
+ * onAcquireBufferLocked amends the ConsumerBase method to update the
+ * mImageSlots array in addition to the ConsumerBase behavior.
+ */
+ void onAcquireBufferLocked(BufferItem* item);
+
+ /**
+ * onReleaseBufferLocked amends the ConsumerBase method to update the
+ * mImageSlots array in addition to the ConsumerBase.
+ */
+ void onReleaseBufferLocked(int slot);
+
+ /**
+ * onFreeBufferLocked frees up the given buffer slot. If the slot has been
+ * initialized this will release the reference to the GraphicBuffer in that
+ * slot and destroy the SkImage in that slot. Otherwise it has no effect.
+ */
+ void onFreeBufferLocked(int slotIndex);
+
+private:
+ /**
+ * ImageSlot contains the information and object references that
+ * ImageConsumer maintains about a BufferQueue buffer slot.
+ */
+ struct ImageSlot {
+ ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+ // mImage is the SkImage created from mGraphicBuffer.
+ sk_sp<SkImage> mImage;
+
+ /**
+ * mEglFence is the EGL sync object that must signal before the buffer
+ * associated with this buffer slot may be dequeued.
+ */
+ EGLSyncKHR mEglFence;
+
+ void createIfNeeded(sp<GraphicBuffer> graphicBuffer);
+ };
+
+ /**
+ * ImageConsumer stores the SkImages that have been allocated by the BufferQueue
+ * for each buffer slot. It is initialized to null pointers, and gets
+ * filled in with the result of BufferQueue::acquire when the
+ * client dequeues a buffer from a
+ * slot that has not yet been used. The buffer allocated to a slot will also
+ * be replaced if the requested buffer usage or geometry differs from that
+ * of the buffer allocated to a slot.
+ */
+ ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+};
+
+}; /* namespace android */
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp
new file mode 100644
index 000000000000..4bff715822e8
--- /dev/null
+++ b/libs/hwui/surfacetexture/SurfaceTexture.cpp
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2018 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 <cutils/compiler.h>
+#include <gui/BufferQueue.h>
+#include <math/mat4.h>
+#include <system/window.h>
+
+#include <utils/Trace.h>
+
+#include "Matrix.h"
+#include "SurfaceTexture.h"
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+
+static const mat4 mtxIdentity;
+
+SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
+ uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(bq, isControlledByApp)
+ , mCurrentCrop(Rect::EMPTY_RECT)
+ , mCurrentTransform(0)
+ , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE)
+ , mCurrentFence(Fence::NO_FENCE)
+ , mCurrentTimestamp(0)
+ , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN)
+ , mCurrentFrameNumber(0)
+ , mDefaultWidth(1)
+ , mDefaultHeight(1)
+ , mFilteringEnabled(true)
+ , mTexName(tex)
+ , mUseFenceSync(useFenceSync)
+ , mTexTarget(texTarget)
+ , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT)
+ , mOpMode(OpMode::attachedToGL) {
+ SFT_LOGV("SurfaceTexture");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget,
+ bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(bq, isControlledByApp)
+ , mCurrentCrop(Rect::EMPTY_RECT)
+ , mCurrentTransform(0)
+ , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE)
+ , mCurrentFence(Fence::NO_FENCE)
+ , mCurrentTimestamp(0)
+ , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN)
+ , mCurrentFrameNumber(0)
+ , mDefaultWidth(1)
+ , mDefaultHeight(1)
+ , mFilteringEnabled(true)
+ , mTexName(0)
+ , mUseFenceSync(useFenceSync)
+ , mTexTarget(texTarget)
+ , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT)
+ , mOpMode(OpMode::detached) {
+ SFT_LOGV("SurfaceTexture");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+ mDefaultWidth = w;
+ mDefaultHeight = h;
+ return mConsumer->setDefaultBufferSize(w, h);
+}
+
+status_t SurfaceTexture::updateTexImage() {
+ ATRACE_CALL();
+ SFT_LOGV("updateTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+
+ return mEGLConsumer.updateTexImage(*this);
+}
+
+status_t SurfaceTexture::releaseTexImage() {
+ // releaseTexImage can be invoked even when not attached to a GL context.
+ ATRACE_CALL();
+ SFT_LOGV("releaseTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+
+ return mEGLConsumer.releaseTexImage(*this);
+}
+
+status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+ uint64_t maxFrameNumber) {
+ status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ switch (mOpMode) {
+ case OpMode::attachedToView:
+ mImageConsumer.onAcquireBufferLocked(item);
+ break;
+ case OpMode::attachedToGL:
+ mEGLConsumer.onAcquireBufferLocked(item, *this);
+ break;
+ case OpMode::detached:
+ break;
+ }
+
+ return NO_ERROR;
+}
+
+status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer,
+ EGLDisplay display, EGLSyncKHR eglFence) {
+ // release the buffer if it hasn't already been discarded by the
+ // BufferQueue. This can happen, for example, when the producer of this
+ // buffer has reallocated the original buffer slot after this buffer
+ // was acquired.
+ status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence);
+ // We could be releasing an EGL buffer, even if not currently attached to a GL context.
+ mImageConsumer.onReleaseBufferLocked(buf);
+ mEGLConsumer.onReleaseBufferLocked(buf);
+ return err;
+}
+
+status_t SurfaceTexture::detachFromContext() {
+ ATRACE_CALL();
+ SFT_LOGV("detachFromContext");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("detachFromContext: abandoned SurfaceTexture");
+ return NO_INIT;
+ }
+
+ if (mOpMode != OpMode::attachedToGL) {
+ SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context");
+ return INVALID_OPERATION;
+ }
+
+ status_t err = mEGLConsumer.detachFromContext(*this);
+ if (err == OK) {
+ mOpMode = OpMode::detached;
+ }
+
+ return err;
+}
+
+status_t SurfaceTexture::attachToContext(uint32_t tex) {
+ ATRACE_CALL();
+ SFT_LOGV("attachToContext");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("attachToContext: abandoned SurfaceTexture");
+ return NO_INIT;
+ }
+
+ if (mOpMode != OpMode::detached) {
+ SFT_LOGE(
+ "attachToContext: SurfaceTexture is already attached to a "
+ "context");
+ return INVALID_OPERATION;
+ }
+
+ if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ // release possible ImageConsumer cache
+ mImageConsumer.onFreeBufferLocked(mCurrentTexture);
+ }
+
+ return mEGLConsumer.attachToContext(tex, *this);
+}
+
+void SurfaceTexture::attachToView() {
+ ATRACE_CALL();
+ Mutex::Autolock _l(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("attachToView: abandoned SurfaceTexture");
+ return;
+ }
+ if (mOpMode == OpMode::detached) {
+ mOpMode = OpMode::attachedToView;
+
+ if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ // release possible EGLConsumer texture cache
+ mEGLConsumer.onFreeBufferLocked(mCurrentTexture);
+ mEGLConsumer.onAbandonLocked();
+ }
+ } else {
+ SFT_LOGE("attachToView: already attached");
+ }
+}
+
+void SurfaceTexture::detachFromView() {
+ ATRACE_CALL();
+ Mutex::Autolock _l(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("detachFromView: abandoned SurfaceTexture");
+ return;
+ }
+
+ if (mOpMode == OpMode::attachedToView) {
+ mOpMode = OpMode::detached;
+ } else {
+ SFT_LOGE("detachFromView: not attached to View");
+ }
+}
+
+uint32_t SurfaceTexture::getCurrentTextureTarget() const {
+ return mTexTarget;
+}
+
+void SurfaceTexture::getTransformMatrix(float mtx[16]) {
+ Mutex::Autolock lock(mMutex);
+ memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+}
+
+void SurfaceTexture::setFilteringEnabled(bool enabled) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!");
+ return;
+ }
+ bool needsRecompute = mFilteringEnabled != enabled;
+ mFilteringEnabled = enabled;
+
+ if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
+ SFT_LOGD("setFilteringEnabled called with no current item");
+ }
+
+ if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ computeCurrentTransformMatrixLocked();
+ }
+}
+
+void SurfaceTexture::computeCurrentTransformMatrixLocked() {
+ SFT_LOGV("computeCurrentTransformMatrixLocked");
+ sp<GraphicBuffer> buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
+ ? nullptr
+ : mSlots[mCurrentTexture].mGraphicBuffer;
+ if (buf == nullptr) {
+ SFT_LOGD("computeCurrentTransformMatrixLocked: no current item");
+ }
+ computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform,
+ mFilteringEnabled);
+}
+
+void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+ const Rect& cropRect, uint32_t transform,
+ bool filtering) {
+ // Transform matrices
+ static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+ static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+
+ mat4 xform;
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ xform *= mtxFlipH;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ xform *= mtxFlipV;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ xform *= mtxRot90;
+ }
+
+ if (!cropRect.isEmpty() && buf.get()) {
+ float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
+ float bufferWidth = buf->getWidth();
+ float bufferHeight = buf->getHeight();
+ float shrinkAmount = 0.0f;
+ if (filtering) {
+ // In order to prevent bilinear sampling beyond the edge of the
+ // crop rectangle we may need to shrink it by 2 texels in each
+ // dimension. Normally this would just need to take 1/2 a texel
+ // off each end, but because the chroma channels of YUV420 images
+ // are subsampled we may need to shrink the crop region by a whole
+ // texel on each side.
+ switch (buf->getPixelFormat()) {
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_RGBX_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
+ case PIXEL_FORMAT_RGBA_1010102:
+ case PIXEL_FORMAT_RGB_888:
+ case PIXEL_FORMAT_RGB_565:
+ case PIXEL_FORMAT_BGRA_8888:
+ // We know there's no subsampling of any channels, so we
+ // only need to shrink by a half a pixel.
+ shrinkAmount = 0.5;
+ break;
+
+ default:
+ // If we don't recognize the format, we must assume the
+ // worst case (that we care about), which is YUV420.
+ shrinkAmount = 1.0;
+ break;
+ }
+ }
+
+ // Only shrink the dimensions that are not the size of the buffer.
+ if (cropRect.width() < bufferWidth) {
+ tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+ sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth;
+ }
+ if (cropRect.height() < bufferHeight) {
+ ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight;
+ sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight;
+ }
+
+ mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1);
+ xform = crop * xform;
+ }
+
+ // SurfaceFlinger expects the top of its window textures to be at a Y
+ // coordinate of 0, so SurfaceTexture must behave the same way. We don't
+ // want to expose this to applications, however, so we must add an
+ // additional vertical flip to the transform after all the other transforms.
+ xform = mtxFlipV * xform;
+
+ memcpy(outTransform, xform.asArray(), sizeof(xform));
+}
+
+Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) {
+ Rect outCrop = crop;
+
+ uint32_t newWidth = static_cast<uint32_t>(crop.width());
+ uint32_t newHeight = static_cast<uint32_t>(crop.height());
+
+ if (newWidth * bufferHeight > newHeight * bufferWidth) {
+ newWidth = newHeight * bufferWidth / bufferHeight;
+ ALOGV("too wide: newWidth = %d", newWidth);
+ } else if (newWidth * bufferHeight < newHeight * bufferWidth) {
+ newHeight = newWidth * bufferHeight / bufferWidth;
+ ALOGV("too tall: newHeight = %d", newHeight);
+ }
+
+ uint32_t currentWidth = static_cast<uint32_t>(crop.width());
+ uint32_t currentHeight = static_cast<uint32_t>(crop.height());
+
+ // The crop is too wide
+ if (newWidth < currentWidth) {
+ uint32_t dw = currentWidth - newWidth;
+ auto halfdw = dw / 2;
+ outCrop.left += halfdw;
+ // Not halfdw because it would subtract 1 too few when dw is odd
+ outCrop.right -= (dw - halfdw);
+ // The crop is too tall
+ } else if (newHeight < currentHeight) {
+ uint32_t dh = currentHeight - newHeight;
+ auto halfdh = dh / 2;
+ outCrop.top += halfdh;
+ // Not halfdh because it would subtract 1 too few when dh is odd
+ outCrop.bottom -= (dh - halfdh);
+ }
+
+ ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right,
+ outCrop.bottom);
+
+ return outCrop;
+}
+
+nsecs_t SurfaceTexture::getTimestamp() {
+ SFT_LOGV("getTimestamp");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTimestamp;
+}
+
+android_dataspace SurfaceTexture::getCurrentDataSpace() {
+ SFT_LOGV("getCurrentDataSpace");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentDataSpace;
+}
+
+uint64_t SurfaceTexture::getFrameNumber() {
+ SFT_LOGV("getFrameNumber");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFrameNumber;
+}
+
+Rect SurfaceTexture::getCurrentCrop() const {
+ Mutex::Autolock lock(mMutex);
+ return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
+ ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight)
+ : mCurrentCrop;
+}
+
+uint32_t SurfaceTexture::getCurrentTransform() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTransform;
+}
+
+uint32_t SurfaceTexture::getCurrentScalingMode() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentScalingMode;
+}
+
+sp<Fence> SurfaceTexture::getCurrentFence() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFence;
+}
+
+std::shared_ptr<FenceTime> SurfaceTexture::getCurrentFenceTime() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFenceTime;
+}
+
+void SurfaceTexture::freeBufferLocked(int slotIndex) {
+ SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+ if (slotIndex == mCurrentTexture) {
+ mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+ }
+ // The slotIndex buffer could have EGL or SkImage cache, but there is no way to tell for sure.
+ // Buffers can be freed after SurfaceTexture has detached from GL context or View.
+ mImageConsumer.onFreeBufferLocked(slotIndex);
+ mEGLConsumer.onFreeBufferLocked(slotIndex);
+ ConsumerBase::freeBufferLocked(slotIndex);
+}
+
+void SurfaceTexture::abandonLocked() {
+ SFT_LOGV("abandonLocked");
+ mEGLConsumer.onAbandonLocked();
+ ConsumerBase::abandonLocked();
+}
+
+status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) {
+ return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
+}
+
+void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const {
+ result.appendFormat(
+ "%smTexName=%d mCurrentTexture=%d\n"
+ "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
+ prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top,
+ mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform);
+
+ ConsumerBase::dumpLocked(result, prefix);
+}
+
+sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
+ bool* queueEmpty,
+ uirenderer::RenderState& renderState) {
+ Mutex::Autolock _l(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!");
+ return nullptr;
+ }
+
+ if (mOpMode != OpMode::attachedToView) {
+ SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View");
+ return nullptr;
+ }
+
+ auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState);
+ if (image.get()) {
+ uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix);
+ dataSpace = mCurrentDataSpace;
+ }
+ return image;
+}
+
+}; // namespace android
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h
new file mode 100644
index 000000000000..db392a9f8476
--- /dev/null
+++ b/libs/hwui/surfacetexture/SurfaceTexture.h
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gui/BufferQueueDefs.h>
+#include <gui/ConsumerBase.h>
+
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+
+#include <utils/Mutex.h>
+#include <utils/String8.h>
+
+#include "EGLConsumer.h"
+#include "ImageConsumer.h"
+
+namespace android {
+
+namespace uirenderer {
+class RenderState;
+}
+
+/*
+ * SurfaceTexture consumes buffers of graphics data from a BufferQueue,
+ * and makes them available to HWUI render thread as a SkImage and to
+ * an application GL render thread as an OpenGL texture.
+ *
+ * When attached to an application GL render thread, a typical usage
+ * pattern is to set up the SurfaceTexture with the
+ * desired options, and call updateTexImage() when a new frame is desired.
+ * If a new frame is available, the texture will be updated. If not,
+ * the previous contents are retained.
+ *
+ * When attached to a HWUI render thread, the TextureView implementation
+ * calls dequeueImage, which either pulls a new SkImage or returns the
+ * last cached SkImage if BufferQueue is empty.
+ * When attached to HWUI render thread, SurfaceTexture is compatible to
+ * both Vulkan and GL drawing pipelines.
+ */
+class ANDROID_API SurfaceTexture : public ConsumerBase {
+public:
+ enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES
+ typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
+
+ /**
+ * SurfaceTexture constructs a new SurfaceTexture object. If the constructor with
+ * the tex parameter is used, tex indicates the name of the OpenGL ES
+ * texture to which images are to be streamed. texTarget specifies the
+ * OpenGL ES texture target to which the texture will be bound in
+ * updateTexImage. useFenceSync specifies whether fences should be used to
+ * synchronize access to buffers if that behavior is enabled at
+ * compile-time.
+ *
+ * A SurfaceTexture may be detached from one OpenGL ES context and then
+ * attached to a different context using the detachFromContext and
+ * attachToContext methods, respectively. The intention of these methods is
+ * purely to allow a SurfaceTexture to be transferred from one consumer
+ * context to another. If such a transfer is not needed there is no
+ * requirement that either of these methods be called.
+ *
+ * If the constructor with the tex parameter is used, the SurfaceTexture is
+ * created in a state where it is considered attached to an OpenGL ES
+ * context for the purposes of the attachToContext and detachFromContext
+ * methods. However, despite being considered "attached" to a context, the
+ * specific OpenGL ES context doesn't get latched until the first call to
+ * updateTexImage. After that point, all calls to updateTexImage must be
+ * made with the same OpenGL ES context current.
+ *
+ * If the constructor without the tex parameter is used, the SurfaceTexture is
+ * created in a detached state, and attachToContext must be called before
+ * calls to updateTexImage.
+ */
+ SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texureTarget,
+ bool useFenceSync, bool isControlledByApp);
+
+ SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texureTarget, bool useFenceSync,
+ bool isControlledByApp);
+
+ /**
+ * updateTexImage acquires the most recently queued buffer, and sets the
+ * image contents of the target texture to it.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ *
+ * This calls doGLFenceWait to ensure proper synchronization.
+ */
+ status_t updateTexImage();
+
+ /**
+ * releaseTexImage releases the texture acquired in updateTexImage().
+ * This is intended to be used in single buffer mode.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ */
+ status_t releaseTexImage();
+
+ /**
+ * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix
+ * associated with the texture image set by the most recent call to
+ * updateTexImage.
+ *
+ * This transform matrix maps 2D homogeneous texture coordinates of the form
+ * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture
+ * coordinate that should be used to sample that location from the texture.
+ * Sampling the texture outside of the range of this transform is undefined.
+ *
+ * This transform is necessary to compensate for transforms that the stream
+ * content producer may implicitly apply to the content. By forcing users of
+ * a SurfaceTexture to apply this transform we avoid performing an extra
+ * copy of the data that would be needed to hide the transform from the
+ * user.
+ *
+ * The matrix is stored in column-major order so that it may be passed
+ * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv
+ * functions.
+ */
+ void getTransformMatrix(float mtx[16]);
+
+ /**
+ * Computes the transform matrix documented by getTransformMatrix
+ * from the BufferItem sub parts.
+ */
+ static void computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+ const Rect& cropRect, uint32_t transform, bool filtering);
+
+ /**
+ * Scale the crop down horizontally or vertically such that it has the
+ * same aspect ratio as the buffer does.
+ */
+ static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight);
+
+ /**
+ * getTimestamp retrieves the timestamp associated with the texture image
+ * set by the most recent call to updateTexImage.
+ *
+ * The timestamp is in nanoseconds, and is monotonically increasing. Its
+ * other semantics (zero point, etc) are source-dependent and should be
+ * documented by the source.
+ */
+ int64_t getTimestamp();
+
+ /**
+ * getDataSpace retrieves the DataSpace associated with the texture image
+ * set by the most recent call to updateTexImage.
+ */
+ android_dataspace getCurrentDataSpace();
+
+ /**
+ * getFrameNumber retrieves the frame number associated with the texture
+ * image set by the most recent call to updateTexImage.
+ *
+ * The frame number is an incrementing counter set to 0 at the creation of
+ * the BufferQueue associated with this consumer.
+ */
+ uint64_t getFrameNumber();
+
+ /**
+ * setDefaultBufferSize is used to set the size of buffers returned by
+ * requestBuffers when a with and height of zero is requested.
+ * A call to setDefaultBufferSize() may trigger requestBuffers() to
+ * be called from the client.
+ * The width and height parameters must be no greater than the minimum of
+ * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
+ * An error due to invalid dimensions might not be reported until
+ * updateTexImage() is called.
+ */
+ status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+
+ /**
+ * setFilteringEnabled sets whether the transform matrix should be computed
+ * for use with bilinear filtering.
+ */
+ void setFilteringEnabled(bool enabled);
+
+ /**
+ * getCurrentTextureTarget returns the texture target of the current
+ * texture as returned by updateTexImage().
+ */
+ uint32_t getCurrentTextureTarget() const;
+
+ /**
+ * getCurrentCrop returns the cropping rectangle of the current buffer.
+ */
+ Rect getCurrentCrop() const;
+
+ /**
+ * getCurrentTransform returns the transform of the current buffer.
+ */
+ uint32_t getCurrentTransform() const;
+
+ /**
+ * getCurrentScalingMode returns the scaling mode of the current buffer.
+ */
+ uint32_t getCurrentScalingMode() const;
+
+ /**
+ * getCurrentFence returns the fence indicating when the current buffer is
+ * ready to be read from.
+ */
+ sp<Fence> getCurrentFence() const;
+
+ /**
+ * getCurrentFence returns the FenceTime indicating when the current
+ * buffer is ready to be read from.
+ */
+ std::shared_ptr<FenceTime> getCurrentFenceTime() const;
+
+ /**
+ * setConsumerUsageBits overrides the ConsumerBase method to OR
+ * DEFAULT_USAGE_FLAGS to usage.
+ */
+ status_t setConsumerUsageBits(uint64_t usage);
+
+ /**
+ * detachFromContext detaches the SurfaceTexture from the calling thread's
+ * current OpenGL ES context. This context must be the same as the context
+ * that was current for previous calls to updateTexImage.
+ *
+ * Detaching a SurfaceTexture from an OpenGL ES context will result in the
+ * deletion of the OpenGL ES texture object into which the images were being
+ * streamed. After a SurfaceTexture has been detached from the OpenGL ES
+ * context calls to updateTexImage will fail returning INVALID_OPERATION
+ * until the SurfaceTexture is attached to a new OpenGL ES context using the
+ * attachToContext method.
+ */
+ status_t detachFromContext();
+
+ /**
+ * attachToContext attaches a SurfaceTexture that is currently in the
+ * 'detached' state to the current OpenGL ES context. A SurfaceTexture is
+ * in the 'detached' state iff detachFromContext has successfully been
+ * called and no calls to attachToContext have succeeded since the last
+ * detachFromContext call. Calls to attachToContext made on a
+ * SurfaceTexture that is not in the 'detached' state will result in an
+ * INVALID_OPERATION error.
+ *
+ * The tex argument specifies the OpenGL ES texture object name in the
+ * new context into which the image contents will be streamed. A successful
+ * call to attachToContext will result in this texture object being bound to
+ * the texture target and populated with the image contents that were
+ * current at the time of the last call to detachFromContext.
+ */
+ status_t attachToContext(uint32_t tex);
+
+ sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
+ bool* queueEmpty, uirenderer::RenderState& renderState);
+
+ /**
+ * attachToView attaches a SurfaceTexture that is currently in the
+ * 'detached' state to HWUI View system.
+ */
+ void attachToView();
+
+ /**
+ * detachFromView detaches a SurfaceTexture from HWUI View system.
+ */
+ void detachFromView();
+
+protected:
+ /**
+ * abandonLocked overrides the ConsumerBase method to clear
+ * mCurrentTextureImage in addition to the ConsumerBase behavior.
+ */
+ virtual void abandonLocked();
+
+ /**
+ * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture-
+ * specific info in addition to the ConsumerBase behavior.
+ */
+ virtual void dumpLocked(String8& result, const char* prefix) const override;
+
+ /**
+ * acquireBufferLocked overrides the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase behavior.
+ */
+ virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+ uint64_t maxFrameNumber = 0) override;
+
+ /**
+ * releaseBufferLocked overrides the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase.
+ */
+ virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
+ EGLDisplay display, EGLSyncKHR eglFence) override;
+
+ /**
+ * freeBufferLocked frees up the given buffer slot. If the slot has been
+ * initialized this will release the reference to the GraphicBuffer in that
+ * slot and destroy the EGLImage in that slot. Otherwise it has no effect.
+ *
+ * This method must be called with mMutex locked.
+ */
+ virtual void freeBufferLocked(int slotIndex);
+
+ /**
+ * computeCurrentTransformMatrixLocked computes the transform matrix for the
+ * current texture. It uses mCurrentTransform and the current GraphicBuffer
+ * to compute this matrix and stores it in mCurrentTransformMatrix.
+ * mCurrentTextureImage must not be NULL.
+ */
+ void computeCurrentTransformMatrixLocked();
+
+ /**
+ * The default consumer usage flags that SurfaceTexture always sets on its
+ * BufferQueue instance; these will be OR:d with any additional flags passed
+ * from the SurfaceTexture user. In particular, SurfaceTexture will always
+ * consume buffers as hardware textures.
+ */
+ static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+ /**
+ * mCurrentCrop is the crop rectangle that applies to the current texture.
+ * It gets set each time updateTexImage is called.
+ */
+ Rect mCurrentCrop;
+
+ /**
+ * mCurrentTransform is the transform identifier for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ uint32_t mCurrentTransform;
+
+ /**
+ * mCurrentScalingMode is the scaling mode for the current texture. It gets
+ * set each time updateTexImage is called.
+ */
+ uint32_t mCurrentScalingMode;
+
+ /**
+ * mCurrentFence is the fence received from BufferQueue in updateTexImage.
+ */
+ sp<Fence> mCurrentFence;
+
+ /**
+ * The FenceTime wrapper around mCurrentFence.
+ */
+ std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
+
+ /**
+ * mCurrentTransformMatrix is the transform matrix for the current texture.
+ * It gets computed by computeTransformMatrix each time updateTexImage is
+ * called.
+ */
+ float mCurrentTransformMatrix[16];
+
+ /**
+ * mCurrentTimestamp is the timestamp for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ int64_t mCurrentTimestamp;
+
+ /**
+ * mCurrentDataSpace is the dataspace for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ android_dataspace mCurrentDataSpace;
+
+ /**
+ * mCurrentFrameNumber is the frame counter for the current texture.
+ * It gets set each time updateTexImage is called.
+ */
+ uint64_t mCurrentFrameNumber;
+
+ uint32_t mDefaultWidth, mDefaultHeight;
+
+ /**
+ * mFilteringEnabled indicates whether the transform matrix is computed for
+ * use with bilinear filtering. It defaults to true and is changed by
+ * setFilteringEnabled().
+ */
+ bool mFilteringEnabled;
+
+ /**
+ * mTexName is the name of the OpenGL texture to which streamed images will
+ * be bound when updateTexImage is called. It is set at construction time
+ * and can be changed with a call to attachToContext.
+ */
+ uint32_t mTexName;
+
+ /**
+ * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
+ * extension should be used to prevent buffers from being dequeued before
+ * it's safe for them to be written. It gets set at construction time and
+ * never changes.
+ */
+ const bool mUseFenceSync;
+
+ /**
+ * mTexTarget is the GL texture target with which the GL texture object is
+ * associated. It is set in the constructor and never changed. It is
+ * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android
+ * Browser. In that case it is set to GL_TEXTURE_2D to allow
+ * glCopyTexSubImage to read from the texture. This is a hack to work
+ * around a GL driver limitation on the number of FBO attachments, which the
+ * browser's tile cache exceeds.
+ */
+ const uint32_t mTexTarget;
+
+ /**
+ * mCurrentTexture is the buffer slot index of the buffer that is currently
+ * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
+ * indicating that no buffer slot is currently bound to the texture. Note,
+ * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
+ * that no buffer is bound to the texture. A call to setBufferCount will
+ * reset mCurrentTexture to INVALID_BUFFER_SLOT.
+ */
+ int mCurrentTexture;
+
+ enum class OpMode { detached, attachedToView, attachedToGL };
+ /**
+ * mOpMode indicates whether the SurfaceTexture is currently attached to
+ * an OpenGL ES context or the HWUI view system. For legacy reasons, this is initialized to,
+ * "attachedToGL" indicating that the SurfaceTexture is considered to be attached to
+ * whatever GL context is current at the time of the first updateTexImage call.
+ * It is set to "detached" by detachFromContext, and then set to "attachedToGL" again by
+ * attachToContext.
+ * attachToView/detachFromView are used to attach/detach from HWUI view system.
+ */
+ OpMode mOpMode;
+
+ /**
+ * mEGLConsumer has SurfaceTexture logic used when attached to GL context.
+ */
+ EGLConsumer mEGLConsumer;
+
+ /**
+ * mImageConsumer has SurfaceTexture logic used when attached to HWUI view system.
+ */
+ ImageConsumer mImageConsumer;
+
+ friend class ImageConsumer;
+ friend class EGLConsumer;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp
index 5b361548eeda..d2d37dcb34f2 100644
--- a/libs/hwui/tests/common/LeakChecker.cpp
+++ b/libs/hwui/tests/common/LeakChecker.cpp
@@ -16,7 +16,6 @@
#include "LeakChecker.h"
-#include "Caches.h"
#include "TestUtils.h"
#include <memunreachable/memunreachable.h>
@@ -71,9 +70,6 @@ void LeakChecker::checkForLeaks() {
// thread-local caches so some leaks will not be properly tagged as leaks
UnreachableMemoryInfo rtMemInfo;
TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) {
- if (Caches::hasInstance()) {
- Caches::getInstance().tasks.stop();
- }
// Check for leaks
if (!GetUnreachableMemory(rtMemInfo)) {
cerr << "Failed to get unreachable memory!" << endl;
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 02ac97e0ed5c..66b9b85bdbe7 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -19,16 +19,15 @@
#include "DeferredLayerUpdater.h"
#include "hwui/Paint.h"
-#include <SkClipStack.h>
#include <minikin/Layout.h>
#include <pipeline/skia/SkiaOpenGLPipeline.h>
#include <pipeline/skia/SkiaVulkanPipeline.h>
#include <renderthread/EglManager.h>
-#include <renderthread/OpenGLPipeline.h>
#include <renderthread/VulkanManager.h>
#include <utils/Unicode.h>
-#include <SkGlyphCache.h>
+#include "SkColorData.h"
+#include "SkUnPreMultiply.h"
namespace android {
namespace uirenderer {
@@ -53,9 +52,7 @@ SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end)
sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
renderthread::RenderThread& renderThread) {
android::uirenderer::renderthread::IRenderPipeline* pipeline;
- if (Properties::getRenderPipelineType() == RenderPipelineType::OpenGL) {
- pipeline = new renderthread::OpenGLPipeline(renderThread);
- } else if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
pipeline = new skiapipeline::SkiaOpenGLPipeline(renderThread);
} else {
pipeline = new skiapipeline::SkiaVulkanPipeline(renderThread);
@@ -70,61 +67,28 @@ sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
const SkMatrix& transform) {
sp<DeferredLayerUpdater> layerUpdater = createTextureLayerUpdater(renderThread);
- layerUpdater->backingLayer()->getTransform().load(transform);
+ layerUpdater->backingLayer()->getTransform() = transform;
layerUpdater->setSize(width, height);
layerUpdater->setTransform(&transform);
// updateLayer so it's ready to draw
- layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN);
- if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
- static_cast<GlLayer*>(layerUpdater->backingLayer())
- ->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
- }
+ SkMatrix identity;
+ identity.setIdentity();
+ layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr);
return layerUpdater;
}
-void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text,
- std::vector<glyph_t>* outGlyphs,
- std::vector<float>* outPositions, float* outTotalAdvance,
- Rect* outBounds) {
- Rect bounds;
- float totalAdvance = 0;
- SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
- SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
- while (*text != '\0') {
- size_t nextIndex = 0;
- int32_t unichar = utf32_from_utf8_at(text, 4, 0, &nextIndex);
- text += nextIndex;
-
- glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar);
- autoCache.getCache()->unicharToGlyph(unichar);
-
- // push glyph and its relative position
- outGlyphs->push_back(glyph);
- outPositions->push_back(totalAdvance);
- outPositions->push_back(0);
-
- // compute bounds
- SkGlyph skGlyph = autoCache.getCache()->getUnicharMetrics(unichar);
- Rect glyphBounds(skGlyph.fWidth, skGlyph.fHeight);
- glyphBounds.translate(totalAdvance + skGlyph.fLeft, skGlyph.fTop);
- bounds.unionWith(glyphBounds);
-
- // advance next character
- SkScalar skWidth;
- paint.getTextWidths(&glyph, sizeof(glyph), &skWidth, NULL);
- totalAdvance += skWidth;
- }
- *outBounds = bounds;
- *outTotalAdvance = totalAdvance;
-}
-
void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint, float x,
float y) {
auto utf16 = asciiToUtf16(text);
+ uint32_t length = strlen(text);
SkPaint glyphPaint(paint);
glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- canvas->drawText(utf16.get(), 0, strlen(text), strlen(text), x, y, minikin::Bidi::LTR,
+ canvas->drawText(
+ utf16.get(), length, // text buffer
+ 0, length, // draw range
+ 0, length, // context range
+ x, y, minikin::Bidi::LTR,
glyphPaint, nullptr, nullptr /* measured text */);
}
@@ -143,7 +107,7 @@ void TestUtils::TestTask::run() {
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
renderThread.vulkanManager().initialize();
} else {
- renderThread.eglManager().initialize();
+ renderThread.requireGlContext();
}
rtCallback(renderThread);
@@ -151,8 +115,7 @@ void TestUtils::TestTask::run() {
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
renderThread.vulkanManager().destroy();
} else {
- renderThread.renderState().flush(Caches::FlushMode::Full);
- renderThread.eglManager().destroy();
+ renderThread.destroyGlContext();
}
}
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 2752ae9a8036..c5db861d4f48 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -16,21 +16,17 @@
#pragma once
-#include <DeviceInfo.h>
#include <DisplayList.h>
#include <Matrix.h>
#include <Properties.h>
#include <Rect.h>
#include <RenderNode.h>
-#include <Snapshot.h>
#include <hwui/Bitmap.h>
#include <pipeline/skia/SkiaRecordingCanvas.h>
+#include <private/hwui/DrawGlInfo.h>
#include <renderstate/RenderState.h>
#include <renderthread/RenderThread.h>
-#include <RecordedOp.h>
-#include <RecordingCanvas.h>
-
#include <memory>
namespace android {
@@ -60,18 +56,6 @@ namespace uirenderer {
Properties::overrideRenderPipelineType(oldType); \
};
-/**
- * Like gtests' TEST, but only runs with the OpenGL RenderPipelineType
- */
-#define OPENGL_PIPELINE_TEST(test_case_name, test_name) \
- class test_case_name##_##test_name##_HwuiTest { \
- public: \
- static void doTheThing(); \
- }; \
- INNER_PIPELINE_TEST(test_case_name, test_name, OpenGL, \
- test_case_name##_##test_name##_HwuiTest::doTheThing()) \
- void test_case_name##_##test_name##_HwuiTest::doTheThing()
-
#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \
TestUtils::runOnRenderThread( \
@@ -86,7 +70,6 @@ namespace uirenderer {
public: \
static void doTheThing(renderthread::RenderThread& renderThread); \
}; \
- INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \
INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
/* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
/* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \
@@ -94,18 +77,6 @@ namespace uirenderer {
renderthread::RenderThread& renderThread)
/**
- * Like RENDERTHREAD_TEST, but only runs with the OpenGL RenderPipelineType
- */
-#define RENDERTHREAD_OPENGL_PIPELINE_TEST(test_case_name, test_name) \
- class test_case_name##_##test_name##_RenderThreadTest { \
- public: \
- static void doTheThing(renderthread::RenderThread& renderThread); \
- }; \
- INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \
- void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \
- renderthread::RenderThread& renderThread)
-
-/**
* Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
*/
#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \
@@ -169,14 +140,6 @@ public:
return true;
}
- static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) {
- std::unique_ptr<Snapshot> snapshot(new Snapshot());
- // store clip first, so it isn't transformed
- snapshot->setClip(clip.left, clip.top, clip.right, clip.bottom);
- *(snapshot->transform) = transform;
- return snapshot;
- }
-
static sk_sp<Bitmap> createBitmap(int width, int height,
SkColorType colorType = kN32_SkColorType) {
SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType);
@@ -207,12 +170,6 @@ public:
static sp<RenderNode> createNode(
int left, int top, int right, int bottom,
std::function<void(RenderProperties& props, Canvas& canvas)> setup) {
-#if HWUI_NULL_GPU
- // if RenderNodes are being sync'd/used, device info will be needed, since
- // DeviceInfo::maxTextureSize() affects layer property
- DeviceInfo::initialize();
-#endif
-
sp<RenderNode> node = new RenderNode();
RenderProperties& props = node->mutateStagingProperties();
props.setLeftTopRightBottom(left, top, right, bottom);
@@ -230,12 +187,6 @@ public:
static sp<RenderNode> createNode(
int left, int top, int right, int bottom,
std::function<void(RenderProperties& props, RecordingCanvasType& canvas)> setup) {
-#if HWUI_NULL_GPU
- // if RenderNodes are being sync'd/used, device info will be needed, since
- // DeviceInfo::maxTextureSize() affects layer property
- DeviceInfo::initialize();
-#endif
-
sp<RenderNode> node = new RenderNode();
RenderProperties& props = node->mutateStagingProperties();
props.setLeftTopRightBottom(left, top, right, bottom);
@@ -261,11 +212,6 @@ public:
std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)>
setup,
const char* name = nullptr, skiapipeline::SkiaDisplayList* displayList = nullptr) {
-#if HWUI_NULL_GPU
- // if RenderNodes are being sync'd/used, device info will be needed, since
- // DeviceInfo::maxTextureSize() affects layer property
- DeviceInfo::initialize();
-#endif
sp<RenderNode> node = new RenderNode();
if (name) {
node->setName(name);
@@ -325,11 +271,6 @@ public:
static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
- static void layoutTextUnscaled(const SkPaint& paint, const char* text,
- std::vector<glyph_t>* outGlyphs,
- std::vector<float>* outPositions, float* outTotalAdvance,
- Rect* outBounds);
-
static void drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint, float x,
float y);
@@ -365,16 +306,10 @@ private:
}
auto displayList = node->getDisplayList();
if (displayList) {
- 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);
- }
+ for (auto&& childDr : static_cast<skiapipeline::SkiaDisplayList*>(
+ const_cast<DisplayList*>(displayList))
+ ->mChildNodes) {
+ syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode());
}
}
}
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index fd8c252ff318..9a1ee54bff49 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -46,7 +46,6 @@ class ListViewAnimation : public TestListViewSceneBase {
SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor) <
128 * 3;
paint.setColor(bgDark ? Color::White : Color::Grey_700);
- paint.setTextAlign(SkPaint::kCenter_Align);
paint.setTextSize(size / 2);
char charToShow = 'A' + (rand() % 26);
const SkPoint pos[] = {{SkIntToScalar(size / 2),
diff --git a/libs/hwui/tests/common/scenes/TestSceneBase.h b/libs/hwui/tests/common/scenes/TestSceneBase.h
index 792312a6a7a4..6f76a502ae3e 100644
--- a/libs/hwui/tests/common/scenes/TestSceneBase.h
+++ b/libs/hwui/tests/common/scenes/TestSceneBase.h
@@ -16,7 +16,7 @@
#pragma once
-#include "RecordingCanvas.h"
+#include "hwui/Canvas.h"
#include "RenderNode.h"
#include "tests/common/TestContext.h"
#include "tests/common/TestScene.h"
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 5f5a92e55bf2..5fa008b5b4df 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -132,10 +132,10 @@ void run(const TestScene::Info& info, const TestScene::Options& opts,
ContextFactory factory;
std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode.get(), &factory));
proxy->loadSystemProperties();
- proxy->initialize(surface);
+ proxy->setSurface(surface);
float lightX = width / 2.0;
- proxy->setup(dp(800.0f), 255 * 0.075, 255 * 0.15);
- proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
+ proxy->setLightAlpha(255 * 0.075, 255 * 0.15);
+ proxy->setLightGeometry((Vector3){lightX, dp(-200.0f), dp(800.0f)}, dp(800.0f));
// Do a few cold runs then reset the stats so that the caches are all hot
int warmupFrameCount = 5;
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 6caaf8d7616e..524dfb0fe4ef 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -19,7 +19,6 @@
#include "Properties.h"
#include "hwui/Typeface.h"
-#include "protos/hwui.pb.h"
#include <benchmark/benchmark.h>
#include <getopt.h>
@@ -67,7 +66,7 @@ OPTIONS:
--onscreen Render tests on device screen. By default tests
are offscreen rendered
--benchmark_format Set output format. Possible values are tabular, json, csv
- --renderer=TYPE Sets the render pipeline to use. May be opengl, skiagl, or skiavk
+ --renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk
)");
}
@@ -145,9 +144,7 @@ static bool setBenchmarkFormat(const char* format) {
}
static bool setRenderer(const char* renderer) {
- if (!strcmp(renderer, "opengl")) {
- Properties::overrideRenderPipelineType(RenderPipelineType::OpenGL);
- } else if (!strcmp(renderer, "skiagl")) {
+ if (!strcmp(renderer, "skiagl")) {
Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
} else if (!strcmp(renderer, "skiavk")) {
Properties::overrideRenderPipelineType(RenderPipelineType::SkiaVulkan);
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 0aaf7731c927..70423a70157b 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -17,7 +17,8 @@
#include <benchmark/benchmark.h>
#include "DisplayList.h"
-#include "RecordingCanvas.h"
+#include "hwui/Canvas.h"
+#include "pipeline/skia/SkiaDisplayList.h"
#include "tests/common/TestUtils.h"
using namespace android;
@@ -25,7 +26,7 @@ using namespace android::uirenderer;
void BM_DisplayList_alloc(benchmark::State& benchState) {
while (benchState.KeepRunning()) {
- auto displayList = new DisplayList();
+ auto displayList = new skiapipeline::SkiaDisplayList();
benchmark::DoNotOptimize(displayList);
delete displayList;
}
@@ -34,7 +35,7 @@ BENCHMARK(BM_DisplayList_alloc);
void BM_DisplayList_alloc_theoretical(benchmark::State& benchState) {
while (benchState.KeepRunning()) {
- auto displayList = new char[sizeof(DisplayList)];
+ auto displayList = new char[sizeof(skiapipeline::SkiaDisplayList)];
benchmark::DoNotOptimize(displayList);
delete[] displayList;
}
@@ -114,52 +115,6 @@ void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState)
}
BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView);
-class NullClient : public CanvasStateClient {
- void onViewportInitialized() override {}
- void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
- GLuint getTargetFbo() const override { return 0; }
-};
-
-void BM_CanvasState_saverestore(benchmark::State& benchState) {
- NullClient client;
- CanvasState state(client);
- state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
-
- while (benchState.KeepRunning()) {
- state.save(SaveFlags::MatrixClip);
- state.save(SaveFlags::MatrixClip);
- benchmark::DoNotOptimize(&state);
- state.restore();
- state.restore();
- }
-}
-BENCHMARK(BM_CanvasState_saverestore);
-
-void BM_CanvasState_init(benchmark::State& benchState) {
- NullClient client;
- CanvasState state(client);
- state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
-
- while (benchState.KeepRunning()) {
- state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
- benchmark::DoNotOptimize(&state);
- }
-}
-BENCHMARK(BM_CanvasState_init);
-
-void BM_CanvasState_translate(benchmark::State& benchState) {
- NullClient client;
- CanvasState state(client);
- state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
-
- while (benchState.KeepRunning()) {
- state.translate(5, 5, 0);
- benchmark::DoNotOptimize(&state);
- state.translate(-5, -5, 0);
- }
-}
-BENCHMARK(BM_CanvasState_translate);
-
void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) {
sp<RenderNode> child = TestUtils::createNode(50, 50, 100, 100, [](auto& props, auto& canvas) {
canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
diff --git a/libs/hwui/tests/microbench/FontBench.cpp b/libs/hwui/tests/microbench/FontBench.cpp
deleted file mode 100644
index 4e9b540a5c3d..000000000000
--- a/libs/hwui/tests/microbench/FontBench.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <benchmark/benchmark.h>
-
-#include "GammaFontRenderer.h"
-#include "tests/common/TestUtils.h"
-
-#include <SkPaint.h>
-
-using namespace android;
-using namespace android::uirenderer;
-
-void BM_FontRenderer_precache_cachehits(benchmark::State& state) {
- TestUtils::runOnRenderThread([&state](renderthread::RenderThread& thread) {
- SkPaint paint;
- paint.setTextSize(20);
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- GammaFontRenderer gammaFontRenderer;
- FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
- fontRenderer.setFont(&paint, SkMatrix::I());
-
- std::vector<glyph_t> glyphs;
- std::vector<float> positions;
- float totalAdvance;
- uirenderer::Rect bounds;
- TestUtils::layoutTextUnscaled(paint, "This is a test", &glyphs, &positions, &totalAdvance,
- &bounds);
-
- fontRenderer.precache(&paint, glyphs.data(), glyphs.size(), SkMatrix::I());
-
- while (state.KeepRunning()) {
- fontRenderer.precache(&paint, glyphs.data(), glyphs.size(), SkMatrix::I());
- }
- });
-}
-BENCHMARK(BM_FontRenderer_precache_cachehits);
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
deleted file mode 100644
index b6217665d743..000000000000
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <benchmark/benchmark.h>
-
-#include "BakedOpDispatcher.h"
-#include "BakedOpRenderer.h"
-#include "BakedOpState.h"
-#include "FrameBuilder.h"
-#include "LayerUpdateQueue.h"
-#include "RecordedOp.h"
-#include "RecordingCanvas.h"
-#include "Vector.h"
-#include "tests/common/TestContext.h"
-#include "tests/common/TestScene.h"
-#include "tests/common/TestUtils.h"
-
-#include <vector>
-
-using namespace android;
-using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-using namespace android::uirenderer::test;
-
-const FrameBuilder::LightGeometry sLightGeometry = {{100, 100, 100}, 50};
-const BakedOpRenderer::LightInfo sLightInfo = {128, 128};
-
-static sp<RenderNode> createTestNode() {
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- sk_sp<Bitmap> bitmap(TestUtils::createBitmap(10, 10));
- SkPaint paint;
-
- // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
- // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
- canvas.save(SaveFlags::MatrixClip);
- for (int i = 0; i < 30; i++) {
- canvas.translate(0, 10);
- canvas.drawRect(0, 0, 10, 10, paint);
- canvas.drawBitmap(*bitmap, 5, 0, nullptr);
- }
- canvas.restore();
- });
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
- return node;
-}
-
-void BM_FrameBuilder_defer(benchmark::State& state) {
- TestUtils::runOnRenderThread([&state](RenderThread& thread) {
- auto node = createTestNode();
- while (state.KeepRunning()) {
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*node);
- benchmark::DoNotOptimize(&frameBuilder);
- }
- });
-}
-BENCHMARK(BM_FrameBuilder_defer);
-
-void BM_FrameBuilder_deferAndRender(benchmark::State& state) {
- TestUtils::runOnRenderThread([&state](RenderThread& thread) {
- auto node = createTestNode();
-
- RenderState& renderState = thread.renderState();
- Caches& caches = Caches::getInstance();
-
- while (state.KeepRunning()) {
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry, caches);
- frameBuilder.deferRenderNode(*node);
-
- BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo);
- frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
- benchmark::DoNotOptimize(&renderer);
- }
- });
-}
-BENCHMARK(BM_FrameBuilder_deferAndRender);
-
-static sp<RenderNode> getSyncedSceneNode(const char* sceneName) {
- gDisplay = getBuiltInDisplay(); // switch to real display if present
-
- TestContext testContext;
- TestScene::Options opts;
- std::unique_ptr<TestScene> scene(TestScene::testMap()[sceneName].createScene(opts));
-
- sp<RenderNode> rootNode = TestUtils::createNode<RecordingCanvas>(
- 0, 0, gDisplay.w, gDisplay.h,
- [&scene](RenderProperties& props, RecordingCanvas& canvas) {
- scene->createContent(gDisplay.w, gDisplay.h, canvas);
- });
-
- TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode);
- return rootNode;
-}
-
-static auto SCENES = {
- "listview",
-};
-
-void BM_FrameBuilder_defer_scene(benchmark::State& state) {
- TestUtils::runOnRenderThread([&state](RenderThread& thread) {
- const char* sceneName = *(SCENES.begin() + state.range(0));
- state.SetLabel(sceneName);
- auto node = getSyncedSceneNode(sceneName);
- while (state.KeepRunning()) {
- FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w,
- gDisplay.h, sLightGeometry, Caches::getInstance());
- frameBuilder.deferRenderNode(*node);
- benchmark::DoNotOptimize(&frameBuilder);
- }
- });
-}
-BENCHMARK(BM_FrameBuilder_defer_scene)->DenseRange(0, SCENES.size() - 1);
-
-void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) {
- TestUtils::runOnRenderThread([&state](RenderThread& thread) {
- const char* sceneName = *(SCENES.begin() + state.range(0));
- state.SetLabel(sceneName);
- auto node = getSyncedSceneNode(sceneName);
-
- RenderState& renderState = thread.renderState();
- Caches& caches = Caches::getInstance();
-
- while (state.KeepRunning()) {
- FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w,
- gDisplay.h, sLightGeometry, Caches::getInstance());
- frameBuilder.deferRenderNode(*node);
-
- BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo);
- frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
- benchmark::DoNotOptimize(&renderer);
- }
- });
-}
-BENCHMARK(BM_FrameBuilder_deferAndRender_scene)->DenseRange(0, SCENES.size() - 1);
diff --git a/libs/hwui/tests/microbench/ShadowBench.cpp b/libs/hwui/tests/microbench/ShadowBench.cpp
deleted file mode 100644
index 12da7837e2e9..000000000000
--- a/libs/hwui/tests/microbench/ShadowBench.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <benchmark/benchmark.h>
-
-#include "Matrix.h"
-#include "Rect.h"
-#include "TessellationCache.h"
-#include "Vector.h"
-#include "VertexBuffer.h"
-
-#include <SkPath.h>
-
-#include <memory>
-
-using namespace android;
-using namespace android::uirenderer;
-
-struct ShadowTestData {
- Matrix4 drawTransform;
- Rect localClip;
- Matrix4 casterTransformXY;
- Matrix4 casterTransformZ;
- Vector3 lightCenter;
- float lightRadius;
-};
-
-void createShadowTestData(ShadowTestData* out) {
- static float SAMPLE_DRAW_TRANSFORM[] = {
- 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
- };
- static float SAMPLE_CASTERXY[] = {
- 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 32, 32, 0, 1,
- };
- static float SAMPLE_CASTERZ[] = {
- 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 32, 32, 32, 1,
- };
- static Rect SAMPLE_CLIP(0, 0, 1536, 2048);
- static Vector3 SAMPLE_LIGHT_CENTER{768, -400, 1600};
- static float SAMPLE_LIGHT_RADIUS = 1600;
-
- out->drawTransform.load(SAMPLE_DRAW_TRANSFORM);
- out->localClip = SAMPLE_CLIP;
- out->casterTransformXY.load(SAMPLE_CASTERXY);
- out->casterTransformZ.load(SAMPLE_CASTERZ);
- out->lightCenter = SAMPLE_LIGHT_CENTER;
- out->lightRadius = SAMPLE_LIGHT_RADIUS;
-}
-
-static inline void tessellateShadows(ShadowTestData& testData, bool opaque, const SkPath& shape,
- VertexBuffer* ambient, VertexBuffer* spot) {
- tessellateShadows(&testData.drawTransform, &testData.localClip, opaque, &shape,
- &testData.casterTransformXY, &testData.casterTransformZ, testData.lightCenter,
- testData.lightRadius, *ambient, *spot);
-}
-
-void BM_TessellateShadows_roundrect_opaque(benchmark::State& state) {
- ShadowTestData shadowData;
- createShadowTestData(&shadowData);
- SkPath path;
- path.addRoundRect(SkRect::MakeWH(100, 100), 5, 5);
-
- while (state.KeepRunning()) {
- VertexBuffer ambient;
- VertexBuffer spot;
- tessellateShadows(shadowData, true, path, &ambient, &spot);
- benchmark::DoNotOptimize(&ambient);
- benchmark::DoNotOptimize(&spot);
- }
-}
-BENCHMARK(BM_TessellateShadows_roundrect_opaque);
-
-void BM_TessellateShadows_roundrect_translucent(benchmark::State& state) {
- ShadowTestData shadowData;
- createShadowTestData(&shadowData);
- SkPath path;
- path.reset();
- path.addRoundRect(SkRect::MakeLTRB(0, 0, 100, 100), 5, 5);
-
- while (state.KeepRunning()) {
- std::unique_ptr<VertexBuffer> ambient(new VertexBuffer);
- std::unique_ptr<VertexBuffer> spot(new VertexBuffer);
- tessellateShadows(shadowData, false, path, ambient.get(), spot.get());
- benchmark::DoNotOptimize(ambient.get());
- benchmark::DoNotOptimize(spot.get());
- }
-}
-BENCHMARK(BM_TessellateShadows_roundrect_translucent);
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
deleted file mode 100644
index 09f0b06ded39..000000000000
--- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <BakedOpDispatcher.h>
-#include <BakedOpRenderer.h>
-#include <FrameBuilder.h>
-#include <LayerUpdateQueue.h>
-#include <RecordedOp.h>
-#include <hwui/Paint.h>
-#include <tests/common/TestUtils.h>
-#include <utils/Color.h>
-
-#include <SkBlurDrawLooper.h>
-#include <SkDashPathEffect.h>
-#include <SkPath.h>
-
-using namespace android::uirenderer;
-
-static BakedOpRenderer::LightInfo sLightInfo;
-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, false, sLightInfo)
- , mValidator(validator) {
- mGlopReceiver = ValidatingGlopReceiver;
- }
-
-private:
- static void ValidatingGlopReceiver(BakedOpRenderer& renderer, const Rect* dirtyBounds,
- const ClipBase* clip, const Glop& glop) {
- auto vbor = reinterpret_cast<ValidatingBakedOpRenderer*>(&renderer);
- vbor->mValidator(glop);
- }
- std::function<void(const Glop& glop)> mValidator;
-};
-
-typedef void (*TestBakedOpReceiver)(BakedOpRenderer&, const BakedOpState&);
-
-static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, RecordedOp* op,
- std::function<void(const Glop& glop)> glopVerifier,
- int expectedGlopCount = 1) {
- // Create op, and wrap with basic state.
- LinearAllocator allocator;
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 100));
- auto state = BakedOpState::tryConstruct(allocator, *snapshot, *op);
- ASSERT_NE(nullptr, state);
-
- int glopCount = 0;
- auto glopReceiver = [&glopVerifier, &glopCount, &expectedGlopCount](const Glop& glop) {
- ASSERT_LE(glopCount++, expectedGlopCount) << expectedGlopCount << "glop(s) expected";
- glopVerifier(glop);
- };
- ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver);
-
-// Dispatch based on op type created, similar to Frame/LayerBuilder dispatch behavior
-#define X(Type) \
- [](BakedOpRenderer& renderer, const BakedOpState& state) { \
- BakedOpDispatcher::on##Type(renderer, static_cast<const Type&>(*(state.op)), state); \
- },
- static TestBakedOpReceiver unmergedReceivers[] = BUILD_RENDERABLE_OP_LUT(X);
-#undef X
- unmergedReceivers[op->opId](renderer, *state);
- ASSERT_EQ(expectedGlopCount, glopCount) << "Exactly " << expectedGlopCount
- << "Glop(s) expected";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
- SkPaint strokePaint;
- strokePaint.setStyle(SkPaint::kStroke_Style);
- strokePaint.setStrokeWidth(4);
-
- float intervals[] = {1.0f, 1.0f};
- strokePaint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
-
- auto textureGlopVerifier = [](const Glop& glop) {
- // validate glop produced by renderPathTexture (so texture, unit quad)
- auto texture = glop.fill.texture.texture;
- ASSERT_NE(nullptr, texture);
- float expectedOffset = floor(4 * 1.5f + 0.5f);
- EXPECT_EQ(expectedOffset, reinterpret_cast<PathTexture*>(texture)->offset)
- << "Should see conservative offset from PathCache::computeBounds";
- Rect expectedBounds(10, 15, 20, 25);
- expectedBounds.outset(expectedOffset);
-
- Matrix4 expectedModelView;
- expectedModelView.loadTranslate(10 - expectedOffset, 15 - expectedOffset, 0);
- expectedModelView.scale(10 + 2 * expectedOffset, 10 + 2 * expectedOffset, 1);
- EXPECT_EQ(expectedModelView, glop.transform.modelView)
- << "X and Y offsets, and scale both applied to model view";
- };
-
- // Arc and Oval will render functionally the same glop, differing only in texture content
- ArcOp arcOp(Rect(10, 15, 20, 25), Matrix4::identity(), nullptr, &strokePaint, 0, 270, true);
- testUnmergedGlopDispatch(renderThread, &arcOp, textureGlopVerifier);
-
- OvalOp ovalOp(Rect(10, 15, 20, 25), Matrix4::identity(), nullptr, &strokePaint);
- testUnmergedGlopDispatch(renderThread, &ovalOp, textureGlopVerifier);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
- SkPaint layerPaint;
- 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,
- [](const Glop& glop) { ADD_FAILURE() << "Nothing should happen"; }, 0);
-}
-
-static int getGlopTransformFlags(renderthread::RenderThread& renderThread, RecordedOp* op) {
- int result = 0;
- testUnmergedGlopDispatch(renderThread, op, [&result](const Glop& glop) {
- result = glop.transform.transformFlags;
- });
- return result;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, offsetFlags) {
- Rect bounds(10, 15, 20, 25);
- SkPaint paint;
- SkPaint aaPaint;
- aaPaint.setAntiAlias(true);
-
- RoundRectOp roundRectOp(bounds, Matrix4::identity(), nullptr, &paint, 0, 270);
- EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &roundRectOp))
- << "Expect no offset for round rect op.";
-
- const float points[4] = {0.5, 0.5, 1.0, 1.0};
- PointsOp antiAliasedPointsOp(bounds, Matrix4::identity(), nullptr, &aaPaint, points, 4);
- EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &antiAliasedPointsOp))
- << "Expect no offset for AA points.";
- PointsOp pointsOp(bounds, Matrix4::identity(), nullptr, &paint, points, 4);
- EXPECT_EQ(TransformFlags::OffsetByFudgeFactor, getGlopTransformFlags(renderThread, &pointsOp))
- << "Expect an offset for non-AA points.";
-
- LinesOp antiAliasedLinesOp(bounds, Matrix4::identity(), nullptr, &aaPaint, points, 4);
- EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &antiAliasedLinesOp))
- << "Expect no offset for AA lines.";
- LinesOp linesOp(bounds, Matrix4::identity(), nullptr, &paint, points, 4);
- EXPECT_EQ(TransformFlags::OffsetByFudgeFactor, getGlopTransformFlags(renderThread, &linesOp))
- << "Expect an offset for non-AA lines.";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, renderTextWithShadow) {
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
-
- android::Paint shadowPaint;
- shadowPaint.setColor(SK_ColorRED);
-
- SkScalar sigma = Blur::convertRadiusToSigma(5);
- shadowPaint.setLooper(SkBlurDrawLooper::Make(SK_ColorWHITE, sigma, 3, 3));
-
- TestUtils::drawUtf8ToCanvas(&canvas, "A", shadowPaint, 25, 25);
- TestUtils::drawUtf8ToCanvas(&canvas, "B", shadowPaint, 50, 50);
- });
-
- int glopCount = 0;
- auto glopReceiver = [&glopCount](const Glop& glop) {
- if (glopCount < 2) {
- // two white shadows
- EXPECT_EQ(FloatColor({1, 1, 1, 1}), glop.fill.color);
- } else {
- // two text draws merged into one, drawn after both shadows
- EXPECT_EQ(FloatColor({1, 0, 0, 1}), glop.fill.color);
- }
- glopCount++;
- };
-
- ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver);
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
- ASSERT_EQ(3, glopCount) << "Exactly three glops expected";
-}
-
-static void validateLayerDraw(renderthread::RenderThread& renderThread,
- std::function<void(const Glop& glop)> validator) {
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.mutateLayerProperties().setType(LayerType::RenderLayer);
-
- // provide different blend mode, so decoration draws contrast
- props.mutateLayerProperties().setXferMode(SkBlendMode::kSrc);
- canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
- });
- OffscreenBuffer** layerHandle = node->getLayerHandle();
-
- auto syncedNode = TestUtils::getSyncedNode(node);
-
- // create RenderNode's layer here in same way prepareTree would
- OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
- *layerHandle = &layer;
- {
- LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
- layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(0, 0, 100, 100));
-
- ValidatingBakedOpRenderer renderer(renderThread.renderState(), validator);
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferLayers(layerUpdateQueue);
- frameBuilder.deferRenderNode(*syncedNode);
- frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
- }
-
- // clean up layer pointer, so we can safely destruct RenderNode
- *layerHandle = nullptr;
-}
-
-static FloatColor makeFloatColor(uint32_t color) {
- FloatColor c;
- c.set(color);
- return c;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, layerUpdateProperties) {
- for (bool debugOverdraw : {false, true}) {
- for (bool debugLayersUpdates : {false, true}) {
- ScopedProperty<bool> ovdProp(Properties::debugOverdraw, debugOverdraw);
- ScopedProperty<bool> lupProp(Properties::debugLayersUpdates, debugLayersUpdates);
-
- int glopCount = 0;
- validateLayerDraw(renderThread, [&glopCount, &debugLayersUpdates](const Glop& glop) {
- if (glopCount == 0) {
- // 0 - Black layer fill
- EXPECT_TRUE(glop.fill.colorEnabled);
- EXPECT_EQ(makeFloatColor(Color::Black), glop.fill.color);
- } else if (glopCount == 1) {
- // 1 - Uncolored (textured) layer draw
- EXPECT_FALSE(glop.fill.colorEnabled);
- } else if (glopCount == 2) {
- // 2 - layer overlay, if present
- EXPECT_TRUE(glop.fill.colorEnabled);
- // blend srcover, different from that of layer
- EXPECT_EQ(GLenum(GL_ONE), glop.blend.src);
- EXPECT_EQ(GLenum(GL_ONE_MINUS_SRC_ALPHA), glop.blend.dst);
- EXPECT_EQ(makeFloatColor(debugLayersUpdates ? 0x7f00ff00 : 0), glop.fill.color)
- << "Should be transparent green if debugLayersUpdates";
- } else if (glopCount < 7) {
- // 3 - 6 - overdraw indicator overlays, if present
- EXPECT_TRUE(glop.fill.colorEnabled);
- uint32_t expectedColor = Caches::getInstance().getOverdrawColor(glopCount - 2);
- ASSERT_EQ(makeFloatColor(expectedColor), glop.fill.color);
- } else {
- ADD_FAILURE() << "Too many glops observed";
- }
- glopCount++;
- });
- int expectedCount = 2;
- if (debugLayersUpdates || debugOverdraw) expectedCount++;
- if (debugOverdraw) expectedCount += 4;
- EXPECT_EQ(expectedCount, glopCount);
- }
- }
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTextureSnapping) {
- Rect bounds(10, 15, 20, 25);
- SkPaint paint;
- SkPath path;
- path.addRect(SkRect::MakeXYWH(1.5, 3.8, 100, 90));
- PathOp op(bounds, Matrix4::identity(), nullptr, &paint, &path);
- testUnmergedGlopDispatch(renderThread, &op, [](const Glop& glop) {
- auto texture = glop.fill.texture.texture;
- ASSERT_NE(nullptr, texture);
- EXPECT_EQ(1, reinterpret_cast<PathTexture*>(texture)->left);
- EXPECT_EQ(3, reinterpret_cast<PathTexture*>(texture)->top);
- });
-}
diff --git a/libs/hwui/tests/unit/BakedOpRendererTests.cpp b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
deleted file mode 100644
index 1a3ec39a00d0..000000000000
--- a/libs/hwui/tests/unit/BakedOpRendererTests.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <BakedOpRenderer.h>
-#include <GlopBuilder.h>
-#include <tests/common/TestUtils.h>
-
-using namespace android::uirenderer;
-
-const BakedOpRenderer::LightInfo sLightInfo = {128, 128};
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, startRepaintLayer_clear) {
- BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(), true, false,
- sLightInfo);
- OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200u, 200u);
-
- layer.dirty(Rect(200, 200));
- {
- renderer.startRepaintLayer(&layer, Rect(200, 200));
- EXPECT_TRUE(layer.region.isEmpty()) << "Repaint full layer should clear region";
- renderer.endLayer();
- }
-
- layer.dirty(Rect(200, 200));
- {
- renderer.startRepaintLayer(&layer, Rect(100, 200)); // repainting left side
- EXPECT_TRUE(layer.region.isRect());
- // ALOGD("bounds %d %d %d %d", RECT_ARGS(layer.region.getBounds()));
- EXPECT_EQ(android::Rect(100, 0, 200, 200), layer.region.getBounds())
- << "Left side being repainted, so right side should be clear";
- renderer.endLayer();
- }
-
- // right side is now only dirty portion
- {
- renderer.startRepaintLayer(&layer, Rect(100, 0, 200, 200)); // repainting right side
- EXPECT_TRUE(layer.region.isEmpty())
- << "Now right side being repainted, so region should be entirely clear";
- renderer.endLayer();
- }
-}
-
-static void drawFirstOp(RenderState& renderState, int color, SkBlendMode mode) {
- BakedOpRenderer renderer(Caches::getInstance(), renderState, true, false, sLightInfo);
-
- renderer.startFrame(100, 100, Rect(100, 100));
- SkPaint paint;
- paint.setColor(color);
- paint.setBlendMode(mode);
-
- Rect dest(0, 0, 100, 100);
- Glop glop;
- GlopBuilder(renderState, Caches::getInstance(), &glop)
- .setRoundRectClipState(nullptr)
- .setMeshUnitQuad()
- .setFillPaint(paint, 1.0f)
- .setTransform(Matrix4::identity(), TransformFlags::None)
- .setModelViewMapUnitToRectSnap(dest)
- .build();
- renderer.renderGlop(nullptr, nullptr, glop);
- renderer.endFrame(Rect(100, 100));
-}
-
-static void verifyBlend(RenderState& renderState, GLenum expectedSrc, GLenum expectedDst) {
- EXPECT_TRUE(renderState.blend().getEnabled());
- GLenum src;
- GLenum dst;
- renderState.blend().getFactors(&src, &dst);
- EXPECT_EQ(expectedSrc, src);
- EXPECT_EQ(expectedDst, dst);
-}
-
-static void verifyBlendDisabled(RenderState& renderState) {
- EXPECT_FALSE(renderState.blend().getEnabled());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, firstDrawBlend_clear) {
- // initialize blend state to nonsense value
- renderThread.renderState().blend().setFactors(GL_ONE, GL_ONE);
-
- drawFirstOp(renderThread.renderState(), 0xfeff0000, SkBlendMode::kClear);
- verifyBlend(renderThread.renderState(), GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, firstDrawBlend_srcover) {
- // initialize blend state to nonsense value
- renderThread.renderState().blend().setFactors(GL_ONE, GL_ONE);
-
- drawFirstOp(renderThread.renderState(), 0xfeff0000, SkBlendMode::kSrcOver);
- verifyBlendDisabled(renderThread.renderState());
-}
diff --git a/libs/hwui/tests/unit/BakedOpStateTests.cpp b/libs/hwui/tests/unit/BakedOpStateTests.cpp
deleted file mode 100644
index 6f8e24917767..000000000000
--- a/libs/hwui/tests/unit/BakedOpStateTests.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <BakedOpState.h>
-#include <ClipArea.h>
-#include <RecordedOp.h>
-#include <tests/common/TestUtils.h>
-
-namespace android {
-namespace uirenderer {
-
-TEST(ResolvedRenderState, construct) {
- LinearAllocator allocator;
- Matrix4 translate10x20;
- translate10x20.loadTranslate(10, 20, 0);
-
- SkPaint paint;
- ClipRect clip(Rect(100, 200));
- RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, &clip, &paint);
- {
- // recorded with transform, no parent transform
- auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
- ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
- EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20);
- EXPECT_EQ(Rect(100, 200), state.clipRect());
- EXPECT_EQ(Rect(40, 60, 100, 200), state.clippedBounds); // translated and also clipped
- EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
- }
- {
- // recorded with transform and parent transform
- auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
- ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
-
- Matrix4 expectedTranslate;
- expectedTranslate.loadTranslate(20, 40, 0);
- EXPECT_MATRIX_APPROX_EQ(expectedTranslate, state.transform);
-
- // intersection of parent & transformed child clip
- EXPECT_EQ(Rect(10, 20, 100, 200), state.clipRect());
-
- // translated and also clipped
- EXPECT_EQ(Rect(50, 80, 100, 200), state.clippedBounds);
- EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
- }
-}
-
-TEST(ResolvedRenderState, computeLocalSpaceClip) {
- LinearAllocator allocator;
- Matrix4 translate10x20;
- translate10x20.loadTranslate(10, 20, 0);
-
- SkPaint paint;
- ClipRect clip(Rect(100, 200));
- RectOp recordedOp(Rect(1000, 1000), translate10x20, &clip, &paint);
- {
- // recorded with transform, no parent transform
- auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
- ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
- EXPECT_EQ(Rect(-10, -20, 90, 180), state.computeLocalSpaceClip())
- << "Local clip rect should be 100x200, offset by -10,-20";
- }
- {
- // recorded with transform + parent transform
- auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
- ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
- EXPECT_EQ(Rect(-10, -20, 80, 160), state.computeLocalSpaceClip())
- << "Local clip rect should be 90x190, offset by -10,-20";
- }
-}
-
-const float HAIRLINE = 0.0f;
-
-// Note: bounds will be conservative, but not precise for non-hairline
-// - use approx bounds checks for these
-const float SEMI_HAIRLINE = 0.3f;
-
-struct StrokeTestCase {
- float scale;
- float strokeWidth;
- const std::function<void(const ResolvedRenderState&)> validator;
-};
-
-const static StrokeTestCase sStrokeTestCases[] = {
- {1, HAIRLINE,
- [](const ResolvedRenderState& state) {
- EXPECT_EQ(Rect(49.5f, 49.5f, 150.5f, 150.5f), state.clippedBounds);
- }},
- {1, SEMI_HAIRLINE,
- [](const ResolvedRenderState& state) {
- EXPECT_TRUE(state.clippedBounds.contains(49.5f, 49.5f, 150.5f, 150.5f));
- EXPECT_TRUE(Rect(49, 49, 151, 151).contains(state.clippedBounds));
- }},
- {1, 20,
- [](const ResolvedRenderState& state) {
- EXPECT_EQ(Rect(40, 40, 160, 160), state.clippedBounds);
- }},
-
- // 3x3 scale:
- {3, HAIRLINE,
- [](const ResolvedRenderState& state) {
- EXPECT_EQ(Rect(149.5f, 149.5f, 200, 200), state.clippedBounds);
- EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
- }},
- {3, SEMI_HAIRLINE,
- [](const ResolvedRenderState& state) {
- EXPECT_TRUE(state.clippedBounds.contains(149.5f, 149.5f, 200, 200));
- EXPECT_TRUE(Rect(149, 149, 200, 200).contains(state.clippedBounds));
- }},
- {3, 20,
- [](const ResolvedRenderState& state) {
- EXPECT_TRUE(state.clippedBounds.contains(120, 120, 200, 200));
- EXPECT_TRUE(Rect(119, 119, 200, 200).contains(state.clippedBounds));
- }},
-
- // 0.5f x 0.5f scale
- {0.5f, HAIRLINE,
- [](const ResolvedRenderState& state) {
- EXPECT_EQ(Rect(24.5f, 24.5f, 75.5f, 75.5f), state.clippedBounds);
- }},
- {0.5f, SEMI_HAIRLINE,
- [](const ResolvedRenderState& state) {
- EXPECT_TRUE(state.clippedBounds.contains(24.5f, 24.5f, 75.5f, 75.5f));
- EXPECT_TRUE(Rect(24, 24, 76, 76).contains(state.clippedBounds));
- }},
- {0.5f, 20, [](const ResolvedRenderState& state) {
- EXPECT_TRUE(state.clippedBounds.contains(19.5f, 19.5f, 80.5f, 80.5f));
- EXPECT_TRUE(Rect(19, 19, 81, 81).contains(state.clippedBounds));
- }}};
-
-TEST(ResolvedRenderState, construct_expandForStroke) {
- LinearAllocator allocator;
- // Loop over table of test cases and verify different combinations of stroke width and transform
- for (auto&& testCase : sStrokeTestCases) {
- SkPaint strokedPaint;
- strokedPaint.setAntiAlias(true);
- strokedPaint.setStyle(SkPaint::kStroke_Style);
- strokedPaint.setStrokeWidth(testCase.strokeWidth);
-
- ClipRect clip(Rect(200, 200));
- RectOp recordedOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &strokedPaint);
-
- Matrix4 snapshotMatrix;
- snapshotMatrix.loadScale(testCase.scale, testCase.scale, 1);
- auto parentSnapshot = TestUtils::makeSnapshot(snapshotMatrix, Rect(200, 200));
-
- ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, true, false);
- testCase.validator(state);
- }
-}
-
-TEST(BakedOpState, tryConstruct) {
- Matrix4 translate100x0;
- translate100x0.loadTranslate(100, 0, 0);
-
- SkPaint paint;
- ClipRect clip(Rect(100, 200));
-
- LinearAllocator allocator;
- RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), &clip, &paint);
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
- EXPECT_NE(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, successOp))
- << "successOp NOT rejected by clip, so should be constructed";
- size_t successAllocSize = allocator.usedSize();
- EXPECT_LE(64u, successAllocSize) << "relatively large alloc for non-rejected op";
-
- RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, &clip, &paint);
- EXPECT_EQ(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, rejectOp))
- << "rejectOp rejected by clip, so should not be constructed";
-
- // NOTE: this relies on the clip having already been serialized by the op above
- EXPECT_EQ(successAllocSize, allocator.usedSize()) << "no extra allocation used for rejected op";
-}
-
-TEST(BakedOpState, tryShadowOpConstruct) {
- Matrix4 translate10x20;
- translate10x20.loadTranslate(10, 20, 0);
-
- LinearAllocator allocator;
- {
- auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect()); // Note: empty clip
- BakedOpState* bakedState =
- BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
-
- EXPECT_EQ(nullptr, bakedState) << "op should be rejected by clip, so not constructed";
- EXPECT_EQ(0u, allocator.usedSize()) << "no serialization, even for clip,"
- "since op is quick rejected based on snapshot clip";
- }
- {
- auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
- BakedOpState* bakedState =
- BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
-
- ASSERT_NE(nullptr, bakedState) << "NOT rejected by clip, so op should be constructed";
- EXPECT_LE(64u, allocator.usedSize()) << "relatively large alloc for non-rejected op";
-
- EXPECT_MATRIX_APPROX_EQ(translate10x20, bakedState->computedState.transform);
- EXPECT_EQ(Rect(100, 200), bakedState->computedState.clippedBounds);
- }
-}
-
-TEST(BakedOpState, tryStrokeableOpConstruct) {
- LinearAllocator allocator;
- {
- // check regular rejection
- SkPaint paint;
- paint.setStyle(SkPaint::kStrokeAndFill_Style);
- paint.setStrokeWidth(0.0f);
- ClipRect clip(Rect(100, 200));
- RectOp rejectOp(Rect(100, 200), Matrix4::identity(), &clip, &paint);
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
- auto bakedState = BakedOpState::tryStrokeableOpConstruct(
- allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::StyleDefined, false);
-
- EXPECT_EQ(nullptr, bakedState);
- EXPECT_GT(8u,
- allocator.usedSize()); // no significant allocation space used for rejected op
- }
- {
- // check simple unscaled expansion
- SkPaint paint;
- paint.setStyle(SkPaint::kStrokeAndFill_Style);
- paint.setStrokeWidth(10.0f);
- ClipRect clip(Rect(200, 200));
- RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
- auto bakedState = BakedOpState::tryStrokeableOpConstruct(
- allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::StyleDefined, false);
-
- ASSERT_NE(nullptr, bakedState);
- EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
- EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
- }
- {
- // check simple unscaled expansion, and fill style with stroke forced
- SkPaint paint;
- paint.setStyle(SkPaint::kFill_Style);
- paint.setStrokeWidth(10.0f);
- ClipRect clip(Rect(200, 200));
- RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
- auto bakedState = BakedOpState::tryStrokeableOpConstruct(
- allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::Forced, false);
-
- ASSERT_NE(nullptr, bakedState);
- EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
- EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
- }
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
deleted file mode 100644
index 4c03811b0c96..000000000000
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "CanvasState.h"
-
-#include "Matrix.h"
-#include "Rect.h"
-#include "hwui/Canvas.h"
-#include "utils/LinearAllocator.h"
-
-#include <SkClipOp.h>
-#include <SkPath.h>
-#include <gtest/gtest.h>
-
-namespace android {
-namespace uirenderer {
-
-class NullClient : public CanvasStateClient {
- void onViewportInitialized() override {}
- void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
- GLuint getTargetFbo() const override { return 0; }
-};
-
-static NullClient sNullClient;
-
-static bool approxEqual(const Matrix4& a, const Matrix4& b) {
- for (int i = 0; i < 16; i++) {
- if (!MathUtils::areEqual(a[i], b[i])) {
- return false;
- }
- }
- return true;
-}
-
-TEST(CanvasState, gettersAndSetters) {
- CanvasState state(sNullClient);
- state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
- ASSERT_EQ(state.getWidth(), 200);
- ASSERT_EQ(state.getHeight(), 200);
-
- Matrix4 simpleTranslate;
- simpleTranslate.loadTranslate(10, 20, 0);
- state.setMatrix(simpleTranslate);
-
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200));
- ASSERT_EQ(state.getLocalClipBounds(), Rect(-10, -20, 190, 180));
- EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
- EXPECT_TRUE(state.clipIsSimple());
-}
-
-TEST(CanvasState, simpleClipping) {
- CanvasState state(sNullClient);
- state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
- state.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100));
-
- state.clipRect(10, 10, 200, 200, SkClipOp::kIntersect);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100));
-
- state.clipRect(50, 50, 150, 150, SkClipOp::kReplace_deprecated);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150));
-}
-
-TEST(CanvasState, complexClipping) {
- CanvasState state(sNullClient);
- state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
- state.save(SaveFlags::MatrixClip);
- {
- // rotated clip causes complex clip
- state.rotate(10);
- EXPECT_TRUE(state.clipIsSimple());
- state.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
- EXPECT_FALSE(state.clipIsSimple());
- }
- state.restore();
-
- state.save(SaveFlags::MatrixClip);
- {
- // subtracted clip causes complex clip
- EXPECT_TRUE(state.clipIsSimple());
- state.clipRect(50, 50, 150, 150, SkClipOp::kDifference);
- EXPECT_FALSE(state.clipIsSimple());
- }
- state.restore();
-
- state.save(SaveFlags::MatrixClip);
- {
- // complex path causes complex clip
- SkPath path;
- path.addOval(SkRect::MakeWH(200, 200));
- EXPECT_TRUE(state.clipIsSimple());
- state.clipPath(&path, SkClipOp::kDifference);
- EXPECT_FALSE(state.clipIsSimple());
- }
- state.restore();
-}
-
-TEST(CanvasState, saveAndRestore) {
- CanvasState state(sNullClient);
- state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
- state.save(SaveFlags::Clip);
- {
- state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
- }
- state.restore();
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200)); // verify restore
-
- Matrix4 simpleTranslate;
- simpleTranslate.loadTranslate(10, 10, 0);
- state.save(SaveFlags::Matrix);
- {
- state.translate(10, 10, 0);
- EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
- }
- state.restore();
- EXPECT_FALSE(approxEqual(*state.currentTransform(), simpleTranslate));
-}
-
-TEST(CanvasState, saveAndRestoreButNotTooMuch) {
- CanvasState state(sNullClient);
- state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
- state.save(SaveFlags::Matrix); // NOTE: clip not saved
- {
- state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
- }
- state.restore();
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); // verify not restored
-
- Matrix4 simpleTranslate;
- simpleTranslate.loadTranslate(10, 10, 0);
- state.save(SaveFlags::Clip); // NOTE: matrix not saved
- {
- state.translate(10, 10, 0);
- EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
- }
- state.restore();
- EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate)); // verify not restored
-}
-}
-}
diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp
deleted file mode 100644
index 450bb679a45f..000000000000
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <SkPath.h>
-#include <SkRegion.h>
-#include <gtest/gtest.h>
-
-#include "ClipArea.h"
-
-#include "Matrix.h"
-#include "Rect.h"
-#include "utils/LinearAllocator.h"
-
-namespace android {
-namespace uirenderer {
-
-static Rect kViewportBounds(2048, 2048);
-
-static ClipArea createClipArea() {
- ClipArea area;
- area.setViewportDimensions(kViewportBounds.getWidth(), kViewportBounds.getHeight());
- return area;
-}
-
-TEST(TransformedRectangle, basics) {
- Rect r(0, 0, 100, 100);
- Matrix4 minus90;
- minus90.loadRotate(-90);
- minus90.mapRect(r);
- Rect r2(20, 40, 120, 60);
-
- Matrix4 m90;
- m90.loadRotate(90);
- TransformedRectangle tr(r, m90);
- EXPECT_TRUE(tr.canSimplyIntersectWith(tr));
-
- Matrix4 m0;
- TransformedRectangle tr0(r2, m0);
- EXPECT_FALSE(tr.canSimplyIntersectWith(tr0));
-
- Matrix4 m45;
- m45.loadRotate(45);
- TransformedRectangle tr2(r, m45);
- EXPECT_FALSE(tr2.canSimplyIntersectWith(tr));
-}
-
-TEST(RectangleList, basics) {
- RectangleList list;
- EXPECT_TRUE(list.isEmpty());
-
- Rect r(0, 0, 100, 100);
- Matrix4 m45;
- m45.loadRotate(45);
- list.set(r, m45);
- EXPECT_FALSE(list.isEmpty());
-
- Rect r2(20, 20, 200, 200);
- list.intersectWith(r2, m45);
- EXPECT_FALSE(list.isEmpty());
- EXPECT_EQ(1, list.getTransformedRectanglesCount());
-
- Rect r3(20, 20, 200, 200);
- Matrix4 m30;
- m30.loadRotate(30);
- list.intersectWith(r2, m30);
- EXPECT_FALSE(list.isEmpty());
- EXPECT_EQ(2, list.getTransformedRectanglesCount());
-
- SkRegion clip;
- clip.setRect(0, 0, 2000, 2000);
- SkRegion rgn(list.convertToRegion(clip));
- EXPECT_FALSE(rgn.isEmpty());
-}
-
-TEST(ClipArea, basics) {
- ClipArea area(createClipArea());
- EXPECT_FALSE(area.isEmpty());
-}
-
-TEST(ClipArea, paths) {
- ClipArea area(createClipArea());
- SkPath path;
- SkScalar r = 100;
- path.addCircle(r, r, r);
- area.clipPathWithTransform(path, &Matrix4::identity(), SkRegion::kIntersect_Op);
- EXPECT_FALSE(area.isEmpty());
- EXPECT_FALSE(area.isSimple());
- EXPECT_FALSE(area.isRectangleList());
-
- Rect clipRect(area.getClipRect());
- Rect expected(0, 0, r * 2, r * 2);
- EXPECT_EQ(expected, clipRect);
- SkRegion clipRegion(area.getClipRegion());
- auto skRect(clipRegion.getBounds());
- Rect regionBounds;
- regionBounds.set(skRect);
- EXPECT_EQ(expected, regionBounds);
-}
-
-TEST(ClipArea, replaceNegative) {
- ClipArea area(createClipArea());
- area.setClip(0, 0, 100, 100);
-
- Rect expected(-50, -50, 50, 50);
- area.clipRectWithTransform(expected, &Matrix4::identity(), SkRegion::kReplace_Op);
- EXPECT_EQ(expected, area.getClipRect());
-}
-
-TEST(ClipArea, serializeClip) {
- ClipArea area(createClipArea());
- LinearAllocator allocator;
-
- // unset clip
- EXPECT_EQ(nullptr, area.serializeClip(allocator));
-
- // rect clip
- area.setClip(0, 0, 200, 200);
- {
- auto serializedClip = area.serializeClip(allocator);
- ASSERT_NE(nullptr, serializedClip);
- ASSERT_EQ(ClipMode::Rectangle, serializedClip->mode);
- ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
- EXPECT_EQ(Rect(200, 200), serializedClip->rect);
- EXPECT_EQ(serializedClip, area.serializeClip(allocator))
- << "Requery of clip on unmodified ClipArea must return same pointer.";
- }
-
- // rect list
- Matrix4 rotate;
- rotate.loadRotate(5.0f);
- area.clipRectWithTransform(Rect(50, 50, 150, 150), &rotate, SkRegion::kIntersect_Op);
- {
- auto serializedClip = area.serializeClip(allocator);
- ASSERT_NE(nullptr, serializedClip);
- ASSERT_EQ(ClipMode::RectangleList, serializedClip->mode);
- ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
- auto clipRectList = reinterpret_cast<const ClipRectList*>(serializedClip);
- EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
- EXPECT_EQ(Rect(37, 54, 145, 163), clipRectList->rect);
- EXPECT_EQ(serializedClip, area.serializeClip(allocator))
- << "Requery of clip on unmodified ClipArea must return same pointer.";
- }
-
- // region
- SkPath circlePath;
- circlePath.addCircle(100, 100, 100);
- area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op);
- {
- auto serializedClip = area.serializeClip(allocator);
- ASSERT_NE(nullptr, serializedClip);
- ASSERT_EQ(ClipMode::Region, serializedClip->mode);
- ASSERT_TRUE(serializedClip->intersectWithRoot) << "Replace op, so expect intersectWithRoot";
- auto clipRegion = reinterpret_cast<const ClipRegion*>(serializedClip);
- EXPECT_EQ(SkIRect::MakeWH(200, 200), clipRegion->region.getBounds())
- << "Clip region should be 200x200";
- EXPECT_EQ(Rect(200, 200), clipRegion->rect);
- EXPECT_EQ(serializedClip, area.serializeClip(allocator))
- << "Requery of clip on unmodified ClipArea must return same pointer.";
- }
-}
-
-TEST(ClipArea, serializeClip_pathIntersectWithRoot) {
- ClipArea area(createClipArea());
- LinearAllocator allocator;
- SkPath circlePath;
- circlePath.addCircle(100, 100, 100);
- area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kIntersect_Op);
-
- auto serializedClip = area.serializeClip(allocator);
- ASSERT_NE(nullptr, serializedClip);
- EXPECT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
-}
-
-TEST(ClipArea, serializeIntersectedClip) {
- ClipArea area(createClipArea());
- LinearAllocator allocator;
-
- // simple state;
- EXPECT_EQ(nullptr, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()));
- area.setClip(0, 0, 200, 200);
- {
- auto origRectClip = area.serializeClip(allocator);
- ASSERT_NE(nullptr, origRectClip);
- EXPECT_EQ(origRectClip,
- area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()));
- }
-
- // rect
- {
- ClipRect recordedClip(Rect(100, 100));
- Matrix4 translateScale;
- translateScale.loadTranslate(100, 100, 0);
- translateScale.scale(2, 3, 1);
- auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
- ASSERT_NE(nullptr, resolvedClip);
- ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode);
- EXPECT_EQ(Rect(100, 100, 200, 200), resolvedClip->rect);
-
- EXPECT_EQ(resolvedClip,
- area.serializeIntersectedClip(allocator, &recordedClip, translateScale))
- << "Must return previous serialization, since input is same";
-
- ClipRect recordedClip2(Rect(100, 100));
- EXPECT_NE(resolvedClip,
- area.serializeIntersectedClip(allocator, &recordedClip2, translateScale))
- << "Shouldn't return previous serialization, since matrix location is different";
- }
-
- // rect list
- Matrix4 rotate;
- rotate.loadRotate(2.0f);
- area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op);
- {
- ClipRect recordedClip(Rect(100, 100));
- auto resolvedClip =
- area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity());
- ASSERT_NE(nullptr, resolvedClip);
- ASSERT_EQ(ClipMode::RectangleList, resolvedClip->mode);
- auto clipRectList = reinterpret_cast<const ClipRectList*>(resolvedClip);
- EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
- }
-
- // region
- SkPath circlePath;
- circlePath.addCircle(100, 100, 100);
- area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op);
- {
- SkPath ovalPath;
- ovalPath.addOval(SkRect::MakeLTRB(50, 0, 150, 200));
-
- ClipRegion recordedClip;
- recordedClip.region.setPath(ovalPath, SkRegion(SkIRect::MakeWH(200, 200)));
- recordedClip.rect = Rect(200, 200);
-
- Matrix4 translate10x20;
- translate10x20.loadTranslate(10, 20, 0);
- auto resolvedClip = area.serializeIntersectedClip(
- allocator, &recordedClip,
- translate10x20); // Note: only translate for now, others not handled correctly
- ASSERT_NE(nullptr, resolvedClip);
- ASSERT_EQ(ClipMode::Region, resolvedClip->mode);
- auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip);
- EXPECT_EQ(SkIRect::MakeLTRB(60, 20, 160, 200), clipRegion->region.getBounds());
- }
-}
-
-TEST(ClipArea, serializeIntersectedClip_snap) {
- ClipArea area(createClipArea());
- area.setClip(100.2, 100.4, 500.6, 500.8);
- LinearAllocator allocator;
-
- {
- // no recorded clip case
- auto resolvedClip = area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity());
- EXPECT_EQ(Rect(100, 100, 501, 501), resolvedClip->rect);
- }
- {
- // recorded clip case
- ClipRect recordedClip(Rect(100.12, 100.74));
- Matrix4 translateScale;
- translateScale.loadTranslate(100, 100, 0);
- translateScale.scale(2, 3,
- 1); // recorded clip will have non-int coords, even after transform
- auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
- ASSERT_NE(nullptr, resolvedClip);
- EXPECT_EQ(ClipMode::Rectangle, resolvedClip->mode);
- EXPECT_EQ(Rect(100, 100, 300, 402), resolvedClip->rect);
- }
-}
-
-TEST(ClipArea, serializeIntersectedClip_scale) {
- ClipArea area(createClipArea());
- area.setClip(0, 0, 400, 400);
- LinearAllocator allocator;
-
- SkPath circlePath;
- circlePath.addCircle(50, 50, 50);
-
- ClipRegion recordedClip;
- recordedClip.region.setPath(circlePath, SkRegion(SkIRect::MakeWH(100, 100)));
- recordedClip.rect = Rect(100, 100);
-
- Matrix4 translateScale;
- translateScale.loadTranslate(100, 100, 0);
- translateScale.scale(2, 2, 1);
- auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
-
- ASSERT_NE(nullptr, resolvedClip);
- EXPECT_EQ(ClipMode::Region, resolvedClip->mode);
- EXPECT_EQ(Rect(100, 100, 300, 300), resolvedClip->rect);
- auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip);
- EXPECT_EQ(SkIRect::MakeLTRB(100, 100, 300, 300), clipRegion->region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_identity) {
- SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
- ClipArea::applyTransformToRegion(Matrix4::identity(), &region);
- EXPECT_TRUE(region.isRect());
- EXPECT_EQ(SkIRect::MakeLTRB(1, 2, 3, 4), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_translate) {
- SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
- Matrix4 transform;
- transform.loadTranslate(10, 20, 0);
- ClipArea::applyTransformToRegion(transform, &region);
- EXPECT_TRUE(region.isRect());
- EXPECT_EQ(SkIRect::MakeLTRB(11, 22, 13, 24), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_scale) {
- SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
- Matrix4 transform;
- transform.loadScale(2, 3, 1);
- ClipArea::applyTransformToRegion(transform, &region);
- EXPECT_TRUE(region.isRect());
- EXPECT_EQ(SkIRect::MakeLTRB(2, 6, 6, 12), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_translateScale) {
- SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
- Matrix4 transform;
- transform.translate(10, 20);
- transform.scale(2, 3, 1);
- ClipArea::applyTransformToRegion(transform, &region);
- EXPECT_TRUE(region.isRect());
- EXPECT_EQ(SkIRect::MakeLTRB(12, 26, 16, 32), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_rotate90) {
- SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
- Matrix4 transform;
- transform.loadRotate(90);
- ClipArea::applyTransformToRegion(transform, &region);
- EXPECT_TRUE(region.isRect());
- EXPECT_EQ(SkIRect::MakeLTRB(-4, 1, -2, 3), region.getBounds());
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index f29830f0e34b..6c8775b1bdbb 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -15,12 +15,13 @@
*/
#include "DeferredLayerUpdater.h"
-#include "GlLayer.h"
#include "Properties.h"
#include "tests/common/TestUtils.h"
#include <gtest/gtest.h>
+#include <SkBitmap.h>
+#include <SkImage.h>
using namespace android;
using namespace android::uirenderer;
@@ -31,10 +32,6 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) {
layerUpdater->setBlend(true);
// updates are deferred so the backing layer should still be in its default state
- if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
- GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
- EXPECT_EQ((uint32_t)GL_NONE, glLayer->getRenderTarget());
- }
EXPECT_EQ(0u, layerUpdater->backingLayer()->getWidth());
EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight());
EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter());
@@ -42,19 +39,13 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) {
EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform());
// push the deferred updates to the layer
- Matrix4 scaledMatrix;
- scaledMatrix.loadScale(0.5, 0.5, 0.0);
- layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN);
- if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
- GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
- glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
- }
+ SkMatrix scaledMatrix = SkMatrix::MakeScale(0.5, 0.5);
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(16, 16);
+ sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap);
+ layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage);
// the backing layer should now have all the properties applied.
- if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
- GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
- EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, glLayer->getRenderTarget());
- }
EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth());
EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight());
EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter());
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 9693ce7b6784..89f0c52b49ec 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -42,10 +42,6 @@ public:
const SkPaint& paint) {
ADD_FAILURE() << "onDrawPosTextH not expected in this test";
}
- void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
- const SkMatrix* matrix, const SkPaint& paint) {
- ADD_FAILURE() << "onDrawTextOnPath not expected in this test";
- }
void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
const SkRect* cullRect, const SkPaint& paint) {
ADD_FAILURE() << "onDrawTextRSXform not expected in this test";
diff --git a/libs/hwui/tests/unit/FontRendererTests.cpp b/libs/hwui/tests/unit/FontRendererTests.cpp
deleted file mode 100644
index c78f131ce2ce..000000000000
--- a/libs/hwui/tests/unit/FontRendererTests.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "GammaFontRenderer.h"
-#include "tests/common/TestUtils.h"
-
-using namespace android::uirenderer;
-
-static bool isZero(uint8_t* data, int size) {
- for (int i = 0; i < size; i++) {
- if (data[i]) return false;
- }
- return true;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, renderDropShadow) {
- SkPaint paint;
- paint.setTextSize(10);
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- GammaFontRenderer gammaFontRenderer;
- FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
- fontRenderer.setFont(&paint, SkMatrix::I());
-
- std::vector<glyph_t> glyphs;
- std::vector<float> positions;
- float totalAdvance;
- Rect bounds;
- TestUtils::layoutTextUnscaled(paint, "This is a test", &glyphs, &positions, &totalAdvance,
- &bounds);
-
- for (int radius : {28, 20, 2}) {
- auto result = fontRenderer.renderDropShadow(&paint, glyphs.data(), glyphs.size(), radius,
- positions.data());
- ASSERT_NE(nullptr, result.image);
- EXPECT_FALSE(isZero(result.image, result.width * result.height));
- EXPECT_LE(bounds.getWidth() + radius * 2, (int)result.width);
- EXPECT_LE(bounds.getHeight() + radius * 2, (int)result.height);
- delete result.image;
- }
-}
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
deleted file mode 100644
index 4eb77514f4ae..000000000000
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ /dev/null
@@ -1,2705 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <BakedOpState.h>
-#include <DeferredLayerUpdater.h>
-#include <FrameBuilder.h>
-#include <GlLayer.h>
-#include <LayerUpdateQueue.h>
-#include <RecordedOp.h>
-#include <RecordingCanvas.h>
-#include <tests/common/TestUtils.h>
-
-#include <unordered_map>
-
-namespace android {
-namespace uirenderer {
-
-const FrameBuilder::LightGeometry sLightGeometry = {{100, 100, 100}, 50};
-
-/**
- * Virtual class implemented by each test to redirect static operation / state transitions to
- * virtual methods.
- *
- * Virtual dispatch allows for default behaviors to be specified (very common case in below tests),
- * and allows Renderer vs Dispatching behavior to be merged.
- *
- * onXXXOp methods fail by default - tests should override ops they expect
- * startRepaintLayer fails by default - tests should override if expected
- * startFrame/endFrame do nothing by default - tests should override to intercept
- */
-class TestRendererBase {
-public:
- virtual ~TestRendererBase() {}
- virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
- ADD_FAILURE() << "Temporary layers not expected in this test";
- return nullptr;
- }
- virtual void recycleTemporaryLayer(OffscreenBuffer*) {
- ADD_FAILURE() << "Temporary layers not expected in this test";
- }
- virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
- ADD_FAILURE() << "Layer repaint not expected in this test";
- }
- virtual void endLayer() { ADD_FAILURE() << "Layer updates not expected in this test"; }
- virtual void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) {}
- virtual void endFrame(const Rect& repaintRect) {}
-
-// define virtual defaults for single draw methods
-#define X(Type) \
- virtual void on##Type(const Type&, const BakedOpState&) { \
- ADD_FAILURE() << #Type " not expected in this test"; \
- }
- MAP_RENDERABLE_OPS(X)
-#undef X
-
-// define virtual defaults for merged draw methods
-#define X(Type) \
- virtual void onMerged##Type##s(const MergedBakedOpList& opList) { \
- ADD_FAILURE() << "Merged " #Type "s not expected in this test"; \
- }
- MAP_MERGEABLE_OPS(X)
-#undef X
-
- int getIndex() { return mIndex; }
-
-protected:
- int mIndex = 0;
-};
-
-/**
- * Dispatches all static methods to similar formed methods on renderer, which fail by default but
- * are overridden by subclasses per test.
- */
-class TestDispatcher {
-public:
-// define single op methods, which redirect to TestRendererBase
-#define X(Type) \
- static void on##Type(TestRendererBase& renderer, const Type& op, const BakedOpState& state) { \
- renderer.on##Type(op, state); \
- }
- MAP_RENDERABLE_OPS(X);
-#undef X
-
-// define merged op methods, which redirect to TestRendererBase
-#define X(Type) \
- static void onMerged##Type##s(TestRendererBase& renderer, const MergedBakedOpList& opList) { \
- renderer.onMerged##Type##s(opList); \
- }
- MAP_MERGEABLE_OPS(X);
-#undef X
-};
-
-class FailRenderer : public TestRendererBase {};
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simple) {
- class SimpleTestRenderer : public TestRendererBase {
- public:
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(100u, width);
- EXPECT_EQ(200u, height);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- }
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(3, mIndex++); }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
- canvas.drawRect(0, 0, 100, 200, SkPaint());
- canvas.drawBitmap(*bitmap, 10, 10, nullptr);
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SimpleTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleStroke) {
- class SimpleStrokeTestRenderer : public TestRendererBase {
- public:
- void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- // even though initial bounds are empty...
- EXPECT_TRUE(op.unmappedBounds.isEmpty())
- << "initial bounds should be empty, since they're unstroked";
- EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
- << "final bounds should account for stroke";
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- SkPaint strokedPaint;
- strokedPaint.setStrokeWidth(10);
- canvas.drawPoint(50, 50, strokedPaint);
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SimpleStrokeTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, arcStrokeClip) {
- class ArcStrokeClipTestRenderer : public TestRendererBase {
- public:
- void onArcOp(const ArcOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(25, 25, 175, 175), op.unmappedBounds);
- EXPECT_EQ(Rect(25, 25, 175, 175), state.computedState.clippedBounds);
- EXPECT_EQ(OpClipSideFlags::Full, state.computedState.clipSideFlags)
- << "Arc op clipped conservatively, since path texture may be expanded";
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.clipRect(25, 25, 175, 175, SkClipOp::kIntersect);
- SkPaint aaPaint;
- aaPaint.setAntiAlias(true);
- canvas.drawArc(25, 25, 175, 175, 40, 180, true, aaPaint);
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- ArcStrokeClipTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleRejection) {
- auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props,
- RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); // intersection should be empty
- canvas.drawRect(0, 0, 400, 400, SkPaint());
- canvas.restore();
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- FailRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleBatching) {
- const int LOOPS = 5;
- class SimpleBatchingTestRenderer : public TestRendererBase {
- public:
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects";
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps";
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
-
- sk_sp<Bitmap> bitmap(TestUtils::createBitmap(
- 10, 10,
- kAlpha_8_SkColorType)); // Disable merging by using alpha 8 bitmap
-
- // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects.
- // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group.
- canvas.save(SaveFlags::MatrixClip);
- for (int i = 0; i < LOOPS; i++) {
- canvas.translate(0, 10);
- canvas.drawRect(0, 0, 10, 10, SkPaint());
- canvas.drawBitmap(*bitmap, 5, 0, nullptr);
- }
- canvas.restore();
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SimpleBatchingTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2 * LOOPS, renderer.getIndex()) << "Expect number of ops = 2 * loop count";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNode_translateClip) {
- class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds);
- EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom,
- state.computedState.clipSideFlags);
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 100, 100, SkPaint());
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node
- *TestUtils::getSyncedNode(node));
-
- DeferRenderNodeTranslateClipTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferRenderNodeScene) {
- class DeferRenderNodeSceneTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- const Rect& clippedBounds = state.computedState.clippedBounds;
- Matrix4 expected;
- switch (mIndex++) {
- case 0:
- // background - left side
- EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds);
- expected.loadTranslate(100, 100, 0);
- break;
- case 1:
- // background - top side
- EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds);
- expected.loadTranslate(100, 100, 0);
- break;
- case 2:
- // content
- EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds);
- expected.loadTranslate(-50, -50, 0);
- break;
- case 3:
- // overlay
- EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds);
- break;
- default:
- ADD_FAILURE() << "Too many rects observed";
- }
- EXPECT_EQ(expected, state.computedState.transform);
- }
- };
-
- std::vector<sp<RenderNode>> nodes;
- SkPaint transparentPaint;
- transparentPaint.setAlpha(128);
-
- // backdrop
- nodes.push_back(TestUtils::createNode<RecordingCanvas>(
- 100, 100, 700, 500, // 600x400
- [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 600, 400, transparentPaint);
- }));
-
- // content
- Rect contentDrawBounds(150, 150, 650, 450); // 500x300
- nodes.push_back(TestUtils::createNode<RecordingCanvas>(
- 0, 0, 800, 600, [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 800, 600, transparentPaint);
- }));
-
- // overlay
- nodes.push_back(TestUtils::createNode<RecordingCanvas>(
- 0, 0, 800, 600, [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 800, 200, transparentPaint);
- }));
-
- for (auto& node : nodes) {
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
- }
-
- {
- FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
-
- DeferRenderNodeSceneTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
- }
-
- for (auto& node : nodes) {
- EXPECT_TRUE(node->isValid());
- EXPECT_FALSE(node->nothingToDraw());
- node->setStagingDisplayList(nullptr);
- EXPECT_FALSE(node->isValid());
- EXPECT_FALSE(node->nothingToDraw());
- node->destroyHardwareResources();
- EXPECT_TRUE(node->nothingToDraw());
- EXPECT_FALSE(node->isValid());
- }
-
- {
- // Validate no crashes if any nodes are missing DisplayLists
- FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds);
-
- FailRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- }
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_noFbo0) {
- class EmptyNoFbo0TestRenderer : public TestRendererBase {
- public:
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- ADD_FAILURE() << "Primary frame draw not expected in this test";
- }
- void endFrame(const Rect& repaintRect) override {
- ADD_FAILURE() << "Primary frame draw not expected in this test";
- }
- };
-
- // Use layer update constructor, so no work is enqueued for Fbo0
- LayerUpdateQueue emptyLayerUpdateQueue;
- FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance());
- EmptyNoFbo0TestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, empty_withFbo0) {
- class EmptyWithFbo0TestRenderer : public TestRendererBase {
- public:
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(1, mIndex++); }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) {
- // no drawn content
- });
-
- // Draw, but pass node without draw content, so no work is done for primary frame
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- EmptyWithFbo0TestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
- " but fbo0 update lifecycle should still be observed";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_rects) {
- class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(mIndex++, 0) << "Should be one rect";
- EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds)
- << "Last rect should occlude others.";
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.drawRect(10, 10, 190, 190, SkPaint());
- });
-
- // Damage (and therefore clip) is same as last draw, subset of renderable area.
- // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
- FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
- << "Recording must not have rejected ops, in order for this test to be valid";
-
- AvoidOverdrawRectsTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
- static sk_sp<Bitmap> opaqueBitmap(
- TestUtils::createBitmap(50, 50, SkColorType::kRGB_565_SkColorType));
- static sk_sp<Bitmap> transpBitmap(
- TestUtils::createBitmap(50, 50, SkColorType::kAlpha_8_SkColorType));
- class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
- public:
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- switch (mIndex++) {
- case 0:
- EXPECT_EQ(opaqueBitmap.get(), op.bitmap);
- break;
- case 1:
- EXPECT_EQ(transpBitmap.get(), op.bitmap);
- break;
- default:
- ADD_FAILURE() << "Only two ops expected.";
- }
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 50, 50, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 50, 50, SkPaint());
- canvas.drawRect(0, 0, 50, 50, SkPaint());
- canvas.drawBitmap(*transpBitmap, 0, 0, nullptr);
-
- // only the below draws should remain, since they're
- canvas.drawBitmap(*opaqueBitmap, 0, 0, nullptr);
- canvas.drawBitmap(*transpBitmap, 0, 0, nullptr);
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
- << "Recording must not have rejected ops, in order for this test to be valid";
-
- AvoidOverdrawBitmapsTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clippedMerging) {
- class ClippedMergingTestRenderer : public TestRendererBase {
- public:
- void onMergedBitmapOps(const MergedBakedOpList& opList) override {
- EXPECT_EQ(0, mIndex);
- mIndex += opList.count;
- EXPECT_EQ(4u, opList.count);
- EXPECT_EQ(Rect(10, 10, 90, 90), opList.clip);
- EXPECT_EQ(OpClipSideFlags::Left | OpClipSideFlags::Top | OpClipSideFlags::Right,
- opList.clipSideFlags);
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20));
-
- // left side clipped (to inset left half)
- canvas.clipRect(10, 0, 50, 100, SkClipOp::kReplace_deprecated);
- canvas.drawBitmap(*bitmap, 0, 40, nullptr);
-
- // top side clipped (to inset top half)
- canvas.clipRect(0, 10, 100, 50, SkClipOp::kReplace_deprecated);
- canvas.drawBitmap(*bitmap, 40, 0, nullptr);
-
- // right side clipped (to inset right half)
- canvas.clipRect(50, 0, 90, 100, SkClipOp::kReplace_deprecated);
- canvas.drawBitmap(*bitmap, 80, 40, nullptr);
-
- // bottom not clipped, just abutting (inset bottom half)
- canvas.clipRect(0, 50, 100, 90, SkClipOp::kReplace_deprecated);
- canvas.drawBitmap(*bitmap, 40, 70, nullptr);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- ClippedMergingTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, regionClipStopsMerge) {
- class RegionClipStopsMergeTestRenderer : public TestRendererBase {
- public:
- void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400, [](RenderProperties& props, RecordingCanvas& canvas) {
- SkPath path;
- path.addCircle(200, 200, 200, SkPath::kCW_Direction);
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipPath(&path, SkClipOp::kIntersect);
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(50);
- TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
- TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200);
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- RegionClipStopsMergeTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textMerging) {
- class TextMergingTestRenderer : public TestRendererBase {
- public:
- void onMergedTextOps(const MergedBakedOpList& opList) override {
- EXPECT_EQ(0, mIndex);
- mIndex += opList.count;
- EXPECT_EQ(2u, opList.count);
- EXPECT_EQ(OpClipSideFlags::Top, opList.clipSideFlags);
- EXPECT_EQ(OpClipSideFlags::Top, opList.states[0]->computedState.clipSideFlags);
- EXPECT_EQ(OpClipSideFlags::None, opList.states[1]->computedState.clipSideFlags);
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400, [](RenderProperties& props,
- RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(50);
- TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
- TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- TextMergingTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStrikethrough) {
- const int LOOPS = 5;
- class TextStrikethroughTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text";
- }
- void onMergedTextOps(const MergedBakedOpList& opList) override {
- EXPECT_EQ(0, mIndex);
- mIndex += opList.count;
- EXPECT_EQ(5u, opList.count);
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 2000, [](RenderProperties& props, RecordingCanvas& canvas) {
- SkPaint textPaint;
- textPaint.setAntiAlias(true);
- textPaint.setTextSize(20);
- textPaint.setFlags(textPaint.getFlags() | SkPaint::kStrikeThruText_ReserveFlag);
- for (int i = 0; i < LOOPS; i++) {
- TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
- }
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- TextStrikethroughTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2 * LOOPS, renderer.getIndex()) << "Expect number of ops = 2 * loop count";
-}
-
-static auto styles = {SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style};
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textStyle) {
- class TextStyleTestRenderer : public TestRendererBase {
- public:
- void onMergedTextOps(const MergedBakedOpList& opList) override {
- ASSERT_EQ(0, mIndex);
- ASSERT_EQ(3u, opList.count);
- mIndex += opList.count;
-
- int index = 0;
- for (auto style : styles) {
- auto state = opList.states[index++];
- ASSERT_EQ(style, state->op->paint->getStyle())
- << "Remainder of validation relies upon stable merged order";
- ASSERT_EQ(0, state->computedState.clipSideFlags)
- << "Clipped bounds validation requires unclipped ops";
- }
-
- Rect fill = opList.states[0]->computedState.clippedBounds;
- Rect stroke = opList.states[1]->computedState.clippedBounds;
- EXPECT_EQ(stroke, opList.states[2]->computedState.clippedBounds)
- << "Stroke+Fill should be same as stroke";
-
- EXPECT_TRUE(stroke.contains(fill));
- EXPECT_FALSE(fill.contains(stroke));
-
- // outset by half the stroke width
- Rect outsetFill(fill);
- outsetFill.outset(5);
- EXPECT_EQ(stroke, outsetFill);
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400, [](RenderProperties& props, RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(50);
- paint.setStrokeWidth(10);
-
- // draw 3 copies of the same text overlapping, each with a different style.
- // They'll get merged, but with
- for (auto style : styles) {
- paint.setStyle(style);
- TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
- }
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
- TextStyleTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
- class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
- public:
- void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipRect());
- EXPECT_EQ(Rect(50, 50, 105, 105), state.computedState.clippedBounds);
-
- Matrix4 expected;
- expected.loadTranslate(5, 5, 0);
- EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
- }
- };
-
- auto layerUpdater =
- TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5));
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(50, 50, 150, 150, SkClipOp::kIntersect);
- canvas.drawLayer(layerUpdater.get());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- TextureLayerClipLocalMatrixTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_combineMatrices) {
- class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
- public:
- void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
-
- Matrix4 expected;
- expected.loadTranslate(35, 45, 0);
- EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
- }
- };
-
- auto layerUpdater =
- TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5));
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(30, 40);
- canvas.drawLayer(layerUpdater.get());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- TextureLayerCombineMatricesTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, textureLayer_reject) {
- auto layerUpdater =
- TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5));
- EXPECT_EQ(Layer::Api::OpenGL, layerUpdater->backingLayer()->getApi());
-
- GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
- glLayer->setRenderTarget(GL_NONE); // Should be rejected
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.drawLayer(layerUpdater.get());
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- FailRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, functor_reject) {
- class FunctorTestRenderer : public TestRendererBase {
- public:
- void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- }
- };
- Functor noopFunctor;
-
- // 1 million pixel tall view, scrolled down 80%
- auto scrolledFunctorView = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 1000000, [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.translate(0, -800000);
- canvas.callDrawGLFunction(&noopFunctor, nullptr);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView));
-
- FunctorTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, deferColorOp_unbounded) {
- class ColorTestRenderer : public TestRendererBase {
- public:
- void onColorOp(const ColorOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
- << "Color op should be expanded to bounds of surrounding";
- }
- };
-
- auto unclippedColorView = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 10, 10, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setClipToBounds(false);
- canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView));
-
- ColorTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderNode) {
- class RenderNodeTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- switch (mIndex++) {
- case 0:
- EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
- EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
- break;
- case 1:
- EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
- EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
- break;
- default:
- ADD_FAILURE();
- }
- }
- };
-
- auto child = TestUtils::createNode<RecordingCanvas>(
- 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
-
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [&child](RenderProperties& props, RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setColor(SK_ColorDKGRAY);
- canvas.drawRect(0, 0, 200, 200, paint);
-
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(40, 40);
- canvas.drawRenderNode(child.get());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- RenderNodeTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clipped) {
- class ClippedTestRenderer : public TestRendererBase {
- public:
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect());
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- sk_sp<Bitmap> bitmap(TestUtils::createBitmap(200, 200));
- canvas.drawBitmap(*bitmap, 0, 0, nullptr);
- });
-
- // clip to small area, should see in receiver
- FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- ClippedTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_simple) {
- class SaveLayerSimpleTestRenderer : public TestRendererBase {
- public:
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(180u, width);
- EXPECT_EQ(180u, height);
- return nullptr;
- }
- void endLayer() override { EXPECT_EQ(2, mIndex++); }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
- EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(180, 180), state.computedState.clipRect());
-
- Matrix4 expectedTransform;
- expectedTransform.loadTranslate(-10, -10, 0);
- EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(4, mIndex++);
- EXPECT_EQ(nullptr, offscreenBuffer);
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10, 10, 190, 190, 128, SaveFlags::ClipToLayer);
- canvas.drawRect(10, 10, 190, 190, SkPaint());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerSimpleTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(5, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_nested) {
- /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
- * - startTemporaryLayer2, rect2 endLayer2
- * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
- * - startFrame, layerOp1, endFrame
- */
- class SaveLayerNestedTestRenderer : public TestRendererBase {
- public:
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
- const int index = mIndex++;
- if (index == 0) {
- EXPECT_EQ(400u, width);
- EXPECT_EQ(400u, height);
- return (OffscreenBuffer*)0x400;
- } else if (index == 3) {
- EXPECT_EQ(800u, width);
- EXPECT_EQ(800u, height);
- return (OffscreenBuffer*)0x800;
- } else {
- ADD_FAILURE();
- }
- return (OffscreenBuffer*)nullptr;
- }
- void endLayer() override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 6);
- }
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(7, mIndex++);
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(9, mIndex++); }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- const int index = mIndex++;
- if (index == 1) {
- EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
- } else if (index == 4) {
- EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer rect
- } else {
- ADD_FAILURE();
- }
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- const int index = mIndex++;
- if (index == 5) {
- EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
- EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner layer
- } else if (index == 8) {
- EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
- EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
- } else {
- ADD_FAILURE();
- }
- }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- const int index = mIndex++;
- // order isn't important, but we need to see both
- if (index == 10) {
- EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer);
- } else if (index == 11) {
- EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer);
- } else {
- ADD_FAILURE();
- }
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 800, 800, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(0, 0, 800, 800, 128, SaveFlags::ClipToLayer);
- {
- canvas.drawRect(0, 0, 800, 800, SkPaint());
- canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
- { canvas.drawRect(0, 0, 400, 400, SkPaint()); }
- canvas.restore();
- }
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerNestedTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(12, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayer_contentRejection) {
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect);
- canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
-
- // draw within save layer may still be recorded, but shouldn't be drawn
- canvas.drawRect(200, 200, 400, 400, SkPaint());
-
- canvas.restore();
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- FailRenderer renderer;
- // should see no ops, even within the layer, since the layer should be rejected
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_simple) {
- class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
- public:
- void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
- EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- ASSERT_NE(nullptr, op.paint);
- ASSERT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint));
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- EXPECT_EQ(Rect(200, 200), op.unmappedBounds);
- EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
- EXPECT_CLIP_RECT(Rect(200, 200), state.computedState.clipState);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerUnclippedSimpleTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_round) {
- class SaveLayerUnclippedRoundTestRenderer : public TestRendererBase {
- public:
- void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
- << "Bounds rect should round out";
- }
- void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {}
- void onRectOp(const RectOp& op, const BakedOpState& state) override {}
- void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds)
- << "Bounds rect should round out";
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RenderProperties& props,
- RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10.95f, 10.5f, 189.75f, 189.25f, // values should all round out
- 128, (SaveFlags::Flags)(0));
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerUnclippedRoundTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
- class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
- public:
- void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_GT(4, index);
- EXPECT_EQ(5, op.unmappedBounds.getWidth());
- EXPECT_EQ(5, op.unmappedBounds.getHeight());
- if (index == 0) {
- EXPECT_EQ(Rect(10, 10), state.computedState.clippedBounds);
- } else if (index == 1) {
- EXPECT_EQ(Rect(190, 0, 200, 10), state.computedState.clippedBounds);
- } else if (index == 2) {
- EXPECT_EQ(Rect(0, 190, 10, 200), state.computedState.clippedBounds);
- } else if (index == 3) {
- EXPECT_EQ(Rect(190, 190, 200, 200), state.computedState.clippedBounds);
- }
- }
- void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- ASSERT_EQ(op.vertexCount, 16u);
- for (size_t i = 0; i < op.vertexCount; i++) {
- auto v = op.vertices[i];
- EXPECT_TRUE(v.x == 0 || v.x == 10 || v.x == 190 || v.x == 200);
- EXPECT_TRUE(v.y == 0 || v.y == 10 || v.y == 190 || v.y == 200);
- }
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(5, mIndex++);
- }
- void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
- EXPECT_LT(5, mIndex++);
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
-
- int restoreTo = canvas.save(SaveFlags::MatrixClip);
- canvas.scale(2, 2);
- canvas.saveLayerAlpha(0, 0, 5, 5, 128, SaveFlags::MatrixClip);
- canvas.saveLayerAlpha(95, 0, 100, 5, 128, SaveFlags::MatrixClip);
- canvas.saveLayerAlpha(0, 95, 5, 100, 128, SaveFlags::MatrixClip);
- canvas.saveLayerAlpha(95, 95, 100, 100, 128, SaveFlags::MatrixClip);
- canvas.drawRect(0, 0, 100, 100, SkPaint());
- canvas.restoreToCount(restoreTo);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerUnclippedMergedClearsTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(10, renderer.getIndex())
- << "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_clearClip) {
- class SaveLayerUnclippedClearClipTestRenderer : public TestRendererBase {
- public:
- void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- }
- void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- ASSERT_NE(nullptr, op.paint);
- EXPECT_EQ(SkBlendMode::kClear, PaintUtils::getBlendModeDirect(op.paint));
- EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds)
- << "Expect dirty rect as clip";
- ASSERT_NE(nullptr, state.computedState.clipState);
- EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clipState->rect);
- EXPECT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- }
- void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- // save smaller than clip, so we get unclipped behavior
- canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.restore();
- });
-
- // draw with partial screen dirty, and assert we see that rect later
- FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerUnclippedClearClipTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_reject) {
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- // unclipped savelayer + rect both in area that won't intersect with dirty
- canvas.saveLayerAlpha(100, 100, 200, 200, 128, (SaveFlags::Flags)(0));
- canvas.drawRect(100, 100, 200, 200, SkPaint());
- canvas.restore();
- });
-
- // draw with partial screen dirty that doesn't intersect with savelayer
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- FailRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-}
-
-/* saveLayerUnclipped { saveLayer { saveLayerUnclipped { rect } } } will play back as:
- * - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
- * - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
- */
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, saveLayerUnclipped_complex) {
- class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
- public:
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
- EXPECT_EQ(0, mIndex++); // savelayer first
- return (OffscreenBuffer*)0xabcd;
- }
- void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_TRUE(index == 1 || index == 7);
- }
- void onSimpleRectsOp(const SimpleRectsOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 8);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- Matrix4 expected;
- expected.loadTranslate(-100, -100, 0);
- EXPECT_EQ(Rect(100, 100, 200, 200), state.computedState.clippedBounds);
- EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
- }
- void onCopyFromLayerOp(const CopyFromLayerOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_TRUE(index == 4 || index == 10);
- }
- void endLayer() override { EXPECT_EQ(5, mIndex++); }
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(6, mIndex++);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(9, mIndex++);
- EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(11, mIndex++); }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(12, mIndex++);
- EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer);
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 600, 600, // 500x500 triggers clipping
- [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(0, 0, 500, 500, 128, (SaveFlags::Flags)0); // unclipped
- canvas.saveLayerAlpha(100, 100, 400, 400, 128, SaveFlags::ClipToLayer); // clipped
- canvas.saveLayerAlpha(200, 200, 300, 300, 128, (SaveFlags::Flags)0); // unclipped
- canvas.drawRect(200, 200, 300, 300, SkPaint());
- canvas.restore();
- canvas.restore();
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- SaveLayerUnclippedComplexTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(13, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_simple) {
- class HwLayerSimpleTestRenderer : public TestRendererBase {
- public:
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
- EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
- EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
-
- EXPECT_TRUE(state.computedState.transform.isIdentity())
- << "Transform should be reset within layer";
-
- EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
- << "Damage rect should be used to clip layer content";
- }
- void endLayer() override { EXPECT_EQ(2, mIndex++); }
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(3, mIndex++);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(5, mIndex++); }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.mutateLayerProperties().setType(LayerType::RenderLayer);
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
- OffscreenBuffer** layerHandle = node->getLayerHandle();
-
- // create RenderNode's layer here in same way prepareTree would
- OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
- *layerHandle = &layer;
-
- auto syncedNode = TestUtils::getSyncedNode(node);
-
- // only enqueue partial damage
- LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
- layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferLayers(layerUpdateQueue);
- frameBuilder.deferRenderNode(*syncedNode);
-
- HwLayerSimpleTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(6, renderer.getIndex());
-
- // clean up layer pointer, so we can safely destruct RenderNode
- *layerHandle = nullptr;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, hwLayer_complex) {
- /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
- * - startRepaintLayer(child), rect(grey), endLayer
- * - startTemporaryLayer, drawLayer(child), endLayer
- * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
- * - startFrame, drawLayer(parent), endLayerb
- */
- class HwLayerComplexTestRenderer : public TestRendererBase {
- public:
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
- EXPECT_EQ(3, mIndex++); // savelayer first
- return (OffscreenBuffer*)0xabcd;
- }
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
- int index = mIndex++;
- if (index == 0) {
- // starting inner layer
- EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
- EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
- } else if (index == 6) {
- // starting outer layer
- EXPECT_EQ(200u, offscreenBuffer->viewportWidth);
- EXPECT_EQ(200u, offscreenBuffer->viewportHeight);
- } else {
- ADD_FAILURE();
- }
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- if (index == 1) {
- // inner layer's rect (white)
- EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
- } else if (index == 7) {
- // outer layer's rect (grey)
- EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
- } else {
- ADD_FAILURE();
- }
- }
- void endLayer() override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 5 || index == 9);
- }
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- EXPECT_EQ(10, mIndex++);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- OffscreenBuffer* layer = *op.layerHandle;
- int index = mIndex++;
- if (index == 4) {
- EXPECT_EQ(100u, layer->viewportWidth);
- EXPECT_EQ(100u, layer->viewportHeight);
- } else if (index == 8) {
- EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
- } else if (index == 11) {
- EXPECT_EQ(200u, layer->viewportWidth);
- EXPECT_EQ(200u, layer->viewportHeight);
- } else {
- ADD_FAILURE();
- }
- }
- void endFrame(const Rect& repaintRect) override { EXPECT_EQ(12, mIndex++); }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(13, mIndex++);
- }
- };
-
- auto child = TestUtils::createNode<RecordingCanvas>(
- 50, 50, 150, 150, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.mutateLayerProperties().setType(LayerType::RenderLayer);
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
- OffscreenBuffer childLayer(renderThread.renderState(), Caches::getInstance(), 100, 100);
- *(child->getLayerHandle()) = &childLayer;
-
- RenderNode* childPtr = child.get();
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [childPtr](RenderProperties& props, RecordingCanvas& canvas) {
- props.mutateLayerProperties().setType(LayerType::RenderLayer);
- SkPaint paint;
- paint.setColor(SK_ColorDKGRAY);
- canvas.drawRect(0, 0, 200, 200, paint);
-
- canvas.saveLayerAlpha(50, 50, 150, 150, 128, SaveFlags::ClipToLayer);
- canvas.drawRenderNode(childPtr);
- canvas.restore();
- });
- OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200);
- *(parent->getLayerHandle()) = &parentLayer;
-
- auto syncedNode = TestUtils::getSyncedNode(parent);
-
- LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
- layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
- layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferLayers(layerUpdateQueue);
- frameBuilder.deferRenderNode(*syncedNode);
-
- HwLayerComplexTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(14, renderer.getIndex());
-
- // clean up layer pointers, so we can safely destruct RenderNodes
- *(child->getLayerHandle()) = nullptr;
- *(parent->getLayerHandle()) = nullptr;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, buildLayer) {
- class BuildLayerTestRenderer : public TestRendererBase {
- public:
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
- EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
- EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
- }
- void onColorOp(const ColorOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
-
- EXPECT_TRUE(state.computedState.transform.isIdentity())
- << "Transform should be reset within layer";
-
- EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
- << "Damage rect should be used to clip layer content";
- }
- void endLayer() override { EXPECT_EQ(2, mIndex++); }
- void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
- ADD_FAILURE() << "Primary frame draw not expected in this test";
- }
- void endFrame(const Rect& repaintRect) override {
- ADD_FAILURE() << "Primary frame draw not expected in this test";
- }
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.mutateLayerProperties().setType(LayerType::RenderLayer);
- canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
- });
- OffscreenBuffer** layerHandle = node->getLayerHandle();
-
- // create RenderNode's layer here in same way prepareTree would
- OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
- *layerHandle = &layer;
-
- TestUtils::syncHierarchyPropertiesAndDisplayList(node);
-
- // only enqueue partial damage
- LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
- layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
-
- // Draw, but pass empty node list, so no work is done for primary frame
- FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance());
- BuildLayerTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-
- // clean up layer pointer, so we can safely destruct RenderNode
- *layerHandle = nullptr;
-}
-
-namespace {
-
-static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
- SkPaint paint;
- // order put in blue channel, transparent so overlapped content doesn't get rejected
- paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
- canvas->drawRect(0, 0, 100, 100, paint);
-}
-static void drawOrderedNode(Canvas* canvas, uint8_t expectedDrawOrder, float z) {
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedRect(&canvas, expectedDrawOrder);
- });
- node->mutateStagingProperties().setTranslationZ(z);
- node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
- canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
-}
-
-static void drawOrderedNode(
- Canvas* canvas, uint8_t expectedDrawOrder,
- std::function<void(RenderProperties& props, RecordingCanvas& canvas)> setup) {
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100,
- [expectedDrawOrder, setup](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedRect(&canvas, expectedDrawOrder);
- if (setup) {
- setup(props, canvas);
- }
- });
- canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
-}
-
-class ZReorderTestRenderer : public TestRendererBase {
-public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
- EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
- }
-};
-
-} // end anonymous namespace
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, zReorder) {
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.insertReorderBarrier(true);
- canvas.insertReorderBarrier(false);
- drawOrderedNode(&canvas, 0,
- 10.0f); // in reorder=false at this point, so played inorder
- drawOrderedRect(&canvas, 1);
- canvas.insertReorderBarrier(true);
- drawOrderedNode(&canvas, 6, 2.0f);
- drawOrderedRect(&canvas, 3);
- drawOrderedNode(&canvas, 4, 0.0f);
- drawOrderedRect(&canvas, 5);
- drawOrderedNode(&canvas, 2, -2.0f);
- drawOrderedNode(&canvas, 7, 2.0f);
- canvas.insertReorderBarrier(false);
- drawOrderedRect(&canvas, 8);
- drawOrderedNode(&canvas, 9,
- -10.0f); // in reorder=false at this point, so played inorder
- canvas.insertReorderBarrier(true); // reorder a node ahead of drawrect op
- drawOrderedRect(&canvas, 11);
- drawOrderedNode(&canvas, 10, -1.0f);
- canvas.insertReorderBarrier(false);
- canvas.insertReorderBarrier(true); // test with two empty reorder sections
- canvas.insertReorderBarrier(true);
- canvas.insertReorderBarrier(false);
- drawOrderedRect(&canvas, 12);
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(13, renderer.getIndex());
-};
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorder) {
- static const int scrollX = 5;
- static const int scrollY = 10;
- class ProjectionReorderTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- const int index = mIndex++;
-
- Matrix4 expectedMatrix;
- switch (index) {
- case 0:
- EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
- EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
- expectedMatrix.loadIdentity();
- EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
- break;
- case 1:
- EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds);
- EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
- expectedMatrix.loadTranslate(50 - scrollX, 50 - scrollY, 0);
- ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
- EXPECT_EQ(Rect(-35, -30, 45, 50),
- Rect(state.computedState.localProjectionPathMask->getBounds()));
- break;
- case 2:
- EXPECT_EQ(Rect(100, 50), op.unmappedBounds);
- EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
- expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0);
- EXPECT_EQ(nullptr, state.computedState.localProjectionPathMask);
- break;
- default:
- ADD_FAILURE();
- }
- EXPECT_EQ(expectedMatrix, state.computedState.transform);
- }
- };
-
- /**
- * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C)
- * with a projecting child (P) of its own. P would normally draw between B and C's "background"
- * draw, but because it is projected backwards, it's drawn in between B and C.
- *
- * The parent is scrolled by scrollX/scrollY, but this does not affect the background
- * (which isn't affected by scroll).
- */
- auto receiverBackground = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setProjectionReceiver(true);
- // scroll doesn't apply to background, so undone via translationX/Y
- // NOTE: translationX/Y only! no other transform properties may be set for a proj
- // receiver!
- properties.setTranslationX(scrollX);
- properties.setTranslationY(scrollY);
-
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
- auto projectingRipple = TestUtils::createNode<RecordingCanvas>(
- 50, 0, 100, 50, [](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setProjectBackwards(true);
- properties.setClipToBounds(false);
- SkPaint paint;
- paint.setColor(SK_ColorDKGRAY);
- canvas.drawRect(-10, -10, 60, 60, paint);
- });
- auto child = TestUtils::createNode<RecordingCanvas>(
- 0, 50, 100, 100,
- [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setColor(SK_ColorBLUE);
- canvas.drawRect(0, 0, 100, 50, paint);
- canvas.drawRenderNode(projectingRipple.get());
- });
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100,
- [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
- // Set a rect outline for the projecting ripple to be masked against.
- properties.mutableOutline().setRoundRect(10, 10, 90, 90, 5, 1.0f);
-
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(-scrollX,
- -scrollY); // Apply scroll (note: bg undoes this internally)
- canvas.drawRenderNode(receiverBackground.get());
- canvas.drawRenderNode(child.get());
- canvas.restore();
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ProjectionReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionHwLayer) {
- static const int scrollX = 5;
- static const int scrollY = 10;
- class ProjectionHwLayerTestRenderer : public TestRendererBase {
- public:
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- }
- void onArcOp(const ArcOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
- }
- void endLayer() override { EXPECT_EQ(2, mIndex++); }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
- }
- void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- ASSERT_NE(nullptr, state.computedState.localProjectionPathMask);
- Matrix4 expected;
- expected.loadTranslate(100 - scrollX, 100 - scrollY, 0);
- EXPECT_EQ(expected, state.computedState.transform);
- EXPECT_EQ(Rect(-85, -80, 295, 300),
- Rect(state.computedState.localProjectionPathMask->getBounds()));
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(5, mIndex++);
- ASSERT_EQ(nullptr, state.computedState.localProjectionPathMask);
- }
- };
- auto receiverBackground = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400, [](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setProjectionReceiver(true);
- // scroll doesn't apply to background, so undone via translationX/Y
- // NOTE: translationX/Y only! no other transform properties may be set for a proj
- // receiver!
- properties.setTranslationX(scrollX);
- properties.setTranslationY(scrollY);
-
- canvas.drawRect(0, 0, 400, 400, SkPaint());
- });
- auto projectingRipple = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setProjectBackwards(true);
- properties.setClipToBounds(false);
- canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds
- });
- auto child = TestUtils::createNode<RecordingCanvas>(
- 100, 100, 300, 300,
- [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.mutateLayerProperties().setType(LayerType::RenderLayer);
- canvas.drawRenderNode(projectingRipple.get());
- canvas.drawArc(0, 0, 200, 200, 0.0f, 280.0f, true, SkPaint());
- });
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400,
- [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
- // Set a rect outline for the projecting ripple to be masked against.
- properties.mutableOutline().setRoundRect(10, 10, 390, 390, 0, 1.0f);
- canvas.translate(-scrollX,
- -scrollY); // Apply scroll (note: bg undoes this internally)
- canvas.drawRenderNode(receiverBackground.get());
- canvas.drawRenderNode(child.get());
- });
-
- OffscreenBuffer** layerHandle = child->getLayerHandle();
-
- // create RenderNode's layer here in same way prepareTree would, setting windowTransform
- OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200, 200);
- Matrix4 windowTransform;
- windowTransform.loadTranslate(100, 100, 0); // total transform of layer's origin
- layer.setWindowTransform(windowTransform);
- *layerHandle = &layer;
-
- auto syncedNode = TestUtils::getSyncedNode(parent);
-
- LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
- layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200));
-
- FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferLayers(layerUpdateQueue);
- frameBuilder.deferRenderNode(*syncedNode);
-
- ProjectionHwLayerTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(6, renderer.getIndex());
-
- // clean up layer pointer, so we can safely destruct RenderNode
- *layerHandle = nullptr;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionChildScroll) {
- static const int scrollX = 500000;
- static const int scrollY = 0;
- class ProjectionChildScrollTestRenderer : public TestRendererBase {
- public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- void onOvalOp(const OvalOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- ASSERT_NE(nullptr, state.computedState.clipState);
- ASSERT_EQ(ClipMode::Rectangle, state.computedState.clipState->mode);
- ASSERT_EQ(Rect(400, 400), state.computedState.clipState->rect);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
- };
- auto receiverBackground = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400, [](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setProjectionReceiver(true);
- canvas.drawRect(0, 0, 400, 400, SkPaint());
- });
- auto projectingRipple = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& properties, RecordingCanvas& canvas) {
- // scroll doesn't apply to background, so undone via translationX/Y
- // NOTE: translationX/Y only! no other transform properties may be set for a proj
- // receiver!
- properties.setTranslationX(scrollX);
- properties.setTranslationY(scrollY);
- properties.setProjectBackwards(true);
- properties.setClipToBounds(false);
- canvas.drawOval(0, 0, 200, 200, SkPaint());
- });
- auto child = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400,
- [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
- // Record time clip will be ignored by projectee
- canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
-
- canvas.translate(-scrollX,
- -scrollY); // Apply scroll (note: bg undoes this internally)
- canvas.drawRenderNode(projectingRipple.get());
- });
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 400, 400,
- [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) {
- canvas.drawRenderNode(receiverBackground.get());
- canvas.drawRenderNode(child.get());
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ProjectionChildScrollTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-// creates a 100x100 shadow casting node with provided translationZ
-static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
- return TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [translationZ](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setTranslationZ(translationZ);
- properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f);
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadow) {
- class ShadowTestRenderer : public TestRendererBase {
- public:
- void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_FLOAT_EQ(1.0f, op.casterAlpha);
- EXPECT_TRUE(op.shadowTask->casterPerimeter.isRect(nullptr));
- EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.shadowTask->transformXY);
-
- Matrix4 expectedZ;
- expectedZ.loadTranslate(0, 0, 5);
- EXPECT_MATRIX_APPROX_EQ(expectedZ, op.shadowTask->transformZ);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- }
- };
-
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.insertReorderBarrier(true);
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ShadowTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowSaveLayer) {
- class ShadowSaveLayerTestRenderer : public TestRendererBase {
- public:
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, mIndex++);
- return nullptr;
- }
- void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
- EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- }
- void endLayer() override { EXPECT_EQ(3, mIndex++); }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(5, mIndex++);
- }
- };
-
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- // save/restore outside of reorderBarrier, so they don't get moved out of place
- canvas.translate(20, 10);
- int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SaveFlags::ClipToLayer);
- canvas.insertReorderBarrier(true);
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
- canvas.insertReorderBarrier(false);
- canvas.restoreToCount(count);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
- (FrameBuilder::LightGeometry){{100, 100, 100}, 50},
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ShadowSaveLayerTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(6, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowHwLayer) {
- class ShadowHwLayerTestRenderer : public TestRendererBase {
- public:
- void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
- EXPECT_EQ(0, mIndex++);
- }
- void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- EXPECT_FLOAT_EQ(50, op.shadowTask->lightCenter.x);
- EXPECT_FLOAT_EQ(40, op.shadowTask->lightCenter.y);
- EXPECT_FLOAT_EQ(30, op.shadowTask->lightRadius);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- }
- void endLayer() override { EXPECT_EQ(3, mIndex++); }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- }
- };
-
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 50, 60, 150, 160, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.mutateLayerProperties().setType(LayerType::RenderLayer);
- canvas.insertReorderBarrier(true);
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(20, 10);
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
- canvas.restore();
- });
- OffscreenBuffer** layerHandle = parent->getLayerHandle();
-
- // create RenderNode's layer here in same way prepareTree would, setting windowTransform
- OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
- Matrix4 windowTransform;
- windowTransform.loadTranslate(50, 60, 0); // total transform of layer's origin
- layer.setWindowTransform(windowTransform);
- *layerHandle = &layer;
-
- auto syncedNode = TestUtils::getSyncedNode(parent);
- LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
- layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
- (FrameBuilder::LightGeometry){{100, 100, 100}, 30},
- Caches::getInstance());
- frameBuilder.deferLayers(layerUpdateQueue);
- frameBuilder.deferRenderNode(*syncedNode);
-
- ShadowHwLayerTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(5, renderer.getIndex());
-
- // clean up layer pointer, so we can safely destruct RenderNode
- *layerHandle = nullptr;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowLayering) {
- class ShadowLayeringTestRenderer : public TestRendererBase {
- public:
- void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_TRUE(index == 0 || index == 1);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 3);
- }
- };
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.insertReorderBarrier(true);
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
- });
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
- (FrameBuilder::LightGeometry){{100, 100, 100}, 50},
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ShadowLayeringTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, shadowClipping) {
- class ShadowClippingTestRenderer : public TestRendererBase {
- public:
- void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipState->rect)
- << "Shadow must respect pre-barrier canvas clip value.";
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- }
- };
- auto parent = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- // Apply a clip before the reorder barrier/shadow casting child is drawn.
- // This clip must be applied to the shadow cast by the child.
- canvas.clipRect(25, 25, 75, 75, SkClipOp::kIntersect);
- canvas.insertReorderBarrier(true);
- canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100,
- (FrameBuilder::LightGeometry){{100, 100, 100}, 50},
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent));
-
- ShadowClippingTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-static void testProperty(
- std::function<void(RenderProperties&)> propSetupCallback,
- std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
- class PropertyTestRenderer : public TestRendererBase {
- public:
- explicit PropertyTestRenderer(
- std::function<void(const RectOp&, const BakedOpState&)> callback)
- : mCallback(callback) {}
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(mIndex++, 0);
- mCallback(op, state);
- }
- std::function<void(const RectOp&, const BakedOpState&)> mCallback;
- };
-
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [propSetupCallback](RenderProperties& props, RecordingCanvas& canvas) {
- propSetupCallback(props);
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- PropertyTestRenderer renderer(opValidateCallback);
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
- testProperty(
- [](RenderProperties& properties) {
- properties.setAlpha(0.5f);
- properties.setHasOverlappingRendering(false);
- },
- [](const RectOp& op, const BakedOpState& state) {
- EXPECT_EQ(0.5f, state.alpha) << "Alpha should be applied directly to op";
- });
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropClipping) {
- testProperty(
- [](RenderProperties& properties) {
- properties.setClipToBounds(true);
- properties.setClipBounds(Rect(10, 20, 300, 400));
- },
- [](const RectOp& op, const BakedOpState& state) {
- EXPECT_EQ(Rect(10, 20, 100, 100), state.computedState.clippedBounds)
- << "Clip rect should be intersection of node bounds and clip bounds";
- });
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropRevealClip) {
- testProperty(
- [](RenderProperties& properties) {
- properties.mutableRevealClip().set(true, 50, 50, 25);
- },
- [](const RectOp& op, const BakedOpState& state) {
- ASSERT_NE(nullptr, state.roundRectClipState);
- EXPECT_TRUE(state.roundRectClipState->highPriority);
- EXPECT_EQ(25, state.roundRectClipState->radius);
- EXPECT_EQ(Rect(50, 50, 50, 50), state.roundRectClipState->innerRect);
- });
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropOutlineClip) {
- testProperty(
- [](RenderProperties& properties) {
- properties.mutableOutline().setShouldClip(true);
- properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
- },
- [](const RectOp& op, const BakedOpState& state) {
- ASSERT_NE(nullptr, state.roundRectClipState);
- EXPECT_FALSE(state.roundRectClipState->highPriority);
- EXPECT_EQ(5, state.roundRectClipState->radius);
- EXPECT_EQ(Rect(15, 25, 25, 35), state.roundRectClipState->innerRect);
- });
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropTransform) {
- testProperty(
- [](RenderProperties& properties) {
- properties.setLeftTopRightBottom(10, 10, 110, 110);
-
- SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f);
- properties.setStaticMatrix(&staticMatrix);
-
- // ignored, since static overrides animation
- SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15);
- properties.setAnimationMatrix(&animationMatrix);
-
- properties.setTranslationX(10);
- properties.setTranslationY(20);
- properties.setScaleX(0.5f);
- properties.setScaleY(0.7f);
- },
- [](const RectOp& op, const BakedOpState& state) {
- Matrix4 matrix;
- matrix.loadTranslate(10, 10, 0); // left, top
- matrix.scale(1.2f, 1.2f, 1); // static matrix
- // ignore animation matrix, since static overrides it
-
- // translation xy
- matrix.translate(10, 20);
-
- // scale xy (from default pivot - center)
- matrix.translate(50, 50);
- matrix.scale(0.5f, 0.7f, 1);
- matrix.translate(-50, -50);
- EXPECT_MATRIX_APPROX_EQ(matrix, state.computedState.transform)
- << "Op draw matrix must match expected combination of transformation "
- "properties";
- });
-}
-
-struct SaveLayerAlphaData {
- uint32_t layerWidth = 0;
- uint32_t layerHeight = 0;
- Rect rectClippedBounds;
- Matrix4 rectMatrix;
- Matrix4 drawLayerMatrix;
-};
-/**
- * Constructs a view to hit the temporary layer alpha property implementation:
- * a) 0 < alpha < 1
- * b) too big for layer (larger than maxTextureSize)
- * c) overlapping rendering content
- * returning observed data about layer size and content clip/transform.
- *
- * Used to validate clipping behavior of temporary layer, where requested layer size is reduced
- * (for efficiency, and to fit in layer size constraints) based on parent clip.
- */
-void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
- std::function<void(RenderProperties&)> propSetupCallback) {
- class SaveLayerAlphaClipTestRenderer : public TestRendererBase {
- public:
- explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) : mOutData(outData) {}
-
- OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, mIndex++);
- mOutData->layerWidth = width;
- mOutData->layerHeight = height;
- return nullptr;
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
-
- mOutData->rectClippedBounds = state.computedState.clippedBounds;
- mOutData->rectMatrix = state.computedState.transform;
- }
- void endLayer() override { EXPECT_EQ(2, mIndex++); }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- mOutData->drawLayerMatrix = state.computedState.transform;
- }
- void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(4, mIndex++);
- }
-
- private:
- SaveLayerAlphaData* mOutData;
- };
-
- ASSERT_GT(10000, DeviceInfo::get()->maxTextureSize())
- << "Node must be bigger than max texture size to exercise saveLayer codepath";
- auto node = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 10000, 10000,
- [&propSetupCallback](RenderProperties& properties, RecordingCanvas& canvas) {
- properties.setHasOverlappingRendering(true);
- properties.setAlpha(0.5f); // force saveLayer, since too big for HW layer
- // apply other properties
- propSetupCallback(properties);
-
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 10000, 10000, paint);
- });
- auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*syncedNode);
-
- SaveLayerAlphaClipTestRenderer renderer(outObservedData);
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
-
- // assert, since output won't be valid if we haven't seen a save layer triggered
- ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
- SaveLayerAlphaData observedData;
- testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
- properties.setTranslationX(10); // offset rendering content
- properties.setTranslationY(-2000); // offset rendering content
- });
- EXPECT_EQ(190u, observedData.layerWidth);
- EXPECT_EQ(200u, observedData.layerHeight);
- EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
- << "expect content to be clipped to screen area";
- Matrix4 expected;
- expected.loadTranslate(0, -2000, 0);
- EXPECT_MATRIX_APPROX_EQ(expected, observedData.rectMatrix)
- << "expect content to be translated as part of being clipped";
- expected.loadTranslate(10, 0, 0);
- EXPECT_MATRIX_APPROX_EQ(expected, observedData.drawLayerMatrix)
- << "expect drawLayer to be translated as part of being clipped";
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
- SaveLayerAlphaData observedData;
- testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
- // Translate and rotate the view so that the only visible part is the top left corner of
- // the view. It will form an isosceles right triangle with a long side length of 200 at the
- // bottom of the viewport.
- properties.setTranslationX(100);
- properties.setTranslationY(100);
- properties.setPivotX(0);
- properties.setPivotY(0);
- properties.setRotation(45);
- });
- // ceil(sqrt(2) / 2 * 200) = 142
- EXPECT_EQ(142u, observedData.layerWidth);
- EXPECT_EQ(142u, observedData.layerHeight);
- EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
- EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
- SaveLayerAlphaData observedData;
- testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
- properties.setPivotX(0);
- properties.setPivotY(0);
- properties.setScaleX(2);
- properties.setScaleY(0.5f);
- });
- EXPECT_EQ(100u, observedData.layerWidth);
- EXPECT_EQ(400u, observedData.layerHeight);
- EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
- EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, clip_replace) {
- class ClipReplaceTestRenderer : public TestRendererBase {
- public:
- void onColorOp(const ColorOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_TRUE(op.localClip->intersectWithRoot);
- EXPECT_EQ(Rect(20, 10, 30, 40), state.computedState.clipState->rect)
- << "Expect resolved clip to be intersection of viewport clip and clip op";
- }
- };
- auto node = TestUtils::createNode<RecordingCanvas>(
- 20, 20, 30, 30, [](RenderProperties& props, RecordingCanvas& canvas) {
- canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated);
- canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
- });
-
- FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
-
- ClipReplaceTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(1, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedInMiddle) {
- /* R is backward projected on B
- A
- / \
- B C
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 2, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 1,
- [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectLast) {
- /* R is backward projected on E
- A
- / | \
- / | \
- B C E
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, nullptr); // nodeB
- drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 3, [](RenderProperties& props,
- RecordingCanvas& canvas) { // drawn as 2
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- drawOrderedNode(&canvas, 2, [](RenderProperties& props,
- RecordingCanvas& canvas) { // drawn as 3
- props.setProjectionReceiver(true);
- }); // nodeE
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderNoReceivable) {
- /* R is backward projected without receiver
- A
- / \
- B C
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, nullptr); // nodeB
- drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 255,
- [](RenderProperties& props, RecordingCanvas& canvas) {
- // not having a projection receiver is an undefined behavior
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderParentReceivable) {
- /* R is backward projected on C
- A
- / \
- B C
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, nullptr); // nodeB
- drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectionReceiver(true);
- drawOrderedNode(&canvas, 2,
- [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderSameNodeReceivable) {
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, nullptr); // nodeB
- drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 255,
- [](RenderProperties& props, RecordingCanvas& canvas) {
- // having a node that is projected on itself is an
- // undefined/unexpected behavior
- props.setProjectionReceiver(true);
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(2, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling) {
- // TODO: this test together with the next "projectionReorderProjectedSibling2" likely expose a
- // bug in HWUI. First test draws R, while the second test does not draw R for a nearly identical
- // tree setup. The correct behaviour is to not draw R, because the receiver cannot be a sibling
- /* R is backward projected on B. R is not expected to be drawn (see Sibling2 outcome below),
- but for some reason it is drawn.
- A
- /|\
- / | \
- B C R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 2,
- [](RenderProperties& props, RecordingCanvas& canvas) {}); // nodeC
- drawOrderedNode(&canvas, 1, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderProjectedSibling2) {
- /* R is set to project on B, but R is not drawn because projecting on a sibling is not allowed.
- A
- |
- G
- /|\
- / | \
- B C R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, [](RenderProperties& props,
- RecordingCanvas& canvas) { // G
- drawOrderedNode(&canvas, 1,
- [](RenderProperties& props, RecordingCanvas& canvas) { // B
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 2,
- [](RenderProperties& props, RecordingCanvas& canvas) { // C
- }); // nodeC
- drawOrderedNode(&canvas, 255,
- [](RenderProperties& props, RecordingCanvas& canvas) { // R
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeG
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderGrandparentReceivable) {
- /* R is backward projected on B
- A
- |
- B
- |
- C
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0, [](RenderProperties& props, RecordingCanvas& canvas) {
- props.setProjectionReceiver(true);
- drawOrderedNode(&canvas, 1,
- [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 2, [](RenderProperties& props,
- RecordingCanvas& canvas) {
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeB
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(3, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivables) {
- /* B and G are receivables, R is backward projected
- A
- / \
- B C
- / \
- G R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0,
- [](RenderProperties& props, RecordingCanvas& canvas) { // B
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 2, [](RenderProperties& props,
- RecordingCanvas& canvas) { // C
- drawOrderedNode(&canvas, 3,
- [](RenderProperties& props, RecordingCanvas& canvas) { // G
- props.setProjectionReceiver(true);
- }); // nodeG
- drawOrderedNode(&canvas, 1,
- [](RenderProperties& props, RecordingCanvas& canvas) { // R
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesLikelyScenario) {
- /* B and G are receivables, G is backward projected
- A
- / \
- B C
- / \
- G R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0,
- [](RenderProperties& props, RecordingCanvas& canvas) { // B
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 2, [](RenderProperties& props,
- RecordingCanvas& canvas) { // C
- drawOrderedNode(&canvas, 1,
- [](RenderProperties& props, RecordingCanvas& canvas) { // G
- props.setProjectionReceiver(true);
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeG
- drawOrderedNode(&canvas, 3,
- [](RenderProperties& props, RecordingCanvas& canvas) { // R
- }); // nodeR
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, projectionReorderTwoReceivablesDeeper) {
- /* B and G are receivables, R is backward projected
- A
- / \
- B C
- / \
- G D
- |
- R
- */
- auto nodeA = TestUtils::createNode<RecordingCanvas>(
- 0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
- drawOrderedNode(&canvas, 0,
- [](RenderProperties& props, RecordingCanvas& canvas) { // B
- props.setProjectionReceiver(true);
- }); // nodeB
- drawOrderedNode(&canvas, 1, [](RenderProperties& props,
- RecordingCanvas& canvas) { // C
- drawOrderedNode(&canvas, 2,
- [](RenderProperties& props, RecordingCanvas& canvas) { // G
- props.setProjectionReceiver(true);
- }); // nodeG
- drawOrderedNode(
- &canvas, 4, [](RenderProperties& props, RecordingCanvas& canvas) { // D
- drawOrderedNode(&canvas, 3, [](RenderProperties& props,
- RecordingCanvas& canvas) { // R
- props.setProjectBackwards(true);
- props.setClipToBounds(false);
- }); // nodeR
- }); // nodeD
- }); // nodeC
- }); // nodeA
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(nodeA));
-
- ZReorderTestRenderer renderer;
- frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(5, renderer.getIndex());
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp
deleted file mode 100644
index c8bfc99fac92..000000000000
--- a/libs/hwui/tests/unit/GlopBuilderTests.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "Glop.h"
-#include "GlopBuilder.h"
-#include "Rect.h"
-#include "tests/common/TestUtils.h"
-#include "utils/Color.h"
-
-#include <SkPaint.h>
-
-using namespace android::uirenderer;
-
-static void expectFillEq(Glop::Fill& expectedFill, Glop::Fill& builtFill) {
- EXPECT_EQ(expectedFill.colorEnabled, builtFill.colorEnabled);
- if (expectedFill.colorEnabled) EXPECT_EQ(expectedFill.color, builtFill.color);
-
- EXPECT_EQ(expectedFill.filterMode, builtFill.filterMode);
- if (expectedFill.filterMode == ProgramDescription::ColorFilterMode::Blend) {
- EXPECT_EQ(expectedFill.filter.color, builtFill.filter.color);
- } else if (expectedFill.filterMode == ProgramDescription::ColorFilterMode::Matrix) {
- Glop::Fill::Filter::Matrix& expectedMatrix = expectedFill.filter.matrix;
- Glop::Fill::Filter::Matrix& builtMatrix = expectedFill.filter.matrix;
- EXPECT_TRUE(std::memcmp(expectedMatrix.matrix, builtMatrix.matrix,
- sizeof(Glop::Fill::Filter::Matrix::matrix)));
- EXPECT_TRUE(std::memcmp(expectedMatrix.vector, builtMatrix.vector,
- sizeof(Glop::Fill::Filter::Matrix::vector)));
- }
- EXPECT_EQ(expectedFill.skiaShaderData.skiaShaderType, builtFill.skiaShaderData.skiaShaderType);
- EXPECT_EQ(expectedFill.texture.clamp, builtFill.texture.clamp);
- EXPECT_EQ(expectedFill.texture.filter, builtFill.texture.filter);
- EXPECT_TRUE((expectedFill.texture.texture && builtFill.texture.texture) ||
- (!expectedFill.texture.texture && !builtFill.texture.texture));
- if (expectedFill.texture.texture) {
- EXPECT_EQ(expectedFill.texture.texture->target(), builtFill.texture.texture->target());
- }
- EXPECT_EQ(expectedFill.texture.textureTransform, builtFill.texture.textureTransform);
-}
-
-static void expectBlendEq(Glop::Blend& expectedBlend, Glop::Blend& builtBlend) {
- EXPECT_EQ(expectedBlend.src, builtBlend.src);
- EXPECT_EQ(expectedBlend.dst, builtBlend.dst);
-}
-
-static void expectMeshEq(Glop::Mesh& expectedMesh, Glop::Mesh& builtMesh) {
- EXPECT_EQ(expectedMesh.elementCount, builtMesh.elementCount);
- EXPECT_EQ(expectedMesh.primitiveMode, builtMesh.primitiveMode);
- EXPECT_EQ(expectedMesh.indices.indices, builtMesh.indices.indices);
- EXPECT_EQ(expectedMesh.indices.bufferObject, builtMesh.indices.bufferObject);
- EXPECT_EQ(expectedMesh.vertices.attribFlags, builtMesh.vertices.attribFlags);
- EXPECT_EQ(expectedMesh.vertices.bufferObject, builtMesh.vertices.bufferObject);
- EXPECT_EQ(expectedMesh.vertices.color, builtMesh.vertices.color);
- EXPECT_EQ(expectedMesh.vertices.position, builtMesh.vertices.position);
- EXPECT_EQ(expectedMesh.vertices.stride, builtMesh.vertices.stride);
- EXPECT_EQ(expectedMesh.vertices.texCoord, builtMesh.vertices.texCoord);
-
- if (builtMesh.vertices.position) {
- for (int i = 0; i < 4; i++) {
- TextureVertex& expectedVertex = expectedMesh.mappedVertices[i];
- TextureVertex& builtVertex = builtMesh.mappedVertices[i];
- EXPECT_EQ(expectedVertex.u, builtVertex.u);
- EXPECT_EQ(expectedVertex.v, builtVertex.v);
- EXPECT_EQ(expectedVertex.x, builtVertex.x);
- EXPECT_EQ(expectedVertex.y, builtVertex.y);
- }
- }
-}
-
-static void expectTransformEq(Glop::Transform& expectedTransform, Glop::Transform& builtTransform) {
- EXPECT_EQ(expectedTransform.canvas, builtTransform.canvas);
- EXPECT_EQ(expectedTransform.modelView, builtTransform.modelView);
- EXPECT_EQ(expectedTransform.transformFlags, expectedTransform.transformFlags);
-}
-
-static void expectGlopEq(Glop& expectedGlop, Glop& builtGlop) {
- expectBlendEq(expectedGlop.blend, builtGlop.blend);
- expectFillEq(expectedGlop.fill, builtGlop.fill);
- expectMeshEq(expectedGlop.mesh, builtGlop.mesh);
- expectTransformEq(expectedGlop.transform, builtGlop.transform);
-}
-
-static std::unique_ptr<Glop> blackUnitQuadGlop(RenderState& renderState) {
- std::unique_ptr<Glop> glop(new Glop());
- glop->blend = {GL_ZERO, GL_ZERO};
- glop->mesh.elementCount = 4;
- glop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
- glop->mesh.indices.indices = nullptr;
- glop->mesh.indices.bufferObject = GL_ZERO;
- glop->mesh.vertices = {renderState.meshState().getUnitQuadVBO(),
- VertexAttribFlags::None,
- nullptr,
- nullptr,
- nullptr,
- kTextureVertexStride};
- glop->transform.modelView.loadIdentity();
- glop->fill.colorEnabled = true;
- glop->fill.color.set(Color::Black);
- glop->fill.skiaShaderData.skiaShaderType = kNone_SkiaShaderType;
- glop->fill.filterMode = ProgramDescription::ColorFilterMode::None;
- glop->fill.texture = {nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr};
- return glop;
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(GlopBuilder, rectSnapTest) {
- RenderState& renderState = renderThread.renderState();
- Caches& caches = Caches::getInstance();
- SkPaint paint;
- Rect dest(1, 1, 100, 100);
- Matrix4 simpleTranslate;
- simpleTranslate.loadTranslate(0.7, 0.7, 0);
- Glop glop;
- GlopBuilder(renderState, caches, &glop)
- .setRoundRectClipState(nullptr)
- .setMeshUnitQuad()
- .setFillPaint(paint, 1.0f)
- .setTransform(simpleTranslate, TransformFlags::None)
- .setModelViewMapUnitToRectSnap(dest)
- .build();
-
- std::unique_ptr<Glop> goldenGlop(blackUnitQuadGlop(renderState));
- // Rect(1,1,100,100) is the set destination,
- // so unit quad should be translated by (1,1) and scaled by (99, 99)
- // Tricky part: because translate (0.7, 0.7) and snapping were set in glopBuilder,
- // unit quad also should be translate by additional (0.3, 0.3) to snap to exact pixels.
- goldenGlop->transform.modelView.loadTranslate(1.3, 1.3, 0);
- goldenGlop->transform.modelView.scale(99, 99, 1);
- goldenGlop->transform.canvas = simpleTranslate;
- goldenGlop->fill.texture.filter = GL_NEAREST;
- expectGlopEq(*goldenGlop, glop);
-}
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
index 9bfb08292be2..08b967964c59 100644
--- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
+++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
@@ -39,7 +39,7 @@ public:
// current thread can spoof being a GPU thread
static void destroyEglContext() {
if (TestUtils::isRenderThreadRunning()) {
- TestUtils::runOnRenderThread([](RenderThread& thread) { thread.eglManager().destroy(); });
+ TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyGlContext(); });
}
}
diff --git a/libs/hwui/tests/unit/GradientCacheTests.cpp b/libs/hwui/tests/unit/GradientCacheTests.cpp
deleted file mode 100644
index 6710c71c386f..000000000000
--- a/libs/hwui/tests/unit/GradientCacheTests.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "Extensions.h"
-#include "GradientCache.h"
-#include "tests/common/TestUtils.h"
-
-using namespace android;
-using namespace android::uirenderer;
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(GradientCache, addRemove) {
- Extensions extensions;
- GradientCache cache(extensions);
- ASSERT_LT(1000u, cache.getMaxSize()) << "Expect non-trivial size";
-
- SkColor colors[] = {0xFF00FF00, 0xFFFF0000, 0xFF0000FF};
- float positions[] = {1, 2, 3};
- Texture* texture = cache.get(colors, positions, 3);
- ASSERT_TRUE(texture);
- ASSERT_FALSE(texture->cleanup);
- ASSERT_EQ((uint32_t)texture->objectSize(), cache.getSize());
- ASSERT_TRUE(cache.getSize());
- cache.clear();
- ASSERT_EQ(cache.getSize(), 0u);
-}
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
deleted file mode 100644
index 20ec0848212f..000000000000
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "BakedOpDispatcher.h"
-#include "BakedOpRenderer.h"
-#include "FrameBuilder.h"
-#include "LayerUpdateQueue.h"
-#include "RecordingCanvas.h"
-#include "tests/common/TestUtils.h"
-
-#include <gtest/gtest.h>
-
-using namespace android;
-using namespace android::uirenderer;
-
-const FrameBuilder::LightGeometry sLightGeometery = {{100, 100, 100}, 50};
-const BakedOpRenderer::LightInfo sLightInfo = {128, 128};
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayer_overdrawRejection) {
- auto node = TestUtils::createNode(0, 0, 100, 100, [](RenderProperties& props, Canvas& canvas) {
- canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
- canvas.drawRect(0, 0, 100, 100, SkPaint());
- canvas.restore();
-
- // opaque draw, rejects saveLayer beneath
- canvas.drawRect(0, 0, 100, 100, SkPaint());
- });
- RenderState& renderState = renderThread.renderState();
- Caches& caches = Caches::getInstance();
-
- FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometery,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
- BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo);
- frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayerUnclipped_simple) {
- auto node = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) {
- canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.restore();
- });
- RenderState& renderState = renderThread.renderState();
- Caches& caches = Caches::getInstance();
-
- FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometery,
- Caches::getInstance());
- frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
- BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo);
- frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
-}
diff --git a/libs/hwui/tests/unit/MeshStateTests.cpp b/libs/hwui/tests/unit/MeshStateTests.cpp
deleted file mode 100644
index 1573fd30d5cb..000000000000
--- a/libs/hwui/tests/unit/MeshStateTests.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <debug/MockGlesDriver.h>
-#include <debug/ScopedReplaceDriver.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <renderstate/MeshState.h>
-#include <tests/common/TestUtils.h>
-
-using namespace android::uirenderer;
-using namespace testing;
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(MeshState, genOrUpdate) {
- debug::ScopedReplaceDriver<debug::MockGlesDriver> driverRef;
- auto& mockGlDriver = driverRef.get();
- EXPECT_CALL(mockGlDriver, glGenBuffers_(_, _)).WillOnce(SetArgPointee<1>(35));
- EXPECT_CALL(mockGlDriver, glBindBuffer_(_, 35));
- EXPECT_CALL(mockGlDriver, glBufferData_(_, _, _, _));
-
- GLuint buffer = 0;
- renderThread.renderState().meshState().genOrUpdateMeshBuffer(&buffer, 10, nullptr,
- GL_DYNAMIC_DRAW);
-}
diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
deleted file mode 100644
index 0d4736757629..000000000000
--- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <Rect.h>
-#include <gtest/gtest.h>
-#include <renderstate/OffscreenBufferPool.h>
-
-#include <tests/common/TestUtils.h>
-
-using namespace android::uirenderer;
-
-TEST(OffscreenBuffer, computeIdealDimension) {
- EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(1));
- EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(31));
- EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(33));
- EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(64));
- EXPECT_EQ(1024u, OffscreenBuffer::computeIdealDimension(1000));
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, construct) {
- OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 49u, 149u);
- EXPECT_EQ(49u, layer.viewportWidth);
- EXPECT_EQ(149u, layer.viewportHeight);
-
- EXPECT_EQ(64u, layer.texture.width());
- EXPECT_EQ(192u, layer.texture.height());
-
- 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), layerAligned.getTextureCoordinates());
-
- OffscreenBuffer layerUnaligned(renderThread.renderState(), Caches::getInstance(), 200u, 225u);
- EXPECT_EQ(Rect(0, 225.0f / 256.0f, 200.0f / 256.0f, 0), layerUnaligned.getTextureCoordinates());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, dirty) {
- OffscreenBuffer buffer(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
- buffer.dirty(Rect(-100, -100, 100, 100));
- EXPECT_EQ(android::Rect(100, 100), buffer.region.getBounds());
-}
-
-RENDERTHREAD_TEST(OffscreenBufferPool, construct) {
- OffscreenBufferPool pool;
- EXPECT_EQ(0u, pool.getCount()) << "pool must be created empty";
- EXPECT_EQ(0u, pool.getSize()) << "pool must be created empty";
- // TODO: Does this really make sense as a test?
- EXPECT_EQ(DeviceInfo::multiplyByResolution(4 * 4), pool.getMaxSize());
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, getPutClear) {
- OffscreenBufferPool pool;
-
- auto layer = pool.get(renderThread.renderState(), 100u, 200u);
- EXPECT_EQ(100u, layer->viewportWidth);
- EXPECT_EQ(200u, layer->viewportHeight);
-
- ASSERT_LT(layer->getSizeInBytes(), pool.getMaxSize());
-
- pool.putOrDelete(layer);
- ASSERT_EQ(layer->getSizeInBytes(), pool.getSize());
-
- auto layer2 = pool.get(renderThread.renderState(), 102u, 202u);
- EXPECT_EQ(layer, layer2) << "layer should be recycled";
- ASSERT_EQ(0u, pool.getSize()) << "pool should have been emptied by removing only layer";
-
- pool.putOrDelete(layer);
- EXPECT_EQ(1u, pool.getCount());
- pool.clear();
- EXPECT_EQ(0u, pool.getSize());
- 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;
-
- auto layer = pool.get(renderThread.renderState(), 64u, 64u);
- layer->dirty(Rect(64, 64));
-
- // resize in place
- ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
- EXPECT_TRUE(layer->region.isEmpty()) << "In place resize should clear usage region";
- EXPECT_EQ(60u, layer->viewportWidth);
- EXPECT_EQ(55u, layer->viewportHeight);
- EXPECT_EQ(64u, layer->texture.width());
- EXPECT_EQ(64u, layer->texture.height());
-
- // resized to use different object in pool
- auto layer2 = pool.get(renderThread.renderState(), 128u, 128u);
- layer2->dirty(Rect(128, 128));
- EXPECT_FALSE(layer2->region.isEmpty());
- pool.putOrDelete(layer2);
- ASSERT_EQ(1u, pool.getCount());
-
- ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
- EXPECT_TRUE(layer2->region.isEmpty()) << "Swap resize should clear usage region";
- EXPECT_EQ(120u, layer2->viewportWidth);
- EXPECT_EQ(125u, layer2->viewportHeight);
- EXPECT_EQ(128u, layer2->texture.width());
- EXPECT_EQ(128u, layer2->texture.height());
-
- // original allocation now only thing in pool
- EXPECT_EQ(1u, pool.getCount());
- EXPECT_EQ(layer->getSizeInBytes(), pool.getSize());
-
- 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
- // Note: this relies on the fact that the pool won't reject based on max texture size
- auto hugeLayer = pool.get(renderThread.renderState(), pool.getMaxSize() / 64, 64);
- EXPECT_GT(hugeLayer->getSizeInBytes(), pool.getMaxSize());
- pool.putOrDelete(hugeLayer);
- EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead)
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, clear) {
- EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
- OffscreenBufferPool pool;
-
- // Create many buffers, with several at each size
- std::vector<OffscreenBuffer*> buffers;
- for (int size = 32; size <= 128; size += 32) {
- for (int i = 0; i < 10; i++) {
- buffers.push_back(pool.get(renderThread.renderState(), size, size));
- }
- }
- EXPECT_EQ(0u, pool.getCount()) << "Expect nothing inside";
- for (auto& buffer : buffers) pool.putOrDelete(buffer);
- EXPECT_EQ(40u, pool.getCount()) << "Expect all items added";
- EXPECT_EQ(40, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
- pool.clear();
- EXPECT_EQ(0u, pool.getCount()) << "Expect all items cleared";
-
- EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
-}
diff --git a/libs/hwui/tests/unit/OpDumperTests.cpp b/libs/hwui/tests/unit/OpDumperTests.cpp
deleted file mode 100644
index ef30e872a7bd..000000000000
--- a/libs/hwui/tests/unit/OpDumperTests.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "OpDumper.h"
-#include "tests/common/TestUtils.h"
-
-using namespace android;
-using namespace android::uirenderer;
-
-TEST(OpDumper, dump) {
- SkPaint paint;
- RectOp op(uirenderer::Rect(100, 100), Matrix4::identity(), nullptr, &paint);
-
- std::stringstream stream;
- OpDumper::dump(op, stream);
- EXPECT_STREQ("RectOp [100 x 100]", stream.str().c_str());
-
- stream.str("");
- OpDumper::dump(op, stream, 2);
- EXPECT_STREQ(" RectOp [100 x 100]", stream.str().c_str());
-
- ClipRect clipRect(uirenderer::Rect(50, 50));
- op.localClip = &clipRect;
-
- stream.str("");
- OpDumper::dump(op, stream, 2);
- EXPECT_STREQ(" RectOp [100 x 100] clip=[50 x 50] mode=0", stream.str().c_str());
-}
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
deleted file mode 100644
index 8a9e34f81c6d..000000000000
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ /dev/null
@@ -1,847 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <DeferredLayerUpdater.h>
-#include <RecordedOp.h>
-#include <RecordingCanvas.h>
-#include <hwui/Paint.h>
-#include <minikin/Layout.h>
-#include <tests/common/TestUtils.h>
-#include <utils/Color.h>
-
-#include <SkGradientShader.h>
-#include <SkImagePriv.h>
-#include <SkShader.h>
-
-namespace android {
-namespace uirenderer {
-
-static void playbackOps(const DisplayList& displayList,
- std::function<void(const RecordedOp&)> opReceiver) {
- for (auto& chunk : displayList.getChunks()) {
- for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
- RecordedOp* op = displayList.getOps()[opIndex];
- opReceiver(*op);
- }
- }
-}
-
-static void validateSingleOp(std::unique_ptr<DisplayList>& dl,
- std::function<void(const RecordedOp& op)> opValidator) {
- ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
- opValidator(*(dl->getOps()[0]));
-}
-
-// The RecordingCanvas is only ever used by the OpenGL RenderPipeline and never when Skia is in use.
-// Thus, even though many of these test are not directly dependent on the current RenderPipeline, we
-// set them all to be OPENGL_PIPELINE_TESTs in case the underlying code in RecordingCanvas ever
-// changes to require the use of the OPENGL_PIPELINE. Currently the textureLayer test is the only
-// test that requires being an OPENGL_PIPELINE_TEST.
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPlayback) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.restore();
- });
- playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, clipRect) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
- canvas.drawRect(0, 0, 50, 50, SkPaint());
- canvas.drawRect(50, 50, 100, 100, SkPaint());
- canvas.restore();
- });
-
- ASSERT_EQ(2u, dl->getOps().size()) << "Must be exactly two ops";
- EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[0]->localClip);
- EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[1]->localClip);
- EXPECT_EQ(dl->getOps()[0]->localClip, dl->getOps()[1]->localClip)
- << "Clip should be serialized once";
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, emptyClipRect) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
- canvas.clipRect(100, 100, 200, 200, SkClipOp::kIntersect);
- canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time
- canvas.restore();
- });
- ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected.";
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPaintRejection) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- SkPaint emptyPaint;
- emptyPaint.setColor(Color::Transparent);
-
- float points[] = {0, 0, 200, 200};
- canvas.drawPoints(points, 4, emptyPaint);
- canvas.drawLines(points, 4, emptyPaint);
- canvas.drawRect(0, 0, 200, 200, emptyPaint);
- canvas.drawRegion(SkRegion(SkIRect::MakeWH(200, 200)), emptyPaint);
- canvas.drawRoundRect(0, 0, 200, 200, 10, 10, emptyPaint);
- canvas.drawCircle(100, 100, 100, emptyPaint);
- canvas.drawOval(0, 0, 200, 200, emptyPaint);
- canvas.drawArc(0, 0, 200, 200, 0, 360, true, emptyPaint);
- SkPath path;
- path.addRect(0, 0, 200, 200);
- canvas.drawPath(path, emptyPaint);
- });
- EXPECT_EQ(0u, dl->getOps().size()) << "Op should be rejected";
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawArc) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint());
- canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint());
- });
-
- auto&& ops = dl->getOps();
- ASSERT_EQ(2u, ops.size()) << "Must be exactly two ops";
- EXPECT_EQ(RecordedOpId::ArcOp, ops[0]->opId);
- EXPECT_EQ(Rect(200, 200), ops[0]->unmappedBounds);
-
- EXPECT_EQ(RecordedOpId::OvalOp, ops[1]->opId) << "Circular arcs should be converted to ovals";
- EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawLines) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setStrokeWidth(
- 20); // doesn't affect recorded bounds - would be resolved at bake time
- float points[] = {0, 0, 20, 10, 30, 40, 90}; // NB: only 1 valid line
- canvas.drawLines(&points[0], 7, paint);
- });
-
- ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
- auto op = dl->getOps()[0];
- ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
- EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
- << "float count must be rounded down to closest multiple of 4";
- EXPECT_EQ(Rect(20, 10), op->unmappedBounds)
- << "unmapped bounds must be size of line, and not outset for stroke width";
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawRect) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(
- 100, 200, [](RecordingCanvas& canvas) { canvas.drawRect(10, 20, 90, 180, SkPaint()); });
-
- ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
- auto op = *(dl->getOps()[0]);
- ASSERT_EQ(RecordedOpId::RectOp, op.opId);
- EXPECT_EQ(nullptr, op.localClip);
- EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawRoundRect) {
- // Round case - stays rounded
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
- canvas.drawRoundRect(0, 0, 100, 100, 10, 10, SkPaint());
- });
- ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
- ASSERT_EQ(RecordedOpId::RoundRectOp, dl->getOps()[0]->opId);
-
- // Non-rounded case - turned into drawRect
- dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
- canvas.drawRoundRect(0, 0, 100, 100, 0, -1, SkPaint());
- });
- ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
- ASSERT_EQ(RecordedOpId::RectOp, dl->getOps()[0]->opId)
- << "Non-rounded rects should be converted";
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(20);
- TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
- });
-
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- count++;
- ASSERT_EQ(RecordedOpId::TextOp, op.opId);
- EXPECT_EQ(nullptr, op.localClip);
- EXPECT_TRUE(op.localMatrix.isIdentity());
- EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25))
- << "Op expected to be 25+ pixels wide, 10+ pixels tall";
- });
- ASSERT_EQ(1, count);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(20);
- for (int i = 0; i < 2; i++) {
- for (int j = 0; j < 2; j++) {
- uint32_t flags = paint.getFlags();
- if (i != 0) {
- flags |= SkPaint::kUnderlineText_ReserveFlag;
- } else {
- flags &= ~SkPaint::kUnderlineText_ReserveFlag;
- }
- if (j != 0) {
- flags |= SkPaint::kStrikeThruText_ReserveFlag;
- } else {
- flags &= ~SkPaint::kStrikeThruText_ReserveFlag;
- }
- paint.setFlags(flags);
- TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
- }
- }
- });
-
- auto ops = dl->getOps();
- ASSERT_EQ(8u, ops.size());
-
- int index = 0;
- EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough
-
- EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
- EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only
-
- EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
- EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only
-
- EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
- EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline
- EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(20);
- paint.setTextAlign(SkPaint::kLeft_Align);
- TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
- paint.setTextAlign(SkPaint::kCenter_Align);
- TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
- paint.setTextAlign(SkPaint::kRight_Align);
- TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
- });
-
- int count = 0;
- float lastX = FLT_MAX;
- playbackOps(*dl, [&count, &lastX](const RecordedOp& op) {
- count++;
- ASSERT_EQ(RecordedOpId::TextOp, op.opId);
- EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign())
- << "recorded drawText commands must force kLeft_Align on their paint";
-
- // verify TestUtils alignment offsetting (TODO: move asserts to Canvas base class)
- EXPECT_GT(lastX, ((const TextOp&)op).x)
- << "x coordinate should reduce across each of the draw commands, from alignment";
- lastX = ((const TextOp&)op).x;
- });
- ASSERT_EQ(3, count);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawColor) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
- });
-
- ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
- auto op = *(dl->getOps()[0]);
- EXPECT_EQ(RecordedOpId::ColorOp, op.opId);
- EXPECT_EQ(nullptr, op.localClip);
- EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds";
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, backgroundAndImage) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
- sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
- SkPaint paint;
- paint.setColor(SK_ColorBLUE);
-
- canvas.save(SaveFlags::MatrixClip);
- {
- // a background!
- canvas.save(SaveFlags::MatrixClip);
- canvas.drawRect(0, 0, 100, 200, paint);
- canvas.restore();
- }
- {
- // an image!
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(25, 25);
- canvas.scale(2, 2);
- canvas.drawBitmap(*bitmap, 0, 0, nullptr);
- canvas.restore();
- }
- canvas.restore();
- });
-
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- if (count == 0) {
- ASSERT_EQ(RecordedOpId::RectOp, op.opId);
- ASSERT_NE(nullptr, op.paint);
- EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
- EXPECT_EQ(Rect(100, 200), op.unmappedBounds);
- EXPECT_EQ(nullptr, op.localClip);
-
- Matrix4 expectedMatrix;
- expectedMatrix.loadIdentity();
- EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
- } else {
- ASSERT_EQ(RecordedOpId::BitmapOp, op.opId);
- EXPECT_EQ(nullptr, op.paint);
- EXPECT_EQ(Rect(25, 25), op.unmappedBounds);
- EXPECT_EQ(nullptr, op.localClip);
-
- Matrix4 expectedMatrix;
- expectedMatrix.loadTranslate(25, 25, 0);
- expectedMatrix.scale(2, 2, 1);
- EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
- }
- count++;
- });
- ASSERT_EQ(2, count);
-}
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(RecordingCanvas, textureLayer) {
- auto layerUpdater =
- TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5));
-
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(
- 200, 200,
- [&layerUpdater](RecordingCanvas& canvas) { canvas.drawLayer(layerUpdater.get()); });
-
- validateSingleOp(dl, [](const RecordedOp& op) {
- ASSERT_EQ(RecordedOpId::TextureLayerOp, op.opId);
- ASSERT_TRUE(op.localMatrix.isIdentity()) << "Op must not apply matrix at record time.";
- });
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simple) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
- canvas.drawRect(10, 20, 190, 180, SkPaint());
- canvas.restore();
- });
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- Matrix4 expectedMatrix;
- switch (count++) {
- case 0:
- EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
- EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
- EXPECT_EQ(nullptr, op.localClip);
- EXPECT_TRUE(op.localMatrix.isIdentity());
- break;
- case 1:
- EXPECT_EQ(RecordedOpId::RectOp, op.opId);
- EXPECT_CLIP_RECT(Rect(180, 160), op.localClip);
- EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
- expectedMatrix.loadTranslate(-10, -20, 0);
- EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
- break;
- case 2:
- EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
- // Don't bother asserting recording state data - it's not used
- break;
- default:
- ADD_FAILURE();
- }
- });
- EXPECT_EQ(3, count);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rounding) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10.25f, 10.75f, 89.25f, 89.75f, 128, SaveFlags::ClipToLayer);
- canvas.drawRect(20, 20, 80, 80, SkPaint());
- canvas.restore();
- });
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- Matrix4 expectedMatrix;
- switch (count++) {
- case 0:
- EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
- EXPECT_EQ(Rect(10, 10, 90, 90), op.unmappedBounds) << "Expect bounds rounded out";
- break;
- case 1:
- EXPECT_EQ(RecordedOpId::RectOp, op.opId);
- expectedMatrix.loadTranslate(-10, -10, 0);
- EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix) << "Expect rounded offset";
- break;
- case 2:
- EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
- // Don't bother asserting recording state data - it's not used
- break;
- default:
- ADD_FAILURE();
- }
- });
- EXPECT_EQ(3, count);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_missingRestore) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- // Note: restore omitted, shouldn't result in unmatched save
- });
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- if (count++ == 2) {
- EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
- }
- });
- EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer";
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
- canvas.drawRect(10, 20, 190, 180, SkPaint());
- canvas.restore();
- });
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- switch (count++) {
- case 0:
- EXPECT_EQ(RecordedOpId::BeginUnclippedLayerOp, op.opId);
- EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
- EXPECT_EQ(nullptr, op.localClip);
- EXPECT_TRUE(op.localMatrix.isIdentity());
- break;
- case 1:
- EXPECT_EQ(RecordedOpId::RectOp, op.opId);
- EXPECT_EQ(nullptr, op.localClip);
- EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
- EXPECT_TRUE(op.localMatrix.isIdentity());
- break;
- case 2:
- EXPECT_EQ(RecordedOpId::EndUnclippedLayerOp, op.opId);
- // Don't bother asserting recording state data - it's not used
- break;
- default:
- ADD_FAILURE();
- }
- });
- EXPECT_EQ(3, count);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_addClipFlag) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(10, 20, 190, 180, SkClipOp::kIntersect);
- canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
- canvas.drawRect(10, 20, 190, 180, SkPaint());
- canvas.restore();
- canvas.restore();
- });
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- if (count++ == 0) {
- EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId)
- << "Clip + unclipped saveLayer should result in a clipped layer";
- }
- });
- EXPECT_EQ(3, count);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_viewportCrop) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- // shouldn't matter, since saveLayer will clip to its bounds
- canvas.clipRect(-1000, -1000, 1000, 1000, SkClipOp::kReplace_deprecated);
-
- canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
- canvas.drawRect(0, 0, 400, 400, SkPaint());
- canvas.restore();
- });
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- if (count++ == 1) {
- Matrix4 expectedMatrix;
- EXPECT_EQ(RecordedOpId::RectOp, op.opId);
- EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be
- // intersection of viewport and saveLayer bounds, in layer space;
- EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
- expectedMatrix.loadTranslate(-100, -100, 0);
- EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
- }
- });
- EXPECT_EQ(3, count);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(100, 100);
- canvas.rotate(45);
- canvas.translate(-50, -50);
-
- canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
- canvas.drawRect(0, 0, 100, 100, SkPaint());
- canvas.restore();
-
- canvas.restore();
- });
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- if (count++ == 1) {
- EXPECT_EQ(RecordedOpId::RectOp, op.opId);
- EXPECT_CLIP_RECT(Rect(100, 100), op.localClip);
- EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
- EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix)
- << "Recorded op shouldn't see any canvas transform before the saveLayer";
- }
- });
- EXPECT_EQ(3, count);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateClipped) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(100, 100);
- canvas.rotate(45);
- canvas.translate(-200, -200);
-
- // area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
- canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
- canvas.drawRect(0, 0, 400, 400, SkPaint());
- canvas.restore();
-
- canvas.restore();
- });
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- if (count++ == 1) {
- Matrix4 expectedMatrix;
- EXPECT_EQ(RecordedOpId::RectOp, op.opId);
-
- // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by
- // the parent 200x200 viewport, but prior to rotation
- ASSERT_NE(nullptr, op.localClip);
- ASSERT_EQ(ClipMode::Rectangle, op.localClip->mode);
- // NOTE: this check relies on saveLayer altering the clip post-viewport init. This
- // causes the clip to be recorded by contained draw commands, though it's not necessary
- // since the same clip will be computed at draw time. If such a change is made, this
- // check could be done at record time by querying the clip, or the clip could be altered
- // slightly so that it is serialized.
- EXPECT_EQ(Rect(59, 59, 341, 341), op.localClip->rect);
- EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
- expectedMatrix.loadIdentity();
- EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
- }
- });
- EXPECT_EQ(3, count);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rejectBegin) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.translate(0, -20); // avoid identity case
- // empty clip rect should force layer + contents to be rejected
- canvas.clipRect(0, -20, 200, -20, SkClipOp::kIntersect);
- canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
- canvas.drawRect(0, 0, 200, 200, SkPaint());
- canvas.restore();
- canvas.restore();
- });
-
- ASSERT_EQ(0u, dl->getOps().size()) << "Begin/Rect/End should all be rejected.";
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_rejection) {
- auto child =
- TestUtils::createNode(50, 50, 150, 150, [](RenderProperties& props, Canvas& canvas) {
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
-
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(
- 200, 200, [&child](RecordingCanvas& canvas) {
- canvas.clipRect(0, 0, 0, 0, SkClipOp::kIntersect); // empty clip, reject node
- canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node...
- });
- ASSERT_TRUE(dl->isEmpty());
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_projection) {
- sp<RenderNode> background =
- TestUtils::createNode(50, 50, 150, 150, [](RenderProperties& props, Canvas& canvas) {
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
- });
- {
- background->mutateStagingProperties().setProjectionReceiver(false);
-
- // NO RECEIVER PRESENT
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(
- 200, 200, [&background](RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 100, 100, SkPaint());
- canvas.drawRenderNode(background.get());
- canvas.drawRect(0, 0, 100, 100, SkPaint());
- });
- EXPECT_EQ(-1, dl->projectionReceiveIndex)
- << "no projection receiver should have been observed";
- }
- {
- background->mutateStagingProperties().setProjectionReceiver(true);
-
- // RECEIVER PRESENT
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(
- 200, 200, [&background](RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 100, 100, SkPaint());
- canvas.drawRenderNode(background.get());
- canvas.drawRect(0, 0, 100, 100, SkPaint());
- });
-
- ASSERT_EQ(3u, dl->getOps().size()) << "Must be three ops";
- auto op = dl->getOps()[1];
- EXPECT_EQ(RecordedOpId::RenderNodeOp, op->opId);
- EXPECT_EQ(1, dl->projectionReceiveIndex) << "correct projection receiver not identified";
-
- // verify the behavior works even though projection receiver hasn't been sync'd yet
- EXPECT_TRUE(background->stagingProperties().isProjectionReceiver());
- EXPECT_FALSE(background->properties().isProjectionReceiver());
- }
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, firstClipWillReplace) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- // since no explicit clip set on canvas, this should be the one observed on op:
- canvas.clipRect(-100, -100, 300, 300, SkClipOp::kIntersect);
-
- SkPaint paint;
- paint.setColor(SK_ColorWHITE);
- canvas.drawRect(0, 0, 100, 100, paint);
-
- canvas.restore();
- });
- ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
- // first clip must be preserved, even if it extends beyond canvas bounds
- EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
- canvas.save(SaveFlags::MatrixClip);
- canvas.clipRect(-10, -10, 110, 110, SkClipOp::kReplace_deprecated);
- canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
- canvas.restore();
- });
- ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
- // first clip must be preserved, even if it extends beyond canvas bounds
- EXPECT_CLIP_RECT(Rect(-10, -10, 110, 110), dl->getOps()[0]->localClip);
- EXPECT_TRUE(dl->getOps()[0]->localClip->intersectWithRoot);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- canvas.drawRect(0, 0, 400, 400, SkPaint());
- canvas.insertReorderBarrier(true);
- canvas.insertReorderBarrier(false);
- canvas.insertReorderBarrier(false);
- canvas.insertReorderBarrier(true);
- canvas.drawRect(0, 0, 400, 400, SkPaint());
- canvas.insertReorderBarrier(false);
- });
-
- auto chunks = dl->getChunks();
- EXPECT_EQ(0u, chunks[0].beginOpIndex);
- EXPECT_EQ(1u, chunks[0].endOpIndex);
- EXPECT_FALSE(chunks[0].reorderChildren);
-
- EXPECT_EQ(1u, chunks[1].beginOpIndex);
- EXPECT_EQ(2u, chunks[1].endOpIndex);
- EXPECT_TRUE(chunks[1].reorderChildren);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier_clip) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- // first chunk: no recorded clip
- canvas.insertReorderBarrier(true);
- canvas.drawRect(0, 0, 400, 400, SkPaint());
-
- // second chunk: no recorded clip, since inorder region
- canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
- canvas.insertReorderBarrier(false);
- canvas.drawRect(0, 0, 400, 400, SkPaint());
-
- // third chunk: recorded clip
- canvas.insertReorderBarrier(true);
- canvas.drawRect(0, 0, 400, 400, SkPaint());
- });
-
- auto chunks = dl->getChunks();
- ASSERT_EQ(3u, chunks.size());
-
- EXPECT_TRUE(chunks[0].reorderChildren);
- EXPECT_EQ(nullptr, chunks[0].reorderClip);
-
- EXPECT_FALSE(chunks[1].reorderChildren);
- EXPECT_EQ(nullptr, chunks[1].reorderClip);
-
- EXPECT_TRUE(chunks[2].reorderChildren);
- ASSERT_NE(nullptr, chunks[2].reorderClip);
- EXPECT_EQ(Rect(200, 200), chunks[2].reorderClip->rect);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, refPaint) {
- SkPaint paint;
-
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(
- 200, 200, [&paint](RecordingCanvas& canvas) {
- paint.setColor(SK_ColorBLUE);
- // first two should use same paint
- canvas.drawRect(0, 0, 200, 10, paint);
- SkPaint paintCopy(paint);
- canvas.drawRect(0, 10, 200, 20, paintCopy);
-
- // only here do we use different paint ptr
- paint.setColor(SK_ColorRED);
- canvas.drawRect(0, 20, 200, 30, paint);
- });
- auto ops = dl->getOps();
- ASSERT_EQ(3u, ops.size());
-
- // first two are the same
- EXPECT_NE(nullptr, ops[0]->paint);
- EXPECT_NE(&paint, ops[0]->paint);
- EXPECT_EQ(ops[0]->paint, ops[1]->paint);
-
- // last is different, but still copied / non-null
- EXPECT_NE(nullptr, ops[2]->paint);
- EXPECT_NE(ops[0]->paint, ops[2]->paint);
- EXPECT_NE(&paint, ops[2]->paint);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmap) {
- sk_sp<Bitmap> bitmap(TestUtils::createBitmap(100, 100));
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(
- 100, 100,
- [&bitmap](RecordingCanvas& canvas) { canvas.drawBitmap(*bitmap, 0, 0, nullptr); });
- auto& bitmaps = dl->getBitmapResources();
- EXPECT_EQ(1u, bitmaps.size());
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
- sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(
- 100, 100, [&bitmap](RecordingCanvas& canvas) {
- SkPaint paint;
- SkBitmap skBitmap;
- bitmap->getSkBitmap(&skBitmap);
- sk_sp<SkImage> image =
- SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
- sk_sp<SkShader> shader =
- image->makeShader(SkShader::TileMode::kClamp_TileMode,
- SkShader::TileMode::kClamp_TileMode, nullptr);
- paint.setShader(std::move(shader));
- canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
- });
- auto& bitmaps = dl->getBitmapResources();
- EXPECT_EQ(1u, bitmaps.size());
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_composeShader) {
- sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(
- 100, 100, [&bitmap](RecordingCanvas& canvas) {
- SkPaint paint;
- SkBitmap skBitmap;
- bitmap->getSkBitmap(&skBitmap);
- sk_sp<SkImage> image =
- SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
- sk_sp<SkShader> shader1 =
- image->makeShader(SkShader::TileMode::kClamp_TileMode,
- SkShader::TileMode::kClamp_TileMode, nullptr);
-
- SkPoint center;
- center.set(50, 50);
- SkColor colors[2];
- colors[0] = Color::Black;
- colors[1] = Color::White;
- sk_sp<SkShader> shader2 = SkGradientShader::MakeRadial(
- center, 50, colors, nullptr, 2, SkShader::TileMode::kRepeat_TileMode);
-
- sk_sp<SkShader> composeShader = SkShader::MakeComposeShader(
- std::move(shader1), std::move(shader2), SkBlendMode::kMultiply);
- paint.setShader(std::move(composeShader));
- canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
- });
- auto& bitmaps = dl->getBitmapResources();
- EXPECT_EQ(1u, bitmaps.size());
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawText) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- Paint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(20);
- TestUtils::drawUtf8ToCanvas(&canvas, "HELLO", paint, 25, 25);
- });
-
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- count++;
- ASSERT_EQ(RecordedOpId::TextOp, op.opId);
- EXPECT_EQ(nullptr, op.localClip);
- EXPECT_TRUE(op.localMatrix.isIdentity());
- EXPECT_TRUE(op.unmappedBounds.getHeight() >= 10);
- EXPECT_TRUE(op.unmappedBounds.getWidth() >= 25);
- });
- ASSERT_EQ(1, count);
-}
-
-OPENGL_PIPELINE_TEST(RecordingCanvas, drawTextInHighContrast) {
- Properties::enableHighContrastText = true;
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- Paint paint;
- paint.setColor(SK_ColorWHITE);
- paint.setAntiAlias(true);
- paint.setTextSize(20);
- TestUtils::drawUtf8ToCanvas(&canvas, "HELLO", paint, 25, 25);
- });
- Properties::enableHighContrastText = false;
-
- int count = 0;
- playbackOps(*dl, [&count](const RecordedOp& op) {
- ASSERT_EQ(RecordedOpId::TextOp, op.opId);
- if (count++ == 0) {
- EXPECT_EQ(SK_ColorBLACK, op.paint->getColor());
- EXPECT_EQ(SkPaint::kStrokeAndFill_Style, op.paint->getStyle());
- } else {
- EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
- EXPECT_EQ(SkPaint::kFill_Style, op.paint->getStyle());
- }
-
- });
- ASSERT_EQ(2, count);
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 15c0ab1ad8d2..2c73940b9b9c 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -18,13 +18,13 @@
#include <gtest/gtest.h>
#include <SkClipStack.h>
-#include <SkLiteRecorder.h>
#include <SkSurface_Base.h>
#include <string.h>
#include "AnimationContext.h"
#include "DamageAccumulator.h"
#include "FatalTestCanvas.h"
#include "IContextFactory.h"
+#include "RecordingCanvas.h"
#include "SkiaCanvas.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
@@ -32,6 +32,7 @@
#include "pipeline/skia/SkiaRecordingCanvas.h"
#include "renderthread/CanvasContext.h"
#include "tests/common/TestUtils.h"
+#include "utils/Color.h"
using namespace android;
using namespace android::uirenderer;
@@ -44,8 +45,8 @@ TEST(RenderNodeDrawable, create) {
canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
});
- SkLiteDL skLiteDL;
- SkLiteRecorder canvas;
+ DisplayListData skLiteDL;
+ RecordingCanvas canvas;
canvas.reset(&skLiteDL, SkIRect::MakeWH(1, 1));
canvas.translate(100, 100);
RenderNodeDrawable drawable(rootNode.get(), &canvas);
@@ -537,7 +538,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
layerUpdateQueue.enqueueLayerWithDamage(child.get(),
android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT));
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
- pipeline->renderLayersImpl(layerUpdateQueue, true, false);
+ pipeline->renderLayersImpl(layerUpdateQueue, true);
EXPECT_EQ(1, drawCounter); // assert index 0 is drawn on the layer
RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true);
@@ -1094,7 +1095,7 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) {
class ShadowTestCanvas : public SkCanvas {
public:
ShadowTestCanvas(int width, int height) : SkCanvas(width, height) {}
- int getIndex() { return mDrawCounter; }
+ int getDrawCounter() { return mDrawCounter; }
virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
// expect to draw 2 RenderNodeDrawable, 1 StartReorderBarrierDrawable,
@@ -1109,17 +1110,36 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) {
EXPECT_EQ(dy, TRANSLATE_Y);
}
+ virtual void didSetMatrix(const SkMatrix& matrix) override {
+ mDrawCounter++;
+ // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
+ // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
+ EXPECT_TRUE(matrix.isIdentity());
+ EXPECT_TRUE(getTotalMatrix().isIdentity());
+ }
+
virtual void didConcat(const SkMatrix& matrix) override {
- // This function is invoked by EndReorderBarrierDrawable::drawShadow to apply shadow
- // matrix.
mDrawCounter++;
- EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X, CASTER_Y), matrix);
- EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
- getTotalMatrix());
+ if (mFirstDidConcat) {
+ // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
+ mFirstDidConcat = false;
+ EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
+ matrix);
+ EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
+ getTotalMatrix());
+ } else {
+ // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
+ EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
+ matrix);
+ EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
+ getTotalMatrix());
+ }
}
protected:
int mDrawCounter = 0;
+ private:
+ bool mFirstDidConcat = true;
};
auto parent = TestUtils::createSkiaNode(
@@ -1143,7 +1163,7 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) {
ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
RenderNodeDrawable drawable(parent.get(), &canvas, false);
canvas.drawDrawable(&drawable);
- EXPECT_EQ(6, canvas.getIndex());
+ EXPECT_EQ(9, canvas.getDrawCounter());
}
// Draw a vector drawable twice but with different bounds and verify correct bounds are used.
diff --git a/libs/hwui/tests/unit/RenderPropertiesTests.cpp b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
index 85655fc2728a..3e8e0576bf49 100644
--- a/libs/hwui/tests/unit/RenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
@@ -22,8 +22,6 @@ using namespace android;
using namespace android::uirenderer;
TEST(RenderProperties, layerValidity) {
- DeviceInfo::initialize();
-
const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
ASSERT_LE(2048, maxTextureSize);
ASSERT_GT(100000, maxTextureSize);
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 43080a9460b3..1433aa0349f4 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -48,11 +48,18 @@ public:
*/
static void terminate(ShaderCache& cache, bool saveContent) {
std::lock_guard<std::mutex> lock(cache.mMutex);
- if (cache.mInitialized && cache.mBlobCache && saveContent) {
- cache.mBlobCache->writeToFile();
- }
+ cache.mSavePending = saveContent;
+ cache.saveToDiskLocked();
cache.mBlobCache = NULL;
}
+
+ /**
+ *
+ */
+ template <typename T>
+ static bool validateCache(ShaderCache& cache, std::vector<T> hash) {
+ return cache.validateCache(hash.data(), hash.size() * sizeof(T));
+ }
};
} /* namespace skiapipeline */
@@ -75,26 +82,39 @@ bool folderExist(const std::string& folderName) {
return false;
}
-bool checkShader(const sk_sp<SkData>& shader, const char* program) {
+inline bool
+checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
+ return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size()
+ && 0 == memcmp(shader1->data(), shader2->data(), shader1->size());
+}
+
+inline bool
+checkShader(const sk_sp<SkData>& shader, const char* program) {
sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
- return shader->size() == shader2->size()
- && 0 == memcmp(shader->data(), shader2->data(), shader->size());
+ return checkShader(shader, shader2);
}
-bool checkShader(const sk_sp<SkData>& shader, std::vector<char>& program) {
- sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size());
- return shader->size() == shader2->size()
- && 0 == memcmp(shader->data(), shader2->data(), shader->size());
+template <typename T>
+bool checkShader(const sk_sp<SkData>& shader, std::vector<T>& program) {
+ sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size() * sizeof(T));
+ return checkShader(shader, shader2);
}
void setShader(sk_sp<SkData>& shader, const char* program) {
shader = SkData::MakeWithCString(program);
}
-void setShader(sk_sp<SkData>& shader, std::vector<char>& program) {
- shader = SkData::MakeWithCopy(program.data(), program.size());
+template <typename T>
+void setShader(sk_sp<SkData>& shader, std::vector<T>& buffer) {
+ shader = SkData::MakeWithCopy(buffer.data(), buffer.size() * sizeof(T));
}
+template <typename T>
+void genRandomData(std::vector<T>& buffer) {
+ for (auto& data : buffer) {
+ data = T(std::rand());
+ }
+}
#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
@@ -110,6 +130,7 @@ TEST(ShaderCacheTest, testWriteAndRead) {
//remove any test files from previous test run
int deleteFile = remove(cacheFile1.c_str());
ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+ std::srand(0);
//read the cache from a file that does not exist
ShaderCache::get().setFilename(cacheFile1.c_str());
@@ -158,10 +179,8 @@ TEST(ShaderCacheTest, testWriteAndRead) {
//write and read big data chunk (50K)
size_t dataSize = 50*1024;
- std::vector<char> dataBuffer(dataSize);
- for (size_t i = 0; i < dataSize; i++) {
- dataBuffer[0] = dataSize % 256;
- }
+ std::vector<uint8_t> dataBuffer(dataSize);
+ genRandomData(dataBuffer);
setShader(inVS, dataBuffer);
ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
@@ -173,4 +192,96 @@ TEST(ShaderCacheTest, testWriteAndRead) {
remove(cacheFile1.c_str());
}
+TEST(ShaderCacheTest, testCacheValidation) {
+ if (!folderExist(getExternalStorageFolder())) {
+ //don't run the test if external storage folder is not available
+ return;
+ }
+ std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+ std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
+
+ //remove any test files from previous test run
+ int deleteFile = remove(cacheFile1.c_str());
+ ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+ std::srand(0);
+
+ //generate identity and read the cache from a file that does not exist
+ ShaderCache::get().setFilename(cacheFile1.c_str());
+ ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+ std::vector<uint8_t> identity(1024);
+ genRandomData(identity);
+ ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
+ sizeof(decltype(identity)::value_type));
+
+ // generate random content in cache and store to disk
+ constexpr size_t numBlob(10);
+ constexpr size_t keySize(1024);
+ constexpr size_t dataSize(50 * 1024);
+
+ std::vector< std::pair<sk_sp<SkData>, sk_sp<SkData>> > blobVec(numBlob);
+ for (auto& blob : blobVec) {
+ std::vector<uint8_t> keyBuffer(keySize);
+ std::vector<uint8_t> dataBuffer(dataSize);
+ genRandomData(keyBuffer);
+ genRandomData(dataBuffer);
+
+ sk_sp<SkData> key, data;
+ setShader(key, keyBuffer);
+ setShader(data, dataBuffer);
+
+ blob = std::make_pair(key, data);
+ ShaderCache::get().store(*key.get(), *data.get());
+ }
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
+
+ // change to a file that does not exist and verify validation fails
+ ShaderCache::get().setFilename(cacheFile2.c_str());
+ ShaderCache::get().initShaderDiskCache();
+ ASSERT_FALSE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+
+ // restore the original file and verify validation succeeds
+ ShaderCache::get().setFilename(cacheFile1.c_str());
+ ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
+ sizeof(decltype(identity)::value_type));
+ ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ for (const auto& blob : blobVec) {
+ auto outVS = ShaderCache::get().load(*blob.first.get());
+ ASSERT_TRUE( checkShader(outVS, blob.second) );
+ }
+
+ // generate error identity and verify load fails
+ ShaderCache::get().initShaderDiskCache(identity.data(), -1);
+ for (const auto& blob : blobVec) {
+ ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ }
+ ShaderCache::get().initShaderDiskCache(nullptr, identity.size() *
+ sizeof(decltype(identity)::value_type));
+ for (const auto& blob : blobVec) {
+ ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ }
+
+ // verify the cache validation again after load fails
+ ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
+ sizeof(decltype(identity)::value_type));
+ ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ for (const auto& blob : blobVec) {
+ auto outVS = ShaderCache::get().load(*blob.first.get());
+ ASSERT_TRUE( checkShader(outVS, blob.second) );
+ }
+
+ // generate another identity and verify load fails
+ for (auto& data : identity) {
+ data += std::rand();
+ }
+ ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
+ sizeof(decltype(identity)::value_type));
+ for (const auto& blob : blobVec) {
+ ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ }
+
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+ remove(cacheFile1.c_str());
+}
+
} // namespace
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index bc742b0c5a63..df5f45618070 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -35,23 +35,6 @@ SkBitmap createSkBitmap(int width, int height) {
return bitmap;
}
-/**
- * 1x1 bitmaps must not be optimized into solid color shaders, since HWUI can't
- * compose/render color shaders
- */
-TEST(SkiaBehavior, CreateBitmapShader1x1) {
- SkBitmap origBitmap = createSkBitmap(1, 1);
- sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(origBitmap, kNever_SkCopyPixelsMode);
- sk_sp<SkShader> s =
- image->makeShader(SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, nullptr);
-
- SkBitmap bitmap;
- SkShader::TileMode xy[2];
- ASSERT_TRUE(s->isABitmap(&bitmap, nullptr, xy))
- << "1x1 bitmap shader must query as bitmap shader";
- EXPECT_EQ(origBitmap.pixelRef(), bitmap.pixelRef());
-}
-
TEST(SkiaBehavior, genIds) {
SkBitmap bitmap = createSkBitmap(100, 100);
uint32_t genId = bitmap.getGenerationID();
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 1d7dc3d06ee4..634ceffe0741 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -16,7 +16,6 @@
#include "tests/common/TestUtils.h"
-#include <RecordingCanvas.h>
#include <SkBlurDrawLooper.h>
#include <SkCanvasStateUtils.h>
#include <SkPicture.h>
@@ -26,41 +25,6 @@
using namespace android;
using namespace android::uirenderer;
-/**
- * Verify that we get the same culling bounds for text for (1) drawing glyphs
- * directly to a Canvas or (2) going through a SkPicture as an intermediate step.
- */
-OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) {
- auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
- // setup test variables
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(20);
- static const char* text = "testing text bounds";
-
- // draw text directly into Recording canvas
- TestUtils::drawUtf8ToCanvas(&canvas, text, paint, 25, 25);
-
- // record the same text draw into a SkPicture and replay it into a Recording canvas
- SkPictureRecorder recorder;
- SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0);
- std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas));
- TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25);
- sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
-
- canvas.asSkCanvas()->drawPicture(picture);
- });
-
- // verify that the text bounds and matrices match
- ASSERT_EQ(2U, dl->getOps().size());
- auto directOp = dl->getOps()[0];
- auto pictureOp = dl->getOps()[1];
- ASSERT_EQ(RecordedOpId::TextOp, directOp->opId);
- EXPECT_EQ(directOp->opId, pictureOp->opId);
- EXPECT_EQ(directOp->unmappedBounds, pictureOp->unmappedBounds);
- EXPECT_EQ(directOp->localMatrix, pictureOp->localMatrix);
-}
-
TEST(SkiaCanvas, drawShadowLayer) {
auto surface = SkSurface::MakeRasterN32Premul(10, 10);
SkiaCanvas canvas(surface->getCanvas());
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 88d6dcf990a5..415f9e8517ff 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -20,6 +20,7 @@
#include "AnimationContext.h"
#include "DamageAccumulator.h"
#include "IContextFactory.h"
+#include "pipeline/skia/GLFunctorDrawable.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "renderthread/CanvasContext.h"
#include "tests/common/TestUtils.h"
@@ -36,32 +37,37 @@ TEST(SkiaDisplayList, create) {
}
TEST(SkiaDisplayList, reset) {
- SkiaDisplayList skiaDL;
+ std::unique_ptr<SkiaDisplayList> skiaDL;
+ {
+ SkiaRecordingCanvas canvas{nullptr, 1, 1};
+ canvas.drawColor(0, SkBlendMode::kSrc);
+ skiaDL.reset(canvas.finishRecording());
+ }
SkCanvas dummyCanvas;
RenderNodeDrawable drawable(nullptr, &dummyCanvas);
- skiaDL.mChildNodes.emplace_back(nullptr, &dummyCanvas);
- skiaDL.mChildFunctors.emplace_back(nullptr, nullptr, &dummyCanvas);
- skiaDL.mMutableImages.push_back(nullptr);
- skiaDL.mVectorDrawables.push_back(nullptr);
- skiaDL.mDisplayList.drawAnnotation(SkRect::MakeWH(200, 200), "testAnnotation", nullptr);
- skiaDL.mProjectionReceiver = &drawable;
-
- ASSERT_FALSE(skiaDL.mChildNodes.empty());
- ASSERT_FALSE(skiaDL.mChildFunctors.empty());
- ASSERT_FALSE(skiaDL.mMutableImages.empty());
- ASSERT_FALSE(skiaDL.mVectorDrawables.empty());
- ASSERT_FALSE(skiaDL.isEmpty());
- ASSERT_TRUE(skiaDL.mProjectionReceiver);
-
- skiaDL.reset();
-
- ASSERT_TRUE(skiaDL.mChildNodes.empty());
- ASSERT_TRUE(skiaDL.mChildFunctors.empty());
- ASSERT_TRUE(skiaDL.mMutableImages.empty());
- ASSERT_TRUE(skiaDL.mVectorDrawables.empty());
- ASSERT_TRUE(skiaDL.isEmpty());
- ASSERT_FALSE(skiaDL.mProjectionReceiver);
+ skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas);
+ GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas);
+ skiaDL->mChildFunctors.push_back(&functorDrawable);
+ skiaDL->mMutableImages.push_back(nullptr);
+ skiaDL->mVectorDrawables.push_back(nullptr);
+ skiaDL->mProjectionReceiver = &drawable;
+
+ ASSERT_FALSE(skiaDL->mChildNodes.empty());
+ ASSERT_FALSE(skiaDL->mChildFunctors.empty());
+ ASSERT_FALSE(skiaDL->mMutableImages.empty());
+ ASSERT_FALSE(skiaDL->mVectorDrawables.empty());
+ ASSERT_FALSE(skiaDL->isEmpty());
+ ASSERT_TRUE(skiaDL->mProjectionReceiver);
+
+ skiaDL->reset();
+
+ ASSERT_TRUE(skiaDL->mChildNodes.empty());
+ ASSERT_TRUE(skiaDL->mChildFunctors.empty());
+ ASSERT_TRUE(skiaDL->mMutableImages.empty());
+ ASSERT_TRUE(skiaDL->mVectorDrawables.empty());
+ ASSERT_TRUE(skiaDL->isEmpty());
+ ASSERT_FALSE(skiaDL->mProjectionReceiver);
}
TEST(SkiaDisplayList, reuseDisplayList) {
@@ -91,7 +97,8 @@ TEST(SkiaDisplayList, syncContexts) {
SkCanvas dummyCanvas;
TestUtils::MockFunctor functor;
- skiaDL.mChildFunctors.emplace_back(&functor, nullptr, &dummyCanvas);
+ GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
+ skiaDL.mChildFunctors.push_back(&functorDrawable);
SkRect bounds = SkRect::MakeWH(200, 200);
VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 42a92fcc7d89..65b4e26ab62f 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -18,13 +18,13 @@
#include <gtest/gtest.h>
#include <SkClipStack.h>
-#include <SkLiteRecorder.h>
#include <SkSurface_Base.h>
#include <string.h>
#include "AnimationContext.h"
#include "DamageAccumulator.h"
#include "IContextFactory.h"
#include "SkiaCanvas.h"
+#include "pipeline/skia/SkiaUtils.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaRecordingCanvas.h"
@@ -42,7 +42,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
});
LayerUpdateQueue layerUpdateQueue;
- SkRect dirty = SkRect::MakeLargest();
+ SkRect dirty = SkRectMakeLargest();
std::vector<sp<RenderNode>> renderNodes;
renderNodes.push_back(redNode);
bool opaque = true;
@@ -51,7 +51,7 @@ 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, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
@@ -63,7 +63,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) {
});
LayerUpdateQueue layerUpdateQueue;
- SkRect dirty = SkRect::MakeLargest();
+ SkRect dirty = SkRectMakeLargest();
std::vector<sp<RenderNode>> renderNodes;
renderNodes.push_back(redNode);
bool opaque = true;
@@ -84,7 +84,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) {
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
// drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
@@ -98,7 +98,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
bottomHalfGreenCanvas.drawRect(0, 1, 2, 2, greenPaint);
});
LayerUpdateQueue layerUpdateQueue;
- SkRect dirty = SkRect::MakeLargest();
+ SkRect dirty = SkRectMakeLargest();
std::vector<sp<RenderNode>> renderNodes;
renderNodes.push_back(halfGreenNode);
android::uirenderer::Rect contentDrawBounds(0, 0, 2, 2);
@@ -106,11 +106,11 @@ 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, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
@@ -130,7 +130,7 @@ 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, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE);
@@ -161,18 +161,18 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
// attach both layers to the update queue
LayerUpdateQueue layerUpdateQueue;
- SkRect dirty = SkRect::MakeLargest();
+ SkRect dirty = SkRectMakeLargest();
layerUpdateQueue.enqueueLayerWithDamage(redNode.get(), dirty);
layerUpdateQueue.enqueueLayerWithDamage(blueNode.get(), SkRect::MakeWH(2, 1));
ASSERT_EQ(layerUpdateQueue.entries().size(), 2UL);
bool opaque = true;
- FrameBuilder::LightGeometry lightGeometry;
+ LightGeometry lightGeometry;
lightGeometry.radius = 1.0f;
lightGeometry.center = {0.0f, 0.0f, 0.0f};
- BakedOpRenderer::LightInfo lightInfo;
+ LightInfo lightInfo;
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
- pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, false, lightInfo);
+ pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, lightInfo);
ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorRED);
ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorBLUE);
ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 1), SK_ColorWHITE);
@@ -203,37 +203,37 @@ 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, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
// 1 Overdraw, should be blue blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff);
// 2 Overdraw, should be green blended onto white
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0);
// 3 Overdraw, should be pink blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0);
// 4 Overdraw, should be red blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
// 5 Overdraw, should be red blended onto white.
renderNodes.push_back(whiteNode);
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds,
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
surface);
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
}
@@ -319,7 +319,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, false, contentDrawBounds, surface);
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface);
EXPECT_EQ(4, surface->canvas()->mDrawCounter);
}
@@ -349,7 +349,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, false,
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
@@ -379,7 +379,17 @@ 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, false,
+ pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ EXPECT_FALSE(pipeline->isSurfaceReady());
+ EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::SRGB));
+ EXPECT_TRUE(pipeline->isSurfaceReady());
+ renderThread.destroyGlContext();
+ EXPECT_FALSE(pipeline->isSurfaceReady());
+}
+
diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
index ad5fdacc8594..479c462bd1a6 100644
--- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -18,7 +18,6 @@
#include <gtest/gtest.h>
#include <SkClipStack.h>
-#include <SkLiteRecorder.h>
#include <SkSurface_Base.h>
#include <string.h>
#include "AnimationContext.h"
diff --git a/libs/hwui/tests/unit/SnapshotTests.cpp b/libs/hwui/tests/unit/SnapshotTests.cpp
deleted file mode 100644
index 9d673c82d4d0..000000000000
--- a/libs/hwui/tests/unit/SnapshotTests.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <Snapshot.h>
-
-#include <tests/common/TestUtils.h>
-
-using namespace android::uirenderer;
-
-TEST(Snapshot, serializeIntersectedClip) {
- auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100));
- auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90));
- auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
- root->previous = actualRoot.get();
- child->previous = root.get();
-
- LinearAllocator allocator;
- ClipRect rect(Rect(0, 0, 75, 75));
- {
- auto intersectWithChild =
- child->serializeIntersectedClip(allocator, &rect, Matrix4::identity());
- ASSERT_NE(nullptr, intersectWithChild);
- EXPECT_EQ(Rect(50, 50, 75, 75), intersectWithChild->rect) << "Expect intersect with child";
- }
-
- rect.intersectWithRoot = true;
- {
- auto intersectWithRoot =
- child->serializeIntersectedClip(allocator, &rect, Matrix4::identity());
- ASSERT_NE(nullptr, intersectWithRoot);
- EXPECT_EQ(Rect(10, 10, 75, 75), intersectWithRoot->rect) << "Expect intersect with root";
- }
-}
-
-TEST(Snapshot, applyClip) {
- auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100));
- auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90));
- root->previous = actualRoot.get();
-
- ClipRect rect(Rect(0, 0, 75, 75));
- {
- auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
- child->previous = root.get();
- child->applyClip(&rect, Matrix4::identity());
-
- EXPECT_TRUE(child->getClipArea().isSimple());
- EXPECT_EQ(Rect(50, 50, 75, 75), child->getRenderTargetClip());
- }
-
- {
- rect.intersectWithRoot = true;
- auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
- child->previous = root.get();
- child->applyClip(&rect, Matrix4::identity());
-
- EXPECT_TRUE(child->getClipArea().isSimple());
- EXPECT_EQ(Rect(10, 10, 75, 75), child->getRenderTargetClip());
- }
-}
diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
deleted file mode 100644
index 92d05e44c6ca..000000000000
--- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "GammaFontRenderer.h"
-#include "TextDropShadowCache.h"
-#include "tests/common/TestUtils.h"
-#include "utils/Blur.h"
-
-#include <SkPaint.h>
-
-using namespace android;
-using namespace android::uirenderer;
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) {
- SkPaint paint;
- paint.setTextSize(20);
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-
- GammaFontRenderer gammaFontRenderer;
- FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
- fontRenderer.setFont(&paint, SkMatrix::I());
- TextDropShadowCache cache(MB(5));
- cache.setFontRenderer(fontRenderer);
-
- std::vector<glyph_t> glyphs;
- std::vector<float> positions;
- float totalAdvance;
- uirenderer::Rect bounds;
- TestUtils::layoutTextUnscaled(paint, "This is a test", &glyphs, &positions, &totalAdvance,
- &bounds);
- EXPECT_TRUE(bounds.contains(5, -10, 100, 0)) << "Expect input to be nontrivially sized";
-
- ShadowTexture* texture = cache.get(&paint, glyphs.data(), glyphs.size(), 10, positions.data());
-
- ASSERT_TRUE(texture);
- ASSERT_FALSE(texture->cleanup);
- ASSERT_EQ((uint32_t)texture->objectSize(), cache.getSize());
- ASSERT_TRUE(cache.getSize());
- cache.clear();
- ASSERT_EQ(cache.getSize(), 0u);
-}
diff --git a/libs/hwui/tests/unit/TextureCacheTests.cpp b/libs/hwui/tests/unit/TextureCacheTests.cpp
deleted file mode 100644
index ab740dd8330e..000000000000
--- a/libs/hwui/tests/unit/TextureCacheTests.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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 "Extensions.h"
-#include "TextureCache.h"
-#include "tests/common/TestUtils.h"
-
-using namespace android;
-using namespace android::uirenderer;
-
-RENDERTHREAD_OPENGL_PIPELINE_TEST(TextureCache, clear) {
- TextureCache cache;
- ASSERT_EQ(cache.getSize(), 0u);
- // it is not 0, because FontRenderer allocates one texture
- int initialCount = GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture);
- SkBitmap skBitmap;
- SkImageInfo info = SkImageInfo::Make(100, 100, kN32_SkColorType, kPremul_SkAlphaType);
- skBitmap.setInfo(info);
- sk_sp<Bitmap> hwBitmap(renderThread.allocateHardwareBitmap(skBitmap));
- cache.get(hwBitmap.get());
- ASSERT_EQ(GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture), initialCount + 1);
- cache.clear();
- ASSERT_EQ(GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture), initialCount);
-}
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index e424a266bf72..b645aeb55074 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -55,7 +55,8 @@ std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) {
sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
- std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
+ std::move(typeface), data, st.st_size, fileName, 0,
+ std::vector<minikin::FontVariation>());
std::vector<minikin::Font> fonts;
fonts.push_back(minikin::Font::Builder(font).build());
return std::make_shared<minikin::FontFamily>(std::move(fonts));
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 9e6d9a8c27de..aecceb3609f5 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -17,12 +17,13 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include "Caches.h"
#include "debug/GlesDriver.h"
#include "debug/NullGlesDriver.h"
#include "hwui/Typeface.h"
#include "Properties.h"
#include "tests/common/LeakChecker.h"
+#include "thread/TaskProcessor.h"
+#include "thread/Task.h"
#include "thread/TaskManager.h"
#include <signal.h>
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/thread/ThreadBase.h
index 8068121f64cf..f9de8a5037e5 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/thread/ThreadBase.h
@@ -47,6 +47,8 @@ public:
void join() { Thread::join(); }
+ bool isRunning() const { return Thread::isRunning(); }
+
protected:
void waitForWork() {
nsecs_t nextWakeup;
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 75740e8b5baf..3fb6a31a7d97 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -16,8 +16,10 @@
#include "Color.h"
-
#include <utils/Log.h>
+#include <ui/ColorSpace.h>
+
+#include <algorithm>
#include <cmath>
namespace android {
@@ -55,6 +57,26 @@ bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) {
return false;
}
+android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) {
+ switch (colorType) {
+ case kRGBA_8888_SkColorType:
+ return PIXEL_FORMAT_RGBA_8888;
+ case kRGBA_F16_SkColorType:
+ return PIXEL_FORMAT_RGBA_FP16;
+ case kRGB_565_SkColorType:
+ return PIXEL_FORMAT_RGB_565;
+ case kRGB_888x_SkColorType:
+ return PIXEL_FORMAT_RGBX_8888;
+ case kRGBA_1010102_SkColorType:
+ return PIXEL_FORMAT_RGBA_1010102;
+ case kARGB_4444_SkColorType:
+ return PIXEL_FORMAT_RGBA_4444;
+ default:
+ ALOGW("Unsupported colorType: %d, return RGBA_8888 by default", (int)colorType);
+ return PIXEL_FORMAT_RGBA_8888;
+ }
+}
+
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
SkColorSpace::Gamut gamut;
@@ -107,5 +129,97 @@ sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
}
}
+template<typename T>
+static constexpr T clamp(T x, T min, T max) {
+ return x < min ? min : x > max ? max : x;
+}
+
+//static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
+static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
+static const mat3 BRADFORD = mat3{
+ float3{ 0.8951f, -0.7502f, 0.0389f},
+ float3{ 0.2664f, 1.7135f, -0.0685f},
+ float3{-0.1614f, 0.0367f, 1.0296f}
+};
+
+static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
+ float3 srcLMS = matrix * srcWhitePoint;
+ float3 dstLMS = matrix * dstWhitePoint;
+ return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
+}
+
+namespace LabColorSpace {
+
+static constexpr float A = 216.0f / 24389.0f;
+static constexpr float B = 841.0f / 108.0f;
+static constexpr float C = 4.0f / 29.0f;
+static constexpr float D = 6.0f / 29.0f;
+
+float3 toXyz(const Lab& lab) {
+ float3 v { lab.L, lab.a, lab.b };
+ v[0] = clamp(v[0], 0.0f, 100.0f);
+ v[1] = clamp(v[1], -128.0f, 128.0f);
+ v[2] = clamp(v[2], -128.0f, 128.0f);
+
+ float fy = (v[0] + 16.0f) / 116.0f;
+ float fx = fy + (v[1] * 0.002f);
+ float fz = fy - (v[2] * 0.005f);
+ float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C);
+ float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C);
+ float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C);
+
+ v[0] = X * ILLUMINANT_D50_XYZ[0];
+ v[1] = Y * ILLUMINANT_D50_XYZ[1];
+ v[2] = Z * ILLUMINANT_D50_XYZ[2];
+
+ return v;
+}
+
+Lab fromXyz(const float3& v) {
+ float X = v[0] / ILLUMINANT_D50_XYZ[0];
+ float Y = v[1] / ILLUMINANT_D50_XYZ[1];
+ float Z = v[2] / ILLUMINANT_D50_XYZ[2];
+
+ float fx = X > A ? pow(X, 1.0f / 3.0f) : B * X + C;
+ float fy = Y > A ? pow(Y, 1.0f / 3.0f) : B * Y + C;
+ float fz = Z > A ? pow(Z, 1.0f / 3.0f) : B * Z + C;
+
+ float L = 116.0f * fy - 16.0f;
+ float a = 500.0f * (fx - fy);
+ float b = 200.0f * (fy - fz);
+
+ return Lab {
+ clamp(L, 0.0f, 100.0f),
+ clamp(a, -128.0f, 128.0f),
+ clamp(b, -128.0f, 128.0f)
+ };
+}
+
+};
+
+Lab sRGBToLab(SkColor color) {
+ auto colorSpace = ColorSpace::sRGB();
+ float3 rgb;
+ rgb.r = SkColorGetR(color) / 255.0f;
+ rgb.g = SkColorGetG(color) / 255.0f;
+ rgb.b = SkColorGetB(color) / 255.0f;
+ float3 xyz = colorSpace.rgbToXYZ(rgb);
+ float3 srcXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
+ xyz = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * xyz;
+ return LabColorSpace::fromXyz(xyz);
+}
+
+SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) {
+ auto colorSpace = ColorSpace::sRGB();
+ float3 xyz = LabColorSpace::toXyz(lab);
+ float3 dstXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1});
+ xyz = adaptation(BRADFORD, ILLUMINANT_D50_XYZ, dstXYZ) * xyz;
+ float3 rgb = colorSpace.xyzToRGB(xyz);
+ return SkColorSetARGB(alpha,
+ static_cast<uint8_t>(rgb.r * 255),
+ static_cast<uint8_t>(rgb.g * 255),
+ static_cast<uint8_t>(rgb.b * 255));
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 2bec1f5f7852..4daccda78e23 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -18,9 +18,11 @@
#include <math.h>
#include <system/graphics.h>
+#include <ui/PixelFormat.h>
#include <SkColor.h>
#include <SkColorSpace.h>
+#include <SkImageInfo.h>
namespace android {
namespace uirenderer {
@@ -113,7 +115,19 @@ static constexpr float EOCF(float srgb) {
// returns true for sRGB, gamma 2.2 and Display P3 for instance
bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
+android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType);
+
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
+
+struct Lab {
+ float L;
+ float a;
+ float b;
+};
+
+Lab sRGBToLab(SkColor color);
+SkColor LabToSRGB(const Lab& lab, SkAlpha alpha);
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
index bf27300029c6..fcd036c451e9 100644
--- a/libs/hwui/utils/GLUtils.cpp
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -59,5 +59,22 @@ bool GLUtils::dumpGLErrors() {
#endif
}
+const char* GLUtils::getGLFramebufferError() {
+ switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
+ case GL_FRAMEBUFFER_COMPLETE:
+ return "GL_FRAMEBUFFER_COMPLETE";
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
+ case GL_FRAMEBUFFER_UNSUPPORTED:
+ return "GL_FRAMEBUFFER_UNSUPPORTED";
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
+ default:
+ return "Unknown error";
+ }
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h
index debfb5d06944..ca8810bde070 100644
--- a/libs/hwui/utils/GLUtils.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -20,6 +20,12 @@
#include <log/log.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
namespace android {
namespace uirenderer {
@@ -43,8 +49,53 @@ public:
*/
static bool dumpGLErrors();
+ static const char* getGLFramebufferError();
}; // class GLUtils
+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); }
+
+ GLuint mTexture = 0;
+};
+
+class AutoGLFramebuffer {
+public:
+ AutoGLFramebuffer() {
+ glGenFramebuffers(1, &mFb);
+ glBindFramebuffer(GL_FRAMEBUFFER, mFb);
+ }
+
+ ~AutoGLFramebuffer() { glDeleteFramebuffers(1, &mFb); }
+
+ GLuint mFb;
+};
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index 5a59de8b922a..3e5021cd45d4 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -29,6 +29,7 @@
#include <stdlib.h>
#include <utils/Log.h>
+#include <utils/Macros.h>
// The ideal size of a page allocation (these need to be multiples of 8)
#define INITIAL_PAGE_SIZE ((size_t)512) // 512b
@@ -41,15 +42,6 @@
// Must be smaller than INITIAL_PAGE_SIZE
#define MAX_WASTE_RATIO (0.5f)
-#if ALIGN_DOUBLE
-#define ALIGN_SZ (sizeof(double))
-#else
-#define ALIGN_SZ (sizeof(int))
-#endif
-
-#define ALIGN(x) (((x) + ALIGN_SZ - 1) & ~(ALIGN_SZ - 1))
-#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)(p))))
-
#if LOG_NDEBUG
#define ADD_ALLOCATION()
#define RM_ALLOCATION()
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index d758f29d6dcb..eee6785afd53 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -34,4 +34,13 @@ private: \
#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#if ALIGN_DOUBLE
+#define ALIGN_SZ (sizeof(double))
+#else
+#define ALIGN_SZ (sizeof(int))
+#endif
+
+#define ALIGN(x) (((x) + ALIGN_SZ - 1) & ~(ALIGN_SZ - 1))
+#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)(p))))
+
#endif /* MACROS_H */
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index 233adae1df24..ebf2343c5518 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -16,10 +16,12 @@
#ifndef PAINT_UTILS_H
#define PAINT_UTILS_H
+#include <GLES2/gl2.h>
#include <utils/Blur.h>
#include <SkColorFilter.h>
#include <SkDrawLooper.h>
+#include <SkPaint.h>
#include <SkShader.h>
namespace android {
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
deleted file mode 100644
index 700d3b3cf000..000000000000
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "TestWindowContext.h"
-
-#include "AnimationContext.h"
-#include "IContextFactory.h"
-#include "RecordingCanvas.h"
-#include "RenderNode.h"
-#include "SkTypes.h"
-#include "gui/BufferQueue.h"
-#include "gui/CpuConsumer.h"
-#include "gui/IGraphicBufferConsumer.h"
-#include "gui/IGraphicBufferProducer.h"
-#include "gui/Surface.h"
-#include "renderthread/RenderProxy.h"
-
-#include <cutils/memory.h>
-
-namespace {
-
-/**
- * Helper class for setting up android::uirenderer::renderthread::RenderProxy.
- */
-class ContextFactory : public android::uirenderer::IContextFactory {
-public:
- android::uirenderer::AnimationContext* createAnimationContext(
- android::uirenderer::renderthread::TimeLord& clock) override {
- return new android::uirenderer::AnimationContext(clock);
- }
-};
-
-} // anonymous namespace
-
-namespace android {
-namespace uirenderer {
-
-/**
- Android strong pointers (android::sp) can't hold forward-declared classes,
- so we have to use pointer-to-implementation here if we want to hide the
- details from our non-framework users.
-*/
-
-class TestWindowContext::TestWindowData {
-public:
- explicit TestWindowData(SkISize size) : mSize(size) {
- android::BufferQueue::createBufferQueue(&mProducer, &mConsumer);
- mCpuConsumer = new android::CpuConsumer(mConsumer, 1);
- mCpuConsumer->setName(android::String8("TestWindowContext"));
- mCpuConsumer->setDefaultBufferSize(mSize.width(), mSize.height());
- mAndroidSurface = new android::Surface(mProducer);
- native_window_set_buffers_dimensions(mAndroidSurface.get(), mSize.width(), mSize.height());
- native_window_set_buffers_format(mAndroidSurface.get(), android::PIXEL_FORMAT_RGBA_8888);
- native_window_set_usage(mAndroidSurface.get(), GRALLOC_USAGE_SW_READ_OFTEN |
- GRALLOC_USAGE_SW_WRITE_NEVER |
- GRALLOC_USAGE_HW_RENDER);
- mRootNode.reset(new android::uirenderer::RenderNode());
- mRootNode->incStrong(nullptr);
- mRootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, mSize.width(),
- mSize.height());
- mRootNode->mutateStagingProperties().setClipToBounds(false);
- mRootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::GENERIC);
- ContextFactory factory;
- mProxy.reset(new android::uirenderer::renderthread::RenderProxy(false, mRootNode.get(),
- &factory));
- mProxy->loadSystemProperties();
- mProxy->initialize(mAndroidSurface.get());
- float lightX = mSize.width() / 2.0f;
- android::uirenderer::Vector3 lightVector{lightX, -200.0f, 800.0f};
- mProxy->setup(800.0f, 255 * 0.075f, 255 * 0.15f);
- mProxy->setLightCenter(lightVector);
- mCanvas.reset(new android::uirenderer::RecordingCanvas(mSize.width(), mSize.height()));
- }
-
- SkCanvas* prepareToDraw() {
- // mCanvas->reset(mSize.width(), mSize.height());
- mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), SkClipOp::kReplace_deprecated);
- return mCanvas->asSkCanvas();
- }
-
- void finishDrawing() {
- mRootNode->setStagingDisplayList(mCanvas->finishRecording());
- mProxy->syncAndDrawFrame();
- // Surprisingly, calling mProxy->fence() here appears to make no difference to
- // the timings we record.
- }
-
- void fence() { mProxy->fence(); }
-
- bool capturePixels(SkBitmap* bmp) {
- sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
- SkImageInfo destinationConfig =
- SkImageInfo::Make(mSize.width(), mSize.height(), kRGBA_8888_SkColorType,
- kPremul_SkAlphaType, colorSpace);
- bmp->allocPixels(destinationConfig);
- android_memset32((uint32_t*)bmp->getPixels(), SK_ColorRED,
- mSize.width() * mSize.height() * 4);
-
- android::CpuConsumer::LockedBuffer nativeBuffer;
- android::status_t retval = mCpuConsumer->lockNextBuffer(&nativeBuffer);
- if (retval == android::BAD_VALUE) {
- SkDebugf("write_canvas_png() got no buffer; returning transparent");
- // No buffer ready to read - commonly triggered by dm sending us
- // a no-op source, or calling code that doesn't do anything on this
- // backend.
- bmp->eraseColor(SK_ColorTRANSPARENT);
- return false;
- } else if (retval) {
- SkDebugf("Failed to lock buffer to read pixels: %d.", retval);
- return false;
- }
-
- // Move the pixels into the destination SkBitmap
-
- LOG_ALWAYS_FATAL_IF(nativeBuffer.format != android::PIXEL_FORMAT_RGBA_8888,
- "Native buffer not RGBA!");
- SkImageInfo nativeConfig = SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height,
- kRGBA_8888_SkColorType, kPremul_SkAlphaType);
-
- // Android stride is in pixels, Skia stride is in bytes
- SkBitmap nativeWrapper;
- bool success = nativeWrapper.installPixels(nativeConfig, nativeBuffer.data,
- nativeBuffer.stride * 4);
- if (!success) {
- SkDebugf("Failed to wrap HWUI buffer in a SkBitmap");
- return false;
- }
-
- LOG_ALWAYS_FATAL_IF(bmp->colorType() != kRGBA_8888_SkColorType,
- "Destination buffer not RGBA!");
- success = nativeWrapper.readPixels(destinationConfig, bmp->getPixels(), bmp->rowBytes(), 0,
- 0);
- if (!success) {
- SkDebugf("Failed to extract pixels from HWUI buffer");
- return false;
- }
-
- mCpuConsumer->unlockBuffer(nativeBuffer);
-
- return true;
- }
-
-private:
- std::unique_ptr<android::uirenderer::RenderNode> mRootNode;
- std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy;
- std::unique_ptr<android::uirenderer::RecordingCanvas> mCanvas;
- android::sp<android::IGraphicBufferProducer> mProducer;
- android::sp<android::IGraphicBufferConsumer> mConsumer;
- android::sp<android::CpuConsumer> mCpuConsumer;
- android::sp<android::Surface> mAndroidSurface;
- SkISize mSize;
-};
-
-TestWindowContext::TestWindowContext() : mData(nullptr) {}
-
-TestWindowContext::~TestWindowContext() {
- delete mData;
-}
-
-void TestWindowContext::initialize(int width, int height) {
- mData = new TestWindowData(SkISize::Make(width, height));
-}
-
-SkCanvas* TestWindowContext::prepareToDraw() {
- return mData ? mData->prepareToDraw() : nullptr;
-}
-
-void TestWindowContext::finishDrawing() {
- if (mData) {
- mData->finishDrawing();
- }
-}
-
-void TestWindowContext::fence() {
- if (mData) {
- mData->fence();
- }
-}
-
-bool TestWindowContext::capturePixels(SkBitmap* bmp) {
- return mData ? mData->capturePixels(bmp) : false;
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/utils/TestWindowContext.h b/libs/hwui/utils/TestWindowContext.h
deleted file mode 100644
index 17ad1e3fef55..000000000000
--- a/libs/hwui/utils/TestWindowContext.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef TESTWINDOWCONTEXT_H_
-#define TESTWINDOWCONTEXT_H_
-
-#include <cutils/compiler.h>
-
-class SkBitmap;
-class SkCanvas;
-
-namespace android {
-
-namespace uirenderer {
-
-/**
- Wraps all libui/libgui classes and types that external tests depend on,
- exposing only primitive Skia types.
-*/
-
-class ANDROID_API TestWindowContext {
-public:
- TestWindowContext();
- ~TestWindowContext();
-
- /// We need to know the size of the window.
- void initialize(int width, int height);
-
- /// Returns a canvas to draw into; NULL if not yet initialize()d.
- SkCanvas* prepareToDraw();
-
- /// Flushes all drawing commands to HWUI; no-op if not yet initialize()d.
- void finishDrawing();
-
- /// Blocks until HWUI has processed all pending drawing commands;
- /// no-op if not yet initialize()d.
- void fence();
-
- /// Returns false if not yet initialize()d.
- bool capturePixels(SkBitmap* bmp);
-
-private:
- /// Hidden implementation.
- class TestWindowData;
-
- TestWindowData* mData;
-};
-
-} // namespace uirenderer
-} // namespace android
-
-#endif // TESTWINDOWCONTEXT_H_
diff --git a/libs/hwui/utils/TypeLogic.h b/libs/hwui/utils/TypeLogic.h
new file mode 100644
index 000000000000..dbdad33d8335
--- /dev/null
+++ b/libs/hwui/utils/TypeLogic.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <type_traits>
+
+namespace android::uirenderer {
+
+template <typename D, typename S> struct copy_const {
+ using type = std::conditional_t<std::is_const<S>::value, std::add_const_t<D>, D>;
+};
+template <typename D, typename S> using copy_const_t = typename copy_const<D, S>::type;
+
+template <typename D, typename S> struct copy_volatile {
+ using type = std::conditional_t<std::is_volatile<S>::value, std::add_volatile_t<D>, D>;
+};
+template <typename D, typename S> using copy_volatile_t = typename copy_volatile<D, S>::type;
+
+template <typename D, typename S> struct copy_cv {
+ using type = copy_volatile_t<copy_const_t<D, S>, S>;
+};
+
+template <typename D, typename S> using same_cv = copy_cv<std::remove_cv_t<D>, S>;
+template <typename D, typename S> using same_cv_t = typename same_cv<D, S>::type;
+
+}; // namespace android::uirenderer \ No newline at end of file
diff --git a/libs/incident/proto/android/section.proto b/libs/incident/proto/android/section.proto
index 45f3c91850e7..5afe22a3095f 100644
--- a/libs/incident/proto/android/section.proto
+++ b/libs/incident/proto/android/section.proto
@@ -51,10 +51,9 @@ enum SectionType {
message SectionFlags {
optional SectionType type = 1 [default = SECTION_NONE];
optional string args = 2;
- optional bool device_specific = 3 [default = false];
// If true, then the section will only be generated for userdebug and eng
// builds.
- optional bool userdebug_and_eng_only = 4 [default = false];
+ optional bool userdebug_and_eng_only = 3 [default = false];
}
extend google.protobuf.FieldOptions {
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 67682a06cb6a..c5a6ec590d30 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -36,9 +36,8 @@ cc_library_shared {
cflags: [
"-Wall",
+ "-Wextra",
"-Werror",
- "-Wunused",
- "-Wunreachable-code",
],
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 4794f3da824c..eb3469e0b3f0 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -28,7 +28,6 @@
#include <utils/BitSet.h>
#include <utils/RefBase.h>
#include <utils/Looper.h>
-#include <utils/String8.h>
#include <gui/DisplayEventReceiver.h>
namespace android {
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 173cd507d943..eb2bc98ec9e9 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -23,14 +23,10 @@
#include <utils/String8.h>
#include <gui/Surface.h>
-// ToDo: Fix code to be warning free
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkPaint.h>
-#pragma GCC diagnostic pop
#include <android/native_window.h>
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
index 7ad83ca79695..44bc97adb946 100644
--- a/libs/protoutil/Android.bp
+++ b/libs/protoutil/Android.bp
@@ -12,9 +12,8 @@
// 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 {
- name: "libprotoutil",
+cc_defaults {
+ name: "libprotoutil_defaults",
cflags: [
"-Wall",
@@ -30,27 +29,39 @@ cc_library {
"src/protobuf.cpp",
],
- export_include_dirs: ["include"],
-
shared_libs: [
+ "libbase",
"libcutils",
"liblog",
],
}
+cc_library {
+ name: "libprotoutil",
+
+ defaults: ["libprotoutil_defaults"],
+
+ export_include_dirs: ["include"],
+}
+
cc_test {
name: "libprotoutil_test",
- srcs: [
- "tests/EncodedBuffer_test.cpp",
- ],
+ defaults: ["libprotoutil_defaults"],
+
+ local_include_dirs: ["include"],
+
+ srcs: ["tests/*"],
shared_libs: [
- "libcutils",
- "libprotoutil",
+ "libprotobuf-cpp-full",
],
static_libs: [
"libgmock",
],
+
+ proto: {
+ type: "full",
+ }
}
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index e17f48b98037..ad765592eb55 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -17,10 +17,11 @@
#ifndef ANDROID_UTIL_PROTOOUTPUT_STREAM_H
#define ANDROID_UTIL_PROTOOUTPUT_STREAM_H
-#include <android/util/EncodedBuffer.h>
-
+#include <cstdint>
#include <string>
+#include <android/util/EncodedBuffer.h>
+
namespace android {
namespace util {
@@ -104,7 +105,7 @@ public:
/**
* Starts a sub-message write session.
* Returns a token of this write session.
- * Must call end(token) when finish write this sub-message.
+ * Must call end(token) exactly once when finish write this sub-message.
*/
uint64_t start(uint64_t fieldId);
void end(uint64_t token);
@@ -143,16 +144,16 @@ private:
inline void writeDoubleImpl(uint32_t id, double val);
inline void writeFloatImpl(uint32_t id, float val);
- inline void writeInt64Impl(uint32_t id, long long val);
- inline void writeInt32Impl(uint32_t id, int val);
+ inline void writeInt64Impl(uint32_t id, int64_t val);
+ inline void writeInt32Impl(uint32_t id, int32_t val);
inline void writeUint64Impl(uint32_t id, uint64_t val);
inline void writeUint32Impl(uint32_t id, uint32_t val);
inline void writeFixed64Impl(uint32_t id, uint64_t val);
inline void writeFixed32Impl(uint32_t id, uint32_t val);
- inline void writeSFixed64Impl(uint32_t id, long long val);
- inline void writeSFixed32Impl(uint32_t id, int val);
- inline void writeZigzagInt64Impl(uint32_t id, long long val);
- inline void writeZigzagInt32Impl(uint32_t id, int val);
+ inline void writeSFixed64Impl(uint32_t id, int64_t val);
+ inline void writeSFixed32Impl(uint32_t id, int32_t val);
+ inline void writeZigzagInt64Impl(uint32_t id, int64_t val);
+ inline void writeZigzagInt32Impl(uint32_t id, int32_t val);
inline void writeEnumImpl(uint32_t id, int val);
inline void writeBoolImpl(uint32_t id, bool val);
inline void writeUtf8StringImpl(uint32_t id, const char* val, size_t size);
@@ -161,6 +162,9 @@ private:
bool compact();
size_t editEncodedSize(size_t rawSize);
bool compactSize(size_t rawSize);
+
+ template<typename T>
+ bool internalWrite(uint64_t fieldId, T val, const char* typeName);
};
}
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index 22b9709ef066..ff3fad6055e1 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -15,8 +15,10 @@
*/
#define LOG_TAG "libprotoutil"
-#include <inttypes.h>
+#include <cinttypes>
+#include <type_traits>
+#include <android-base/file.h>
#include <android/util/protobuf.h>
#include <android/util/ProtoOutputStream.h>
#include <cutils/log.h>
@@ -50,112 +52,73 @@ ProtoOutputStream::clear()
mExpectedObjectToken = UINT64_C(-1);
}
+template<typename T>
bool
-ProtoOutputStream::write(uint64_t fieldId, double val)
+ProtoOutputStream::internalWrite(uint64_t fieldId, T val, const char* typeName)
{
if (mCompact) return false;
const uint32_t id = (uint32_t)fieldId;
switch (fieldId & FIELD_TYPE_MASK) {
case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_INT64: writeInt64Impl(id, (int64_t)val); break;
case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case FIELD_TYPE_INT32: writeInt32Impl(id, (int32_t)val); break;
case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int32_t)val); break;
+ case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (int64_t)val); break;
+ case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int32_t)val); break;
+ case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (int64_t)val); break;
+ case FIELD_TYPE_ENUM:
+ if (std::is_integral<T>::value) {
+ writeEnumImpl(id, (int)val);
+ } else {
+ goto unsupported;
+ }
+ break;
+ case FIELD_TYPE_BOOL:
+ if (std::is_integral<T>::value) {
+ writeBoolImpl(id, val != 0);
+ } else {
+ goto unsupported;
+ }
+ break;
default:
- ALOGW("Field type %d is not supported when writing double val.",
- (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
- return false;
+ goto unsupported;
}
return true;
+
+unsupported:
+ ALOGW("Field type %" PRIu64 " is not supported when writing %s val.",
+ (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT, typeName);
+ return false;
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, double val)
+{
+ return internalWrite(fieldId, val, "double");
}
+
bool
ProtoOutputStream::write(uint64_t fieldId, float val)
{
- if (mCompact) return false;
- const uint32_t id = (uint32_t)fieldId;
- switch (fieldId & FIELD_TYPE_MASK) {
- case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
- default:
- ALOGW("Field type %d is not supported when writing float val.",
- (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
- return false;
- }
- return true;
+ return internalWrite(fieldId, val, "float");
}
bool
ProtoOutputStream::write(uint64_t fieldId, int val)
{
- if (mCompact) return false;
- const uint32_t id = (uint32_t)fieldId;
- switch (fieldId & FIELD_TYPE_MASK) {
- case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
- case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
- case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
- default:
- ALOGW("Field type %d is not supported when writing int val.",
- (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
- return false;
- }
- return true;
+ return internalWrite(fieldId, val, "int");
}
bool
ProtoOutputStream::write(uint64_t fieldId, long long val)
{
- if (mCompact) return false;
- const uint32_t id = (uint32_t)fieldId;
- switch (fieldId & FIELD_TYPE_MASK) {
- case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
- case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
- case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
- default:
- ALOGW("Field type %d is not supported when writing long long val.",
- (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
- return false;
- }
- return true;
+ return internalWrite(fieldId, val, "long long");
}
bool
@@ -168,8 +131,8 @@ ProtoOutputStream::write(uint64_t fieldId, bool val)
writeBoolImpl(id, val);
return true;
default:
- ALOGW("Field type %d is not supported when writing bool val.",
- (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+ ALOGW("Field type %" PRIu64 " is not supported when writing bool val.",
+ (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT);
return false;
}
}
@@ -184,8 +147,8 @@ ProtoOutputStream::write(uint64_t fieldId, std::string val)
writeUtf8StringImpl(id, val.c_str(), val.size());
return true;
default:
- ALOGW("Field type %d is not supported when writing string val.",
- (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+ ALOGW("Field type %" PRIu64 " is not supported when writing string val.",
+ (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT);
return false;
}
}
@@ -205,8 +168,8 @@ ProtoOutputStream::write(uint64_t fieldId, const char* val, size_t size)
writeMessageBytesImpl(id, val, size);
return true;
default:
- ALOGW("Field type %d is not supported when writing char[] val.",
- (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+ ALOGW("Field type %" PRIu64 " is not supported when writing char[] val.",
+ (fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT);
return false;
}
}
@@ -281,12 +244,14 @@ ProtoOutputStream::end(uint64_t token)
{
if (token != mExpectedObjectToken) {
ALOGE("Unexpected token: 0x%" PRIx64 ", should be 0x%" PRIx64, token, mExpectedObjectToken);
+ mDepth = UINT32_C(-1); // make depth invalid
return;
}
uint32_t depth = getDepthFromToken(token);
if (depth != (mDepth & 0x01ff)) {
ALOGE("Unexpected depth: %" PRIu32 ", should be %" PRIu32, depth, mDepth);
+ mDepth = UINT32_C(-1); // make depth invalid
return;
}
mDepth--;
@@ -319,7 +284,7 @@ bool
ProtoOutputStream::compact() {
if (mCompact) return true;
if (mDepth != 0) {
- ALOGE("Can't compact when depth(%" PRIu32 ") is not zero. Missing calls to end.", mDepth);
+ ALOGE("Can't compact when depth(%" PRIu32 ") is not zero. Missing or extra calls to end.", mDepth);
return false;
}
// record the size of the original buffer.
@@ -462,24 +427,11 @@ ProtoOutputStream::size()
{
if (!compact()) {
ALOGE("compact failed, the ProtoOutputStream data is corrupted!");
- // TODO: handle this error
+ return 0;
}
return mBuffer.size();
}
-static bool write_all(int fd, uint8_t const* buf, size_t size)
-{
- while (size > 0) {
- ssize_t amt = ::write(fd, buf, size);
- if (amt < 0) {
- return false;
- }
- size -= amt;
- buf += amt;
- }
- return true;
-}
-
bool
ProtoOutputStream::flush(int fd)
{
@@ -488,7 +440,7 @@ ProtoOutputStream::flush(int fd)
EncodedBuffer::iterator it = mBuffer.begin();
while (it.readBuffer() != NULL) {
- if (!write_all(fd, it.readBuffer(), it.currentToRead())) return false;
+ if (!android::base::WriteFully(fd, it.readBuffer(), it.currentToRead())) return false;
it.rp()->move(it.currentToRead());
}
return true;
@@ -499,7 +451,7 @@ ProtoOutputStream::data()
{
if (!compact()) {
ALOGE("compact failed, the ProtoOutputStream data is corrupted!");
- // TODO: handle this error
+ mBuffer.clear();
}
return mBuffer.begin();
}
@@ -554,17 +506,17 @@ ProtoOutputStream::writeFloatImpl(uint32_t id, float val)
}
inline void
-ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
+ProtoOutputStream::writeInt64Impl(uint32_t id, int64_t val)
{
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
- mBuffer.writeRawVarint64((uint64_t)val);
+ mBuffer.writeRawVarint64(val);
}
inline void
-ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
+ProtoOutputStream::writeInt32Impl(uint32_t id, int32_t val)
{
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
- mBuffer.writeRawVarint32((uint32_t)val);
+ mBuffer.writeRawVarint32(val);
}
inline void
@@ -596,28 +548,28 @@ ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
}
inline void
-ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
+ProtoOutputStream::writeSFixed64Impl(uint32_t id, int64_t val)
{
mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
- mBuffer.writeRawFixed64((uint64_t)val);
+ mBuffer.writeRawFixed64(val);
}
inline void
-ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
+ProtoOutputStream::writeSFixed32Impl(uint32_t id, int32_t val)
{
mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
- mBuffer.writeRawFixed32((uint32_t)val);
+ mBuffer.writeRawFixed32(val);
}
inline void
-ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
+ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, int64_t val)
{
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint64((val << 1) ^ (val >> 63));
}
inline void
-ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val)
+ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int32_t val)
{
mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
mBuffer.writeRawVarint32((val << 1) ^ (val >> 31));
diff --git a/libs/protoutil/tests/EncodedBuffer_test.cpp b/libs/protoutil/tests/EncodedBuffer_test.cpp
index 615ab4ab29ed..964fc8ec9ee0 100644
--- a/libs/protoutil/tests/EncodedBuffer_test.cpp
+++ b/libs/protoutil/tests/EncodedBuffer_test.cpp
@@ -17,9 +17,138 @@
using namespace android::util;
+constexpr size_t TEST_CHUNK_SIZE = 16UL;
+constexpr size_t TEST_CHUNK_HALF_SIZE = TEST_CHUNK_SIZE / 2;
+constexpr size_t TEST_CHUNK_3X_SIZE = 3 * TEST_CHUNK_SIZE;
+
+static void expectPointer(EncodedBuffer::Pointer* p, size_t pos) {
+ EXPECT_EQ(p->pos(), pos);
+ EXPECT_EQ(p->index(), pos / TEST_CHUNK_SIZE);
+ EXPECT_EQ(p->offset(), pos % TEST_CHUNK_SIZE);
+}
+
+TEST(EncodedBufferTest, WriteSimple) {
+ EncodedBuffer buffer(TEST_CHUNK_SIZE);
+ EXPECT_EQ(buffer.size(), 0UL);
+ expectPointer(buffer.wp(), 0);
+ EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_SIZE);
+ for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) {
+ buffer.writeRawByte(50 + i);
+ }
+ EXPECT_EQ(buffer.size(), TEST_CHUNK_HALF_SIZE);
+ expectPointer(buffer.wp(), TEST_CHUNK_HALF_SIZE);
+ EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE);
+ for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) {
+ buffer.writeRawByte(80 + i);
+ }
+ EXPECT_EQ(buffer.size(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
+ expectPointer(buffer.wp(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
+ EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE);
+
+ // verifies the buffer's data
+ expectPointer(buffer.ep(), 0);
+ for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) {
+ EXPECT_EQ(buffer.readRawByte(), 50 + i);
+ }
+ for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) {
+ EXPECT_EQ(buffer.readRawByte(), 80 + i);
+ }
+
+ // clears the buffer
+ buffer.clear();
+ EXPECT_EQ(buffer.size(), 0UL);
+ expectPointer(buffer.wp(), 0);
+}
+
+TEST(EncodedBufferTest, WriteVarint) {
+ EncodedBuffer buffer(TEST_CHUNK_SIZE);
+ size_t expected_buffer_size = 0;
+ EXPECT_EQ(buffer.writeRawVarint32(13), 1);
+ expected_buffer_size += 1;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+ EXPECT_EQ(buffer.writeRawVarint32(UINT32_C(-1)), 5);
+ expected_buffer_size += 5;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+ EXPECT_EQ(buffer.writeRawVarint64(200), 2);
+ expected_buffer_size += 2;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+ EXPECT_EQ(buffer.writeRawVarint64(UINT64_C(-1)), 10);
+ expected_buffer_size += 10;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+ buffer.writeRawFixed32(UINT32_C(-1));
+ expected_buffer_size += 4;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+ buffer.writeRawFixed64(UINT64_C(-1));
+ expected_buffer_size += 8;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+ EXPECT_EQ(buffer.writeHeader(32, 2), 2);
+ expected_buffer_size += 2;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+ // verify data are correctly written to the buffer.
+ expectPointer(buffer.ep(), 0);
+ EXPECT_EQ(buffer.readRawVarint(), UINT32_C(13));
+ EXPECT_EQ(buffer.readRawVarint(), UINT32_C(-1));
+ EXPECT_EQ(buffer.readRawVarint(), UINT64_C(200));
+ EXPECT_EQ(buffer.readRawVarint(), UINT64_C(-1));
+ EXPECT_EQ(buffer.readRawFixed32(), UINT32_C(-1));
+ EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(-1));
+ EXPECT_EQ(buffer.readRawVarint(), UINT64_C((32 << 3) + 2));
+ expectPointer(buffer.ep(), expected_buffer_size);
+}
+
+TEST(EncodedBufferTest, Edit) {
+ EncodedBuffer buffer(TEST_CHUNK_SIZE);
+ buffer.writeRawFixed64(0xdeadbeefdeadbeef);
+ EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0xdeadbeefdeadbeef));
+
+ buffer.editRawFixed32(4, 0x12345678);
+ // fixed 64 is little endian order.
+ buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0
+ EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678deadbeef));
+
+ buffer.wp()->rewind();
+ expectPointer(buffer.wp(), 0);
+ buffer.copy(4, 3);
+ buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0
+ EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678de345678));
+}
+
+TEST(EncodedBufferTest, ReadSimple) {
+ EncodedBuffer buffer(TEST_CHUNK_SIZE);
+ for (size_t i = 0; i < TEST_CHUNK_3X_SIZE; i++) {
+ buffer.writeRawByte(i);
+ }
+ auto iter = buffer.begin();
+ EXPECT_EQ(iter.size(), TEST_CHUNK_3X_SIZE);
+ EXPECT_EQ(iter.bytesRead(), 0);
+
+ expectPointer(iter.rp(), 0);
+ while (iter.readBuffer() != NULL) {
+ iter.rp()->move(iter.currentToRead());
+ }
+ EXPECT_EQ(iter.bytesRead(), TEST_CHUNK_3X_SIZE);
+ expectPointer(iter.rp(), TEST_CHUNK_3X_SIZE);
+
+ iter.rp()->rewind();
+ expectPointer(iter.rp(), 0);
+ uint8_t val = 0;
+ while (iter.hasNext()) {
+ EXPECT_EQ(iter.next(), val);
+ val++;
+ }
+ EXPECT_EQ(iter.bytesRead(), TEST_CHUNK_3X_SIZE);
+ expectPointer(iter.rp(), TEST_CHUNK_3X_SIZE);
+}
+
TEST(EncodedBufferTest, ReadVarint) {
EncodedBuffer buffer;
uint64_t val = UINT64_C(1522865904593);
- buffer.writeRawVarint64(val);
- EXPECT_EQ(val, buffer.begin().readRawVarint());
+ size_t len = buffer.writeRawVarint64(val);
+ auto iter = buffer.begin();
+ EXPECT_EQ(iter.size(), len);
+ EXPECT_EQ(iter.readRawVarint(), val);
}
diff --git a/libs/protoutil/tests/ProtoOutputStream_test.cpp b/libs/protoutil/tests/ProtoOutputStream_test.cpp
new file mode 100644
index 000000000000..27ee13d4dfe1
--- /dev/null
+++ b/libs/protoutil/tests/ProtoOutputStream_test.cpp
@@ -0,0 +1,228 @@
+// Copyright (C) 2018 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 <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <android/util/protobuf.h>
+#include <android/util/ProtoOutputStream.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "frameworks/base/libs/protoutil/tests/test.pb.h"
+
+using namespace android::base;
+using namespace android::util;
+using ::testing::StrEq;
+
+static std::string flushToString(ProtoOutputStream* proto) {
+ TemporaryFile tf;
+ std::string content;
+
+ EXPECT_NE(tf.fd, -1);
+ EXPECT_TRUE(proto->flush(tf.fd));
+ EXPECT_TRUE(ReadFileToString(tf.path, &content));
+ return content;
+}
+
+static std::string iterateToString(ProtoOutputStream* proto) {
+ std::string content;
+ content.reserve(proto->size());
+ auto iter = proto->data();
+ while (iter.hasNext()) {
+ content.push_back(iter.next());
+ }
+ return content;
+}
+
+TEST(ProtoOutputStreamTest, Primitives) {
+ std::string s = "hello";
+ const char b[5] = { 'a', 'p', 'p', 'l', 'e' };
+
+ ProtoOutputStream proto;
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | PrimitiveProto::kValInt32FieldNumber, 123));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT64 | PrimitiveProto::kValInt64FieldNumber, -1LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_FLOAT | PrimitiveProto::kValFloatFieldNumber, -23.5f));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_DOUBLE | PrimitiveProto::kValDoubleFieldNumber, 324.5));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_UINT32 | PrimitiveProto::kValUint32FieldNumber, 3424));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_UINT64 | PrimitiveProto::kValUint64FieldNumber, 57LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED32 | PrimitiveProto::kValFixed32FieldNumber, -20));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED64 | PrimitiveProto::kValFixed64FieldNumber, -37LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, true));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | PrimitiveProto::kValStringFieldNumber, s));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | PrimitiveProto::kValBytesFieldNumber, b, 5));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED32 | PrimitiveProto::kValSfixed32FieldNumber, 63));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED64 | PrimitiveProto::kValSfixed64FieldNumber, -54));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SINT32 | PrimitiveProto::kValSint32FieldNumber, -533));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SINT64 | PrimitiveProto::kValSint64FieldNumber, -61224762453LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 2));
+
+ PrimitiveProto primitives;
+ ASSERT_TRUE(primitives.ParseFromString(flushToString(&proto)));
+ EXPECT_EQ(primitives.val_int32(), 123);
+ EXPECT_EQ(primitives.val_int64(), -1);
+ EXPECT_EQ(primitives.val_float(), -23.5f);
+ EXPECT_EQ(primitives.val_double(), 324.5f);
+ EXPECT_EQ(primitives.val_uint32(), 3424);
+ EXPECT_EQ(primitives.val_uint64(), 57);
+ EXPECT_EQ(primitives.val_fixed32(), -20);
+ EXPECT_EQ(primitives.val_fixed64(), -37);
+ EXPECT_EQ(primitives.val_bool(), true);
+ EXPECT_THAT(primitives.val_string(), StrEq(s.c_str()));
+ EXPECT_THAT(primitives.val_bytes(), StrEq("apple"));
+ EXPECT_EQ(primitives.val_sfixed32(), 63);
+ EXPECT_EQ(primitives.val_sfixed64(), -54);
+ EXPECT_EQ(primitives.val_sint32(), -533);
+ EXPECT_EQ(primitives.val_sint64(), -61224762453LL);
+ EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO);
+}
+
+TEST(ProtoOutputStreamTest, Complex) {
+ std::string name1 = "cat";
+ std::string name2 = "dog";
+ const char data1[6] = { 'f', 'u', 'n', 'n', 'y', '!' };
+ const char data2[4] = { 'f', 'o', 'o', 'd' };
+
+ ProtoOutputStream proto;
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 23));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 101));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, -72));
+ uint64_t token1 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 12));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name1));
+ // specify the length to test the write(id, bytes, length) function.
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data1, 5));
+ proto.end(token1);
+ uint64_t token2 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 98));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name2));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data2, 4));
+ proto.end(token2);
+
+ ComplexProto complex;
+ ASSERT_TRUE(complex.ParseFromString(iterateToString(&proto)));
+ EXPECT_EQ(complex.ints_size(), 3);
+ EXPECT_EQ(complex.ints(0), 23);
+ EXPECT_EQ(complex.ints(1), 101);
+ EXPECT_EQ(complex.ints(2), -72);
+ EXPECT_EQ(complex.logs_size(), 2);
+ ComplexProto::Log log1 = complex.logs(0);
+ EXPECT_EQ(log1.id(), 12);
+ EXPECT_THAT(log1.name(), StrEq(name1.c_str()));
+ EXPECT_THAT(log1.data(), StrEq("funny")); // should not contain '!'
+ ComplexProto::Log log2 = complex.logs(1);
+ EXPECT_EQ(log2.id(), 98);
+ EXPECT_THAT(log2.name(), StrEq(name2.c_str()));
+ EXPECT_THAT(log2.data(), StrEq("food"));
+}
+
+TEST(ProtoOutputStreamTest, Reusability) {
+ ProtoOutputStream proto;
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 32));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 15));
+ EXPECT_EQ(proto.bytesWritten(), 4);
+ EXPECT_EQ(proto.size(), 4);
+ // Can't write to proto after compact
+ EXPECT_FALSE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 94));
+
+ ComplexProto beforeClear;
+ ASSERT_TRUE(beforeClear.ParseFromString(flushToString(&proto)));
+ EXPECT_EQ(beforeClear.ints_size(), 2);
+ EXPECT_EQ(beforeClear.ints(0), 32);
+ EXPECT_EQ(beforeClear.ints(1), 15);
+
+ proto.clear();
+ EXPECT_EQ(proto.bytesWritten(), 0);
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 1076));
+
+ ComplexProto afterClear;
+ ASSERT_TRUE(afterClear.ParseFromString(flushToString(&proto)));
+ EXPECT_EQ(afterClear.ints_size(), 1);
+ EXPECT_EQ(afterClear.ints(0), 1076);
+}
+
+TEST(ProtoOutputStreamTest, AdvancedEncoding) {
+ ProtoOutputStream proto;
+ proto.writeRawVarint((ComplexProto::kIntsFieldNumber << FIELD_ID_SHIFT) + WIRE_TYPE_VARINT);
+ proto.writeRawVarint(UINT64_C(-123809234));
+ proto.writeLengthDelimitedHeader(ComplexProto::kLogsFieldNumber, 8);
+ proto.writeRawByte((ComplexProto::Log::kDataFieldNumber << FIELD_ID_SHIFT) + WIRE_TYPE_LENGTH_DELIMITED);
+ proto.writeRawByte(6);
+ proto.writeRawByte('b');
+ proto.writeRawByte('a');
+ proto.writeRawByte('n');
+ proto.writeRawByte('a');
+ proto.writeRawByte('n');
+ proto.writeRawByte('a');
+ uint64_t token = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 14);
+ proto.end(token);
+
+ ComplexProto complex;
+ ASSERT_TRUE(complex.ParseFromString(flushToString(&proto)));
+ EXPECT_EQ(complex.ints_size(), 1);
+ EXPECT_EQ(complex.ints(0), UINT64_C(-123809234));
+ EXPECT_EQ(complex.logs_size(), 2);
+ ComplexProto::Log log1 = complex.logs(0);
+ EXPECT_FALSE(log1.has_id());
+ EXPECT_FALSE(log1.has_name());
+ EXPECT_THAT(log1.data(), StrEq("banana"));
+ ComplexProto::Log log2 = complex.logs(1);
+ EXPECT_EQ(log2.id(), 14);
+ EXPECT_FALSE(log2.has_name());
+ EXPECT_FALSE(log2.has_data());
+}
+
+TEST(ProtoOutputStreamTest, InvalidTypes) {
+ ProtoOutputStream proto;
+ EXPECT_FALSE(proto.write(FIELD_TYPE_UNKNOWN | PrimitiveProto::kValInt32FieldNumber, 790));
+ EXPECT_FALSE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 234.34));
+ EXPECT_FALSE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, 18.73f));
+ EXPECT_EQ(proto.size(), 0);
+}
+
+TEST(ProtoOutputStreamTest, NoEndCalled) {
+ ProtoOutputStream proto;
+ proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 53);
+ // no proto.end called
+ EXPECT_NE(proto.bytesWritten(), 0);
+ EXPECT_EQ(proto.size(), 0);
+ EXPECT_EQ(proto.data().size(), 0);
+ EXPECT_FALSE(proto.flush(STDOUT_FILENO));
+}
+
+
+TEST(ProtoOutputStreamTest, TwoEndCalled) {
+ ProtoOutputStream proto;
+ uint64_t token = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 53);
+ proto.end(token);
+ proto.end(token);
+ EXPECT_NE(proto.bytesWritten(), 0);
+ EXPECT_EQ(proto.size(), 0);
+ EXPECT_EQ(proto.data().size(), 0);
+ EXPECT_FALSE(proto.flush(STDOUT_FILENO));
+}
+
+TEST(ProtoOutputStreamTest, NoStartCalled) {
+ ProtoOutputStream proto;
+ uint64_t wrongToken = UINT64_C(324536345);
+ // no proto.start called
+ proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 53);
+ proto.end(wrongToken);
+ EXPECT_NE(proto.bytesWritten(), 0);
+ EXPECT_EQ(proto.size(), 0);
+ EXPECT_EQ(proto.data().size(), 0);
+ EXPECT_FALSE(proto.flush(STDOUT_FILENO));
+}
diff --git a/libs/protoutil/tests/protobuf_test.cpp b/libs/protoutil/tests/protobuf_test.cpp
new file mode 100644
index 000000000000..5ca3e6477c01
--- /dev/null
+++ b/libs/protoutil/tests/protobuf_test.cpp
@@ -0,0 +1,51 @@
+// Copyright (C) 2018 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 <android/util/protobuf.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace android::util;
+
+TEST(ProtobufTest, All) {
+ EXPECT_EQ(read_wire_type(UINT32_C(17)), 1);
+ EXPECT_EQ(read_field_id(UINT32_C(17)), 2);
+ EXPECT_EQ(get_varint_size(UINT64_C(234134)), 3);
+ EXPECT_EQ(get_varint_size(UINT64_C(-1)), 10);
+
+ constexpr uint8_t UNSET_BYTE = 0xAB;
+
+ uint8_t buf[11];
+ memset(buf, UNSET_BYTE, sizeof(buf));
+ EXPECT_EQ(write_raw_varint(buf, UINT64_C(150)) - buf, 2);
+ EXPECT_EQ(buf[0], 0x96);
+ EXPECT_EQ(buf[1], 0x01);
+ EXPECT_EQ(buf[2], UNSET_BYTE);
+
+ memset(buf, UNSET_BYTE, sizeof(buf));
+ EXPECT_EQ(write_raw_varint(buf, UINT64_C(-2)) - buf, 10);
+ EXPECT_EQ(buf[0], 0xfe);
+ for (int i = 1; i < 9; i++) {
+ EXPECT_EQ(buf[i], 0xff);
+ }
+ EXPECT_EQ(buf[9], 0x01);
+ EXPECT_EQ(buf[10], UNSET_BYTE);
+
+ uint8_t header[20];
+ memset(header, UNSET_BYTE, sizeof(header));
+ EXPECT_EQ(write_length_delimited_tag_header(header, 3, 150) - header, 3);
+ EXPECT_EQ(header[0], 26);
+ EXPECT_EQ(header[1], 0x96);
+ EXPECT_EQ(header[2], 0x01);
+ EXPECT_EQ(header[3], UNSET_BYTE);
+} \ No newline at end of file
diff --git a/libs/protoutil/tests/test.proto b/libs/protoutil/tests/test.proto
new file mode 100644
index 000000000000..52c55f39f326
--- /dev/null
+++ b/libs/protoutil/tests/test.proto
@@ -0,0 +1,42 @@
+// This proto file is only used for testing purpose.
+syntax = "proto2";
+
+package android.util;
+
+message PrimitiveProto {
+
+ optional int32 val_int32 = 1;
+ optional int64 val_int64 = 2;
+ optional float val_float = 3;
+ optional double val_double = 4;
+ optional uint32 val_uint32 = 5;
+ optional uint64 val_uint64 = 6;
+ optional fixed32 val_fixed32 = 7;
+ optional fixed64 val_fixed64 = 8;
+ optional bool val_bool = 9;
+ optional string val_string = 10;
+ optional bytes val_bytes = 11;
+ optional sfixed32 val_sfixed32 = 12;
+ optional sfixed64 val_sfixed64 = 13;
+ optional sint32 val_sint32 = 14;
+ optional sint64 val_sint64 = 15;
+
+ enum Count {
+ ZERO = 0;
+ ONE = 1;
+ TWO = 2;
+ };
+ optional Count val_enum = 16;
+}
+
+message ComplexProto {
+
+ repeated int32 ints = 1;
+
+ message Log {
+ optional int32 id = 1;
+ optional string name = 2;
+ optional bytes data = 3;
+ }
+ repeated Log logs = 2;
+}
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index 255619c6226c..f60c338bf9c4 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -25,6 +25,63 @@
namespace android {
namespace os {
+/**
+ * A wrapper for a union type to contain multiple types of values.
+ *
+ */
+struct StatsLogValue {
+ // Keep in sync with FieldValue.h
+ enum STATS_LOG_VALUE_TYPE {
+ UNKNOWN = 0,
+ INT = 1,
+ LONG = 2,
+ FLOAT = 3,
+ DOUBLE = 4,
+ STRING = 5,
+ STORAGE = 6
+ };
+
+ StatsLogValue() : type(UNKNOWN) {}
+
+ StatsLogValue(int32_t v) {
+ int_value = v;
+ type = INT;
+ }
+
+ StatsLogValue(int64_t v) {
+ long_value = v;
+ type = LONG;
+ }
+
+ StatsLogValue(float v) {
+ float_value = v;
+ type = FLOAT;
+ }
+
+ StatsLogValue(double v) {
+ double_value = v;
+ type = DOUBLE;
+ }
+
+ StatsLogValue(const std::string& v) {
+ str_value = v;
+ type = STRING;
+ }
+
+ void setType(STATS_LOG_VALUE_TYPE t) { type = t; }
+
+ union {
+ int32_t int_value;
+ int64_t long_value;
+ float float_value;
+ double double_value;
+ };
+ std::string str_value;
+ std::vector<uint8_t> storage_value;
+
+ STATS_LOG_VALUE_TYPE type;
+};
+
// Represents a parcelable object. Only used to send data from Android OS to statsd.
class StatsLogEventWrapper : public android::Parcelable {
public:
@@ -36,8 +93,22 @@ class StatsLogEventWrapper : public android::Parcelable {
android::status_t readFromParcel(const android::Parcel* in);
- // These are public for ease of conversion.
- std::vector<uint8_t> bytes;
+ int getTagId() const { return mTagId; }
+
+ int64_t getElapsedRealTimeNs() const { return mElapsedRealTimeNs; }
+
+ int64_t getWallClockTimeNs() const { return mWallClockTimeNs; }
+
+ std::vector<StatsLogValue> getElements() const { return mElements; }
+
+ private:
+ int mTagId;
+
+ int64_t mElapsedRealTimeNs;
+
+ int64_t mWallClockTimeNs;
+
+ std::vector<StatsLogValue> mElements;
};
} // Namespace os
} // Namespace android
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
index c2907a66fb99..8282518f75c6 100644
--- a/libs/services/src/os/DropBoxManager.cpp
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -236,7 +236,7 @@ DropBoxManager::getNextEntry(const String16& tag, long msec, Entry* entry)
if (service == NULL) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service");
}
- return service->getNextEntry(tag, msec, entry);
+ return service->getNextEntry(tag, msec, android::String16("android"), entry);
}
}} // namespace android::os
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 8b3aa9ab4257..a1a6d9fe0e22 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -32,13 +32,73 @@ namespace os {
StatsLogEventWrapper::StatsLogEventWrapper(){};
status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const {
- out->writeByteVector(bytes);
- return ::android::NO_ERROR;
+ // Implement me if desired. We don't currently use this.
+ ALOGE(
+ "Cannot do c++ StatsLogEventWrapper.writeToParcel(); it is not "
+ "implemented.");
+ (void)out; // To prevent compile error of unused parameter 'in'
+ return UNKNOWN_ERROR;
};
status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) {
- in->readByteVector(&bytes);
- return ::android::NO_ERROR;
+ status_t res = OK;
+ if (in == NULL) {
+ ALOGE("statsd received parcel argument was NULL.");
+ return BAD_VALUE;
+ }
+ if ((res = in->readInt32(&mTagId)) != OK) {
+ ALOGE("statsd could not read tagId from parcel");
+ return res;
+ }
+ if ((res = in->readInt64(&mElapsedRealTimeNs)) != OK) {
+ ALOGE("statsd could not read elapsed real time from parcel");
+ return res;
+ }
+ if ((res = in->readInt64(&mWallClockTimeNs)) != OK) {
+ ALOGE("statsd could not read wall clock time from parcel");
+ return res;
+ }
+ int dataSize = 0;
+ if ((res = in->readInt32(&dataSize)) != OK) {
+ ALOGE("statsd could not read data size from parcel");
+ return res;
+ }
+ if (mTagId <= 0 || mElapsedRealTimeNs <= 0 || mWallClockTimeNs <= 0 ||
+ dataSize <= 0) {
+ ALOGE("statsd received invalid parcel");
+ return BAD_VALUE;
+ }
+
+ for (int i = 0; i < dataSize; i++) {
+ int type = in->readInt32();
+ switch (type) {
+ case StatsLogValue::INT:
+ mElements.push_back(StatsLogValue(in->readInt32()));
+ break;
+ case StatsLogValue::LONG:
+ mElements.push_back(StatsLogValue(in->readInt64()));
+ break;
+ case StatsLogValue::STRING:
+ mElements.push_back(
+ StatsLogValue(std::string(String8(in->readString16()).string())));
+ break;
+ case StatsLogValue::FLOAT:
+ mElements.push_back(StatsLogValue(in->readFloat()));
+ break;
+ case StatsLogValue::DOUBLE:
+ mElements.push_back(StatsLogValue(in->readDouble()));
+ break;
+ case StatsLogValue::STORAGE:
+ mElements.push_back(StatsLogValue());
+ mElements.back().setType(StatsLogValue::STORAGE);
+ in->readByteVector(&(mElements.back().storage_value));
+ break;
+ default:
+ ALOGE("unrecognized data type: %d", type);
+ return BAD_TYPE;
+ }
+ }
+ return NO_ERROR;
};
} // Namespace os
diff --git a/libs/storage/Android.bp b/libs/storage/Android.bp
index 911bd1d25393..c19933e39c96 100644
--- a/libs/storage/Android.bp
+++ b/libs/storage/Android.bp
@@ -6,6 +6,7 @@ cc_library_static {
"IMountShutdownObserver.cpp",
"IObbActionListener.cpp",
"IMountService.cpp",
+ "ObbInfo.cpp",
],
export_include_dirs: ["include"],
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index fa3d8bd0930f..fd6e6e932ebc 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -443,7 +443,7 @@ public:
}
void mountObb(const String16& rawPath, const String16& canonicalPath, const String16& key,
- const sp<IObbActionListener>& token, int32_t nonce)
+ const sp<IObbActionListener>& token, int32_t nonce, const sp<ObbInfo>& obbInfo)
{
Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
@@ -452,6 +452,7 @@ public:
data.writeString16(key);
data.writeStrongBinder(IInterface::asBinder(token));
data.writeInt32(nonce);
+ obbInfo->writeToParcel(&data);
if (remote()->transact(TRANSACTION_mountObb, data, &reply) != NO_ERROR) {
ALOGD("mountObb could not contact remote\n");
return;
diff --git a/libs/storage/ObbInfo.cpp b/libs/storage/ObbInfo.cpp
new file mode 100644
index 000000000000..1bb6b3a89b86
--- /dev/null
+++ b/libs/storage/ObbInfo.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 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 <storage/ObbInfo.h>
+
+#include <binder/Parcel.h>
+#include <utils/String16.h>
+#include <sys/types.h>
+
+namespace android {
+
+ObbInfo::ObbInfo(const String16 fileName, const String16 packageName, int32_t version,
+ int32_t flags, size_t saltSize, const uint8_t* salt) : mFileName(fileName),
+ mPackageName(packageName), mVersion(version), mFlags(flags), mSaltSize(saltSize),
+ mSalt(salt) {}
+
+ObbInfo::~ObbInfo() {}
+
+status_t ObbInfo::readFromParcel(const Parcel*) {
+ return INVALID_OPERATION;
+}
+
+status_t ObbInfo::writeToParcel(Parcel* p) const {
+ // Parcel write code must be kept in sync with
+ // frameworks/base/core/java/android/content/res/ObbInfo.java
+ p->writeString16(mFileName);
+ p->writeString16(mPackageName);
+ p->writeInt32(mVersion);
+ p->writeInt32(mFlags);
+ p->writeByteArray(mSaltSize, mSalt);
+ return OK;
+}
+
+}; // namespace android \ No newline at end of file
diff --git a/libs/storage/include/storage/IMountService.h b/libs/storage/include/storage/IMountService.h
index c3d34d84958b..2463e023efc1 100644
--- a/libs/storage/include/storage/IMountService.h
+++ b/libs/storage/include/storage/IMountService.h
@@ -20,6 +20,7 @@
#include <storage/IMountServiceListener.h>
#include <storage/IMountShutdownObserver.h>
#include <storage/IObbActionListener.h>
+#include <storage/ObbInfo.h>
#include <utils/String8.h>
@@ -64,7 +65,7 @@ public:
virtual void finishMediaUpdate() = 0;
virtual void mountObb(const String16& rawPath, const String16& canonicalPath,
const String16& key, const sp<IObbActionListener>& token,
- const int32_t nonce) = 0;
+ const int32_t nonce, const sp<ObbInfo>& obbInfo) = 0;
virtual void unmountObb(const String16& filename, const bool force,
const sp<IObbActionListener>& token, const int32_t nonce) = 0;
virtual bool isObbMounted(const String16& filename) = 0;
diff --git a/libs/storage/include/storage/ObbInfo.h b/libs/storage/include/storage/ObbInfo.h
new file mode 100644
index 000000000000..e4cc353d64c7
--- /dev/null
+++ b/libs/storage/include/storage/ObbInfo.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OBBINFO_H
+#define ANDROID_OBBINFO_H
+
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <sys/types.h>
+
+namespace android {
+
+class ObbInfo : public Parcelable, public virtual RefBase {
+
+public:
+ ObbInfo(const String16 fileName, const String16 packageName, int32_t version,
+ int32_t flags, size_t saltSize, const uint8_t* salt);
+ ~ObbInfo();
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+private:
+ const String16 mFileName;
+ const String16 mPackageName;
+ int32_t mVersion;
+ int32_t mFlags;
+ size_t mSaltSize;
+ const uint8_t* mSalt;
+};
+
+}; // namespace android
+
+#endif // ANDROID_OBBINFO_H \ No newline at end of file