summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/AssetManager.cpp8
-rw-r--r--libs/androidfw/ResourceTypes.cpp60
-rw-r--r--libs/androidfw/tests/Android.mk1
-rw-r--r--libs/androidfw/tests/AppAsLib_test.cpp53
-rw-r--r--libs/androidfw/tests/data/appaslib/R.h (renamed from libs/hwui/unit_tests/main.cpp)24
-rw-r--r--libs/hwui/AmbientShadow.cpp13
-rw-r--r--libs/hwui/Android.common.mk127
-rw-r--r--libs/hwui/Android.mk265
-rw-r--r--libs/hwui/Animator.cpp28
-rw-r--r--libs/hwui/Animator.h20
-rw-r--r--libs/hwui/AssetAtlas.cpp2
-rw-r--r--libs/hwui/BakedOpRenderer.cpp126
-rw-r--r--libs/hwui/BakedOpRenderer.h75
-rw-r--r--libs/hwui/BakedOpState.h142
-rw-r--r--libs/hwui/Caches.cpp83
-rw-r--r--libs/hwui/Caches.h37
-rw-r--r--libs/hwui/Canvas.h13
-rw-r--r--libs/hwui/CanvasState.cpp88
-rw-r--r--libs/hwui/CanvasState.h33
-rw-r--r--libs/hwui/ClipArea.cpp104
-rw-r--r--libs/hwui/ClipArea.h47
-rw-r--r--libs/hwui/DamageAccumulator.cpp18
-rw-r--r--libs/hwui/DamageAccumulator.h5
-rw-r--r--libs/hwui/Debug.h3
-rw-r--r--libs/hwui/DeferredDisplayList.cpp52
-rw-r--r--libs/hwui/DeferredDisplayList.h17
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp8
-rw-r--r--libs/hwui/DeferredLayerUpdater.h3
-rw-r--r--libs/hwui/DisplayList.cpp49
-rw-r--r--libs/hwui/DisplayList.h80
-rw-r--r--libs/hwui/DisplayListCanvas.cpp185
-rw-r--r--libs/hwui/DisplayListCanvas.h77
-rw-r--r--libs/hwui/DisplayListOp.h90
-rw-r--r--libs/hwui/Extensions.cpp79
-rw-r--r--libs/hwui/Extensions.h14
-rw-r--r--libs/hwui/FboCache.cpp2
-rw-r--r--libs/hwui/FontRenderer.cpp74
-rw-r--r--libs/hwui/FontRenderer.h20
-rw-r--r--libs/hwui/GammaFontRenderer.cpp213
-rw-r--r--libs/hwui/GammaFontRenderer.h159
-rw-r--r--libs/hwui/Glop.h4
-rw-r--r--libs/hwui/GlopBuilder.cpp54
-rw-r--r--libs/hwui/GlopBuilder.h8
-rw-r--r--libs/hwui/GradientCache.cpp2
-rw-r--r--libs/hwui/GradientCache.h2
-rw-r--r--libs/hwui/Image.cpp2
-rw-r--r--libs/hwui/Interpolator.cpp8
-rw-r--r--libs/hwui/Layer.cpp20
-rw-r--r--libs/hwui/Layer.h22
-rw-r--r--libs/hwui/LayerCache.cpp39
-rw-r--r--libs/hwui/LayerCache.h13
-rw-r--r--libs/hwui/LayerRenderer.cpp16
-rw-r--r--libs/hwui/LayerRenderer.h4
-rw-r--r--libs/hwui/Matrix.cpp6
-rw-r--r--libs/hwui/Matrix.h6
-rw-r--r--libs/hwui/OpReorderer.cpp402
-rw-r--r--libs/hwui/OpReorderer.h143
-rw-r--r--libs/hwui/OpenGLRenderer.cpp186
-rwxr-xr-xlibs/hwui/OpenGLRenderer.h129
-rw-r--r--libs/hwui/Outline.h68
-rw-r--r--libs/hwui/Patch.cpp21
-rw-r--r--libs/hwui/Patch.h6
-rw-r--r--libs/hwui/PatchCache.cpp2
-rw-r--r--libs/hwui/PathCache.cpp14
-rw-r--r--libs/hwui/PathCache.h5
-rw-r--r--libs/hwui/PathTessellator.cpp87
-rw-r--r--libs/hwui/PathTessellator.h18
-rw-r--r--libs/hwui/PixelBuffer.cpp2
-rw-r--r--libs/hwui/Program.cpp3
-rw-r--r--libs/hwui/Program.h33
-rw-r--r--libs/hwui/ProgramCache.cpp67
-rw-r--r--libs/hwui/Properties.cpp17
-rw-r--r--libs/hwui/Properties.h60
-rw-r--r--libs/hwui/RecordedOp.h118
-rw-r--r--libs/hwui/RecordingCanvas.cpp402
-rw-r--r--libs/hwui/RecordingCanvas.h300
-rw-r--r--libs/hwui/Rect.h47
-rw-r--r--libs/hwui/RenderBufferCache.cpp23
-rw-r--r--libs/hwui/RenderBufferCache.h15
-rw-r--r--libs/hwui/RenderNode.cpp368
-rw-r--r--libs/hwui/RenderNode.h83
-rw-r--r--libs/hwui/RenderProperties.cpp26
-rw-r--r--libs/hwui/RenderProperties.h46
-rw-r--r--libs/hwui/ResourceCache.cpp2
-rw-r--r--libs/hwui/RevealClip.h3
-rw-r--r--libs/hwui/ShadowTessellator.cpp8
-rw-r--r--libs/hwui/SkiaCanvas.cpp41
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp66
-rw-r--r--libs/hwui/SkiaShader.cpp11
-rw-r--r--libs/hwui/Snapshot.cpp27
-rw-r--r--libs/hwui/Snapshot.h15
-rw-r--r--libs/hwui/SpotShadow.cpp17
-rw-r--r--libs/hwui/TessellationCache.cpp10
-rw-r--r--libs/hwui/TessellationCache.h1
-rw-r--r--libs/hwui/TextDropShadowCache.cpp2
-rw-r--r--libs/hwui/Texture.cpp2
-rw-r--r--libs/hwui/TextureCache.cpp144
-rw-r--r--libs/hwui/TextureCache.h17
-rw-r--r--libs/hwui/TreeInfo.h2
-rw-r--r--libs/hwui/Vertex.h1
-rw-r--r--libs/hwui/VertexBuffer.h6
-rw-r--r--libs/hwui/font/Font.cpp13
-rw-r--r--libs/hwui/microbench/DisplayListCanvasBench.cpp190
-rw-r--r--libs/hwui/microbench/LinearAllocatorBench.cpp53
-rw-r--r--libs/hwui/microbench/MicroBench.h35
-rw-r--r--libs/hwui/microbench/OpReordererBench.cpp72
-rw-r--r--libs/hwui/protos/ProtoHelpers.h41
-rw-r--r--libs/hwui/protos/hwui.proto99
-rw-r--r--libs/hwui/renderstate/Blend.cpp41
-rw-r--r--libs/hwui/renderstate/Blend.h3
-rw-r--r--libs/hwui/renderstate/RenderState.cpp12
-rw-r--r--libs/hwui/renderstate/RenderState.h2
-rw-r--r--libs/hwui/renderstate/Stencil.cpp24
-rw-r--r--libs/hwui/renderstate/Stencil.h25
-rw-r--r--libs/hwui/renderstate/TextureState.cpp136
-rw-r--r--libs/hwui/renderstate/TextureState.h12
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp296
-rw-r--r--libs/hwui/renderthread/CanvasContext.h32
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp8
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h7
-rw-r--r--libs/hwui/renderthread/EglManager.cpp152
-rw-r--r--libs/hwui/renderthread/EglManager.h40
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp84
-rw-r--r--libs/hwui/renderthread/RenderProxy.h10
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp10
-rw-r--r--libs/hwui/renderthread/RenderThread.h3
-rw-r--r--libs/hwui/tests/Android.mk36
-rw-r--r--libs/hwui/tests/Benchmark.h54
-rw-r--r--libs/hwui/tests/TestContext.cpp29
-rw-r--r--libs/hwui/tests/TestContext.h2
-rw-r--r--libs/hwui/tests/TreeContentAnimation.cpp433
-rw-r--r--libs/hwui/tests/how_to_run.txt16
-rw-r--r--libs/hwui/tests/main.cpp422
-rw-r--r--libs/hwui/tests/nullgles.cpp2
-rw-r--r--libs/hwui/thread/TaskManager.cpp14
-rw-r--r--libs/hwui/thread/TaskManager.h7
-rw-r--r--libs/hwui/unit_tests/Android.mk35
-rw-r--r--libs/hwui/unit_tests/BakedOpStateTests.cpp97
-rw-r--r--libs/hwui/unit_tests/CanvasStateTests.cpp166
-rw-r--r--libs/hwui/unit_tests/ClipAreaTests.cpp3
-rw-r--r--libs/hwui/unit_tests/DamageAccumulatorTests.cpp72
-rw-r--r--libs/hwui/unit_tests/LinearAllocatorTests.cpp28
-rw-r--r--libs/hwui/unit_tests/OpReordererTests.cpp189
-rw-r--r--libs/hwui/unit_tests/RecordingCanvasTests.cpp113
-rw-r--r--libs/hwui/unit_tests/StringUtilsTests.cpp41
-rw-r--r--libs/hwui/unit_tests/TestUtils.h117
-rwxr-xr-xlibs/hwui/unit_tests/how_to_run.txt2
-rw-r--r--libs/hwui/utils/Blur.cpp18
-rw-r--r--libs/hwui/utils/Blur.h2
-rw-r--r--libs/hwui/utils/LinearAllocator.cpp27
-rw-r--r--libs/hwui/utils/LinearAllocator.h50
-rw-r--r--libs/hwui/utils/MathUtils.h13
-rw-r--r--libs/hwui/utils/NinePatch.h37
-rw-r--r--libs/hwui/utils/NinePatchImpl.cpp326
-rw-r--r--libs/hwui/utils/PaintUtils.h41
-rw-r--r--libs/hwui/utils/SortedList.h242
-rw-r--r--libs/hwui/utils/SortedListImpl.cpp126
-rw-r--r--libs/hwui/utils/SortedListImpl.h65
-rw-r--r--libs/hwui/utils/StringUtils.cpp40
-rw-r--r--libs/hwui/utils/StringUtils.h36
-rw-r--r--libs/input/PointerController.cpp32
-rw-r--r--libs/input/PointerController.h9
162 files changed, 6788 insertions, 3323 deletions
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 623ea896626b..8a03b94492d8 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -176,7 +176,7 @@ AssetManager::~AssetManager(void)
delete[] mVendor;
}
-bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
+bool AssetManager::addAssetPath(const String8& path, int32_t* cookie, bool appAsLib)
{
AutoMutex _l(mLock);
@@ -238,7 +238,7 @@ bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
#endif
if (mResources != NULL) {
- appendPathToResTable(ap);
+ appendPathToResTable(ap, appAsLib);
}
return true;
@@ -610,7 +610,7 @@ FileType AssetManager::getFileType(const char* fileName)
return kFileTypeRegular;
}
-bool AssetManager::appendPathToResTable(const asset_path& ap) const {
+bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const {
// skip those ap's that correspond to system overlays
if (ap.isSystemOverlay) {
return true;
@@ -685,7 +685,7 @@ bool AssetManager::appendPathToResTable(const asset_path& ap) const {
mResources->add(sharedRes);
} else {
ALOGV("Parsing resources for %s", ap.path.string());
- mResources->add(ass, idmap, nextEntryIdx + 1, !shared);
+ mResources->add(ass, idmap, nextEntryIdx + 1, !shared, appAsLib);
}
onlyEmptyResources = false;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 806eeda3555a..21b543eefa01 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3080,13 +3080,13 @@ struct ResTable::Package
// table that defined the package); the ones after are skins on top of it.
struct ResTable::PackageGroup
{
- PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id)
+ PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id, bool appAsLib)
: owner(_owner)
, name(_name)
, id(_id)
, largestTypeId(0)
, bags(NULL)
- , dynamicRefTable(static_cast<uint8_t>(_id))
+ , dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
{ }
~PackageGroup() {
@@ -3532,7 +3532,7 @@ ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool cop
{
memset(&mParams, 0, sizeof(mParams));
memset(mPackageMap, 0, sizeof(mPackageMap));
- addInternal(data, size, NULL, 0, cookie, copyData);
+ addInternal(data, size, NULL, 0, false, cookie, copyData);
LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
if (kDebugTableSuperNoisy) {
ALOGI("Creating ResTable %p\n", this);
@@ -3553,12 +3553,12 @@ inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const
}
status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
- return addInternal(data, size, NULL, 0, cookie, copyData);
+ return addInternal(data, size, NULL, 0, false, cookie, copyData);
}
status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie, bool copyData) {
- return addInternal(data, size, idmapData, idmapDataSize, cookie, copyData);
+ const int32_t cookie, bool copyData, bool appAsLib) {
+ return addInternal(data, size, idmapData, idmapDataSize, appAsLib, cookie, copyData);
}
status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) {
@@ -3568,10 +3568,12 @@ status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) {
return UNKNOWN_ERROR;
}
- return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, 0, cookie, copyData);
+ return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, false, 0, cookie,
+ copyData);
}
-status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) {
+status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData,
+ bool appAsLib) {
const void* data = asset->getBuffer(true);
if (data == NULL) {
ALOGW("Unable to get buffer of resource asset file");
@@ -3590,7 +3592,7 @@ status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bo
}
return addInternal(data, static_cast<size_t>(asset->getLength()),
- idmapData, idmapSize, cookie, copyData);
+ idmapData, idmapSize, appAsLib, cookie, copyData);
}
status_t ResTable::add(ResTable* src)
@@ -3603,7 +3605,7 @@ status_t ResTable::add(ResTable* src)
for (size_t i=0; i<src->mPackageGroups.size(); i++) {
PackageGroup* srcPg = src->mPackageGroups[i];
- PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id);
+ PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id, false);
for (size_t j=0; j<srcPg->packages.size(); j++) {
pg->packages.add(srcPg->packages[j]);
}
@@ -3644,7 +3646,7 @@ status_t ResTable::addEmpty(const int32_t cookie) {
}
status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie, bool copyData)
+ bool appAsLib, const int32_t cookie, bool copyData)
{
if (!data) {
return NO_ERROR;
@@ -3747,7 +3749,7 @@ status_t ResTable::addInternal(const void* data, size_t dataSize, const void* id
return (mError=BAD_TYPE);
}
- if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) {
+ if (parsePackage((ResTable_package*)chunk, header, appAsLib) != NO_ERROR) {
return mError;
}
curPackage++;
@@ -5660,11 +5662,15 @@ const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) con
return NULL;
}
-void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap) const
-{
+void ResTable::getConfigurations(Vector<ResTable_config>* configs, bool ignoreMipmap,
+ bool ignoreAndroidPackage) const {
const size_t packageCount = mPackageGroups.size();
+ String16 android("android");
for (size_t i = 0; i < packageCount; i++) {
const PackageGroup* packageGroup = mPackageGroups[i];
+ if (ignoreAndroidPackage && android == packageGroup->name) {
+ continue;
+ }
const size_t typeCount = packageGroup->types.size();
for (size_t j = 0; j < typeCount; j++) {
const TypeList& typeList = packageGroup->types[j];
@@ -5931,7 +5937,7 @@ status_t ResTable::getEntry(
}
status_t ResTable::parsePackage(const ResTable_package* const pkg,
- const Header* const header)
+ const Header* const header, bool appAsLib)
{
const uint8_t* base = (const uint8_t*)pkg;
status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
@@ -5979,7 +5985,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
if (id >= 256) {
LOG_ALWAYS_FATAL("Package id out of range");
return NO_ERROR;
- } else if (id == 0) {
+ } else if (id == 0 || appAsLib) {
// This is a library so assign an ID
id = mNextPackageId++;
}
@@ -6012,7 +6018,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];
strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));
- group = new PackageGroup(this, String16(tmpName), id);
+ group = new PackageGroup(this, String16(tmpName), id, appAsLib);
if (group == NULL) {
delete package;
return (mError=NO_MEMORY);
@@ -6224,8 +6230,9 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
return NO_ERROR;
}
-DynamicRefTable::DynamicRefTable(uint8_t packageId)
+DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
: mAssignedPackageId(packageId)
+ , mAppAsLib(appAsLib)
{
memset(mLookupTable, 0, sizeof(mLookupTable));
@@ -6310,16 +6317,18 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
uint32_t res = *resId;
size_t packageId = Res_GETPACKAGE(res) + 1;
- if (packageId == APP_PACKAGE_ID) {
+ if (packageId == APP_PACKAGE_ID && !mAppAsLib) {
// No lookup needs to be done, app package IDs are absolute.
return NO_ERROR;
}
- if (packageId == 0) {
+ if (packageId == 0 || (packageId == APP_PACKAGE_ID && mAppAsLib)) {
// The package ID is 0x00. That means that a shared library is accessing
- // its own local resource, so we fix up the resource with the calling
- // package ID.
- *resId |= ((uint32_t) mAssignedPackageId) << 24;
+ // its own local resource.
+ // Or if app resource is loaded as shared library, the resource which has
+ // app package Id is local resources.
+ // so we fix up those resources with the calling package ID.
+ *resId = (0xFFFFFF & (*resId)) | (((uint32_t) mAssignedPackageId) << 24);
return NO_ERROR;
}
@@ -6341,7 +6350,10 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
}
status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
- if (value->dataType != Res_value::TYPE_DYNAMIC_REFERENCE) {
+ if (value->dataType != Res_value::TYPE_DYNAMIC_REFERENCE &&
+ (value->dataType != Res_value::TYPE_REFERENCE || !mAppAsLib)) {
+ // If the package is loaded as shared library, the resource reference
+ // also need to be fixed.
return NO_ERROR;
}
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index a353575b4073..2bc026b79ca2 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -21,6 +21,7 @@
LOCAL_PATH:= $(call my-dir)
testFiles := \
+ AppAsLib_test.cpp \
AttributeFinder_test.cpp \
ByteBucketArray_test.cpp \
Config_test.cpp \
diff --git a/libs/androidfw/tests/AppAsLib_test.cpp b/libs/androidfw/tests/AppAsLib_test.cpp
new file mode 100644
index 000000000000..bdb0c3d38f6f
--- /dev/null
+++ b/libs/androidfw/tests/AppAsLib_test.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 <androidfw/ResourceTypes.h>
+
+#include "data/basic/R.h"
+#include "data/appaslib/R.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+#include "data/basic/basic_arsc.h"
+
+TEST(AppAsLibTest, loadedAsApp) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(base::R::integer::number2, &val);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(base::R::array::integerArray1, val.data);
+}
+
+TEST(AppAsLibTest, loadedAsSharedLib) {
+ ResTable table;
+ // Load as shared library.
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len, NULL, 0, -1, false, true));
+
+ Res_value val;
+ ssize_t block = table.getResource(appaslib::R::integer::number2, &val);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(appaslib::R::array::integerArray1, val.data);
+}
+
+}
diff --git a/libs/hwui/unit_tests/main.cpp b/libs/androidfw/tests/data/appaslib/R.h
index c9b96360b36b..f89d4bfdd15e 100644
--- a/libs/hwui/unit_tests/main.cpp
+++ b/libs/androidfw/tests/data/appaslib/R.h
@@ -14,9 +14,25 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
+#ifndef __APPASLIB_R_H
+#define __APPASLIB_R_H
-int main(int argc, char **argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+namespace appaslib {
+namespace R {
+
+namespace integer {
+ enum {
+ number2 = 0x02040001, // default
+ };
+}
+
+namespace array {
+ enum {
+ integerArray1 = 0x02060000, // default
+ };
}
+
+} // namespace R
+} // namespace appaslib
+
+#endif // __APPASLIB_R_H
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index a4100a2d44fb..20ecda28b22a 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
/**
* Extra vertices for the corner for smoother corner.
* Only for outer vertices.
@@ -54,15 +52,14 @@
// If this is set to negative value, then all the edge will be tessellated.
#define ALPHA_THRESHOLD (0.1f / 255.0f)
-#include <math.h>
-#include <utils/Log.h>
-#include <utils/Vector.h>
-
#include "AmbientShadow.h"
+
#include "ShadowTessellator.h"
#include "Vertex.h"
#include "VertexBuffer.h"
-#include "utils/MathUtils.h"
+
+#include <algorithm>
+#include <utils/Log.h>
namespace android {
namespace uirenderer {
@@ -81,7 +78,7 @@ inline Vector2 getNormalFromVertices(const Vector3* vertices, int current, int n
// 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 + MathUtils::max(factoredZ, 0.0f));
+ return 1.0 / (1 + std::max(factoredZ, 0.0f));
}
// The shader is using gaussian function e^-(1-x)*(1-x)*4, therefore, we transform
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
deleted file mode 100644
index 38e8be907720..000000000000
--- a/libs/hwui/Android.common.mk
+++ /dev/null
@@ -1,127 +0,0 @@
-# getConfig in external/skia/include/core/SkBitmap.h is deprecated.
-# Allow Gnu extension: in-class initializer of static 'const float' member.
-# DeferredLayerUpdater.h: private field 'mRenderThread' is not used.
-LOCAL_CLANG_CFLAGS += \
- -Wno-deprecated-declarations \
- -Wno-gnu-static-float-init \
- -Wno-unused-private-field
-
-LOCAL_SRC_FILES := \
- font/CacheTexture.cpp \
- font/Font.cpp \
- renderstate/Blend.cpp \
- renderstate/MeshState.cpp \
- renderstate/PixelBufferState.cpp \
- renderstate/RenderState.cpp \
- renderstate/Scissor.cpp \
- renderstate/Stencil.cpp \
- renderstate/TextureState.cpp \
- renderthread/CanvasContext.cpp \
- renderthread/DrawFrameTask.cpp \
- renderthread/EglManager.cpp \
- renderthread/RenderProxy.cpp \
- renderthread/RenderTask.cpp \
- renderthread/RenderThread.cpp \
- renderthread/TimeLord.cpp \
- thread/TaskManager.cpp \
- utils/Blur.cpp \
- utils/GLUtils.cpp \
- utils/LinearAllocator.cpp \
- utils/SortedListImpl.cpp \
- AmbientShadow.cpp \
- AnimationContext.cpp \
- Animator.cpp \
- AnimatorManager.cpp \
- AssetAtlas.cpp \
- Caches.cpp \
- CanvasState.cpp \
- ClipArea.cpp \
- DamageAccumulator.cpp \
- DeferredDisplayList.cpp \
- DeferredLayerUpdater.cpp \
- DisplayList.cpp \
- DisplayListCanvas.cpp \
- Dither.cpp \
- Extensions.cpp \
- FboCache.cpp \
- FontRenderer.cpp \
- FrameInfo.cpp \
- FrameInfoVisualizer.cpp \
- GammaFontRenderer.cpp \
- GlopBuilder.cpp \
- GradientCache.cpp \
- Image.cpp \
- Interpolator.cpp \
- JankTracker.cpp \
- Layer.cpp \
- LayerCache.cpp \
- LayerRenderer.cpp \
- Matrix.cpp \
- OpenGLRenderer.cpp \
- Patch.cpp \
- PatchCache.cpp \
- PathCache.cpp \
- PathTessellator.cpp \
- PixelBuffer.cpp \
- Program.cpp \
- ProgramCache.cpp \
- Properties.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
-
-intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
-
-LOCAL_C_INCLUDES += \
- external/skia/src/core
-
-LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
-LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui libgui
-
-ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
- LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
- LOCAL_SHARED_LIBRARIES += libRS libRScpp
- LOCAL_C_INCLUDES += \
- $(intermediates) \
- frameworks/rs/cpp \
- frameworks/rs \
-
-endif
-
-ifndef HWUI_COMPILE_SYMBOLS
- LOCAL_CFLAGS += -fvisibility=hidden
-endif
-
-ifdef HWUI_COMPILE_FOR_PERF
- # TODO: Non-arm?
- LOCAL_CFLAGS += -fno-omit-frame-pointer -marm -mapcs
-endif
-
-ifeq (true, $(HWUI_NULL_GPU))
- LOCAL_SRC_FILES += \
- tests/nullegl.cpp \
- tests/nullgles.cpp
-
- LOCAL_CFLAGS += -DHWUI_NULL_GPU
-endif
-
-# Defaults for ATRACE_TAG and LOG_TAG for libhwui
-LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\"
-LOCAL_CFLAGS += -Wall -Wno-unused-parameter -Wunreachable-code
-LOCAL_CFLAGS += -ffast-math -O3
-
-# b/21698669
-ifneq ($(USE_CLANG_PLATFORM_BUILD),true)
- LOCAL_CFLAGS += -Werror
-endif
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 91e289c32b97..842f575553a5 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,11 +2,270 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+HWUI_NEW_OPS := false
+
+hwui_src_files := \
+ font/CacheTexture.cpp \
+ font/Font.cpp \
+ renderstate/Blend.cpp \
+ renderstate/MeshState.cpp \
+ renderstate/PixelBufferState.cpp \
+ renderstate/RenderState.cpp \
+ renderstate/Scissor.cpp \
+ renderstate/Stencil.cpp \
+ renderstate/TextureState.cpp \
+ renderthread/CanvasContext.cpp \
+ renderthread/DrawFrameTask.cpp \
+ renderthread/EglManager.cpp \
+ renderthread/RenderProxy.cpp \
+ renderthread/RenderTask.cpp \
+ renderthread/RenderThread.cpp \
+ renderthread/TimeLord.cpp \
+ thread/TaskManager.cpp \
+ utils/Blur.cpp \
+ utils/GLUtils.cpp \
+ utils/LinearAllocator.cpp \
+ utils/NinePatchImpl.cpp \
+ utils/StringUtils.cpp \
+ AmbientShadow.cpp \
+ AnimationContext.cpp \
+ Animator.cpp \
+ AnimatorManager.cpp \
+ AssetAtlas.cpp \
+ Caches.cpp \
+ CanvasState.cpp \
+ ClipArea.cpp \
+ DamageAccumulator.cpp \
+ DeferredDisplayList.cpp \
+ DeferredLayerUpdater.cpp \
+ DisplayList.cpp \
+ DisplayListCanvas.cpp \
+ Dither.cpp \
+ Extensions.cpp \
+ FboCache.cpp \
+ FontRenderer.cpp \
+ FrameInfo.cpp \
+ FrameInfoVisualizer.cpp \
+ GammaFontRenderer.cpp \
+ GlopBuilder.cpp \
+ GradientCache.cpp \
+ Image.cpp \
+ Interpolator.cpp \
+ JankTracker.cpp \
+ Layer.cpp \
+ LayerCache.cpp \
+ LayerRenderer.cpp \
+ Matrix.cpp \
+ OpenGLRenderer.cpp \
+ Patch.cpp \
+ PatchCache.cpp \
+ PathCache.cpp \
+ PathTessellator.cpp \
+ PixelBuffer.cpp \
+ Program.cpp \
+ ProgramCache.cpp \
+ Properties.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 \
+ protos/hwui.proto
+
+hwui_cflags := \
+ -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES \
+ -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" \
+ -Wall -Wno-unused-parameter -Wunreachable-code \
+ -ffast-math -O3 -Werror
+
+ifeq (true, $(HWUI_NEW_OPS))
+ hwui_src_files += \
+ BakedOpRenderer.cpp \
+ OpReorderer.cpp \
+ RecordingCanvas.cpp
+
+ hwui_cflags += -DHWUI_NEW_OPS
+
+endif
+
+ifndef HWUI_COMPILE_SYMBOLS
+ hwui_cflags += -fvisibility=hidden
+endif
+
+ifdef HWUI_COMPILE_FOR_PERF
+ # TODO: Non-arm?
+ hwui_cflags += -fno-omit-frame-pointer -marm -mapcs
+endif
+
+# This has to be lazy-resolved because it depends on the LOCAL_MODULE_CLASS
+# which varies depending on what is being built
+define hwui_proto_include
+$(call local-generated-sources-dir)/proto/$(LOCAL_PATH)
+endef
+
+hwui_c_includes += \
+ external/skia/src/core
+
+hwui_shared_libraries := \
+ liblog \
+ libcutils \
+ libutils \
+ libEGL \
+ libGLESv2 \
+ libskia \
+ libui \
+ libgui \
+ libprotobuf-cpp-lite \
+
+ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
+ hwui_cflags += -DANDROID_ENABLE_RENDERSCRIPT
+ hwui_shared_libraries += libRS libRScpp
+ hwui_c_includes += \
+ $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) \
+ frameworks/rs/cpp \
+ frameworks/rs
+endif
+
+
+# ------------------------
+# static library
+# ------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := libhwui_static
+LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
+LOCAL_CFLAGS := $(hwui_cflags)
+LOCAL_SRC_FILES := $(hwui_src_files)
+LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
+
+include $(BUILD_STATIC_LIBRARY)
+
+# ------------------------
+# static library null gpu
+# ------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := libhwui_static_null_gpu
+LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
+LOCAL_CFLAGS := \
+ $(hwui_cflags) \
+ -DHWUI_NULL_GPU
+LOCAL_SRC_FILES := \
+ $(hwui_src_files) \
+ tests/nullegl.cpp \
+ tests/nullgles.cpp
+LOCAL_C_INCLUDES := $(hwui_c_includes) $(call hwui_proto_include)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(hwui_c_includes) $(call hwui_proto_include)
+
+include $(BUILD_STATIC_LIBRARY)
+
+# ------------------------
+# shared library
+# ------------------------
+
+include $(CLEAR_VARS)
+
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE := libhwui
-
-include $(LOCAL_PATH)/Android.common.mk
+LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
+LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
include $(BUILD_SHARED_LIBRARY)
-include $(call all-makefiles-under,$(LOCAL_PATH))
+# ------------------------
+# unit tests
+# ------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := hwui_unit_tests
+LOCAL_MODULE_TAGS := tests
+LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
+LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu
+LOCAL_CFLAGS := $(hwui_cflags)
+
+LOCAL_SRC_FILES += \
+ unit_tests/CanvasStateTests.cpp \
+ unit_tests/ClipAreaTests.cpp \
+ unit_tests/DamageAccumulatorTests.cpp \
+ unit_tests/LinearAllocatorTests.cpp \
+ unit_tests/StringUtilsTests.cpp
+
+ifeq (true, $(HWUI_NEW_OPS))
+ LOCAL_SRC_FILES += \
+ unit_tests/BakedOpStateTests.cpp \
+ unit_tests/RecordingCanvasTests.cpp \
+ unit_tests/OpReordererTests.cpp
+endif
+
+include $(BUILD_NATIVE_TEST)
+
+# ------------------------
+# test app
+# ------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_MODULE:= hwuitest
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := hwuitest
+LOCAL_MODULE_STEM_64 := hwuitest64
+LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
+LOCAL_CFLAGS := $(hwui_cflags)
+
+# set to libhwui_static_null_gpu to skip actual GL commands
+LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static
+
+LOCAL_SRC_FILES += \
+ tests/TestContext.cpp \
+ tests/TreeContentAnimation.cpp \
+ tests/main.cpp
+
+include $(BUILD_EXECUTABLE)
+
+# ------------------------
+# Micro-bench app
+# ---------------------
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_MODULE:= hwuimicro
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := hwuimicro
+LOCAL_MODULE_STEM_64 := hwuimicro64
+LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries)
+LOCAL_CFLAGS := $(hwui_cflags)
+LOCAL_C_INCLUDES += bionic/benchmarks/
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static_null_gpu
+LOCAL_STATIC_LIBRARIES := libbenchmark libbase
+
+LOCAL_SRC_FILES += \
+ microbench/DisplayListCanvasBench.cpp \
+ microbench/LinearAllocatorBench.cpp
+
+ifeq (true, $(HWUI_NEW_OPS))
+ LOCAL_SRC_FILES += \
+ microbench/OpReordererBench.cpp
+endif
+
+include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 512e0e24aa93..5ca2a2fa37ab 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -36,8 +36,8 @@ BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue)
, mFinalValue(finalValue)
, mDeltaValue(0)
, mFromValue(0)
- , mStagingPlayState(NOT_STARTED)
- , mPlayState(NOT_STARTED)
+ , mStagingPlayState(PlayState::NotStarted)
+ , mPlayState(PlayState::NotStarted)
, mHasStartValue(false)
, mStartTime(0)
, mDuration(300)
@@ -50,7 +50,7 @@ BaseRenderNodeAnimator::~BaseRenderNodeAnimator() {
void BaseRenderNodeAnimator::checkMutable() {
// Should be impossible to hit as the Java-side also has guards for this
- LOG_ALWAYS_FATAL_IF(mStagingPlayState != NOT_STARTED,
+ LOG_ALWAYS_FATAL_IF(mStagingPlayState != PlayState::NotStarted,
"Animator has already been started!");
}
@@ -92,9 +92,9 @@ void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) {
if (mStagingPlayState > mPlayState) {
mPlayState = mStagingPlayState;
// Oh boy, we're starting! Man the battle stations!
- if (mPlayState == RUNNING) {
+ if (mPlayState == PlayState::Running) {
transitionToRunning(context);
- } else if (mPlayState == FINISHED) {
+ } else if (mPlayState == PlayState::Finished) {
callOnFinishedListener(context);
}
}
@@ -124,10 +124,10 @@ void BaseRenderNodeAnimator::transitionToRunning(AnimationContext& context) {
}
bool BaseRenderNodeAnimator::animate(AnimationContext& context) {
- if (mPlayState < RUNNING) {
+ if (mPlayState < PlayState::Running) {
return false;
}
- if (mPlayState == FINISHED) {
+ if (mPlayState == PlayState::Finished) {
return true;
}
@@ -141,18 +141,18 @@ bool BaseRenderNodeAnimator::animate(AnimationContext& context) {
}
float fraction = 1.0f;
- if (mPlayState == RUNNING && mDuration > 0) {
+ if (mPlayState == PlayState::Running && mDuration > 0) {
fraction = (float)(context.frameTimeMs() - mStartTime) / mDuration;
}
if (fraction >= 1.0f) {
fraction = 1.0f;
- mPlayState = FINISHED;
+ mPlayState = PlayState::Finished;
}
fraction = mInterpolator->interpolate(fraction);
setValue(mTarget, mFromValue + (mDeltaValue * fraction));
- if (mPlayState == FINISHED) {
+ if (mPlayState == PlayState::Finished) {
callOnFinishedListener(context);
return true;
}
@@ -161,8 +161,8 @@ bool BaseRenderNodeAnimator::animate(AnimationContext& context) {
}
void BaseRenderNodeAnimator::forceEndNow(AnimationContext& context) {
- if (mPlayState < FINISHED) {
- mPlayState = FINISHED;
+ if (mPlayState < PlayState::Finished) {
+ mPlayState = PlayState::Finished;
callOnFinishedListener(context);
}
}
@@ -212,9 +212,9 @@ void RenderPropertyAnimator::onAttached() {
}
void RenderPropertyAnimator::onStagingPlayStateChanged() {
- if (mStagingPlayState == RUNNING) {
+ if (mStagingPlayState == PlayState::Running) {
(mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
- } else if (mStagingPlayState == FINISHED) {
+ } else if (mStagingPlayState == PlayState::Finished) {
// We're being canceled, so make sure that whatever values the UI thread
// is observing for us is pushed over
mTarget->setPropertyFieldsDirty(dirtyMask());
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 1b3d8e7f4842..aea95bfc1c0e 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -59,8 +59,8 @@ public:
mMayRunAsync = mayRunAsync;
}
bool mayRunAsync() { return mMayRunAsync; }
- ANDROID_API void start() { mStagingPlayState = RUNNING; onStagingPlayStateChanged(); }
- ANDROID_API void end() { mStagingPlayState = FINISHED; onStagingPlayStateChanged(); }
+ ANDROID_API void start() { mStagingPlayState = PlayState::Running; onStagingPlayStateChanged(); }
+ ANDROID_API void end() { mStagingPlayState = PlayState::Finished; onStagingPlayStateChanged(); }
void attach(RenderNode* target);
virtual void onAttached() {}
@@ -68,8 +68,8 @@ public:
void pushStaging(AnimationContext& context);
bool animate(AnimationContext& context);
- bool isRunning() { return mPlayState == RUNNING; }
- bool isFinished() { return mPlayState == FINISHED; }
+ bool isRunning() { return mPlayState == PlayState::Running; }
+ bool isFinished() { return mPlayState == PlayState::Finished; }
float finalValue() { return mFinalValue; }
ANDROID_API virtual uint32_t dirtyMask() = 0;
@@ -77,6 +77,12 @@ public:
void forceEndNow(AnimationContext& context);
protected:
+ enum class PlayState {
+ NotStarted,
+ Running,
+ Finished,
+ };
+
BaseRenderNodeAnimator(float finalValue);
virtual ~BaseRenderNodeAnimator();
@@ -88,12 +94,6 @@ protected:
virtual void onStagingPlayStateChanged() {}
- enum PlayState {
- NOT_STARTED,
- RUNNING,
- FINISHED,
- };
-
RenderNode* mTarget;
float mFinalValue;
diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
index 2889d2ff0b78..7e0969916825 100644
--- a/libs/hwui/AssetAtlas.cpp
+++ b/libs/hwui/AssetAtlas.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include "AssetAtlas.h"
#include "Caches.h"
#include "Image.h"
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
new file mode 100644
index 000000000000..4d9f9b479343
--- /dev/null
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 "renderstate/RenderState.h"
+#include "utils/GLUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+Texture* BakedOpRenderer::Info::getTexture(const SkBitmap* bitmap) {
+ Texture* texture = renderState.assetAtlas().getEntryTexture(bitmap);
+ if (!texture) {
+ return caches.textureCache.get(bitmap);
+ }
+ return texture;
+}
+
+void BakedOpRenderer::Info::renderGlop(const BakedOpState& state, const Glop& glop) {
+ bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None;
+ renderState.scissor().setEnabled(useScissor);
+ if (useScissor) {
+ const Rect& clip = state.computedState.clipRect;
+ renderState.scissor().set(clip.left, viewportHeight - clip.bottom,
+ clip.getWidth(), clip.getHeight());
+ }
+ renderState.render(glop, orthoMatrix);
+ didDraw = true;
+}
+
+void BakedOpRenderer::startFrame(Info& info) {
+ info.renderState.setViewport(info.viewportWidth, info.viewportHeight);
+ info.renderState.blend().syncEnabled();
+ Caches::getInstance().clearGarbage();
+
+ if (!info.opaque) {
+ // TODO: partial invalidate!
+ info.renderState.scissor().setEnabled(false);
+ glClear(GL_COLOR_BUFFER_BIT);
+ info.didDraw = true;
+ }
+}
+void BakedOpRenderer::endFrame(Info& info) {
+ info.caches.pathCache.trim();
+ info.caches.tessellationCache.trim();
+
+#if DEBUG_OPENGL
+ GLUtils::dumpGLErrors();
+#endif
+
+#if DEBUG_MEMORY_USAGE
+ info.caches.dumpMemoryUsage();
+#else
+ if (Properties::debugLevel & kDebugMemory) {
+ info.caches.dumpMemoryUsage();
+ }
+#endif
+}
+
+void BakedOpRenderer::onRenderNodeOp(Info*, const RenderNodeOp&, const BakedOpState&) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpRenderer::onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
+ info->caches.textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
+ Texture* texture = info->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(info->renderState, info->caches, &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshTexturedUnitQuad(texture->uvMapper)
+ .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
+ .build();
+ info->renderGlop(state, glop);
+}
+
+void BakedOpRenderer::onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
+ Glop glop;
+ GlopBuilder(info->renderState, info->caches, &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshUnitQuad()
+ .setFillPaint(*op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewMapUnitToRect(op.unmappedBounds)
+ .build();
+ info->renderGlop(state, glop);
+}
+
+void BakedOpRenderer::onSimpleRectsOp(Info* info, const SimpleRectsOp& op, const BakedOpState& state) {
+ Glop glop;
+ GlopBuilder(info->renderState, info->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();
+ info->renderGlop(state, glop);
+}
+
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
new file mode 100644
index 000000000000..b8b4426412e4
--- /dev/null
+++ b/libs/hwui/BakedOpRenderer.h
@@ -0,0 +1,75 @@
+/*
+ * 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_RENDERER_H
+#define ANDROID_HWUI_BAKED_OP_RENDERER_H
+
+#include "BakedOpState.h"
+#include "Matrix.h"
+
+namespace android {
+namespace uirenderer {
+
+class Caches;
+struct Glop;
+class RenderState;
+
+class BakedOpRenderer {
+public:
+ class Info {
+ public:
+ Info(Caches& caches, RenderState& renderState, int viewportWidth, int viewportHeight, bool opaque)
+ : renderState(renderState)
+ , caches(caches)
+ , opaque(opaque)
+ , viewportWidth(viewportWidth)
+ , viewportHeight(viewportHeight) {
+ orthoMatrix.loadOrtho(viewportWidth, viewportHeight);
+ }
+
+ Texture* getTexture(const SkBitmap* bitmap);
+
+ void renderGlop(const BakedOpState& state, const Glop& glop);
+ RenderState& renderState;
+ Caches& caches;
+
+ bool didDraw = false;
+ bool opaque;
+
+
+ // where should these live? layer state object?
+ int viewportWidth;
+ int viewportHeight;
+ Matrix4 orthoMatrix;
+ };
+
+ static void startFrame(Info& info);
+ static void endFrame(Info& info);
+
+ /**
+ * Declare all "onBitmapOp(...)" style function for every op type.
+ *
+ * These functions will perform the actual rendering of the individual operations in OpenGL,
+ * given the transform/clip and other state built into the BakedOpState object passed in.
+ */
+ #define BAKED_OP_RENDERER_METHOD(Type) static void on##Type(Info* info, const Type& op, const BakedOpState& state);
+ MAP_OPS(BAKED_OP_RENDERER_METHOD);
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_BAKED_OP_RENDERER_H
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
new file mode 100644
index 000000000000..e2201ca06a4b
--- /dev/null
+++ b/libs/hwui/BakedOpState.h
@@ -0,0 +1,142 @@
+/*
+ * 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 the resolved clip, transform, and bounds of a recordedOp, when replayed with a snapshot
+ */
+class ResolvedRenderState {
+public:
+ // TODO: remove the mapRects/matrix multiply when snapshot & recorded transforms are translates
+ ResolvedRenderState(const Snapshot& snapshot, const RecordedOp& recordedOp) {
+ /* TODO: benchmark a fast path for translate-only matrices, such as:
+ if (CC_LIKELY(snapshot.transform->getType() == Matrix4::kTypeTranslate
+ && recordedOp.localMatrix.getType() == Matrix4::kTypeTranslate)) {
+ float translateX = snapshot.transform->getTranslateX() + recordedOp.localMatrix.getTranslateX();
+ float translateY = snapshot.transform->getTranslateY() + recordedOp.localMatrix.getTranslateY();
+ transform.loadTranslate(translateX, translateY, 0);
+
+ // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
+ clipRect = recordedOp.localClipRect;
+ clipRect.translate(translateX, translateY);
+ clipRect.doIntersect(snapshot.getClipRect());
+ clipRect.snapToPixelBoundaries();
+
+ // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
+ clippedBounds = recordedOp.unmappedBounds;
+ clippedBounds.translate(translateX, translateY);
+ } ... */
+
+ // resolvedMatrix = parentMatrix * localMatrix
+ transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix);
+
+ // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
+ clipRect = recordedOp.localClipRect;
+ snapshot.transform->mapRect(clipRect);
+ clipRect.doIntersect(snapshot.getClipRect());
+ clipRect.snapToPixelBoundaries();
+
+ // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
+ clippedBounds = recordedOp.unmappedBounds;
+ transform.mapRect(clippedBounds);
+
+ if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left;
+ if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top;
+ if (clipRect.right < clippedBounds.right) clipSideFlags |= OpClipSideFlags::Right;
+ if (clipRect.bottom < clippedBounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
+ clippedBounds.doIntersect(clipRect);
+
+ /**
+ * TODO: once we support complex clips, we may want to reject to avoid that work where
+ * possible. Should we:
+ * 1 - quickreject based on clippedBounds, quick early (duplicating logic in resolvedOp)
+ * 2 - merge stuff into tryConstruct factory method, so it can handle quickRejection
+ * and early return null in one place.
+ */
+ }
+ Matrix4 transform;
+ Rect clipRect;
+ int clipSideFlags = 0;
+ Rect clippedBounds;
+};
+
+/**
+ * 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,
+ const Snapshot& snapshot, const RecordedOp& recordedOp) {
+ BakedOpState* bakedOp = new (allocator) BakedOpState(
+ snapshot, recordedOp);
+ if (bakedOp->computedState.clippedBounds.isEmpty()) {
+ // bounds are empty, so op is rejected
+ allocator.rewindIfLastAlloc(bakedOp);
+ return nullptr;
+ }
+ return bakedOp;
+ }
+
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ // computed state:
+ const ResolvedRenderState computedState;
+
+ // simple state (straight pointer/value storage):
+ const float alpha;
+ const RoundRectClipState* roundRectClipState;
+ const ProjectionPathMask* projectionPathMask;
+ const RecordedOp* op;
+
+private:
+ BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp)
+ : computedState(snapshot, recordedOp)
+ , alpha(snapshot.alpha)
+ , roundRectClipState(snapshot.roundRectClipState)
+ , projectionPathMask(snapshot.projectionPathMask)
+ , 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
index aa73d44af14b..7c63e316ba38 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include "Caches.h"
#include "GammaFontRenderer.h"
@@ -56,7 +54,6 @@ Caches::Caches(RenderState& renderState)
, mInitialized(false) {
INIT_LOGD("Creating OpenGL renderer caches");
init();
- initFont();
initConstraints();
initStaticProperties();
initExtensions();
@@ -70,8 +67,6 @@ bool Caches::init() {
mRegionMesh = nullptr;
mProgram = nullptr;
- mFunctorsCount = 0;
-
patchCache.init();
mInitialized = true;
@@ -82,10 +77,6 @@ bool Caches::init() {
return true;
}
-void Caches::initFont() {
- fontRenderer = GammaFontRenderer::createRenderer();
-}
-
void Caches::initExtensions() {
if (mExtensions.hasDebugMarker()) {
eventMark = glInsertEventMarkerEXT;
@@ -104,15 +95,9 @@ void Caches::initConstraints() {
}
void Caches::initStaticProperties() {
- gpuPixelBuffersEnabled = false;
-
// OpenGL ES 3.0+ specific features
- if (mExtensions.hasPixelBufferObjects()) {
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "true") > 0) {
- gpuPixelBuffersEnabled = !strcmp(property, "true");
- }
- }
+ gpuPixelBuffersEnabled = mExtensions.hasPixelBufferObjects()
+ && property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true);
}
void Caches::terminate() {
@@ -207,14 +192,14 @@ void Caches::dumpMemoryUsage(String8 &log) {
dropShadowCache.getMaxSize());
log.appendFormat(" PatchCache %8d / %8d\n",
patchCache.getSize(), patchCache.getMaxSize());
- for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
- const uint32_t sizeA8 = fontRenderer->getFontRendererSize(i, GL_ALPHA);
- const uint32_t sizeRGBA = fontRenderer->getFontRendererSize(i, GL_RGBA);
- log.appendFormat(" FontRenderer %d A8 %8d / %8d\n", i, sizeA8, sizeA8);
- log.appendFormat(" FontRenderer %d RGBA %8d / %8d\n", i, sizeRGBA, sizeRGBA);
- log.appendFormat(" FontRenderer %d total %8d / %8d\n", i, sizeA8 + sizeRGBA,
- sizeA8 + sizeRGBA);
- }
+
+ const uint32_t sizeA8 = fontRenderer.getFontRendererSize(GL_ALPHA);
+ const uint32_t sizeRGBA = fontRenderer.getFontRendererSize(GL_RGBA);
+ log.appendFormat(" FontRenderer A8 %8d / %8d\n", sizeA8, sizeA8);
+ log.appendFormat(" FontRenderer RGBA %8d / %8d\n", sizeRGBA, sizeRGBA);
+ log.appendFormat(" FontRenderer total %8d / %8d\n", sizeA8 + sizeRGBA,
+ sizeA8 + sizeRGBA);
+
log.appendFormat("Other:\n");
log.appendFormat(" FboCache %8d / %8d\n",
fboCache.getSize(), fboCache.getMaxSize());
@@ -226,10 +211,8 @@ void Caches::dumpMemoryUsage(String8 &log) {
total += tessellationCache.getSize();
total += dropShadowCache.getSize();
total += patchCache.getSize();
- for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
- total += fontRenderer->getFontRendererSize(i, GL_ALPHA);
- total += fontRenderer->getFontRendererSize(i, GL_RGBA);
- }
+ total += fontRenderer.getFontRendererSize(GL_ALPHA);
+ total += fontRenderer.getFontRendererSize(GL_RGBA);
log.appendFormat("Total memory usage:\n");
log.appendFormat(" %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
@@ -249,22 +232,22 @@ void Caches::flush(FlushMode mode) {
FLUSH_LOGD("Flushing caches (mode %d)", mode);
switch (mode) {
- case kFlushMode_Full:
+ case FlushMode::Full:
textureCache.clear();
patchCache.clear();
dropShadowCache.clear();
gradientCache.clear();
- fontRenderer->clear();
+ fontRenderer.clear();
fboCache.clear();
dither.clear();
// fall through
- case kFlushMode_Moderate:
- fontRenderer->flush();
+ case FlushMode::Moderate:
+ fontRenderer.flush();
textureCache.flush();
pathCache.clear();
tessellationCache.clear();
// fall through
- case kFlushMode_Layers:
+ case FlushMode::Layers:
layerCache.clear();
renderBufferCache.clear();
break;
@@ -278,38 +261,6 @@ void Caches::flush(FlushMode mode) {
}
///////////////////////////////////////////////////////////////////////////////
-// Tiling
-///////////////////////////////////////////////////////////////////////////////
-
-void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard) {
- if (mExtensions.hasTiledRendering() && !Properties::debugOverdraw) {
- glStartTilingQCOM(x, y, width, height, (discard ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM));
- }
-}
-
-void Caches::endTiling() {
- if (mExtensions.hasTiledRendering() && !Properties::debugOverdraw) {
- glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
- }
-}
-
-bool Caches::hasRegisteredFunctors() {
- return mFunctorsCount > 0;
-}
-
-void Caches::registerFunctors(uint32_t functorCount) {
- mFunctorsCount += functorCount;
-}
-
-void Caches::unregisterFunctors(uint32_t functorCount) {
- if (functorCount > mFunctorsCount) {
- mFunctorsCount = 0;
- } else {
- mFunctorsCount -= functorCount;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
// Regions
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 804f609e9a0f..61e958d42148 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -17,15 +17,11 @@
#ifndef ANDROID_HWUI_CACHES_H
#define ANDROID_HWUI_CACHES_H
-#ifndef LOG_TAG
- #define LOG_TAG "OpenGLRenderer"
-#endif
-
-
#include "AssetAtlas.h"
#include "Dither.h"
#include "Extensions.h"
#include "FboCache.h"
+#include "GammaFontRenderer.h"
#include "GradientCache.h"
#include "LayerCache.h"
#include "PatchCache.h"
@@ -48,17 +44,16 @@
#include <utils/KeyedVector.h>
#include <utils/Singleton.h>
-#include <utils/Vector.h>
#include <cutils/compiler.h>
#include <SkPath.h>
+#include <vector>
+
namespace android {
namespace uirenderer {
-class GammaFontRenderer;
-
///////////////////////////////////////////////////////////////////////////////
// Caches
///////////////////////////////////////////////////////////////////////////////
@@ -87,10 +82,10 @@ private:
static Caches* sInstance;
public:
- enum FlushMode {
- kFlushMode_Layers = 0,
- kFlushMode_Moderate,
- kFlushMode_Full
+ enum class FlushMode {
+ Layers = 0,
+ Moderate,
+ Full
};
/**
@@ -107,7 +102,7 @@ public:
/**
* Destroys all resources associated with this cache. This should
- * be called after a flush(kFlushMode_Full).
+ * be called after a flush(FlushMode::Full).
*/
void terminate();
@@ -128,10 +123,6 @@ public:
*/
void deleteLayerDeferred(Layer* layer);
-
- void startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard);
- void endTiling();
-
/**
* Returns the mesh used to draw regions. Calling this method will
* bind a VBO of type GL_ELEMENT_ARRAY_BUFFER that contains the
@@ -145,10 +136,6 @@ public:
void dumpMemoryUsage();
void dumpMemoryUsage(String8& log);
- bool hasRegisteredFunctors();
- void registerFunctors(uint32_t functorCount);
- void unregisterFunctors(uint32_t functorCount);
-
// Misc
GLint maxTextureSize;
@@ -168,7 +155,7 @@ public:
TextDropShadowCache dropShadowCache;
FboCache fboCache;
- GammaFontRenderer* fontRenderer;
+ GammaFontRenderer fontRenderer;
TaskManager tasks;
@@ -190,8 +177,6 @@ public:
TextureState& textureState() { return *mTextureState; }
private:
-
- void initFont();
void initExtensions();
void initConstraints();
void initStaticProperties();
@@ -206,12 +191,10 @@ private:
std::unique_ptr<TextureVertex[]> mRegionMesh;
mutable Mutex mGarbageLock;
- Vector<Layer*> mLayerGarbage;
+ std::vector<Layer*> mLayerGarbage;
bool mInitialized;
- uint32_t mFunctorsCount;
-
// TODO: move below to RenderState
PixelBufferState* mPixelBufferState = nullptr;
TextureState* mTextureState = nullptr;
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index 160d9a8a87dd..4bd4ac8d4c37 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -19,6 +19,8 @@
#include <cutils/compiler.h>
+#include "utils/NinePatch.h"
+
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkMatrix.h>
@@ -62,6 +64,9 @@ public:
virtual int width() = 0;
virtual int height() = 0;
+ virtual void setHighContrastText(bool highContrastText) = 0;
+ virtual bool isHighContrastText() = 0;
+
// ----------------------------------------------------------------------------
// Canvas state operations
// ----------------------------------------------------------------------------
@@ -80,10 +85,6 @@ public:
virtual void getMatrix(SkMatrix* outMatrix) const = 0;
virtual void setMatrix(const SkMatrix& matrix) = 0;
- /// Like setMatrix(), but to be translated into local / view-relative coordinates
- /// rather than executed in global / device coordinates at rendering time.
- virtual void setLocalMatrix(const SkMatrix& matrix) = 0;
-
virtual void concat(const SkMatrix& matrix) = 0;
virtual void rotate(float degrees) = 0;
virtual void scale(float sx, float sy) = 0;
@@ -118,6 +119,7 @@ public:
virtual void drawLines(const float* points, int count, const SkPaint& paint) = 0;
virtual void drawRect(float left, float top, float right, float bottom,
const SkPaint& paint) = 0;
+ 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 drawCircle(float x, float y, float radius, const SkPaint& paint) = 0;
@@ -140,6 +142,9 @@ public:
float dstRight, float dstBottom, const SkPaint* paint) = 0;
virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) = 0;
+ virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) = 0;
// Text
/**
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index e22b0d3084ab..eca71c6e0e8d 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -28,19 +28,36 @@ CanvasState::CanvasState(CanvasStateClient& renderer)
, mWidth(-1)
, mHeight(-1)
, mSaveCount(1)
- , mFirstSnapshot(new Snapshot)
, mCanvas(renderer)
- , mSnapshot(mFirstSnapshot) {
-
+ , 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::initializeSaveStack(float clipLeft, float clipTop,
+void CanvasState::initializeSaveStack(
+ int viewportWidth, int viewportHeight,
+ float clipLeft, float clipTop,
float clipRight, float clipBottom, const Vector3& lightCenter) {
- mSnapshot = new Snapshot(mFirstSnapshot,
+ if (mWidth != viewportWidth || mHeight != viewportHeight) {
+ mWidth = viewportWidth;
+ mHeight = viewportHeight;
+ mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
+ mCanvas.onViewportInitialized();
+ }
+
+ freeAllSnapshots();
+ mSnapshot = allocSnapshot(&mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
mSnapshot->fbo = mCanvas.getTargetFbo();
@@ -48,18 +65,36 @@ void CanvasState::initializeSaveStack(float clipLeft, float clipTop,
mSaveCount = 1;
}
-void CanvasState::setViewport(int width, int height) {
- mWidth = width;
- mHeight = height;
- mFirstSnapshot->initializeViewport(width, height);
- mCanvas.onViewportInitialized();
+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);
+}
- // create a temporary 1st snapshot, so old snapshots are released,
- // and viewport can be queried safely.
- // TODO: remove, combine viewport + save stack initialization
- mSnapshot = new Snapshot(mFirstSnapshot,
- SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- mSaveCount = 1;
+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);
+ }
}
///////////////////////////////////////////////////////////////////////////////
@@ -73,7 +108,7 @@ void CanvasState::setViewport(int width, int height) {
* stack, and ensures restoreToCount() doesn't call back into subclass overrides.
*/
int CanvasState::saveSnapshot(int flags) {
- mSnapshot = new Snapshot(mSnapshot, flags);
+ mSnapshot = allocSnapshot(mSnapshot, flags);
return mSaveCount++;
}
@@ -85,14 +120,16 @@ int CanvasState::save(int flags) {
* Guaranteed to restore without side-effects.
*/
void CanvasState::restoreSnapshot() {
- sp<Snapshot> toRemove = mSnapshot;
- sp<Snapshot> toRestore = mSnapshot->previous;
+ Snapshot* toRemove = mSnapshot;
+ Snapshot* toRestore = mSnapshot->previous;
mSaveCount--;
mSnapshot = toRestore;
// subclass handles restore implementation
mCanvas.onSnapshotRestored(*toRemove, *toRestore);
+
+ freeSnapshot(toRemove);
}
void CanvasState::restore() {
@@ -138,7 +175,7 @@ void CanvasState::setMatrix(const SkMatrix& matrix) {
}
void CanvasState::setMatrix(const Matrix4& matrix) {
- mSnapshot->transform->load(matrix);
+ *(mSnapshot->transform) = matrix;
}
void CanvasState::concatMatrix(const SkMatrix& matrix) {
@@ -155,17 +192,20 @@ void CanvasState::concatMatrix(const Matrix4& matrix) {
///////////////////////////////////////////////////////////////////////////////
bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
- mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op);
+ mSnapshot->clip(left, top, right, bottom, op);
+ mDirtyClip = true;
return !mSnapshot->clipIsEmpty();
}
bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) {
- mDirtyClip |= mSnapshot->clipPath(*path, op);
+ mSnapshot->clipPath(*path, op);
+ mDirtyClip = true;
return !mSnapshot->clipIsEmpty();
}
bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) {
- mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op);
+ mSnapshot->clipRegionTransformed(*region, op);
+ mDirtyClip = true;
return !mSnapshot->clipIsEmpty();
}
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index b35db28eaf82..be57f44210ef 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -17,12 +17,12 @@
#ifndef ANDROID_HWUI_CANVAS_STATE_H
#define ANDROID_HWUI_CANVAS_STATE_H
+#include "Snapshot.h"
+
#include <SkMatrix.h>
#include <SkPath.h>
#include <SkRegion.h>
-#include "Snapshot.h"
-
namespace android {
namespace uirenderer {
@@ -71,7 +71,7 @@ public:
* (getClip/Matrix), but so that quickRejection can also be used.
*/
-class ANDROID_API CanvasState {
+class CanvasState {
public:
CanvasState(CanvasStateClient& renderer);
~CanvasState();
@@ -80,11 +80,10 @@ public:
* Initializes the first snapshot, computing the projection matrix,
* and stores the dimensions of the render target.
*/
- void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom,
+ void initializeSaveStack(int viewportWidth, int viewportHeight,
+ float clipLeft, float clipTop, float clipRight, float clipBottom,
const Vector3& lightCenter);
- void setViewport(int width, int height);
-
bool hasRectToRectTransform() const {
return CC_LIKELY(currentTransform()->rectToRect());
}
@@ -159,15 +158,14 @@ public:
int getHeight() const { return mHeight; }
bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); }
- inline const Snapshot* currentSnapshot() const {
- return mSnapshot != nullptr ? mSnapshot.get() : mFirstSnapshot.get();
- }
- inline Snapshot* writableSnapshot() { return mSnapshot.get(); }
- inline const Snapshot* firstSnapshot() const { return mFirstSnapshot.get(); }
+ inline const Snapshot* currentSnapshot() const { return mSnapshot; }
+ inline Snapshot* writableSnapshot() { return mSnapshot; }
+ inline const Snapshot* firstSnapshot() const { return &mFirstSnapshot; }
private:
- /// No default constructor - must supply a CanvasStateClient (mCanvas).
- CanvasState();
+ Snapshot* allocSnapshot(Snapshot* previous, int savecount);
+ void freeSnapshot(Snapshot* snapshot);
+ void freeAllSnapshots();
/// indicates that the clip has been changed since the last time it was consumed
bool mDirtyClip;
@@ -179,13 +177,18 @@ private:
int mSaveCount;
/// Base state
- sp<Snapshot> mFirstSnapshot;
+ Snapshot mFirstSnapshot;
/// Host providing callbacks
CanvasStateClient& mCanvas;
/// Current state
- sp<Snapshot> mSnapshot;
+ Snapshot* mSnapshot;
+
+ // Pool of allocated snapshots to re-use
+ // NOTE: The dtors have already been invoked!
+ Snapshot* mSnapshotPool = nullptr;
+ int mSnapshotPoolCount = 0;
}; // class CanvasState
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index b1a68447fc21..a9d1e4284d2e 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -23,18 +23,8 @@
namespace android {
namespace uirenderer {
-static bool intersect(Rect& r, const Rect& r2) {
- bool hasIntersection = r.intersect(r2);
- if (!hasIntersection) {
- r.setEmpty();
- }
- return hasIntersection;
-}
-
static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
- Vertex v;
- v.x = x;
- v.y = y;
+ Vertex v = {x, y};
transform.mapPoint(v.x, v.y);
transformedBounds.expandToCoverVertex(v.x, v.y);
}
@@ -69,9 +59,8 @@ bool TransformedRectangle::canSimplyIntersectWith(
return mTransform == other.mTransform;
}
-bool TransformedRectangle::intersectWith(const TransformedRectangle& other) {
- Rect translatedBounds(other.mBounds);
- return intersect(mBounds, translatedBounds);
+void TransformedRectangle::intersectWith(const TransformedRectangle& other) {
+ mBounds.doIntersect(other.mBounds);
}
bool TransformedRectangle::isEmpty() const {
@@ -148,7 +137,7 @@ Rect RectangleList::calculateBounds() const {
if (index == 0) {
bounds = tr.transformedBounds();
} else {
- bounds.intersect(tr.transformedBounds());
+ bounds.doIntersect(tr.transformedBounds());
}
}
return bounds;
@@ -187,7 +176,7 @@ SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
*/
ClipArea::ClipArea()
- : mMode(kModeRectangle) {
+ : mMode(Mode::Rectangle) {
}
/*
@@ -200,45 +189,46 @@ void ClipArea::setViewportDimensions(int width, int height) {
}
void ClipArea::setEmpty() {
- mMode = kModeRectangle;
+ mMode = Mode::Rectangle;
mClipRect.setEmpty();
mClipRegion.setEmpty();
mRectangleList.setEmpty();
}
void ClipArea::setClip(float left, float top, float right, float bottom) {
- mMode = kModeRectangle;
+ mMode = Mode::Rectangle;
mClipRect.set(left, top, right, bottom);
mClipRegion.setEmpty();
}
-bool ClipArea::clipRectWithTransform(float left, float top, float right,
+void ClipArea::clipRectWithTransform(float left, float top, float right,
float bottom, const mat4* transform, SkRegion::Op op) {
Rect r(left, top, right, bottom);
- return clipRectWithTransform(r, transform, op);
+ clipRectWithTransform(r, transform, op);
}
-bool ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
+void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
SkRegion::Op op) {
switch (mMode) {
- case kModeRectangle:
- return rectangleModeClipRectWithTransform(r, transform, op);
- case kModeRectangleList:
- return rectangleListModeClipRectWithTransform(r, transform, op);
- case kModeRegion:
- return regionModeClipRectWithTransform(r, transform, op);
+ case Mode::Rectangle:
+ rectangleModeClipRectWithTransform(r, transform, op);
+ break;
+ case Mode::RectangleList:
+ rectangleListModeClipRectWithTransform(r, transform, op);
+ break;
+ case Mode::Region:
+ regionModeClipRectWithTransform(r, transform, op);
+ break;
}
- return false;
}
-bool ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
+void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
enterRegionMode();
mClipRegion.op(region, op);
onClipRegionUpdated();
- return true;
}
-bool ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
+void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
SkRegion::Op op) {
SkMatrix skTransform;
transform->copyTo(skTransform);
@@ -246,7 +236,7 @@ bool ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
path.transform(skTransform, &transformed);
SkRegion region;
regionFromPath(transformed, region);
- return clipRegion(region, op);
+ clipRegion(region, op);
}
/*
@@ -257,41 +247,38 @@ 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 = kModeRectangle;
+ mMode = Mode::Rectangle;
}
-bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
+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 true;
+ return;
} else if (op != SkRegion::kIntersect_Op) {
enterRegionMode();
- return regionModeClipRectWithTransform(r, transform, op);
+ regionModeClipRectWithTransform(r, transform, op);
+ return;
}
if (transform->rectToRect()) {
Rect transformed(r);
transform->mapRect(transformed);
- bool hasIntersection = mClipRect.intersect(transformed);
- if (!hasIntersection) {
- mClipRect.setEmpty();
- }
- return true;
+ mClipRect.doIntersect(transformed);
+ return;
}
enterRectangleListMode();
- return rectangleListModeClipRectWithTransform(r, transform, op);
+ rectangleListModeClipRectWithTransform(r, transform, op);
}
-bool ClipArea::rectangleModeClipRectWithTransform(float left, float top,
+void ClipArea::rectangleModeClipRectWithTransform(float left, float top,
float right, float bottom, const mat4* transform, SkRegion::Op op) {
Rect r(left, top, right, bottom);
- bool result = rectangleModeClipRectWithTransform(r, transform, op);
+ rectangleModeClipRectWithTransform(r, transform, op);
mClipRect = mRectangleList.calculateBounds();
- return result;
}
/*
@@ -302,25 +289,24 @@ 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 == kModeRectangle);
- mMode = kModeRectangleList;
+ ALOG_ASSERT(mMode == Mode::Rectangle);
+ mMode = Mode::RectangleList;
mRectangleList.set(mClipRect, Matrix4::identity());
}
-bool ClipArea::rectangleListModeClipRectWithTransform(const Rect& r,
+void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r,
const mat4* transform, SkRegion::Op op) {
if (op != SkRegion::kIntersect_Op
|| !mRectangleList.intersectWith(r, *transform)) {
enterRegionMode();
- return regionModeClipRectWithTransform(r, transform, op);
+ regionModeClipRectWithTransform(r, transform, op);
}
- return true;
}
-bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top,
+void ClipArea::rectangleListModeClipRectWithTransform(float left, float top,
float right, float bottom, const mat4* transform, SkRegion::Op op) {
Rect r(left, top, right, bottom);
- return rectangleListModeClipRectWithTransform(r, transform, op);
+ rectangleListModeClipRectWithTransform(r, transform, op);
}
/*
@@ -329,9 +315,9 @@ bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top,
void ClipArea::enterRegionMode() {
Mode oldMode = mMode;
- mMode = kModeRegion;
- if (oldMode != kModeRegion) {
- if (oldMode == kModeRectangle) {
+ mMode = Mode::Region;
+ if (oldMode != Mode::Region) {
+ if (oldMode == Mode::Rectangle) {
mClipRegion.setRect(mClipRect.left, mClipRect.top,
mClipRect.right, mClipRect.bottom);
} else {
@@ -341,20 +327,18 @@ void ClipArea::enterRegionMode() {
}
}
-bool ClipArea::regionModeClipRectWithTransform(const Rect& r,
+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();
- return true;
}
-bool ClipArea::regionModeClipRectWithTransform(float left, float top,
+void ClipArea::regionModeClipRectWithTransform(float left, float top,
float right, float bottom, const mat4* transform, SkRegion::Op op) {
- return regionModeClipRectWithTransform(Rect(left, top, right, bottom),
- transform, op);
+ regionModeClipRectWithTransform(Rect(left, top, right, bottom), transform, op);
}
void ClipArea::onClipRegionUpdated() {
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 51ef27b4e9cc..f88fd92e234d 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -33,7 +33,7 @@ public:
TransformedRectangle(const Rect& bounds, const Matrix4& transform);
bool canSimplyIntersectWith(const TransformedRectangle& other) const;
- bool intersectWith(const TransformedRectangle& other);
+ void intersectWith(const TransformedRectangle& other);
bool isEmpty() const;
@@ -80,6 +80,13 @@ private:
};
class ClipArea {
+private:
+ enum class Mode {
+ Rectangle,
+ Region,
+ RectangleList
+ };
+
public:
ClipArea();
@@ -91,12 +98,12 @@ public:
void setEmpty();
void setClip(float left, float top, float right, float bottom);
- bool clipRectWithTransform(float left, float top, float right, float bottom,
- const mat4* transform, SkRegion::Op op = SkRegion::kIntersect_Op);
- bool clipRectWithTransform(const Rect& r, const mat4* transform,
- SkRegion::Op op = SkRegion::kIntersect_Op);
- bool clipRegion(const SkRegion& region, SkRegion::Op op = SkRegion::kIntersect_Op);
- bool clipPathWithTransform(const SkPath& path, const mat4* transform,
+ void clipRectWithTransform(float left, float top, float right, float bottom,
+ const mat4* transform, SkRegion::Op op);
+ void clipRectWithTransform(const Rect& r, const mat4* transform,
+ SkRegion::Op op);
+ void clipRegion(const SkRegion& region, SkRegion::Op op);
+ void clipPathWithTransform(const SkPath& path, const mat4* transform,
SkRegion::Op op);
const Rect& getClipRect() const {
@@ -112,41 +119,39 @@ public:
}
bool isRegion() const {
- return kModeRegion == mMode;
+ return Mode::Region == mMode;
}
bool isSimple() const {
- return mMode == kModeRectangle;
+ return mMode == Mode::Rectangle;
}
bool isRectangleList() const {
- return mMode == kModeRectangleList;
+ return mMode == Mode::RectangleList;
}
private:
void enterRectangleMode();
- bool rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
- bool rectangleModeClipRectWithTransform(float left, float top, float right,
+ void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
+ void rectangleModeClipRectWithTransform(float left, float top, float right,
float bottom, const mat4* transform, SkRegion::Op op);
void enterRectangleListMode();
- bool rectangleListModeClipRectWithTransform(float left, float top,
+ void rectangleListModeClipRectWithTransform(float left, float top,
float right, float bottom, const mat4* transform, SkRegion::Op op);
- bool rectangleListModeClipRectWithTransform(const Rect& r,
+ void rectangleListModeClipRectWithTransform(const Rect& r,
const mat4* transform, SkRegion::Op op);
void enterRegionModeFromRectangleMode();
void enterRegionModeFromRectangleListMode();
void enterRegionMode();
- bool regionModeClipRectWithTransform(const Rect& r, const mat4* transform,
+ void regionModeClipRectWithTransform(const Rect& r, const mat4* transform,
SkRegion::Op op);
- bool regionModeClipRectWithTransform(float left, float top, float right,
+ void regionModeClipRectWithTransform(float left, float top, float right,
float bottom, const mat4* transform, SkRegion::Op op);
void ensureClipRegion();
void onClipRegionUpdated();
- bool clipRegionOp(float left, float top, float right, float bottom,
- SkRegion::Op op);
SkRegion createViewportRegion() {
return SkRegion(mViewportBounds.toSkIRect());
@@ -158,12 +163,6 @@ private:
pathAsRegion.setPath(path, createViewportRegion());
}
- enum Mode {
- kModeRectangle,
- kModeRegion,
- kModeRectangleList
- };
-
Mode mMode;
Rect mViewportBounds;
Rect mClipRect;
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 9bd3bdc5617e..c2e14a29f29e 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -121,7 +121,14 @@ void DamageAccumulator::popTransform() {
static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
if (in.isEmpty()) return;
Rect temp(in);
- matrix->mapRect(temp);
+ if (CC_LIKELY(!matrix->isPerspective())) {
+ matrix->mapRect(temp);
+ } else {
+ // Don't attempt to calculate damage for a perspective transform
+ // as the numbers this works with can break the perspective
+ // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX
+ temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
+ }
out->join(RECT_ARGS(temp));
}
@@ -134,7 +141,14 @@ static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRe
const SkMatrix* transform = props.getTransformMatrix();
SkRect temp(in);
if (transform && !transform->isIdentity()) {
- transform->mapRect(&temp);
+ if (CC_LIKELY(!transform->hasPerspective())) {
+ transform->mapRect(&temp);
+ } else {
+ // Don't attempt to calculate damage for a perspective transform
+ // as the numbers this works with can break the perspective
+ // calculations. Just give up and expand to DIRTY_MIN/DIRTY_MAX
+ temp.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
+ }
}
temp.offset(props.getLeft(), props.getTop());
out->join(temp);
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index dd3365a49d78..e44fc20feaa8 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -24,6 +24,11 @@
#include "utils/Macros.h"
+// Smaller than INT_MIN/INT_MAX because we offset these values
+// and thus don't want to be adding offsets to INT_MAX, that's bad
+#define DIRTY_MIN (-0x7ffffff-1)
+#define DIRTY_MAX (0x7ffffff)
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 5808aaca76be..e98fa0440591 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -20,9 +20,6 @@
// Turn on to check for OpenGL errors on each frame
#define DEBUG_OPENGL 1
-// Turn on to display informations about the GPU
-#define DEBUG_EXTENSIONS 0
-
// Turn on to enable initialization information
#define DEBUG_INIT 0
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 03aecd42d16a..a1825c5bc4c1 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
#include <SkCanvas.h>
#include <utils/Trace.h>
@@ -47,6 +44,12 @@ namespace uirenderer {
#define DEBUG_COLOR_MERGEDBATCH 0x5f7f7fff
#define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f
+static bool avoidOverdraw() {
+ // Don't avoid overdraw when visualizing it, since that makes it harder to
+ // debug where it's coming from, and when the problem occurs.
+ return !Properties::debugOverdraw;
+};
+
/////////////////////////////////////////////////////////////////////////////////
// Operation Batches
/////////////////////////////////////////////////////////////////////////////////
@@ -72,7 +75,7 @@ public:
// NOTE: ignore empty bounds special case, since we don't merge across those ops
mBounds.unionWith(state->mBounds);
mAllOpsOpaque &= opaqueOverBounds;
- mOps.add(OpStatePair(op, state));
+ mOps.push_back(OpStatePair(op, state));
}
bool intersects(const Rect& rect) {
@@ -136,7 +139,7 @@ public:
inline int count() const { return mOps.size(); }
protected:
- Vector<OpStatePair> mOps;
+ std::vector<OpStatePair> mOps;
Rect mBounds; // union of bounds of contained ops
private:
bool mAllOpsOpaque;
@@ -221,7 +224,10 @@ public:
// if paints are equal, then modifiers + paint attribs don't need to be compared
if (op->mPaint == mOps[0].op->mPaint) return true;
- if (op->getPaintAlpha() != mOps[0].op->getPaintAlpha()) return false;
+ if (PaintUtils::getAlphaDirect(op->mPaint)
+ != PaintUtils::getAlphaDirect(mOps[0].op->mPaint)) {
+ return false;
+ }
if (op->mPaint && mOps[0].op->mPaint &&
op->mPaint->getColorFilter() != mOps[0].op->mPaint->getColorFilter()) {
@@ -421,7 +427,7 @@ void DeferredDisplayList::addSaveLayer(OpenGLRenderer& renderer,
this, op, op->getFlags(), newSaveCount);
storeStateOpBarrier(renderer, op);
- mSaveStack.push(newSaveCount);
+ mSaveStack.push_back(newSaveCount);
}
/**
@@ -436,7 +442,7 @@ void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newS
// store and replay the save operation, as it may be needed to correctly playback the clip
DEFER_LOGD(" adding save barrier with new save count %d", newSaveCount);
storeStateOpBarrier(renderer, op);
- mSaveStack.push(newSaveCount);
+ mSaveStack.push_back(newSaveCount);
}
}
@@ -459,11 +465,11 @@ void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* o
resetBatchingState();
}
- if (mSaveStack.isEmpty() || newSaveCount > mSaveStack.top()) {
+ if (mSaveStack.empty() || newSaveCount > mSaveStack.back()) {
return;
}
- while (!mSaveStack.isEmpty() && mSaveStack.top() >= newSaveCount) mSaveStack.pop();
+ while (!mSaveStack.empty() && mSaveStack.back() >= newSaveCount) mSaveStack.pop_back();
storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + FLUSH_SAVE_STACK_DEPTH);
}
@@ -495,10 +501,10 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
// the merge path in those cases
deferInfo.mergeable &= !recordingComplexClip();
deferInfo.opaqueOverBounds &= !recordingComplexClip()
- && mSaveStack.isEmpty()
+ && mSaveStack.empty()
&& !state->mRoundRectClipState;
- if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
+ if (CC_LIKELY(avoidOverdraw()) && mBatches.size() &&
state->mClipSideFlags != kClipSide_ConservativeFull &&
deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) {
// avoid overdraw by resetting drawing state + discarding drawing ops
@@ -510,7 +516,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
// TODO: elegant way to reuse batches?
DrawBatch* b = new DrawBatch(deferInfo);
b->add(op, state, deferInfo.opaqueOverBounds);
- mBatches.add(b);
+ mBatches.push_back(b);
return;
}
@@ -520,12 +526,12 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
// insertion point of a new batch, will hopefully be immediately after similar batch
// (eventually, should be similar shader)
int insertBatchIndex = mBatches.size();
- if (!mBatches.isEmpty()) {
+ if (!mBatches.empty()) {
if (state->mBounds.isEmpty()) {
- // don't know the bounds for op, so add to last batch and start from scratch on next op
+ // don't know the bounds for op, so create new batch and start from scratch on next op
DrawBatch* b = new DrawBatch(deferInfo);
b->add(op, state, deferInfo.opaqueOverBounds);
- mBatches.add(b);
+ mBatches.push_back(b);
resetBatchingState();
#if DEBUG_DEFER
DEFER_LOGD("Warning: Encountered op with empty bounds, resetting batches");
@@ -594,7 +600,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
DEFER_LOGD("creating %singBatch %p, bid %x, at %d",
deferInfo.mergeable ? "Merg" : "Draw",
targetBatch, deferInfo.batchId, insertBatchIndex);
- mBatches.insertAt(targetBatch, insertBatchIndex);
+ mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
}
targetBatch->add(op, state, deferInfo.opaqueOverBounds);
@@ -605,7 +611,7 @@ void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp*
DeferredDisplayState* state = createState();
renderer.storeDisplayState(*state, getStateOpDeferFlags());
- mBatches.add(new StateOpBatch(op, state));
+ mBatches.push_back(new StateOpBatch(op, state));
resetBatchingState();
}
@@ -618,7 +624,7 @@ void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, S
// doesn't have kClip_SaveFlag set
DeferredDisplayState* state = createState();
renderer.storeDisplayState(*state, getStateOpDeferFlags());
- mBatches.add(new RestoreToCountBatch(op, state, newSaveCount));
+ mBatches.push_back(new RestoreToCountBatch(op, state, newSaveCount));
resetBatchingState();
}
@@ -626,7 +632,7 @@ void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, S
// Replay / flush
/////////////////////////////////////////////////////////////////////////////////
-static void replayBatchList(const Vector<Batch*>& batchList,
+static void replayBatchList(const std::vector<Batch*>& batchList,
OpenGLRenderer& renderer, Rect& dirty) {
for (unsigned int i = 0; i < batchList.size(); i++) {
@@ -639,7 +645,7 @@ static void replayBatchList(const Vector<Batch*>& batchList,
void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
ATRACE_NAME("flush drawing commands");
- Caches::getInstance().fontRenderer->endPrecaching();
+ Caches::getInstance().fontRenderer.endPrecaching();
if (isEmpty()) return; // nothing to flush
renderer.restoreToCount(1);
@@ -650,7 +656,7 @@ void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
// save and restore so that reordering doesn't affect final state
renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- if (CC_LIKELY(mAvoidOverdraw)) {
+ if (CC_LIKELY(avoidOverdraw())) {
for (unsigned int i = 1; i < mBatches.size(); i++) {
if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
discardDrawingBatches(i - 1);
@@ -672,7 +678,7 @@ void DeferredDisplayList::discardDrawingBatches(const unsigned int maxIndex) {
// leave deferred state ops alone for simplicity (empty save restore pairs may now exist)
if (mBatches[i] && mBatches[i]->purelyDrawBatch()) {
delete mBatches[i];
- mBatches.replaceAt(nullptr, i);
+ mBatches[i] = nullptr;
}
}
mEarliestUnclearedIndex = maxIndex + 1;
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 3bc4904d6921..2d5979f2f1a7 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -21,12 +21,13 @@
#include <utils/Errors.h>
#include <utils/LinearAllocator.h>
-#include <utils/Vector.h>
#include "Matrix.h"
#include "OpenGLRenderer.h"
#include "Rect.h"
+#include <vector>
+
class SkBitmap;
namespace android {
@@ -48,7 +49,7 @@ typedef const void* mergeid_t;
class DeferredDisplayState {
public:
- /** static void* operator new(size_t size); PURPOSELY OMITTED **/
+ static void* operator new(size_t size) = delete;
static void* operator new(size_t size, LinearAllocator& allocator) {
return allocator.alloc(size);
}
@@ -60,7 +61,6 @@ public:
bool mClipValid;
Rect mClip;
int mClipSideFlags; // specifies which sides of the bounds are clipped, unclipped if cleared
- bool mClipped;
mat4 mMatrix;
float mAlpha;
const RoundRectClipState* mRoundRectClipState;
@@ -82,8 +82,8 @@ public:
class DeferredDisplayList {
friend struct DeferStateStruct; // used to give access to allocator
public:
- DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) :
- mBounds(bounds), mAvoidOverdraw(avoidOverdraw) {
+ DeferredDisplayList(const Rect& bounds)
+ : mBounds(bounds) {
clear();
}
~DeferredDisplayList() { clear(); }
@@ -101,7 +101,7 @@ public:
kOpBatch_Count, // Add other batch ids before this
};
- bool isEmpty() { return mBatches.isEmpty(); }
+ bool isEmpty() { return mBatches.empty(); }
/**
* Plays back all of the draw ops recorded into batches to the renderer.
@@ -151,17 +151,16 @@ private:
// layer space bounds of rendering
Rect mBounds;
- const bool mAvoidOverdraw;
/**
* At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so
* that when an associated restoreToCount is deferred, it can be recorded as a
* RestoreToCountBatch
*/
- Vector<int> mSaveStack;
+ std::vector<int> mSaveStack;
int mComplexClipStackStart;
- Vector<Batch*> mBatches;
+ std::vector<Batch*> mBatches;
// Maps batch ids to the most recent *non-merging* batch of that id
Batch* mBatchLookup[kOpBatch_Count];
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index a17904e31047..70383340fc8d 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -24,14 +24,13 @@
namespace android {
namespace uirenderer {
-DeferredLayerUpdater::DeferredLayerUpdater(renderthread::RenderThread& thread, Layer* layer)
+DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer)
: mSurfaceTexture(nullptr)
, mTransform(nullptr)
, mNeedsGLContextAttach(false)
, mUpdateTexImage(false)
, mLayer(layer)
- , mCaches(Caches::getInstance())
- , mRenderThread(thread) {
+ , mCaches(Caches::getInstance()) {
mWidth = mLayer->layer.getWidth();
mHeight = mLayer->layer.getHeight();
mBlend = mLayer->isBlend();
@@ -48,7 +47,8 @@ DeferredLayerUpdater::~DeferredLayerUpdater() {
}
void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
- OpenGLRenderer::getAlphaAndModeDirect(paint, &mAlpha, &mMode);
+ mAlpha = PaintUtils::getAlphaDirect(paint);
+ mMode = PaintUtils::getXfermodeDirect(paint);
SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
SkRefCnt_SafeAssign(mColorFilter, colorFilter);
}
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 82f2741b7478..df7c594cf187 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -35,7 +35,7 @@ class DeferredLayerUpdater : public VirtualLightRefBase {
public:
// Note that DeferredLayerUpdater assumes it is taking ownership of the layer
// and will not call incrementRef on it as a result.
- ANDROID_API DeferredLayerUpdater(renderthread::RenderThread& thread, Layer* layer);
+ ANDROID_API DeferredLayerUpdater(Layer* layer);
ANDROID_API ~DeferredLayerUpdater();
ANDROID_API bool setSize(int width, int height) {
@@ -101,7 +101,6 @@ private:
Layer* mLayer;
Caches& mCaches;
- renderthread::RenderThread& mRenderThread;
void doUpdateTexImage();
};
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index e679bff18c86..59f0d7cc7346 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
#include <SkCanvas.h>
#include <algorithm>
@@ -23,32 +21,51 @@
#include "Debug.h"
#include "DisplayList.h"
+#include "RenderNode.h"
+
+#if HWUI_NEW_OPS
+#include "RecordedOp.h"
+#else
#include "DisplayListOp.h"
+#endif
namespace android {
namespace uirenderer {
-DisplayListData::DisplayListData()
+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)
, hasDrawOps(false) {
}
-DisplayListData::~DisplayListData() {
+DisplayList::~DisplayList() {
cleanupResources();
}
-void DisplayListData::cleanupResources() {
- ResourceCache& resourceCache = ResourceCache::getInstance();
- resourceCache.lock();
+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.itemAt(i));
- }
+ for (size_t i = 0; i < patchResources.size(); i++) {
+ resourceCache.decrementRefcountLocked(patchResources[i]);
+ }
- resourceCache.unlock();
+ resourceCache.unlock();
+ }
for (size_t i = 0; i < pathResources.size(); i++) {
- const SkPath* path = pathResources.itemAt(i);
+ const SkPath* path = pathResources[i];
if (path->unique() && Caches::hasInstance()) {
Caches::getInstance().pathCache.removeDeferred(path);
}
@@ -61,9 +78,11 @@ void DisplayListData::cleanupResources() {
regions.clear();
}
-size_t DisplayListData::addChild(DrawRenderNodeOp* op) {
- mReferenceHolders.push(op->renderNode());
- return mChildren.add(op);
+size_t DisplayList::addChild(NodeOpType* op) {
+ referenceHolders.push_back(op->renderNode);
+ size_t index = children.size();
+ children.push_back(op);
+ return index;
}
}; // namespace uirenderer
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 7fbda1f6b192..86796c5a5e0c 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -17,10 +17,6 @@
#ifndef ANDROID_HWUI_DISPLAY_LIST_H
#define ANDROID_HWUI_DISPLAY_LIST_H
-#ifndef LOG_TAG
- #define LOG_TAG "OpenGLRenderer"
-#endif
-
#include <SkCamera.h>
#include <SkMatrix.h>
@@ -31,7 +27,6 @@
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
-#include <utils/Vector.h>
#include <cutils/compiler.h>
@@ -43,6 +38,8 @@
#include "Matrix.h"
#include "RenderProperties.h"
+#include <vector>
+
class SkBitmap;
class SkPaint;
class SkPath;
@@ -58,12 +55,19 @@ class OpenGLRenderer;
class Rect;
class Layer;
-class ClipRectOp;
-class SaveLayerOp;
-class SaveOp;
-class RestoreToCountOp;
+#if HWUI_NEW_OPS
+struct RecordedOp;
+struct RenderNodeOp;
+
+typedef RecordedOp BaseOpType;
+typedef RenderNodeOp NodeOpType;
+#else
class DrawRenderNodeOp;
+typedef DisplayListOp BaseOpType;
+typedef DrawRenderNodeOp NodeOpType;
+#endif
+
/**
* Holds data used in the playback a tree of DisplayLists.
*/
@@ -108,15 +112,16 @@ struct ReplayStateStruct : public PlaybackStateStruct {
/**
* Data structure that holds the list of commands used in display list stream
*/
-class DisplayListData {
+class DisplayList {
friend class DisplayListCanvas;
+ friend class RecordingCanvas;
public:
struct Chunk {
- // range of included ops in DLD::displayListOps
+ // range of included ops in DisplayList::ops()
size_t beginOpIndex;
size_t endOpIndex;
- // range of included children in DLD::mChildren
+ // range of included children in DisplayList::children()
size_t beginChildIndex;
size_t endChildIndex;
@@ -124,32 +129,25 @@ public:
bool reorderChildren;
};
- DisplayListData();
- ~DisplayListData();
-
- // pointers to all ops within display list, pointing into allocator data
- Vector<DisplayListOp*> displayListOps;
+ DisplayList();
+ ~DisplayList();
// index of DisplayListOp restore, after which projected descendents should be drawn
int projectionReceiveIndex;
- Vector<const SkBitmap*> bitmapResources;
- Vector<const SkPath*> pathResources;
- Vector<const Res_png_9patch*> patchResources;
+ const LsaVector<Chunk>& getChunks() const { return chunks; }
+ const LsaVector<BaseOpType*>& getOps() const { return ops; }
- std::vector<std::unique_ptr<const SkPaint>> paints;
- std::vector<std::unique_ptr<const SkRegion>> regions;
- Vector<Functor*> functors;
+ const LsaVector<NodeOpType*>& getChildren() const { return children; }
- const Vector<Chunk>& getChunks() const {
- return chunks;
- }
+ const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
+ const LsaVector<Functor*>& getFunctors() const { return functors; }
+
+ size_t addChild(NodeOpType* childOp);
- size_t addChild(DrawRenderNodeOp* childOp);
- const Vector<DrawRenderNodeOp*>& children() { return mChildren; }
void ref(VirtualLightRefBase* prop) {
- mReferenceHolders.push(prop);
+ referenceHolders.push_back(prop);
}
size_t getUsedSize() {
@@ -160,15 +158,27 @@ public:
}
private:
- Vector< sp<VirtualLightRefBase> > mReferenceHolders;
+ // allocator into which all ops and LsaVector arrays allocated
+ LinearAllocator allocator;
+ LinearStdAllocator<void*> stdAllocator;
- // list of children display lists for quick, non-drawing traversal
- Vector<DrawRenderNodeOp*> mChildren;
+ LsaVector<Chunk> chunks;
+ LsaVector<BaseOpType*> ops;
- Vector<Chunk> chunks;
+ // 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<const SkBitmap*> 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<Functor*> functors;
- // allocator into which all ops were allocated
- LinearAllocator allocator;
bool hasDrawOps;
void cleanupResources();
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 2dd52788074d..bad397219398 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -31,70 +31,62 @@
namespace android {
namespace uirenderer {
-DisplayListCanvas::DisplayListCanvas()
+DisplayListCanvas::DisplayListCanvas(int width, int height)
: mState(*this)
, mResourceCache(ResourceCache::getInstance())
- , mDisplayListData(nullptr)
+ , mDisplayList(nullptr)
, mTranslateX(0.0f)
, mTranslateY(0.0f)
, mHasDeferredTranslate(false)
, mDeferredBarrierType(kBarrier_None)
, mHighContrastText(false)
, mRestoreSaveCount(-1) {
+ reset(width, height);
}
DisplayListCanvas::~DisplayListCanvas() {
- LOG_ALWAYS_FATAL_IF(mDisplayListData,
+ LOG_ALWAYS_FATAL_IF(mDisplayList,
"Destroyed a DisplayListCanvas during a record!");
}
-///////////////////////////////////////////////////////////////////////////////
-// Operations
-///////////////////////////////////////////////////////////////////////////////
-
-DisplayListData* DisplayListCanvas::finishRecording() {
- mPaintMap.clear();
- mRegionMap.clear();
- mPathMap.clear();
- DisplayListData* data = mDisplayListData;
- mDisplayListData = nullptr;
- mSkiaCanvasProxy.reset(nullptr);
- return data;
-}
-
-void DisplayListCanvas::prepareDirty(float left, float top,
- float right, float bottom) {
-
- LOG_ALWAYS_FATAL_IF(mDisplayListData,
+void DisplayListCanvas::reset(int width, int height) {
+ LOG_ALWAYS_FATAL_IF(mDisplayList,
"prepareDirty called a second time during a recording!");
- mDisplayListData = new DisplayListData();
+ mDisplayList = new DisplayList();
- mState.initializeSaveStack(0, 0, mState.getWidth(), mState.getHeight(), Vector3());
+ mState.initializeSaveStack(width, height,
+ 0, 0, width, height, Vector3());
mDeferredBarrierType = kBarrier_InOrder;
mState.setDirtyClip(false);
mRestoreSaveCount = -1;
}
-bool DisplayListCanvas::finish() {
+
+///////////////////////////////////////////////////////////////////////////////
+// Operations
+///////////////////////////////////////////////////////////////////////////////
+
+DisplayList* DisplayListCanvas::finishRecording() {
flushRestoreToCount();
flushTranslate();
- return false;
-}
-void DisplayListCanvas::interrupt() {
-}
-
-void DisplayListCanvas::resume() {
+ mPaintMap.clear();
+ mRegionMap.clear();
+ mPathMap.clear();
+ DisplayList* displayList = mDisplayList;
+ mDisplayList = nullptr;
+ mSkiaCanvasProxy.reset(nullptr);
+ return displayList;
}
void DisplayListCanvas::callDrawGLFunction(Functor *functor) {
addDrawOp(new (alloc()) DrawFunctorOp(functor));
- mDisplayListData->functors.add(functor);
+ mDisplayList->functors.push_back(functor);
}
SkCanvas* DisplayListCanvas::asSkCanvas() {
- LOG_ALWAYS_FATAL_IF(!mDisplayListData,
+ LOG_ALWAYS_FATAL_IF(!mDisplayList,
"attempting to get an SkCanvas when we are not recording!");
if (!mSkiaCanvasProxy) {
mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this));
@@ -176,11 +168,6 @@ void DisplayListCanvas::setMatrix(const SkMatrix& matrix) {
mState.setMatrix(matrix);
}
-void DisplayListCanvas::setLocalMatrix(const SkMatrix& matrix) {
- addStateOp(new (alloc()) SetLocalMatrixOp(matrix));
- mState.setMatrix(matrix);
-}
-
void DisplayListCanvas::concat(const SkMatrix& matrix) {
addStateOp(new (alloc()) ConcatMatrixOp(matrix));
mState.concatMatrix(matrix);
@@ -229,11 +216,11 @@ void DisplayListCanvas::drawRenderNode(RenderNode* renderNode) {
addRenderNodeOp(op);
}
-void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle, float x, float y) {
+void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle) {
// We ref the DeferredLayerUpdater due to its thread-safe ref-counting
// semantics.
- mDisplayListData->ref(layerHandle);
- addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer(), x, y));
+ mDisplayList->ref(layerHandle);
+ addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer()));
}
void DisplayListCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
@@ -330,13 +317,14 @@ void DisplayListCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, in
vertices, colors, paint));
}
-void DisplayListCanvas::drawPatch(const SkBitmap& bitmap, const Res_png_9patch* patch,
- float left, float top, float right, float bottom, const SkPaint* paint) {
+void DisplayListCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& patch,
+ float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
const SkBitmap* bitmapPtr = refBitmap(bitmap);
- patch = refPatch(patch);
+ const Res_png_9patch* patchPtr = refPatch(&patch);
paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawPatchOp(bitmapPtr, patch, left, top, right, bottom, paint));
+ addDrawOp(new (alloc()) DrawPatchOp(bitmapPtr, patchPtr,
+ dstLeft, dstTop, dstRight, dstBottom, paint));
}
void DisplayListCanvas::drawColor(int color, SkXfermode::Mode mode) {
@@ -366,13 +354,13 @@ void DisplayListCanvas::drawRoundRect(
CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
CanvasPropertyPaint* paint) {
- mDisplayListData->ref(left);
- mDisplayListData->ref(top);
- mDisplayListData->ref(right);
- mDisplayListData->ref(bottom);
- mDisplayListData->ref(rx);
- mDisplayListData->ref(ry);
- mDisplayListData->ref(paint);
+ mDisplayList->ref(left);
+ mDisplayList->ref(top);
+ mDisplayList->ref(right);
+ mDisplayList->ref(bottom);
+ mDisplayList->ref(rx);
+ mDisplayList->ref(ry);
+ mDisplayList->ref(paint);
refBitmapsInShader(paint->value.getShader());
addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value,
&right->value, &bottom->value, &rx->value, &ry->value, &paint->value));
@@ -384,10 +372,10 @@ void DisplayListCanvas::drawCircle(float x, float y, float radius, const SkPaint
void DisplayListCanvas::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
- mDisplayListData->ref(x);
- mDisplayListData->ref(y);
- mDisplayListData->ref(radius);
- mDisplayListData->ref(paint);
+ mDisplayList->ref(x);
+ mDisplayList->ref(y);
+ mDisplayList->ref(radius);
+ mDisplayList->ref(paint);
refBitmapsInShader(paint->value.getShader());
addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value,
&radius->value, &paint->value));
@@ -447,16 +435,6 @@ void DisplayListCanvas::drawPosText(const uint16_t* text, const float* positions
addDrawOp(op);
}
-static void simplifyPaint(int color, SkPaint* paint) {
- paint->setColor(color);
- paint->setShader(nullptr);
- paint->setColorFilter(nullptr);
- paint->setLooper(nullptr);
- paint->setStrokeWidth(4 + 0.04 * paint->getTextSize());
- paint->setStrokeJoin(SkPaint::kRound_Join);
- paint->setLooper(nullptr);
-}
-
void DisplayListCanvas::drawText(const uint16_t* glyphs, const float* positions,
int count, const SkPaint& paint, float x, float y,
float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
@@ -469,30 +447,34 @@ void DisplayListCanvas::drawText(const uint16_t* glyphs, const float* positions,
positions = refBuffer<float>(positions, count * 2);
Rect bounds(boundsLeft, boundsTop, boundsRight, boundsBottom);
- if (CC_UNLIKELY(mHighContrastText)) {
- // high contrast draw path
- int color = paint.getColor();
- int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
- bool darken = channelSum < (128 * 3);
-
- // outline
- SkPaint* outlinePaint = copyPaint(&paint);
- simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, outlinePaint);
- outlinePaint->setStyle(SkPaint::kStrokeAndFill_Style);
- addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count,
- x, y, positions, outlinePaint, totalAdvance, bounds)); // bounds?
-
- // inner
- SkPaint* innerPaint = copyPaint(&paint);
- simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, innerPaint);
- innerPaint->setStyle(SkPaint::kFill_Style);
- addDrawOp(new (alloc()) DrawTextOp(text, bytesCount, count,
- x, y, positions, innerPaint, totalAdvance, bounds));
+ DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
+ x, y, positions, refPaint(&paint), totalAdvance, bounds);
+ addDrawOp(op);
+}
+
+void DisplayListCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
+ if (paint.getStyle() != SkPaint::kFill_Style ||
+ (paint.isAntiAlias() && !mState.currentTransform()->isSimple())) {
+ SkRegion::Iterator it(region);
+ while (!it.done()) {
+ const SkIRect& r = it.rect();
+ drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
+ it.next();
+ }
} else {
- // standard draw path
- DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
- x, y, positions, refPaint(&paint), totalAdvance, bounds);
- addDrawOp(op);
+ int count = 0;
+ Vector<float> rects;
+ SkRegion::Iterator it(region);
+ while (!it.done()) {
+ const SkIRect& r = it.rect();
+ rects.push(r.fLeft);
+ rects.push(r.fTop);
+ rects.push(r.fRight);
+ rects.push(r.fBottom);
+ count += 4;
+ it.next();
+ }
+ drawRects(rects.array(), count, &paint);
}
}
@@ -532,21 +514,26 @@ void DisplayListCanvas::flushTranslate() {
}
size_t DisplayListCanvas::addOpAndUpdateChunk(DisplayListOp* op) {
- int insertIndex = mDisplayListData->displayListOps.add(op);
+ int insertIndex = mDisplayList->ops.size();
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("unsupported");
+#else
+ mDisplayList->ops.push_back(op);
+#endif
if (mDeferredBarrierType != kBarrier_None) {
// op is first in new chunk
- mDisplayListData->chunks.push();
- DisplayListData::Chunk& newChunk = mDisplayListData->chunks.editTop();
+ mDisplayList->chunks.emplace_back();
+ DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
newChunk.beginOpIndex = insertIndex;
newChunk.endOpIndex = insertIndex + 1;
newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
- int nextChildIndex = mDisplayListData->children().size();
+ int nextChildIndex = mDisplayList->children.size();
newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
mDeferredBarrierType = kBarrier_None;
} else {
// standard case - append to existing chunk
- mDisplayListData->chunks.editTop().endOpIndex = insertIndex + 1;
+ mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
}
return insertIndex;
}
@@ -569,22 +556,24 @@ size_t DisplayListCanvas::addDrawOp(DrawOp* op) {
op->setQuickRejected(rejected);
}
- mDisplayListData->hasDrawOps = true;
+ mDisplayList->hasDrawOps = true;
return flushAndAddOp(op);
}
size_t DisplayListCanvas::addRenderNodeOp(DrawRenderNodeOp* op) {
int opIndex = addDrawOp(op);
- int childIndex = mDisplayListData->addChild(op);
+#if !HWUI_NEW_OPS
+ int childIndex = mDisplayList->addChild(op);
// update the chunk's child indices
- DisplayListData::Chunk& chunk = mDisplayListData->chunks.editTop();
+ DisplayList::Chunk& chunk = mDisplayList->chunks.back();
chunk.endChildIndex = childIndex + 1;
- if (op->renderNode()->stagingProperties().isProjectionReceiver()) {
+ if (op->renderNode->stagingProperties().isProjectionReceiver()) {
// use staging property, since recording on UI thread
- mDisplayListData->projectionReceiveIndex = opIndex;
+ mDisplayList->projectionReceiveIndex = opIndex;
}
+#endif
return opIndex;
}
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index 4982cc919b5e..fc08504ff60a 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -62,47 +62,23 @@ class StateOp;
*/
class ANDROID_API DisplayListCanvas: public Canvas, public CanvasStateClient {
public:
- DisplayListCanvas();
+ DisplayListCanvas(int width, int height);
virtual ~DisplayListCanvas();
- void insertReorderBarrier(bool enableReorder);
-
- DisplayListData* finishRecording();
-
-// ----------------------------------------------------------------------------
-// HWUI Frame state operations
-// ----------------------------------------------------------------------------
-
- void prepareDirty(float left, float top, float right, float bottom);
- void prepare() { prepareDirty(0.0f, 0.0f, width(), height()); }
- bool finish();
- void interrupt();
- void resume();
+ void reset(int width, int height);
+ __attribute__((warn_unused_result)) DisplayList* finishRecording();
// ----------------------------------------------------------------------------
// HWUI Canvas state operations
// ----------------------------------------------------------------------------
- void setViewport(int width, int height) { mState.setViewport(width, height); }
-
- const Rect& getRenderTargetClipBounds() const { return mState.getRenderTargetClipBounds(); }
-
- bool isCurrentTransformSimple() {
- return mState.currentTransform()->isSimple();
- }
+ void insertReorderBarrier(bool enableReorder);
// ----------------------------------------------------------------------------
// HWUI Canvas draw operations
// ----------------------------------------------------------------------------
- // Bitmap-based
- void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
- // TODO: move drawPatch() to Canvas.h
- void drawPatch(const SkBitmap& bitmap, const Res_png_9patch* patch,
- float left, float top, float right, float bottom, const SkPaint* paint);
-
// Shapes
- void drawRects(const float* rects, int count, const SkPaint* paint);
void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
@@ -114,16 +90,12 @@ public:
// ----------------------------------------------------------------------------
// HWUI Canvas draw operations - special
// ----------------------------------------------------------------------------
- void drawLayer(DeferredLayerUpdater* layerHandle, float x, float y);
+ void drawLayer(DeferredLayerUpdater* layerHandle);
void drawRenderNode(RenderNode* renderNode);
// TODO: rename for consistency
void callDrawGLFunction(Functor* functor);
- void setHighContrastText(bool highContrastText) {
- mHighContrastText = highContrastText;
- }
-
// ----------------------------------------------------------------------------
// CanvasStateClient interface
// ----------------------------------------------------------------------------
@@ -144,6 +116,11 @@ public:
virtual int width() override { return mState.getWidth(); }
virtual int height() override { return mState.getHeight(); }
+ virtual void setHighContrastText(bool highContrastText) override {
+ mHighContrastText = highContrastText;
+ }
+ virtual bool isHighContrastText() override { return mHighContrastText; }
+
// ----------------------------------------------------------------------------
// android/graphics/Canvas state operations
// ----------------------------------------------------------------------------
@@ -165,7 +142,6 @@ public:
// Matrix
virtual void getMatrix(SkMatrix* outMatrix) const override { mState.getMatrix(outMatrix); }
virtual void setMatrix(const SkMatrix& matrix) override;
- virtual void setLocalMatrix(const SkMatrix& matrix) override;
virtual void concat(const SkMatrix& matrix) override;
virtual void rotate(float degrees) override;
@@ -205,6 +181,7 @@ public:
}
virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
+ virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
virtual void drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint& paint) override;
virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
@@ -226,6 +203,9 @@ public:
float dstRight, float dstBottom, const SkPaint* paint) override;
virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) override;
+ virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) override;
// Text
virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
@@ -249,11 +229,14 @@ private:
kBarrier_OutOfOrder,
};
+ void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
+ void drawRects(const float* rects, int count, const SkPaint* paint);
+
void flushRestoreToCount();
void flushTranslate();
void flushReorderBarrier();
- LinearAllocator& alloc() { return mDisplayListData->allocator; }
+ LinearAllocator& alloc() { return mDisplayList->allocator; }
// Each method returns final index of op
size_t addOpAndUpdateChunk(DisplayListOp* op);
@@ -270,7 +253,7 @@ private:
inline const T* refBuffer(const T* srcBuffer, int32_t count) {
if (!srcBuffer) return nullptr;
- T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T));
+ T* dstBuffer = (T*) mDisplayList->allocator.alloc(count * sizeof(T));
memcpy(dstBuffer, srcBuffer, count * sizeof(T));
return dstBuffer;
}
@@ -285,7 +268,7 @@ private:
// 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);
- mDisplayListData->pathResources.add(cachedPath);
+ mDisplayList->pathResources.push_back(cachedPath);
return cachedPath;
}
@@ -309,7 +292,7 @@ private:
if (cachedPaint == nullptr || *cachedPaint != *paint) {
cachedPaint = new SkPaint(*paint);
std::unique_ptr<const SkPaint> copy(cachedPaint);
- mDisplayListData->paints.push_back(std::move(copy));
+ mDisplayList->paints.push_back(std::move(copy));
// replaceValueFor() performs an add if the entry doesn't exist
mPaintMap.replaceValueFor(key, cachedPaint);
@@ -319,16 +302,6 @@ private:
return cachedPaint;
}
- inline SkPaint* copyPaint(const SkPaint* paint) {
- if (!paint) return nullptr;
-
- SkPaint* returnPaint = new SkPaint(*paint);
- std::unique_ptr<const SkPaint> copy(returnPaint);
- mDisplayListData->paints.push_back(std::move(copy));
-
- return returnPaint;
- }
-
inline const SkRegion* refRegion(const SkRegion* region) {
if (!region) {
return region;
@@ -339,7 +312,7 @@ private:
if (cachedRegion == nullptr) {
std::unique_ptr<const SkRegion> copy(new SkRegion(*region));
cachedRegion = copy.get();
- mDisplayListData->regions.push_back(std::move(copy));
+ mDisplayList->regions.push_back(std::move(copy));
// replaceValueFor() performs an add if the entry doesn't exist
mRegionMap.replaceValueFor(region, cachedRegion);
@@ -355,12 +328,12 @@ private:
// which doesn't seem worth the extra cycles for this unlikely case.
SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
alloc().autoDestroy(localBitmap);
- mDisplayListData->bitmapResources.push_back(localBitmap);
+ mDisplayList->bitmapResources.push_back(localBitmap);
return localBitmap;
}
inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
- mDisplayListData->patchResources.add(patch);
+ mDisplayList->patchResources.push_back(patch);
mResourceCache.incrementRefcount(patch);
return patch;
}
@@ -370,7 +343,7 @@ private:
DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
ResourceCache& mResourceCache;
- DisplayListData* mDisplayListData;
+ DisplayList* mDisplayList;
float mTranslateX;
float mTranslateY;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 8b4b4ba2b79e..cb638a476b14 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -139,7 +139,7 @@ public:
* reducing which operations are tagged as mergeable.
*/
virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const Vector<OpStatePair>& ops, const Rect& bounds) {
+ const std::vector<OpStatePair>& ops, const Rect& bounds) {
for (unsigned int i = 0; i < ops.size(); i++) {
renderer.restoreDisplayState(*(ops[i].state), true);
ops[i].op->applyDraw(renderer, dirty);
@@ -172,10 +172,6 @@ public:
void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
bool getQuickRejected() { return mQuickRejected; }
- inline int getPaintAlpha() const {
- return OpenGLRenderer::getAlphaDirect(mPaint);
- }
-
virtual bool hasTextShadow() const {
return false;
}
@@ -213,7 +209,7 @@ protected:
if (state.mAlpha != 1.0f) return false;
- SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
return (mode == SkXfermode::kSrcOver_Mode ||
mode == SkXfermode::kSrc_Mode);
@@ -249,8 +245,8 @@ public:
virtual bool getLocalBounds(Rect& localBounds) override {
localBounds.set(mLocalBounds);
- OpenGLRenderer::TextShadow textShadow;
- if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) {
+ PaintUtils::TextShadow textShadow;
+ if (PaintUtils::getTextShadow(mPaint, &textShadow)) {
Rect shadow(mLocalBounds);
shadow.translate(textShadow.dx, textShadow.dx);
shadow.outset(textShadow.radius);
@@ -372,8 +368,8 @@ public:
private:
bool isSaveLayerAlpha() const {
- SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
- int alpha = OpenGLRenderer::getAlphaDirect(mPaint);
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
+ int alpha = PaintUtils::getAlphaDirect(mPaint);
return alpha < 255 && mode == SkXfermode::kSrcOver_Mode;
}
@@ -472,7 +468,9 @@ public:
: mMatrix(matrix) {}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.setMatrix(mMatrix);
+ // Setting a matrix on a Canvas isn't equivalent to setting a total matrix on the scene.
+ // Set a canvas-relative matrix on the renderer instead.
+ renderer.setLocalMatrix(mMatrix);
}
virtual void output(int level, uint32_t logFlags) const override {
@@ -489,25 +487,6 @@ private:
const SkMatrix mMatrix;
};
-class SetLocalMatrixOp : public StateOp {
-public:
- SetLocalMatrixOp(const SkMatrix& matrix)
- : mMatrix(matrix) {}
-
- virtual void applyState(OpenGLRenderer& renderer, int saveCount) const override {
- renderer.setLocalMatrix(mMatrix);
- }
-
- virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("SetLocalMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
- }
-
- virtual const char* name() override { return "SetLocalMatrix"; }
-
-private:
- const SkMatrix mMatrix;
-};
-
class ConcatMatrixOp : public StateOp {
public:
ConcatMatrixOp(const SkMatrix& matrix)
@@ -648,7 +627,7 @@ public:
* the current layer, if any.
*/
virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const Vector<OpStatePair>& ops, const Rect& bounds) override {
+ const std::vector<OpStatePair>& ops, const Rect& bounds) override {
const DeferredDisplayState& firstState = *(ops[0].state);
renderer.restoreDisplayState(firstState, true); // restore all but the clip
@@ -708,7 +687,7 @@ public:
// TODO: support clipped bitmaps by handling them in SET_TEXTURE
deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() &&
!state.mClipSideFlags &&
- OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
+ PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
(mBitmap->colorType() != kAlpha_8_SkColorType);
}
@@ -819,7 +798,7 @@ public:
* is also responsible for dirtying the current layer, if any.
*/
virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const Vector<OpStatePair>& ops, const Rect& bounds) override {
+ const std::vector<OpStatePair>& ops, const Rect& bounds) override {
const DeferredDisplayState& firstState = *(ops[0].state);
renderer.restoreDisplayState(firstState, true); // restore all but the clip
@@ -912,7 +891,7 @@ public:
deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch;
deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap;
deferInfo.mergeable = state.mMatrix.isPureTranslate() &&
- OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mBitmap->isOpaque();
}
@@ -1258,12 +1237,12 @@ public:
}
virtual bool hasTextShadow() const override {
- return OpenGLRenderer::hasTextShadow(mPaint);
+ return PaintUtils::hasTextShadow(mPaint);
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
const DeferredDisplayState& state) override {
- FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(mPaint);
+ FontRenderer& fontRenderer = renderer.getCaches().fontRenderer.getFontRenderer();
fontRenderer.precache(mPaint, mText, mCount, SkMatrix::I());
deferInfo.batchId = mPaint->getColor() == SK_ColorBLACK ?
@@ -1328,7 +1307,7 @@ public:
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
const DeferredDisplayState& state) override {
- FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(mPaint);
+ FontRenderer& fontRenderer = renderer.getCaches().fontRenderer.getFontRenderer();
SkMatrix transform;
renderer.findBestFontTransform(state.mMatrix, &transform);
if (mPrecacheTransform != transform) {
@@ -1347,7 +1326,7 @@ public:
deferInfo.mergeable = state.mMatrix.isPureTranslate()
&& !hasDecorations
- && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ && PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
}
virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
@@ -1358,7 +1337,7 @@ public:
}
virtual void multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const Vector<OpStatePair>& ops, const Rect& bounds) override {
+ const std::vector<OpStatePair>& ops, const Rect& bounds) override {
for (unsigned int i = 0; i < ops.size(); i++) {
const DeferredDisplayState& state = *(ops[i].state);
DrawOpMode drawOpMode = (i == ops.size() - 1) ? DrawOpMode::kFlush : DrawOpMode::kDefer;
@@ -1417,26 +1396,27 @@ private:
class DrawRenderNodeOp : public DrawBoundedOp {
friend class RenderNode; // grant RenderNode access to info of child
- friend class DisplayListData; // grant DisplayListData access to info of child
+ friend class DisplayList; // grant DisplayList access to info of child
+ friend class DisplayListCanvas;
public:
DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent, bool clipIsSimple)
: DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr)
- , mRenderNode(renderNode)
+ , renderNode(renderNode)
, mRecordedWithPotentialStencilClip(!clipIsSimple || !transformFromParent.isSimple())
, mTransformFromParent(transformFromParent)
, mSkipInOrderDraw(false) {}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) override {
- if (mRenderNode->isRenderable() && !mSkipInOrderDraw) {
- mRenderNode->defer(deferStruct, level + 1);
+ if (renderNode->isRenderable() && !mSkipInOrderDraw) {
+ renderNode->defer(deferStruct, level + 1);
}
}
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
bool useQuickReject) override {
- if (mRenderNode->isRenderable() && !mSkipInOrderDraw) {
- mRenderNode->replay(replayStruct, level + 1);
+ if (renderNode->isRenderable() && !mSkipInOrderDraw) {
+ renderNode->replay(replayStruct, level + 1);
}
}
@@ -1445,18 +1425,16 @@ public:
}
virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw RenderNode %p %s", mRenderNode, mRenderNode->getName());
- if (mRenderNode && (logFlags & kOpLogFlag_Recurse)) {
- mRenderNode->output(level + 1);
+ OP_LOG("Draw RenderNode %p %s", renderNode, renderNode->getName());
+ if (renderNode && (logFlags & kOpLogFlag_Recurse)) {
+ renderNode->output(level + 1);
}
}
virtual const char* name() override { return "DrawRenderNode"; }
- RenderNode* renderNode() { return mRenderNode; }
-
private:
- RenderNode* mRenderNode;
+ RenderNode* renderNode;
/**
* This RenderNode was drawn into a DisplayList with the canvas in a state that will likely
@@ -1541,23 +1519,21 @@ private:
class DrawLayerOp : public DrawOp {
public:
- DrawLayerOp(Layer* layer, float x, float y)
- : DrawOp(nullptr), mLayer(layer), mX(x), mY(y) {}
+ DrawLayerOp(Layer* layer)
+ : DrawOp(nullptr), mLayer(layer) {}
virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
- renderer.drawLayer(mLayer, mX, mY);
+ renderer.drawLayer(mLayer);
}
virtual void output(int level, uint32_t logFlags) const override {
- OP_LOG("Draw Layer %p at %f %f", mLayer, mX, mY);
+ OP_LOG("Draw Layer %p", mLayer);
}
virtual const char* name() override { return "DrawLayer"; }
private:
Layer* mLayer;
- float mX;
- float mY;
};
}; // namespace uirenderer
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index d96775aa7ff1..06c8a21b019b 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -14,29 +14,18 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include "Extensions.h"
#include "Debug.h"
#include "Properties.h"
+#include "utils/StringUtils.h"
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
#include <GLES2/gl2ext.h>
#include <utils/Log.h>
namespace android {
-
-using namespace uirenderer;
-ANDROID_SINGLETON_STATIC_INSTANCE(Extensions);
-
namespace uirenderer {
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
// Debug
#if DEBUG_EXTENSIONS
#define EXT_LOGD(...) ALOGD(__VA_ARGS__)
@@ -44,31 +33,16 @@ namespace uirenderer {
#define EXT_LOGD(...)
#endif
-///////////////////////////////////////////////////////////////////////////////
-// Constructors
-///////////////////////////////////////////////////////////////////////////////
Extensions::Extensions() {
- // Query GL extensions
- findExtensions((const char*) glGetString(GL_EXTENSIONS), mGlExtensionList);
- mHasNPot = hasGlExtension("GL_OES_texture_npot");
- mHasFramebufferFetch = hasGlExtension("GL_NV_shader_framebuffer_fetch");
- mHasDiscardFramebuffer = hasGlExtension("GL_EXT_discard_framebuffer");
- mHasDebugMarker = hasGlExtension("GL_EXT_debug_marker");
- mHasTiledRendering = hasGlExtension("GL_QCOM_tiled_rendering");
- mHas1BitStencil = hasGlExtension("GL_OES_stencil1");
- mHas4BitStencil = hasGlExtension("GL_OES_stencil4");
- mHasUnpackSubImage = hasGlExtension("GL_EXT_unpack_subimage");
-
- // Query EGL extensions
- findExtensions(eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS), mEglExtensionList);
-
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_DEBUG_NV_PROFILING, property, nullptr) > 0) {
- mHasNvSystemTime = !strcmp(property, "true") && hasEglExtension("EGL_NV_system_time");
- } else {
- mHasNvSystemTime = false;
- }
+ StringCollection extensions((const char*) glGetString(GL_EXTENSIONS));
+ mHasNPot = extensions.has("GL_OES_texture_npot");
+ mHasFramebufferFetch = extensions.has("GL_NV_shader_framebuffer_fetch");
+ mHasDiscardFramebuffer = extensions.has("GL_EXT_discard_framebuffer");
+ mHasDebugMarker = extensions.has("GL_EXT_debug_marker");
+ mHas1BitStencil = extensions.has("GL_OES_stencil1");
+ mHas4BitStencil = extensions.has("GL_OES_stencil4");
+ mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage");
const char* version = (const char*) glGetString(GL_VERSION);
@@ -91,40 +65,5 @@ Extensions::Extensions() {
}
}
-///////////////////////////////////////////////////////////////////////////////
-// Methods
-///////////////////////////////////////////////////////////////////////////////
-
-bool Extensions::hasGlExtension(const char* extension) const {
- const String8 s(extension);
- return mGlExtensionList.indexOf(s) >= 0;
-}
-
-bool Extensions::hasEglExtension(const char* extension) const {
- const String8 s(extension);
- return mEglExtensionList.indexOf(s) >= 0;
-}
-
-void Extensions::findExtensions(const char* extensions, SortedVector<String8>& list) const {
- const char* current = extensions;
- const char* head = current;
- EXT_LOGD("Available extensions:");
- do {
- head = strchr(current, ' ');
- String8 s(current, head ? head - current : strlen(current));
- if (s.length()) {
- list.add(s);
- EXT_LOGD(" %s", s.string());
- }
- current = head + 1;
- } while (head);
-}
-
-void Extensions::dump() const {
- ALOGD("%s", (const char*) glGetString(GL_VERSION));
- ALOGD("Supported GL extensions:\n%s", (const char*) glGetString(GL_EXTENSIONS));
- ALOGD("Supported EGL extensions:\n%s", eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS));
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index a4eef0f0bb86..0a30d162f2e8 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -40,10 +40,8 @@ public:
inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; }
inline bool hasDebugMarker() const { return mHasDebugMarker; }
- inline bool hasTiledRendering() const { return mHasTiledRendering; }
inline bool has1BitStencil() const { return mHas1BitStencil; }
inline bool has4BitStencil() const { return mHas4BitStencil; }
- inline bool hasNvSystemTime() const { return mHasNvSystemTime; }
inline bool hasUnpackRowLength() const { return mVersionMajor >= 3 || mHasUnpackSubImage; }
inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
@@ -52,25 +50,13 @@ public:
inline int getMajorGlVersion() const { return mVersionMajor; }
inline int getMinorGlVersion() const { return mVersionMinor; }
- bool hasGlExtension(const char* extension) const;
- bool hasEglExtension(const char* extension) const;
-
- void dump() const;
-
private:
- void findExtensions(const char* extensions, SortedVector<String8>& list) const;
-
- SortedVector<String8> mGlExtensionList;
- SortedVector<String8> mEglExtensionList;
-
bool mHasNPot;
bool mHasFramebufferFetch;
bool mHasDiscardFramebuffer;
bool mHasDebugMarker;
- bool mHasTiledRendering;
bool mHas1BitStencil;
bool mHas4BitStencil;
- bool mHasNvSystemTime;
bool mHasUnpackSubImage;
int mVersionMajor;
diff --git a/libs/hwui/FboCache.cpp b/libs/hwui/FboCache.cpp
index b54d53233a65..cca3cb7a98f1 100644
--- a/libs/hwui/FboCache.cpp
+++ b/libs/hwui/FboCache.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <stdlib.h>
#include "Debug.h"
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 9a2a879e594e..ccf0b48cd4be 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -26,14 +26,12 @@
#include "Rect.h"
#include "renderstate/RenderState.h"
#include "utils/Blur.h"
-#include "utils/MathUtils.h"
#include "utils/Timing.h"
+#include <algorithm>
+#include <cutils/properties.h>
#include <SkGlyph.h>
#include <SkUtils.h>
-
-#include <cutils/properties.h>
-
#include <utils/Log.h>
#ifdef ANDROID_ENABLE_RENDERSCRIPT
@@ -77,8 +75,8 @@ void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) {
static bool sLogFontRendererCreate = true;
-FontRenderer::FontRenderer()
- : mGammaTable(nullptr)
+FontRenderer::FontRenderer(const uint8_t* gammaTable)
+ : mGammaTable(gammaTable)
, mCurrentFont(nullptr)
, mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity)
, mCurrentCacheTexture(nullptr)
@@ -94,34 +92,22 @@ FontRenderer::FontRenderer()
INIT_LOGD("Creating FontRenderer");
}
- mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
- mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
- mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
- mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
-
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, nullptr) > 0) {
- mSmallCacheWidth = atoi(property);
- }
+ mSmallCacheWidth = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_WIDTH,
+ DEFAULT_TEXT_SMALL_CACHE_WIDTH);
+ mSmallCacheHeight = property_get_int32(PROPERTY_TEXT_SMALL_CACHE_HEIGHT,
+ DEFAULT_TEXT_SMALL_CACHE_HEIGHT);
- if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, nullptr) > 0) {
- mSmallCacheHeight = atoi(property);
- }
-
- if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, nullptr) > 0) {
- mLargeCacheWidth = atoi(property);
- }
-
- if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, nullptr) > 0) {
- mLargeCacheHeight = atoi(property);
- }
+ mLargeCacheWidth = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_WIDTH,
+ DEFAULT_TEXT_LARGE_CACHE_WIDTH);
+ mLargeCacheHeight = property_get_int32(PROPERTY_TEXT_LARGE_CACHE_HEIGHT,
+ DEFAULT_TEXT_LARGE_CACHE_HEIGHT);
uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
- mSmallCacheWidth = MathUtils::min(mSmallCacheWidth, maxTextureSize);
- mSmallCacheHeight = MathUtils::min(mSmallCacheHeight, maxTextureSize);
- mLargeCacheWidth = MathUtils::min(mLargeCacheWidth, maxTextureSize);
- mLargeCacheHeight = MathUtils::min(mLargeCacheHeight, maxTextureSize);
+ mSmallCacheWidth = std::min(mSmallCacheWidth, maxTextureSize);
+ mSmallCacheHeight = std::min(mSmallCacheHeight, maxTextureSize);
+ mLargeCacheWidth = std::min(mLargeCacheWidth, maxTextureSize);
+ mLargeCacheHeight = std::min(mLargeCacheHeight, maxTextureSize);
if (sLogFontRendererCreate) {
INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
@@ -134,7 +120,7 @@ FontRenderer::FontRenderer()
sLogFontRendererCreate = false;
}
-void clearCacheTextures(Vector<CacheTexture*>& cacheTextures) {
+void clearCacheTextures(std::vector<CacheTexture*>& cacheTextures) {
for (uint32_t i = 0; i < cacheTextures.size(); i++) {
delete cacheTextures[i];
}
@@ -171,7 +157,7 @@ void FontRenderer::flushAllAndInvalidate() {
mDrawn = false;
}
-void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) {
+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];
@@ -191,7 +177,7 @@ void FontRenderer::flushLargeCaches() {
flushLargeCaches(mRGBACacheTextures);
}
-CacheTexture* FontRenderer::cacheBitmapInTexture(Vector<CacheTexture*>& cacheTextures,
+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)) {
@@ -218,7 +204,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
// choose an appropriate cache texture list for this glyph format
SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
- Vector<CacheTexture*>* cacheTextures = nullptr;
+ std::vector<CacheTexture*>* cacheTextures = nullptr;
switch (format) {
case SkMask::kA8_Format:
case SkMask::kBW_Format:
@@ -399,17 +385,17 @@ void FontRenderer::initTextTexture() {
clearCacheTextures(mRGBACacheTextures);
mUploadTexture = false;
- mACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
+ mACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
GL_ALPHA, true));
- mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
+ mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
GL_ALPHA, false));
- mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
+ mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
GL_ALPHA, false));
- mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight,
+ mACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight,
GL_ALPHA, false));
- mRGBACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
+ mRGBACacheTextures.push_back(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
GL_RGBA, false));
- mRGBACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
+ mRGBACacheTextures.push_back(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
GL_RGBA, false));
mCurrentCacheTexture = mACacheTextures[0];
}
@@ -425,7 +411,7 @@ void FontRenderer::checkInit() {
mInitialized = true;
}
-void checkTextureUpdateForCache(Caches& caches, Vector<CacheTexture*>& cacheTextures,
+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];
@@ -470,7 +456,7 @@ void FontRenderer::checkTextureUpdate() {
mUploadTexture = false;
}
-void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) {
+void FontRenderer::issueDrawCommand(std::vector<CacheTexture*>& cacheTextures) {
if (!mFunctor) return;
bool first = true;
@@ -724,14 +710,14 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, flo
#endif
std::unique_ptr<float[]> gaussian(new float[2 * intRadius + 1]);
- Blur::generateGaussianWeights(gaussian.get(), intRadius);
+ 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 Vector<CacheTexture*>& cacheTextures) {
+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];
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 3da20ee255af..8172312e9a43 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -21,16 +21,16 @@
#include "font/CacheTexture.h"
#include "font/CachedGlyphInfo.h"
#include "font/Font.h"
-#include "utils/SortedList.h"
#include <utils/LruCache.h>
-#include <utils/Vector.h>
#include <utils/StrongPointer.h>
#include <SkPaint.h>
#include <GLES2/gl2.h>
+#include <vector>
+
#ifdef ANDROID_ENABLE_RENDERSCRIPT
#include "RenderScript.h"
namespace RSC {
@@ -72,16 +72,12 @@ public:
class FontRenderer {
public:
- FontRenderer();
+ FontRenderer(const uint8_t* gammaTable);
~FontRenderer();
- void flushLargeCaches(Vector<CacheTexture*>& cacheTextures);
+ void flushLargeCaches(std::vector<CacheTexture*>& cacheTextures);
void flushLargeCaches();
- void setGammaTable(const uint8_t* gammaTable) {
- mGammaTable = gammaTable;
- }
-
void setFont(const SkPaint* paint, const SkMatrix& matrix);
void precache(const SkPaint* paint, const char* text, int numGlyphs, const SkMatrix& matrix);
@@ -127,7 +123,7 @@ private:
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(Vector<CacheTexture*>& cacheTextures, const SkGlyph& glyph,
+ CacheTexture* cacheBitmapInTexture(std::vector<CacheTexture*>& cacheTextures, const SkGlyph& glyph,
uint32_t* startX, uint32_t* startY);
void flushAllAndInvalidate();
@@ -136,7 +132,7 @@ private:
void initRender(const Rect* clip, Rect* bounds, TextDrawFunctor* functor);
void finishRender();
- void issueDrawCommand(Vector<CacheTexture*>& cacheTextures);
+ 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,
@@ -162,8 +158,8 @@ private:
uint32_t mLargeCacheWidth;
uint32_t mLargeCacheHeight;
- Vector<CacheTexture*> mACacheTextures;
- Vector<CacheTexture*> mRGBACacheTextures;
+ std::vector<CacheTexture*> mACacheTextures;
+ std::vector<CacheTexture*> mRGBACacheTextures;
Font* mCurrentFont;
LruCache<Font::FontDescription, Font*> mActiveFonts;
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
index 0bcd83a1a050..96cac86386b5 100644
--- a/libs/hwui/GammaFontRenderer.cpp
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -21,231 +21,22 @@
namespace android {
namespace uirenderer {
-///////////////////////////////////////////////////////////////////////////////
-// Utils
-///////////////////////////////////////////////////////////////////////////////
-
-static int luminance(const SkPaint* paint) {
- uint32_t c = paint->getColor();
- const int r = (c >> 16) & 0xFF;
- const int g = (c >> 8) & 0xFF;
- const int b = (c ) & 0xFF;
- return (r * 2 + g * 5 + b) >> 3;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Base class GammaFontRenderer
-///////////////////////////////////////////////////////////////////////////////
-
-GammaFontRenderer* GammaFontRenderer::createRenderer() {
- // Choose the best renderer
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_TEXT_GAMMA_METHOD, property, DEFAULT_TEXT_GAMMA_METHOD) > 0) {
- if (!strcasecmp(property, "lookup")) {
- return new LookupGammaFontRenderer();
- } else if (!strcasecmp(property, "shader")) {
- return new ShaderGammaFontRenderer(false);
- } else if (!strcasecmp(property, "shader3")) {
- return new ShaderGammaFontRenderer(true);
- }
- }
-
- return new Lookup3GammaFontRenderer();
-}
-
GammaFontRenderer::GammaFontRenderer() {
- // Get the renderer properties
- char property[PROPERTY_VALUE_MAX];
-
- // Get the gamma
- mGamma = DEFAULT_TEXT_GAMMA;
- if (property_get(PROPERTY_TEXT_GAMMA, property, nullptr) > 0) {
- INIT_LOGD(" Setting text gamma to %s", property);
- mGamma = atof(property);
- } else {
- INIT_LOGD(" Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA);
- }
-
- // Get the black gamma threshold
- mBlackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
- if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, nullptr) > 0) {
- INIT_LOGD(" Setting text black gamma threshold to %s", property);
- mBlackThreshold = atoi(property);
- } else {
- INIT_LOGD(" Using default text black gamma threshold of %d",
- DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD);
- }
-
- // Get the white gamma threshold
- mWhiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
- if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, nullptr) > 0) {
- INIT_LOGD(" Setting text white gamma threshold to %s", property);
- mWhiteThreshold = atoi(property);
- } else {
- INIT_LOGD(" Using default white black gamma threshold of %d",
- DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD);
- }
-}
-
-GammaFontRenderer::~GammaFontRenderer() {
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Shader-based renderer
-///////////////////////////////////////////////////////////////////////////////
-
-ShaderGammaFontRenderer::ShaderGammaFontRenderer(bool multiGamma)
- : GammaFontRenderer() {
- INIT_LOGD("Creating shader gamma font renderer");
- mRenderer = nullptr;
- mMultiGamma = multiGamma;
-}
-
-void ShaderGammaFontRenderer::describe(ProgramDescription& description,
- const SkPaint* paint) const {
- if (paint->getShader() == nullptr) {
- if (mMultiGamma) {
- const int l = luminance(paint);
-
- if (l <= mBlackThreshold) {
- description.hasGammaCorrection = true;
- description.gamma = mGamma;
- } else if (l >= mWhiteThreshold) {
- description.hasGammaCorrection = true;
- description.gamma = 1.0f / mGamma;
- }
- } else {
- description.hasGammaCorrection = true;
- description.gamma = 1.0f / mGamma;
- }
- }
-}
-
-void ShaderGammaFontRenderer::setupProgram(ProgramDescription& description,
- Program& program) const {
- if (description.hasGammaCorrection) {
- glUniform1f(program.getUniform("gamma"), description.gamma);
- }
-}
-
-void ShaderGammaFontRenderer::endPrecaching() {
- if (mRenderer) {
- mRenderer->endPrecaching();
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Lookup-based renderer
-///////////////////////////////////////////////////////////////////////////////
-
-LookupGammaFontRenderer::LookupGammaFontRenderer()
- : GammaFontRenderer() {
INIT_LOGD("Creating lookup gamma font renderer");
// Compute the gamma tables
- const float gamma = 1.0f / mGamma;
+ 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));
}
-
- mRenderer = nullptr;
}
-void LookupGammaFontRenderer::endPrecaching() {
+void GammaFontRenderer::endPrecaching() {
if (mRenderer) {
mRenderer->endPrecaching();
}
}
-///////////////////////////////////////////////////////////////////////////////
-// Lookup-based renderer, using 3 different correction tables
-///////////////////////////////////////////////////////////////////////////////
-
-Lookup3GammaFontRenderer::Lookup3GammaFontRenderer()
- : GammaFontRenderer() {
- INIT_LOGD("Creating lookup3 gamma font renderer");
-
- // Compute the gamma tables
- const float blackGamma = mGamma;
- const float whiteGamma = 1.0f / mGamma;
-
- for (uint32_t i = 0; i <= 255; i++) {
- const float v = i / 255.0f;
- const float black = pow(v, blackGamma);
- const float white = pow(v, whiteGamma);
-
- mGammaTable[i] = i;
- mGammaTable[256 + i] = uint8_t((float)::floor(black * 255.0f + 0.5f));
- mGammaTable[512 + i] = uint8_t((float)::floor(white * 255.0f + 0.5f));
- }
-
- memset(mRenderers, 0, sizeof(FontRenderer*) * kGammaCount);
- memset(mRenderersUsageCount, 0, sizeof(uint32_t) * kGammaCount);
-}
-
-void Lookup3GammaFontRenderer::endPrecaching() {
- for (int i = 0; i < kGammaCount; i++) {
- if (mRenderers[i]) {
- mRenderers[i]->endPrecaching();
- }
- }
-}
-
-void Lookup3GammaFontRenderer::clear() {
- for (int i = 0; i < kGammaCount; i++) {
- mRenderers[i].release();
- }
-}
-
-void Lookup3GammaFontRenderer::flush() {
- int count = 0;
- int min = -1;
- uint32_t minCount = UINT_MAX;
-
- for (int i = 0; i < kGammaCount; i++) {
- if (mRenderers[i]) {
- count++;
- if (mRenderersUsageCount[i] < minCount) {
- minCount = mRenderersUsageCount[i];
- min = i;
- }
- }
- }
-
- if (count <= 1 || min < 0) return;
-
- mRenderers[min].release();
-
- // Also eliminate the caches for large glyphs, as they consume significant memory
- for (int i = 0; i < kGammaCount; ++i) {
- if (mRenderers[i]) {
- mRenderers[i]->flushLargeCaches();
- }
- }
-}
-
-FontRenderer* Lookup3GammaFontRenderer::getRenderer(Gamma gamma) {
- if (!mRenderers[gamma]) {
- mRenderers[gamma].reset(new FontRenderer());
- mRenderers[gamma]->setGammaTable(&mGammaTable[gamma * 256]);
- }
- mRenderersUsageCount[gamma]++;
- return mRenderers[gamma].get();
-}
-
-FontRenderer& Lookup3GammaFontRenderer::getFontRenderer(const SkPaint* paint) {
- if (paint->getShader() == nullptr) {
- const int l = luminance(paint);
-
- if (l <= mBlackThreshold) {
- return *getRenderer(kGammaBlack);
- } else if (l >= mWhiteThreshold) {
- return *getRenderer(kGammaWhite);
- }
- }
- return *getRenderer(kGammaDefault);
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index ca55bf1e74e0..146d385e1173 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -17,183 +17,44 @@
#ifndef ANDROID_HWUI_GAMMA_FONT_RENDERER_H
#define ANDROID_HWUI_GAMMA_FONT_RENDERER_H
-#include <SkPaint.h>
-
#include "FontRenderer.h"
#include "Program.h"
+#include <SkPaint.h>
+
namespace android {
namespace uirenderer {
class GammaFontRenderer {
public:
- virtual ~GammaFontRenderer();
-
- virtual void clear() = 0;
- virtual void flush() = 0;
-
- virtual FontRenderer& getFontRenderer(const SkPaint* paint) = 0;
-
- virtual uint32_t getFontRendererCount() const = 0;
- virtual uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const = 0;
-
- virtual void describe(ProgramDescription& description, const SkPaint* paint) const = 0;
- virtual void setupProgram(ProgramDescription& description, Program& program) const = 0;
-
- virtual void endPrecaching() = 0;
-
- static GammaFontRenderer* createRenderer();
-
-protected:
GammaFontRenderer();
- int mBlackThreshold;
- int mWhiteThreshold;
-
- float mGamma;
-};
-
-class ShaderGammaFontRenderer: public GammaFontRenderer {
-public:
- ~ShaderGammaFontRenderer() {
- delete mRenderer;
- }
-
- void clear() override {
- delete mRenderer;
- mRenderer = nullptr;
- }
-
- void flush() override {
- if (mRenderer) {
- mRenderer->flushLargeCaches();
- }
- }
-
- FontRenderer& getFontRenderer(const SkPaint* paint) override {
- if (!mRenderer) {
- mRenderer = new FontRenderer;
- }
- return *mRenderer;
- }
-
- uint32_t getFontRendererCount() const override {
- return 1;
- }
-
- uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
- return mRenderer ? mRenderer->getCacheSize(format) : 0;
- }
-
- void describe(ProgramDescription& description, const SkPaint* paint) const override;
- void setupProgram(ProgramDescription& description, Program& program) const override;
-
- void endPrecaching() override;
-
-private:
- ShaderGammaFontRenderer(bool multiGamma);
-
- FontRenderer* mRenderer;
- bool mMultiGamma;
-
- friend class GammaFontRenderer;
-};
-
-class LookupGammaFontRenderer: public GammaFontRenderer {
-public:
- ~LookupGammaFontRenderer() {
- delete mRenderer;
- }
-
- void clear() override {
- delete mRenderer;
- mRenderer = nullptr;
+ void clear() {
+ mRenderer.release();
}
- void flush() override {
+ void flush() {
if (mRenderer) {
mRenderer->flushLargeCaches();
}
}
- FontRenderer& getFontRenderer(const SkPaint* paint) override {
+ FontRenderer& getFontRenderer() {
if (!mRenderer) {
- mRenderer = new FontRenderer;
- mRenderer->setGammaTable(&mGammaTable[0]);
+ mRenderer.reset(new FontRenderer(&mGammaTable[0]));
}
return *mRenderer;
}
- uint32_t getFontRendererCount() const override {
- return 1;
- }
-
- uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
+ uint32_t getFontRendererSize(GLenum format) const {
return mRenderer ? mRenderer->getCacheSize(format) : 0;
}
- void describe(ProgramDescription& description, const SkPaint* paint) const override {
- }
-
- void setupProgram(ProgramDescription& description, Program& program) const override {
- }
-
- void endPrecaching() override;
+ void endPrecaching();
private:
- LookupGammaFontRenderer();
-
- FontRenderer* mRenderer;
+ std::unique_ptr<FontRenderer> mRenderer;
uint8_t mGammaTable[256];
-
- friend class GammaFontRenderer;
-};
-
-class Lookup3GammaFontRenderer: public GammaFontRenderer {
-public:
- void clear() override;
- void flush() override;
-
- FontRenderer& getFontRenderer(const SkPaint* paint) override;
-
- uint32_t getFontRendererCount() const override {
- return kGammaCount;
- }
-
- uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const override {
- if (fontRenderer >= kGammaCount) return 0;
-
- if (!mRenderers[fontRenderer]) return 0;
-
- return mRenderers[fontRenderer]->getCacheSize(format);
- }
-
- void describe(ProgramDescription& description, const SkPaint* paint) const override {
- }
-
- void setupProgram(ProgramDescription& description, Program& program) const override {
- }
-
- void endPrecaching() override;
-
-private:
- Lookup3GammaFontRenderer();
-
- enum Gamma {
- kGammaDefault = 0,
- kGammaBlack = 1,
- kGammaWhite = 2,
- kGammaCount = 3
- };
-
- FontRenderer* getRenderer(Gamma gamma);
-
- uint32_t mRenderersUsageCount[kGammaCount];
- std::unique_ptr<FontRenderer> mRenderers[kGammaCount];
-
- uint8_t mGammaTable[256 * kGammaCount];
-
- friend class GammaFontRenderer;
};
}; // namespace uirenderer
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index fa20b0807a88..4785ea48cddc 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -135,10 +135,6 @@ struct Glop {
} fill;
struct Transform {
- // Orthographic projection matrix for current FBO
- // TODO: move out of Glop, since this is static per FBO
- Matrix4 ortho;
-
// 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;
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 288fed360162..d2da8513ff56 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -274,7 +274,7 @@ void GlopBuilder::setFill(int color, float alphaScale,
SkXfermode::Mode mode;
SkScalar srcColorMatrix[20];
if (colorFilter->asColorMode(&color, &mode)) {
- mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorBlend;
+ mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Blend;
mDescription.colorMode = mode;
const float alpha = SkColorGetA(color) / 255.0f;
@@ -286,7 +286,7 @@ void GlopBuilder::setFill(int color, float alphaScale,
alpha,
};
} else if (colorFilter->asColorMatrix(srcColorMatrix)) {
- mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::kColorMatrix;
+ mOutGlop->fill.filterMode = mDescription.colorOp = ProgramDescription::ColorFilterMode::Matrix;
float* colorMatrix = mOutGlop->fill.filter.matrix.matrix;
memcpy(colorMatrix, srcColorMatrix, 4 * sizeof(float));
@@ -305,7 +305,7 @@ void GlopBuilder::setFill(int color, float alphaScale,
LOG_ALWAYS_FATAL("unsupported ColorFilter");
}
} else {
- mOutGlop->fill.filterMode = ProgramDescription::kColorNone;
+ mOutGlop->fill.filterMode = ProgramDescription::ColorFilterMode::None;
}
}
@@ -435,7 +435,6 @@ GlopBuilder& GlopBuilder::setFillLayer(Texture& texture, const SkColorFilter* co
mOutGlop->fill.texture = { &texture,
GL_TEXTURE_2D, GL_LINEAR, GL_CLAMP_TO_EDGE, nullptr };
- mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
setFill(SK_ColorWHITE, alpha, mode, modeUsage, nullptr, colorFilter);
@@ -449,7 +448,6 @@ GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) {
mOutGlop->fill.texture = { &(layer.getTexture()),
layer.getRenderTarget(), GL_LINEAR, GL_CLAMP_TO_EDGE, &layer.getTexTransform() };
- mOutGlop->fill.color = { alpha, alpha, alpha, alpha };
setFill(SK_ColorWHITE, alpha, layer.getMode(), Blend::ModeOrderSwap::NoSwap,
nullptr, layer.getColorFilter());
@@ -463,13 +461,12 @@ GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) {
// Transform
////////////////////////////////////////////////////////////////////////////////
-void GlopBuilder::setTransform(const Matrix4& ortho, const Matrix4& canvas,
- const int transformFlags) {
+GlopBuilder& GlopBuilder::setTransform(const Matrix4& canvas, const int transformFlags) {
TRIGGER_STAGE(kTransformStage);
- mOutGlop->transform.ortho.load(ortho);
- mOutGlop->transform.canvas.load(canvas);
+ mOutGlop->transform.canvas = canvas;
mOutGlop->transform.transformFlags = transformFlags;
+ return *this;
}
////////////////////////////////////////////////////////////////////////////////
@@ -615,7 +612,7 @@ void GlopBuilder::build() {
shaderMatrix.loadInverse(mOutGlop->transform.canvas);
shaderMatrix.multiply(mOutGlop->transform.modelView);
} else {
- shaderMatrix.load(mOutGlop->transform.modelView);
+ shaderMatrix = mOutGlop->transform.modelView;
}
SkiaShader::store(mCaches, *mShader, shaderMatrix,
&textureUnit, &mDescription, &(mOutGlop->fill.skiaShaderData));
@@ -635,5 +632,42 @@ void GlopBuilder::build() {
mOutGlop->transform.meshTransform().mapRect(mOutGlop->bounds);
}
+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.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("Glop blend %d %d", glop.blend.src, glop.blend.dst);
+ ALOGD("Glop bounds " RECT_STRING, RECT_ARGS(glop.bounds));
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 549bb21e5f8d..6f5802eedefd 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -71,9 +71,9 @@ public:
GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) {
- setTransform(snapshot.getOrthoMatrix(), *snapshot.transform, transformFlags);
- return *this;
+ return setTransform(*snapshot.transform, transformFlags);
}
+ GlopBuilder& setTransform(const Matrix4& canvas, const int transformFlags);
GlopBuilder& setModelViewMapUnitToRect(const Rect destination);
GlopBuilder& setModelViewMapUnitToRectSnap(const Rect destination);
@@ -98,12 +98,12 @@ public:
GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState);
void build();
+
+ static void dump(const Glop& glop);
private:
void setFill(int color, float alphaScale,
SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
const SkShader* shader, const SkColorFilter* colorFilter);
- void setTransform(const Matrix4& ortho, const Matrix4& canvas,
- const int transformFlags);
enum StageFlags {
kInitialStage = 0,
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index ea93e7f9716b..aa105f9fec0a 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <utils/JenkinsHash.h>
#include "Caches.h"
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 08319ea1ec9b..7534c5d11164 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -25,7 +25,6 @@
#include <utils/LruCache.h>
#include <utils/Mutex.h>
-#include <utils/Vector.h>
namespace android {
namespace uirenderer {
@@ -183,7 +182,6 @@ private:
bool mUseFloatTexture;
bool mHasNpot;
- Vector<SkShader*> mGarbage;
mutable Mutex mLock;
}; // class GradientCache
diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp
index a31c54675f8a..68a356ba1be0 100644
--- a/libs/hwui/Image.cpp
+++ b/libs/hwui/Image.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <utils/Log.h>
#include "Caches.h"
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index e1b0fc3937c5..cc47f0052b73 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -16,11 +16,11 @@
#include "Interpolator.h"
-#include <cmath>
-#include <cutils/log.h>
-
#include "utils/MathUtils.h"
+#include <algorithm>
+#include <cutils/log.h>
+
namespace android {
namespace uirenderer {
@@ -106,7 +106,7 @@ float LUTInterpolator::interpolate(float input) {
weight = modff(lutpos, &ipart);
int i1 = (int) ipart;
- int i2 = MathUtils::min(i1 + 1, (int) mSize - 1);
+ int i2 = std::min(i1 + 1, (int) mSize - 1);
LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!"
" i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f",
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index e16865ede160..f99d92b89420 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include "Layer.h"
#include "Caches.h"
@@ -38,7 +36,7 @@ namespace android {
namespace uirenderer {
Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight)
- : state(kState_Uncached)
+ : state(State::Uncached)
, caches(Caches::getInstance())
, renderState(renderState)
, texture(caches)
@@ -172,7 +170,8 @@ void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right,
}
void Layer::setPaint(const SkPaint* paint) {
- OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+ alpha = PaintUtils::getAlphaDirect(paint);
+ mode = PaintUtils::getXfermodeDirect(paint);
setColorFilter((paint) ? paint->getColorFilter() : nullptr);
}
@@ -238,8 +237,7 @@ void Layer::defer(const OpenGLRenderer& rootRenderer) {
DeferStateStruct deferredState(*deferredList, *renderer,
RenderNode::kReplayFlag_ClipChildren);
- renderer->setViewport(width, height);
- renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
+ renderer->setupFrameState(width, height, dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom, !isBlend());
renderNode->computeOrdering();
@@ -260,9 +258,8 @@ void Layer::flush() {
ATRACE_LAYER_WORK("Issue");
renderer->startMark((renderNode.get() != nullptr) ? renderNode->getName() : "Layer");
- renderer->setViewport(layer.getWidth(), layer.getHeight());
- renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
- !isBlend());
+ renderer->prepareDirty(layer.getWidth(), layer.getHeight(),
+ dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend());
deferredList->flush(*renderer, dirtyRect);
@@ -279,9 +276,8 @@ void Layer::render(const OpenGLRenderer& rootRenderer) {
ATRACE_LAYER_WORK("Direct-Issue");
updateLightPosFromRenderer(rootRenderer);
- renderer->setViewport(layer.getWidth(), layer.getHeight());
- renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
- !isBlend());
+ renderer->prepareDirty(layer.getWidth(), layer.getHeight(),
+ dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend());
renderer->drawRenderNode(renderNode.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren);
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index b670870ca55f..e90f055b667b 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -56,19 +56,19 @@ struct DeferStateStruct;
*/
class Layer : public VirtualLightRefBase {
public:
- enum Type {
- kType_Texture,
- kType_DisplayList,
+ enum class Type {
+ Texture,
+ DisplayList,
};
// layer lifecycle, controlled from outside
- enum State {
- kState_Uncached = 0,
- kState_InCache = 1,
- kState_FailedToCache = 2,
- kState_RemovedFromCache = 3,
- kState_DeletedFromCache = 4,
- kState_InGarbageList = 5,
+ enum class State {
+ Uncached = 0,
+ InCache = 1,
+ FailedToCache = 2,
+ RemovedFromCache = 3,
+ DeletedFromCache = 4,
+ InGarbageList = 5,
};
State state; // public for logging/debugging purposes
@@ -241,7 +241,7 @@ public:
}
inline bool isTextureLayer() const {
- return type == kType_Texture;
+ return type == Type::Texture;
}
inline SkColorFilter* getColorFilter() const {
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index bcbd4129b7e7..39cadd198c83 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <GLES2/gl2.h>
#include <utils/Log.h>
@@ -83,15 +81,14 @@ void LayerCache::deleteLayer(Layer* layer) {
LAYER_LOGD("Destroying layer %dx%d, fbo %d", layer->getWidth(), layer->getHeight(),
layer->getFbo());
mSize -= layer->getWidth() * layer->getHeight() * 4;
- layer->state = Layer::kState_DeletedFromCache;
+ layer->state = Layer::State::DeletedFromCache;
layer->decStrong(nullptr);
}
}
void LayerCache::clear() {
- size_t count = mCache.size();
- for (size_t i = 0; i < count; i++) {
- deleteLayer(mCache.itemAt(i).mLayer);
+ for (auto entry : mCache) {
+ deleteLayer(entry.mLayer);
}
mCache.clear();
}
@@ -100,21 +97,21 @@ Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uin
Layer* layer = nullptr;
LayerEntry entry(width, height);
- ssize_t index = mCache.indexOf(entry);
+ auto iter = mCache.find(entry);
- if (index >= 0) {
- entry = mCache.itemAt(index);
- mCache.removeAt(index);
+ if (iter != mCache.end()) {
+ entry = *iter;
+ mCache.erase(iter);
layer = entry.mLayer;
- layer->state = Layer::kState_RemovedFromCache;
+ layer->state = Layer::State::RemovedFromCache;
mSize -= layer->getWidth() * layer->getHeight() * 4;
LAYER_LOGD("Reusing layer %dx%d", layer->getWidth(), layer->getHeight());
} else {
LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight);
- layer = new Layer(Layer::kType_DisplayList, renderState, entry.mWidth, entry.mHeight);
+ layer = new Layer(Layer::Type::DisplayList, renderState, entry.mWidth, entry.mHeight);
layer->setBlend(true);
layer->generateTexture();
layer->bindTexture();
@@ -131,9 +128,7 @@ Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uin
}
void LayerCache::dump() {
- size_t size = mCache.size();
- for (size_t i = 0; i < size; i++) {
- const LayerEntry& entry = mCache.itemAt(i);
+ for (auto entry : mCache) {
ALOGD(" Layer size %dx%d", entry.mWidth, entry.mHeight);
}
}
@@ -146,13 +141,9 @@ bool LayerCache::put(Layer* layer) {
if (size < mMaxSize) {
// TODO: Use an LRU
while (mSize + size > mMaxSize) {
- size_t position = 0;
-#if LAYER_REMOVE_BIGGEST_FIRST
- position = mCache.size() - 1;
-#endif
- Layer* victim = mCache.itemAt(position).mLayer;
+ Layer* victim = mCache.begin()->mLayer;
deleteLayer(victim);
- mCache.removeAt(position);
+ mCache.erase(mCache.begin());
LAYER_LOGD(" Deleting layer %.2fx%.2f", victim->layer.getWidth(),
victim->layer.getHeight());
@@ -162,14 +153,14 @@ bool LayerCache::put(Layer* layer) {
LayerEntry entry(layer);
- mCache.add(entry);
+ mCache.insert(entry);
mSize += size;
- layer->state = Layer::kState_InCache;
+ layer->state = Layer::State::InCache;
return true;
}
- layer->state = Layer::kState_FailedToCache;
+ layer->state = Layer::State::FailedToCache;
return false;
}
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 7d17b9ba41aa..6fe7b3aae859 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -19,7 +19,8 @@
#include "Debug.h"
#include "Layer.h"
-#include "utils/SortedList.h"
+
+#include <set>
namespace android {
namespace uirenderer {
@@ -118,12 +119,8 @@ private:
return compare(*this, other) != 0;
}
- friend inline int strictly_order_type(const LayerEntry& lhs, const LayerEntry& rhs) {
- return LayerEntry::compare(lhs, rhs) < 0;
- }
-
- friend inline int compare_type(const LayerEntry& lhs, const LayerEntry& rhs) {
- return LayerEntry::compare(lhs, rhs);
+ bool operator<(const LayerEntry& other) const {
+ return LayerEntry::compare(*this, other) < 0;
}
Layer* mLayer;
@@ -133,7 +130,7 @@ private:
void deleteLayer(Layer* layer);
- SortedList<LayerEntry> mCache;
+ std::multiset<LayerEntry> mCache;
uint32_t mSize;
uint32_t mMaxSize;
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 00add2903371..227271d83cf8 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -43,8 +43,8 @@ LayerRenderer::LayerRenderer(RenderState& renderState, Layer* layer)
LayerRenderer::~LayerRenderer() {
}
-void LayerRenderer::prepareDirty(float left, float top, float right, float bottom,
- bool opaque) {
+void LayerRenderer::prepareDirty(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque) {
LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo());
mRenderState.bindFramebuffer(mLayer->getFbo());
@@ -58,13 +58,14 @@ void LayerRenderer::prepareDirty(float left, float top, float right, float botto
mLayer->region.clear();
dirty.set(0.0f, 0.0f, width, height);
} else {
- dirty.intersect(0.0f, 0.0f, width, height);
+ dirty.doIntersect(0.0f, 0.0f, width, height);
android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom);
mLayer->region.subtractSelf(r);
}
mLayer->clipRect.set(dirty);
- OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
+ OpenGLRenderer::prepareDirty(viewportWidth, viewportHeight,
+ dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
}
void LayerRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
@@ -272,7 +273,7 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
Layer* LayerRenderer::createTextureLayer(RenderState& renderState) {
LAYER_RENDERER_LOGD("Creating new texture layer");
- Layer* layer = new Layer(Layer::kType_Texture, renderState, 0, 0);
+ Layer* layer = new Layer(Layer::Type::Texture, renderState, 0, 0);
layer->setCacheable(false);
layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f);
layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f);
@@ -430,9 +431,8 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap*
{
LayerRenderer renderer(renderState, layer);
- renderer.setViewport(bitmap->width(), bitmap->height());
- renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
- bitmap->width(), bitmap->height(), !layer->isBlend());
+ renderer.OpenGLRenderer::prepareDirty(bitmap->width(), bitmap->height(),
+ 0.0f, 0.0f, bitmap->width(), bitmap->height(), !layer->isBlend());
renderState.scissor().setEnabled(false);
renderer.translate(0.0f, bitmap->height());
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index 47ded7e7e0bf..e4a54b0a0520 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -50,8 +50,8 @@ public:
virtual ~LayerRenderer();
virtual void onViewportInitialized() override { /* do nothing */ }
- virtual void prepareDirty(float left, float top, float right, float bottom,
- bool opaque) override;
+ virtual void prepareDirty(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque) override;
virtual void clear(float left, float top, float right, float bottom, bool opaque) override;
virtual bool finish() override;
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 06e67c0e85ad..73ebd1304750 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <math.h>
#include <stdlib.h>
#include <string.h>
@@ -154,10 +152,6 @@ void Matrix4::load(const float* v) {
mType = kTypeUnknown;
}
-void Matrix4::load(const Matrix4& v) {
- *this = v;
-}
-
void Matrix4::load(const SkMatrix& v) {
memset(data, 0, sizeof(data));
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index ed54a25f3edf..c017638db895 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -114,7 +114,6 @@ public:
void loadIdentity();
void load(const float* v);
- void load(const Matrix4& v);
void load(const SkMatrix& v);
void loadInverse(const Matrix4& v);
@@ -127,6 +126,9 @@ public:
void loadMultiply(const Matrix4& u, const Matrix4& v);
void loadOrtho(float left, float right, float bottom, float top, float near, float far);
+ void loadOrtho(int width, int height) {
+ loadOrtho(0, width, height, 0, -1, 1);
+ }
uint8_t getType() const;
@@ -139,7 +141,7 @@ public:
void multiply(const Matrix4& v) {
Matrix4 u;
u.loadMultiply(*this, v);
- load(u);
+ *this = u;
}
void multiply(float v);
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
new file mode 100644
index 000000000000..7c0e2570972a
--- /dev/null
+++ b/libs/hwui/OpReorderer.cpp
@@ -0,0 +1,402 @@
+/*
+ * 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 "OpReorderer.h"
+
+#include "utils/PaintUtils.h"
+#include "RenderNode.h"
+
+#include "SkCanvas.h"
+#include "utils/Trace.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, merging %d, bounds " RECT_STRING, this, mMerging, RECT_ARGS(mBounds));
+ }
+protected:
+ batchid_t mBatchId;
+ Rect mBounds;
+ std::vector<BakedOpState*> mOps;
+ bool mMerging;
+};
+
+class OpBatch : public BatchBase {
+public:
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ 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:
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ MergingOpBatch(batchid_t batchId, BakedOpState* op)
+ : BatchBase(batchId, op, true) {
+ }
+
+ /*
+ * 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) {
+ 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;
+ if (lhs->projectionPathMask != rhs->projectionPathMask) 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);
+
+ const int newClipSideFlags = op->computedState.clipSideFlags;
+ mClipSideFlags |= newClipSideFlags;
+
+ const Rect& opClip = op->computedState.clipRect;
+ if (newClipSideFlags & OpClipSideFlags::Left) mClipRect.left = opClip.left;
+ if (newClipSideFlags & OpClipSideFlags::Top) mClipRect.top = opClip.top;
+ if (newClipSideFlags & OpClipSideFlags::Right) mClipRect.right = opClip.right;
+ if (newClipSideFlags & OpClipSideFlags::Bottom) mClipRect.bottom = opClip.bottom;
+ }
+
+private:
+ int mClipSideFlags = 0;
+ Rect mClipRect;
+};
+
+class NullClient: public CanvasStateClient {
+ void onViewportInitialized() override {}
+ void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+ GLuint getTargetFbo() const override { return 0; }
+};
+static NullClient sNullClient;
+
+OpReorderer::OpReorderer()
+ : mCanvasState(sNullClient) {
+}
+
+void OpReorderer::defer(const SkRect& clip, int viewportWidth, int viewportHeight,
+ const std::vector< sp<RenderNode> >& nodes) {
+ mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
+ clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
+ Vector3());
+ for (const sp<RenderNode>& node : nodes) {
+ if (node->nothingToDraw()) continue;
+
+ // TODO: dedupe this code with onRenderNode()
+ mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ if (node->applyViewProperties(mCanvasState)) {
+ // not rejected do ops...
+ const DisplayList& displayList = node->getDisplayList();
+ deferImpl(displayList);
+ }
+ mCanvasState.restore();
+ }
+}
+
+void OpReorderer::defer(int viewportWidth, int viewportHeight, const DisplayList& displayList) {
+ ATRACE_NAME("prepare drawing commands");
+ mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
+ 0, 0, viewportWidth, viewportHeight, Vector3());
+ deferImpl(displayList);
+}
+
+/**
+ * Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() 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 OpReorderer::onBitmapOp(const BitmapOp&)
+ */
+#define OP_RECIEVER(Type) \
+ [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast<const Type&>(op)); },
+void OpReorderer::deferImpl(const DisplayList& displayList) {
+ static std::function<void(OpReorderer& reorderer, const RecordedOp&)> receivers[] = {
+ MAP_OPS(OP_RECIEVER)
+ };
+ for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
+ for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
+ const RecordedOp* op = displayList.getOps()[opIndex];
+ receivers[op->opId](*this, *op);
+ }
+ }
+}
+
+void OpReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) {
+ ATRACE_NAME("flush drawing commands");
+ for (const BatchBase* batch : mBatches) {
+ // TODO: different behavior based on batch->isMerging()
+ for (const BakedOpState* op : batch->getOps()) {
+ receivers[op->op->opId](arg, *op->op, *op);
+ }
+ }
+}
+
+BakedOpState* OpReorderer::bakeOpState(const RecordedOp& recordedOp) {
+ return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp);
+}
+
+void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
+ if (op.renderNode->nothingToDraw()) {
+ return;
+ }
+ mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+
+ // apply state from RecordedOp
+ mCanvasState.concatMatrix(op.localMatrix);
+ mCanvasState.clipRect(op.localClipRect.left, op.localClipRect.top,
+ op.localClipRect.right, op.localClipRect.bottom, SkRegion::kIntersect_Op);
+
+ // apply RenderProperties state
+ if (op.renderNode->applyViewProperties(mCanvasState)) {
+ // not rejected do ops...
+ deferImpl(op.renderNode->getDisplayList());
+ }
+ mCanvasState.restore();
+}
+
+static batchid_t tessellatedBatchId(const SkPaint& paint) {
+ return paint.getPathEffect()
+ ? OpBatchType::AlphaMaskTexture
+ : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
+}
+
+void OpReorderer::onBitmapOp(const BitmapOp& op) {
+ BakedOpState* bakedStateOp = bakeOpState(op);
+ if (!bakedStateOp) return; // quick rejected
+
+ mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+ // TODO: AssetAtlas
+
+ deferMergeableOp(bakedStateOp, OpBatchType::Bitmap, mergeId);
+}
+
+void OpReorderer::onRectOp(const RectOp& op) {
+ BakedOpState* bakedStateOp = bakeOpState(op);
+ if (!bakedStateOp) return; // quick rejected
+ deferUnmergeableOp(bakedStateOp, tessellatedBatchId(*op.paint));
+}
+
+void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) {
+ BakedOpState* bakedStateOp = bakeOpState(op);
+ if (!bakedStateOp) return; // quick rejected
+ deferUnmergeableOp(bakedStateOp, OpBatchType::Vertices);
+}
+
+// iterate back toward target to see if anything drawn since should overlap the new op
+// if no target, merging ops still interate to find similar batch to insert after
+void OpReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
+ BatchBase** targetBatch, size_t* insertBatchIndex) const {
+ for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; 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 OpReorderer::deferUnmergeableOp(BakedOpState* op, batchid_t batchId) {
+ 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 = new (mAllocator) OpBatch(batchId, op);
+ mBatchLookup[batchId] = targetBatch;
+ mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+ }
+}
+
+// insertion point of a new batch, will hopefully be immediately after similar batch
+// (generally, should be similar shader)
+void OpReorderer::deferMergeableOp(BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
+ MergingOpBatch* targetBatch = nullptr;
+
+ // Try to merge with any existing batch with same mergeId
+ auto getResult = mMergingBatches[batchId].find(mergeId);
+ if (getResult != mMergingBatches[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 = new (mAllocator) MergingOpBatch(batchId, op);
+ mMergingBatches[batchId].insert(std::make_pair(mergeId, targetBatch));
+
+ mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+ }
+}
+
+void OpReorderer::dump() {
+ for (const BatchBase* batch : mBatches) {
+ batch->dump();
+ }
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
new file mode 100644
index 000000000000..6776a3c9fc18
--- /dev/null
+++ b/libs/hwui/OpReorderer.h
@@ -0,0 +1,143 @@
+/*
+ * 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_OP_REORDERER_H
+#define ANDROID_HWUI_OP_REORDERER_H
+
+#include "BakedOpState.h"
+#include "CanvasState.h"
+#include "DisplayList.h"
+#include "RecordedOp.h"
+
+#include <vector>
+#include <unordered_map>
+
+struct SkRect;
+
+namespace android {
+namespace uirenderer {
+
+class BakedOpState;
+class BatchBase;
+class MergingOpBatch;
+class OpBatch;
+class Rect;
+
+typedef int batchid_t;
+typedef const void* mergeid_t;
+
+namespace OpBatchType {
+ enum {
+ None = 0, // Don't batch
+ Bitmap,
+ Patch,
+ AlphaVertices,
+ Vertices,
+ AlphaMaskTexture,
+ Text,
+ ColorText,
+
+ Count // must be last
+ };
+}
+
+class OpReorderer {
+public:
+ OpReorderer();
+
+ // TODO: not final, just presented this way for simplicity. Layers too?
+ void defer(const SkRect& clip, int viewportWidth, int viewportHeight,
+ const std::vector< sp<RenderNode> >& nodes);
+
+ void defer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
+ typedef std::function<void(void*, const RecordedOp&, const BakedOpState&)> BakedOpReceiver;
+
+ /**
+ * replayBakedOps() is templated based on what class will recieve 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.
+ *
+ * For example a BitmapOp would resolve, via the lambda lookup, to calling:
+ *
+ * StaticReceiver::onBitmapOp(Arg* arg, const BitmapOp& op, const BakedOpState& state);
+ */
+#define BAKED_OP_RECEIVER(Type) \
+ [](void* internalArg, const RecordedOp& op, const BakedOpState& state) { \
+ StaticReceiver::on##Type(static_cast<Arg*>(internalArg), static_cast<const Type&>(op), state); \
+ },
+ template <typename StaticReceiver, typename Arg>
+ void replayBakedOps(Arg* arg) {
+ static BakedOpReceiver receivers[] = {
+ MAP_OPS(BAKED_OP_RECEIVER)
+ };
+ StaticReceiver::startFrame(*arg);
+ replayBakedOpsImpl((void*)arg, receivers);
+ StaticReceiver::endFrame(*arg);
+ }
+private:
+ BakedOpState* bakeOpState(const RecordedOp& recordedOp);
+
+ void deferImpl(const DisplayList& displayList);
+
+ void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers);
+
+ /**
+ * Declares all OpReorderer::onXXXXOp() methods for every RecordedOp type.
+ *
+ * These private methods are called from within deferImpl to defer each individual op
+ * type differently.
+ */
+#define INTERNAL_OP_HANDLER(Type) \
+ void on##Type(const Type& op);
+ MAP_OPS(INTERNAL_OP_HANDLER)
+
+ // 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(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(BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
+
+ void dump();
+
+ 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*> mMergingBatches[OpBatchType::Count];
+
+ // Maps batch ids to the most recent *non-merging* batch of that id
+ OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
+ CanvasState mCanvasState;
+
+ // contains ResolvedOps and Batches
+ LinearAllocator mAllocator;
+
+ int mEarliestBatchIndex = 0;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_OP_REORDERER_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2292ef415cfc..cd03ac407d81 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -70,8 +70,6 @@ OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
, mRenderState(renderState)
, mFrameStarted(false)
, mScissorOptimizationDisabled(false)
- , mSuppressTiling(false)
- , mFirstFrameAfterResize(true)
, mDirty(false)
, mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN})
, mLightRadius(FLT_MIN)
@@ -113,13 +111,13 @@ void OpenGLRenderer::setLightCenter(const Vector3& lightCenter) {
void OpenGLRenderer::onViewportInitialized() {
glDisable(GL_DITHER);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- mFirstFrameAfterResize = true;
}
-void OpenGLRenderer::setupFrameState(float left, float top,
- float right, float bottom, bool opaque) {
+void OpenGLRenderer::setupFrameState(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque) {
mCaches.clearGarbage();
- mState.initializeSaveStack(left, top, right, bottom, mLightCenter);
+ mState.initializeSaveStack(viewportWidth, viewportHeight,
+ left, top, right, bottom, mLightCenter);
mOpaque = opaque;
mTilingClip.set(left, top, right, bottom);
}
@@ -134,25 +132,16 @@ void OpenGLRenderer::startFrame() {
mRenderState.setViewport(mState.getWidth(), mState.getHeight());
- // Functors break the tiling extension in pretty spectacular ways
- // This ensures we don't use tiling when a functor is going to be
- // invoked during the frame
- mSuppressTiling = mCaches.hasRegisteredFunctors()
- || mFirstFrameAfterResize;
- mFirstFrameAfterResize = false;
-
- startTilingCurrentClip(true);
-
debugOverdraw(true, true);
clear(mTilingClip.left, mTilingClip.top,
mTilingClip.right, mTilingClip.bottom, mOpaque);
}
-void OpenGLRenderer::prepareDirty(float left, float top,
- float right, float bottom, bool opaque) {
+void OpenGLRenderer::prepareDirty(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque) {
- setupFrameState(left, top, right, bottom, opaque);
+ setupFrameState(viewportWidth, viewportHeight, left, top, right, bottom, opaque);
// Layer renderers will start the frame immediately
// The framebuffer renderer will first defer the display list
@@ -192,46 +181,8 @@ void OpenGLRenderer::clear(float left, float top, float right, float bottom, boo
mRenderState.scissor().reset();
}
-void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) {
- if (!mSuppressTiling) {
- const Snapshot* snapshot = currentSnapshot();
-
- const Rect* clip = &mTilingClip;
- if (snapshot->flags & Snapshot::kFlagFboTarget) {
- clip = &(snapshot->layer->clipRect);
- }
-
- startTiling(*clip, getViewportHeight(), opaque, expand);
- }
-}
-
-void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque, bool expand) {
- if (!mSuppressTiling) {
- if(expand) {
- // Expand the startTiling region by 1
- int leftNotZero = (clip.left > 0) ? 1 : 0;
- int topNotZero = (windowHeight - clip.bottom > 0) ? 1 : 0;
-
- mCaches.startTiling(
- clip.left - leftNotZero,
- windowHeight - clip.bottom - topNotZero,
- clip.right - clip.left + leftNotZero + 1,
- clip.bottom - clip.top + topNotZero + 1,
- opaque);
- } else {
- mCaches.startTiling(clip.left, windowHeight - clip.bottom,
- clip.right - clip.left, clip.bottom - clip.top, opaque);
- }
- }
-}
-
-void OpenGLRenderer::endTiling() {
- if (!mSuppressTiling) mCaches.endTiling();
-}
-
bool OpenGLRenderer::finish() {
renderOverdraw();
- endTiling();
mTempPaths.clear();
// When finish() is invoked on FBO 0 we've reached the end
@@ -381,7 +332,6 @@ bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
&& layer->renderNode.get() && layer->renderNode->isRenderable()) {
if (inFrame) {
- endTiling();
debugOverdraw(false, false);
}
@@ -393,7 +343,6 @@ bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
if (inFrame) {
resumeAfterLayer();
- startTilingCurrentClip();
}
layer->debugDrawUpdate = Properties::debugLayersUpdates;
@@ -419,7 +368,7 @@ void OpenGLRenderer::updateLayers() {
// Note: it is very important to update the layers in order
for (int i = 0; i < count; i++) {
- Layer* layer = mLayerUpdates.itemAt(i).get();
+ Layer* layer = mLayerUpdates[i].get();
updateLayer(layer, false);
}
@@ -438,7 +387,7 @@ void OpenGLRenderer::flushLayers() {
// Note: it is very important to update the layers in order
for (int i = 0; i < count; i++) {
- mLayerUpdates.itemAt(i)->flush();
+ mLayerUpdates[i]->flush();
}
mLayerUpdates.clear();
@@ -455,7 +404,7 @@ void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
// the insertion order. The linear search is not an issue since
// this list is usually very short (typically one item, at most a few)
for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
- if (mLayerUpdates.itemAt(i) == layer) {
+ if (mLayerUpdates[i] == layer) {
return;
}
}
@@ -466,8 +415,8 @@ void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
void OpenGLRenderer::cancelLayerUpdate(Layer* layer) {
if (layer) {
for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
- if (mLayerUpdates.itemAt(i) == layer) {
- mLayerUpdates.removeAt(i);
+ if (mLayerUpdates[i] == layer) {
+ mLayerUpdates.erase(mLayerUpdates.begin() + i);
break;
}
}
@@ -539,7 +488,8 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool
currentTransform()->mapRect(bounds);
// Layers only make sense if they are in the framebuffer's bounds
- if (bounds.intersect(mState.currentClipRect())) {
+ bounds.doIntersect(mState.currentClipRect());
+ if (!bounds.isEmpty()) {
// We cannot work with sub-pixels in this case
bounds.snapToPixelBoundaries();
@@ -548,23 +498,20 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool
// of the framebuffer
const Snapshot& previous = *(currentSnapshot()->previous);
Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
- if (!bounds.intersect(previousViewport)) {
- bounds.setEmpty();
- } else if (fboLayer) {
+
+ bounds.doIntersect(previousViewport);
+ if (!bounds.isEmpty() && fboLayer) {
clip.set(bounds);
mat4 inverse;
inverse.loadInverse(*currentTransform());
inverse.mapRect(clip);
clip.snapToPixelBoundaries();
- if (clip.intersect(untransformedBounds)) {
+ clip.doIntersect(untransformedBounds);
+ if (!clip.isEmpty()) {
clip.translate(-untransformedBounds.left, -untransformedBounds.top);
bounds.set(untransformedBounds);
- } else {
- clip.setEmpty();
}
}
- } else {
- bounds.setEmpty();
}
}
@@ -591,7 +538,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float
Rect bounds(left, top, right, bottom);
Rect clip;
calculateLayerBoundsAndClip(bounds, clip, true);
- updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint));
+ updateSnapshotIgnoreForLayer(bounds, clip, true, PaintUtils::getAlphaDirect(paint));
if (!mState.currentlyIgnored()) {
writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
@@ -666,7 +613,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
Rect clip;
Rect bounds(left, top, right, bottom);
calculateLayerBoundsAndClip(bounds, clip, fboLayer);
- updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint));
+ updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, PaintUtils::getAlphaDirect(paint));
// Bail out if we won't draw in this snapshot
if (mState.currentlyIgnored()) {
@@ -736,7 +683,6 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
writableSnapshot()->initializeViewport(bounds.getWidth(), bounds.getHeight());
writableSnapshot()->roundRectClipState = nullptr;
- endTiling();
debugOverdraw(false, false);
// Bind texture to FBO
mRenderState.bindFramebuffer(layer->getFbo());
@@ -751,9 +697,6 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
layer->getTextureId(), 0);
- // Expand the startTiling region by 1
- startTilingCurrentClip(true, true);
-
// Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
mRenderState.scissor().setEnabled(true);
mRenderState.scissor().set(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
@@ -786,8 +729,6 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto
mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
if (fboLayer) {
- endTiling();
-
// Detach the texture from the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
@@ -796,8 +737,6 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto
// Unbind current FBO and restore previous one
mRenderState.bindFramebuffer(restored.fbo);
debugOverdraw(true, false);
-
- startTilingCurrentClip();
}
if (!fboLayer && layer->getAlpha() < 255) {
@@ -1097,7 +1036,8 @@ void OpenGLRenderer::dirtyLayer(const float left, const float top,
}
void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
- if (CC_LIKELY(!bounds.isEmpty() && bounds.intersect(mState.currentClipRect()))) {
+ bounds.doIntersect(mState.currentClipRect());
+ if (!bounds.isEmpty()) {
bounds.snapToPixelBoundaries();
android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
if (!dirty.isEmpty()) {
@@ -1171,7 +1111,8 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
// is used, it should more closely duplicate the quickReject logic (in how it uses
// snapToPixelBoundaries)
- if (!clippedBounds.intersect(currentClip)) {
+ clippedBounds.doIntersect(currentClip);
+ if (clippedBounds.isEmpty()) {
// quick rejected
return true;
}
@@ -1201,7 +1142,7 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
// Transform and alpha always deferred, since they are used by state operations
// (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything)
- state.mMatrix.load(*currentMatrix);
+ state.mMatrix = *currentMatrix;
state.mAlpha = currentSnapshot()->alpha;
// always store/restore, since these are just pointers
@@ -1211,7 +1152,7 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
}
void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) {
- setMatrix(state.mMatrix);
+ setGlobalMatrix(state.mMatrix);
writableSnapshot()->alpha = state.mAlpha;
writableSnapshot()->roundRectClipState = state.mRoundRectClipState;
writableSnapshot()->projectionPathMask = state.mProjectionPathMask;
@@ -1267,17 +1208,10 @@ void OpenGLRenderer::ensureStencilBuffer() {
void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
// The layer's FBO is already bound when we reach this stage
if (!layer->getStencilRenderBuffer()) {
- // GL_QCOM_tiled_rendering doesn't like it if a renderbuffer
- // is attached after we initiated tiling. We must turn it off,
- // attach the new render buffer then turn tiling back on
- endTiling();
-
RenderBuffer* buffer = mCaches.renderBufferCache.get(
Stencil::getLayerStencilFormat(),
layer->getWidth(), layer->getHeight());
layer->setStencilRenderBuffer(buffer);
-
- startTiling(layer->clipRect, layer->layer.getHeight());
}
}
@@ -1308,9 +1242,8 @@ void OpenGLRenderer::drawRectangleList(const RectangleList& rectangleList) {
Rect bounds = tr.getBounds();
if (transform.rectToRect()) {
transform.mapRect(bounds);
- if (!bounds.intersect(scissorBox)) {
- bounds.setEmpty();
- } else {
+ bounds.doIntersect(scissorBox);
+ if (!bounds.isEmpty()) {
handlePointNoTransform(rectangleVertices, bounds.left, bounds.top);
handlePointNoTransform(rectangleVertices, bounds.right, bounds.top);
handlePointNoTransform(rectangleVertices, bounds.left, bounds.bottom);
@@ -1471,7 +1404,7 @@ void OpenGLRenderer::renderGlop(const Glop& glop, GlopRenderType type) {
setStencilFromClip();
}
- mRenderState.render(glop);
+ mRenderState.render(glop, currentSnapshot()->getOrthoMatrix());
if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) {
// TODO: specify more clearly when a draw should dirty the layer.
// is writing to the stencil the only time we should ignore this?
@@ -1497,10 +1430,7 @@ void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t
return;
}
- // Don't avoid overdraw when visualizing, since that makes it harder to
- // debug where it's coming from, and when the problem occurs.
- bool avoidOverdraw = !Properties::debugOverdraw;
- DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
+ DeferredDisplayList deferredList(mState.currentClipRect());
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
renderNode->defer(deferStruct, 0);
@@ -1918,7 +1848,7 @@ void OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p
// Mask the ripple path by the projection mask, now that it's
// in local space. Note that this can create CCW paths.
- Op(path, maskPath, kIntersect_PathOp, &path);
+ Op(path, maskPath, kIntersect_SkPathOp, &path);
}
drawConvexPath(path, p);
}
@@ -2024,8 +1954,8 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
FontRenderer& fontRenderer, int alpha, float x, float y) {
mCaches.textureState().activateTexture(0);
- TextShadow textShadow;
- if (!getTextShadow(paint, &textShadow)) {
+ PaintUtils::TextShadow textShadow;
+ if (!PaintUtils::getTextShadow(paint, &textShadow)) {
LOG_ALWAYS_FATAL("failed to query shadow attributes");
}
@@ -2053,8 +1983,10 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
renderGlop(glop);
}
+// TODO: remove this, once mState.currentlyIgnored captures snapshot alpha
bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
- float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
+ float alpha = (PaintUtils::hasTextShadow(paint)
+ ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
return MathUtils::isZero(alpha)
&& PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
}
@@ -2080,14 +2012,13 @@ void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
y = floorf(y + currentTransform()->getTranslateY() + 0.5f);
}
- FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+ FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
fontRenderer.setFont(paint, SkMatrix::I());
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
+ int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
- if (CC_UNLIKELY(hasTextShadow(paint))) {
+ if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
alpha, 0.0f, 0.0f);
}
@@ -2165,8 +2096,9 @@ void OpenGLRenderer::skew(float sx, float sy) {
mState.skew(sx, sy);
}
-void OpenGLRenderer::setMatrix(const Matrix4& matrix) {
- mState.setMatrix(matrix);
+void OpenGLRenderer::setLocalMatrix(const Matrix4& matrix) {
+ mState.setMatrix(mBaseTransform);
+ mState.concatMatrix(matrix);
}
void OpenGLRenderer::setLocalMatrix(const SkMatrix& matrix) {
@@ -2227,13 +2159,12 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float
y = floorf(y + transform.getTranslateY() + 0.5f);
}
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
+ int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
- FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+ FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
- if (CC_UNLIKELY(hasTextShadow(paint))) {
+ if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
fontRenderer.setFont(paint, SkMatrix::I());
drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
alpha, oldX, oldY);
@@ -2299,13 +2230,12 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
// TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
mRenderState.scissor().setEnabled(true);
- FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+ FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
fontRenderer.setFont(paint, SkMatrix::I());
fontRenderer.setTextureFiltering(true);
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
+ int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
const Rect* clip = &writableSnapshot()->getLocalClip();
@@ -2334,7 +2264,7 @@ void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
mDirty = true;
}
-void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
+void OpenGLRenderer::drawLayer(Layer* layer) {
if (!layer) {
return;
}
@@ -2350,7 +2280,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
bool clipRequired = false;
const bool rejected = mState.calculateQuickRejectForScissor(
- x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(),
+ 0, 0, layer->layer.getWidth(), layer->layer.getHeight(),
&clipRequired, nullptr, false);
if (rejected) {
@@ -2379,7 +2309,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
.setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount)
.setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
.setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewOffsetRectSnap(x, y, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight()))
+ .setModelViewOffsetRectSnap(0, 0, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight()))
.build();
DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
#if DEBUG_LAYERS_AS_REGIONS
@@ -2392,7 +2322,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
SkPaint paint;
paint.setColor(0x7f00ff00);
- drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), &paint);
+ drawColorRect(0, 0, layer->layer.getWidth(), layer->layer.getHeight(), &paint);
}
}
layer->hasDrawnSinceUpdate = true;
@@ -2595,12 +2525,6 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot
renderGlop(glop);
}
-void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha,
- SkXfermode::Mode* mode) const {
- getAlphaAndModeDirect(paint, alpha, mode);
- *alpha *= currentSnapshot()->alpha;
-}
-
float OpenGLRenderer::getLayerAlpha(const Layer* layer) const {
return (layer->getAlpha() / 255.0f) * currentSnapshot()->alpha;
}
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 402f6edd475d..400c225b53a0 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -35,6 +35,7 @@
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkColorFilter.h>
+#include <SkDrawLooper.h>
#include <SkMatrix.h>
#include <SkPaint.h>
#include <SkRegion.h>
@@ -44,12 +45,13 @@
#include <utils/Functor.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
-#include <utils/Vector.h>
#include <cutils/compiler.h>
#include <androidfw/ResourceTypes.h>
+#include <vector>
+
class SkShader;
namespace android {
@@ -117,15 +119,6 @@ public:
OpenGLRenderer(RenderState& renderState);
virtual ~OpenGLRenderer();
- /**
- * Sets the dimension of the underlying drawing surface. This method must
- * be called at least once every time the drawing surface changes size.
- *
- * @param width The width in pixels of the underlysing surface
- * @param height The height in pixels of the underlysing surface
- */
- void setViewport(int width, int height) { mState.setViewport(width, height); }
-
void initProperties();
void initLight(float lightRadius, uint8_t ambientShadowAlpha,
uint8_t spotShadowAlpha);
@@ -141,21 +134,8 @@ public:
* and will not be cleared. If false, the target surface
* will be cleared
*/
- virtual void prepareDirty(float left, float top, float right, float bottom,
- bool opaque);
-
- /**
- * Prepares the renderer to draw a frame. This method must be invoked
- * at the beginning of each frame. When this method is invoked, the
- * entire drawing surface is assumed to be redrawn.
- *
- * @param opaque If true, the target surface is considered opaque
- * and will not be cleared. If false, the target surface
- * will be cleared
- */
- void prepare(bool opaque) {
- prepareDirty(0.0f, 0.0f, mState.getWidth(), mState.getHeight(), opaque);
- }
+ virtual void prepareDirty(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque);
/**
* Indicates the end of a frame. This method must be invoked whenever
@@ -186,7 +166,7 @@ public:
const SkPaint* paint, int flags);
void drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1);
- void drawLayer(Layer* layer, float x, float y);
+ void drawLayer(Layer* layer);
void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
void drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint);
@@ -280,57 +260,6 @@ public:
void endMark() const;
/**
- * Gets the alpha and xfermode out of a paint object. If the paint is null
- * alpha will be 255 and the xfermode will be SRC_OVER. This method does
- * not multiply the paint's alpha by the current snapshot's alpha, and does
- * not replace the alpha with the overrideLayerAlpha
- *
- * @param paint The paint to extract values from
- * @param alpha Where to store the resulting alpha
- * @param mode Where to store the resulting xfermode
- */
- static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha,
- SkXfermode::Mode* mode) {
- *mode = getXfermodeDirect(paint);
- *alpha = getAlphaDirect(paint);
- }
-
- static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
- if (!paint) return SkXfermode::kSrcOver_Mode;
- return PaintUtils::getXfermode(paint->getXfermode());
- }
-
- static inline int getAlphaDirect(const SkPaint* paint) {
- if (!paint) return 255;
- return paint->getAlpha();
- }
-
- struct TextShadow {
- SkScalar radius;
- float dx;
- float dy;
- SkColor color;
- };
-
- static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
- SkDrawLooper::BlurShadowRec blur;
- if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
- if (textShadow) {
- textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
- textShadow->dx = blur.fOffset.fX;
- textShadow->dy = blur.fOffset.fY;
- textShadow->color = blur.fColor;
- }
- return true;
- }
- return false;
- }
-
- static inline bool hasTextShadow(const SkPaint* paint) {
- return getTextShadow(paint, nullptr);
- }
-
- /**
* Build the best transform to use to rasterize text given a full
* transform matrix, and whether filteration is needed.
*
@@ -366,8 +295,10 @@ public:
void restore();
void restoreToCount(int saveCount);
- void getMatrix(SkMatrix* outMatrix) const { mState.getMatrix(outMatrix); }
- void setMatrix(const SkMatrix& matrix) { mState.setMatrix(matrix); }
+ void setGlobalMatrix(const Matrix4& matrix) {
+ mState.setMatrix(matrix);
+ }
+ void setLocalMatrix(const Matrix4& matrix);
void setLocalMatrix(const SkMatrix& matrix);
void concatMatrix(const SkMatrix& matrix) { mState.concatMatrix(matrix); }
@@ -426,7 +357,8 @@ protected:
* Perform the setup specific to a frame. This method does not
* issue any OpenGL commands.
*/
- void setupFrameState(float left, float top, float right, float bottom, bool opaque);
+ void setupFrameState(int viewportWidth, int viewportHeight,
+ float left, float top, float right, float bottom, bool opaque);
/**
* Indicates the start of rendering. This method will setup the
@@ -510,16 +442,6 @@ protected:
void drawTextureLayer(Layer* layer, const Rect& rect);
/**
- * Gets the alpha and xfermode out of a paint object. If the paint is null
- * alpha will be 255 and the xfermode will be SRC_OVER. Accounts for snapshot alpha.
- *
- * @param paint The paint to extract values from
- * @param alpha Where to store the resulting alpha
- * @param mode Where to store the resulting xfermode
- */
- inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const;
-
- /**
* Gets the alpha from a layer, accounting for snapshot alpha
*
* @param layer The layer from which the alpha is extracted
@@ -554,27 +476,6 @@ private:
void discardFramebuffer(float left, float top, float right, float bottom);
/**
- * Tells the GPU what part of the screen is about to be redrawn.
- * This method will use the current layer space clip rect.
- * This method needs to be invoked every time getTargetFbo() is
- * bound again.
- */
- void startTilingCurrentClip(bool opaque = false, bool expand = false);
-
- /**
- * Tells the GPU what part of the screen is about to be redrawn.
- * This method needs to be invoked every time getTargetFbo() is
- * bound again.
- */
- void startTiling(const Rect& clip, int windowHeight, bool opaque = false, bool expand = false);
-
- /**
- * Tells the GPU that we are done drawing the frame or that we
- * are switching to another render target.
- */
- void endTiling();
-
- /**
* Sets the clipping rectangle using glScissor. The clip is defined by
* the current snapshot's clipRect member.
*/
@@ -855,16 +756,12 @@ private:
// List of rectangles to clear after saveLayer() is invoked
std::vector<Rect> mLayers;
// List of layers to update at the beginning of a frame
- Vector< sp<Layer> > mLayerUpdates;
+ std::vector< sp<Layer> > mLayerUpdates;
// See PROPERTY_DISABLE_SCISSOR_OPTIMIZATION in
// Properties.h
bool mScissorOptimizationDisabled;
- // No-ops start/endTiling when set
- bool mSuppressTiling;
- bool mFirstFrameAfterResize;
-
bool mSkipOutlineClip;
// True if anything has been drawn since the last call to
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index c98932cf095e..922ff7caecb8 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -26,20 +26,43 @@ namespace uirenderer {
class Outline {
public:
+ enum class Type {
+ None = 0,
+ Empty = 1,
+ ConvexPath = 2,
+ RoundRect = 3
+ };
+
Outline()
: mShouldClip(false)
- , mType(kOutlineType_None)
+ , mType(Type::None)
, mRadius(0)
, mAlpha(0.0f) {}
void setRoundRect(int left, int top, int right, int bottom, float radius, float alpha) {
- mType = kOutlineType_RoundRect;
+ mAlpha = alpha;
+ if (mType == Type::RoundRect
+ && left == mBounds.left
+ && right == mBounds.right
+ && top == mBounds.top
+ && bottom == mBounds.bottom
+ && radius == mRadius) {
+ // nothing to change, don't do any work
+ return;
+ }
+
+ mType = Type::RoundRect;
mBounds.set(left, top, right, bottom);
mRadius = radius;
+
+ // update mPath to reflect new outline
mPath.reset();
- mPath.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom),
- radius, radius);
- mAlpha = alpha;
+ if (MathUtils::isPositive(radius)) {
+ mPath.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom),
+ radius, radius);
+ } else {
+ mPath.addRect(left, top, right, bottom);
+ }
}
void setConvexPath(const SkPath* outline, float alpha) {
@@ -47,26 +70,26 @@ public:
setEmpty();
return;
}
- mType = kOutlineType_ConvexPath;
+ mType = Type::ConvexPath;
mPath = *outline;
mBounds.set(outline->getBounds());
mAlpha = alpha;
}
void setEmpty() {
- mType = kOutlineType_Empty;
+ mType = Type::Empty;
mPath.reset();
mAlpha = 0.0f;
}
void setNone() {
- mType = kOutlineType_None;
+ mType = Type::None;
mPath.reset();
mAlpha = 0.0f;
}
bool isEmpty() const {
- return mType == kOutlineType_Empty;
+ return mType == Type::Empty;
}
float getAlpha() const {
@@ -83,7 +106,7 @@ public:
bool willClip() const {
// only round rect outlines can be used for clipping
- return mShouldClip && (mType == kOutlineType_RoundRect);
+ return mShouldClip && (mType == Type::RoundRect);
}
bool willRoundRectClip() const {
@@ -92,7 +115,7 @@ public:
}
bool getAsRoundRect(Rect* outRect, float* outRadius) const {
- if (mType == kOutlineType_RoundRect) {
+ if (mType == Type::RoundRect) {
outRect->set(mBounds);
*outRadius = mRadius;
return true;
@@ -101,21 +124,26 @@ public:
}
const SkPath* getPath() const {
- if (mType == kOutlineType_None || mType == kOutlineType_Empty) return nullptr;
+ if (mType == Type::None || mType == Type::Empty) return nullptr;
return &mPath;
}
-private:
- enum OutlineType {
- kOutlineType_None = 0,
- kOutlineType_Empty = 1,
- kOutlineType_ConvexPath = 2,
- kOutlineType_RoundRect = 3
- };
+ Type getType() const {
+ return mType;
+ }
+
+ const Rect& getBounds() const {
+ return mBounds;
+ }
+
+ float getRadius() const {
+ return mRadius;
+ }
+private:
bool mShouldClip;
- OutlineType mType;
+ Type mType;
Rect mBounds;
float mRadius;
float mAlpha;
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 6a7dfb3890d3..b471e7850a99 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
-#include <cmath>
-
-#include <utils/Log.h>
+#include "Patch.h"
#include "Caches.h"
-#include "Patch.h"
#include "Properties.h"
#include "UvMapper.h"
#include "utils/MathUtils.h"
+#include <algorithm>
+#include <utils/Log.h>
+
namespace android {
namespace uirenderer {
@@ -191,10 +189,10 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f
const uint32_t oldQuadCount = quadCount;
quadCount++;
- x1 = MathUtils::max(x1, 0.0f);
- x2 = MathUtils::max(x2, 0.0f);
- y1 = MathUtils::max(y1, 0.0f);
- y2 = MathUtils::max(y2, 0.0f);
+ 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) {
@@ -208,8 +206,7 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f
// Record all non empty quads
if (hasEmptyQuads) {
- Rect bounds(x1, y1, x2, y2);
- quads.add(bounds);
+ quads.emplace_back(x1, y1, x2, y2);
}
mUvMapper.map(u1, v1, u2, v2);
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index b63bd24456d3..f04416ccabf9 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -21,13 +21,13 @@
#include <GLES2/gl2.h>
-#include <utils/Vector.h>
-
#include <androidfw/ResourceTypes.h>
#include "Rect.h"
#include "UvMapper.h"
+#include <vector>
+
namespace android {
namespace uirenderer {
@@ -52,7 +52,7 @@ public:
uint32_t verticesCount = 0;
uint32_t indexCount = 0;
bool hasEmptyQuads = false;
- Vector<Rect> quads;
+ std::vector<Rect> quads;
GLintptr positionOffset = 0;
GLintptr textureOffset = 0;
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 27652624b498..98812805259e 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <utils/JenkinsHash.h>
#include <utils/Log.h>
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 3af640f76365..4031f2e13f39 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkColor.h>
@@ -141,10 +138,10 @@ PathCache::PathCache():
mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
char property[PROPERTY_VALUE_MAX];
if (property_get(PROPERTY_PATH_CACHE_SIZE, property, nullptr) > 0) {
- INIT_LOGD(" Setting %s cache size to %sMB", name, property);
+ INIT_LOGD(" Setting path cache size to %sMB", property);
mMaxSize = MB(atof(property));
} else {
- INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE);
+ INIT_LOGD(" Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE);
}
mCache.setOnEntryRemovedListener(this);
@@ -341,7 +338,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
void PathCache::removeDeferred(const SkPath* path) {
Mutex::Autolock l(mLock);
- mGarbage.push(path->getGenerationID());
+ mGarbage.push_back(path->getGenerationID());
}
void PathCache::clearGarbage() {
@@ -349,10 +346,7 @@ void PathCache::clearGarbage() {
{ // scope for the mutex
Mutex::Autolock l(mLock);
- size_t count = mGarbage.size();
- for (size_t i = 0; i < count; i++) {
- const uint32_t generationID = mGarbage.itemAt(i);
-
+ for (const uint32_t generationID : mGarbage) {
LruCache<PathDescription, PathTexture*>::Iterator iter(mCache);
while (iter.next()) {
const PathDescription& key = iter.key();
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 70148631db34..31f8d3553375 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -28,7 +28,8 @@
#include <SkPath.h>
#include <utils/LruCache.h>
#include <utils/Mutex.h>
-#include <utils/Vector.h>
+
+#include <vector>
class SkBitmap;
class SkCanvas;
@@ -307,7 +308,7 @@ private:
sp<PathProcessor> mProcessor;
- Vector<uint32_t> mGarbage;
+ std::vector<uint32_t> mGarbage;
mutable Mutex mLock;
}; // class PathCache
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 38f214ab3e5d..b57b8f04d1de 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -13,10 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-#define LOG_TAG "OpenGLRenderer"
#define LOG_NDEBUG 1
-#define ATRACE_TAG ATRACE_TAG_VIEW
#define VERTEX_DEBUG 0
@@ -35,6 +32,15 @@
#define DEBUG_DUMP_BUFFER()
#endif
+#include "PathTessellator.h"
+
+#include "Matrix.h"
+#include "Vector.h"
+#include "Vertex.h"
+#include "utils/MathUtils.h"
+
+#include <algorithm>
+
#include <SkPath.h>
#include <SkPaint.h>
#include <SkPoint.h>
@@ -47,12 +53,6 @@
#include <utils/Log.h>
#include <utils/Trace.h>
-#include "PathTessellator.h"
-#include "Matrix.h"
-#include "Vector.h"
-#include "Vertex.h"
-#include "utils/MathUtils.h"
-
namespace android {
namespace uirenderer {
@@ -155,7 +155,7 @@ public:
// always use 2 points for hairline
if (halfStrokeWidth == 0.0f) return 2;
- float threshold = MathUtils::min(inverseScaleX, inverseScaleY) * ROUND_CAP_THRESH;
+ float threshold = std::min(inverseScaleX, inverseScaleY) * ROUND_CAP_THRESH;
return MathUtils::divisionsNeededToApproximateArc(halfStrokeWidth, PI, threshold);
}
return 0;
@@ -180,7 +180,8 @@ public:
}
};
-void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
+void getFillVerticesFromPerimeter(const std::vector<Vertex>& perimeter,
+ VertexBuffer& vertexBuffer) {
Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size());
int currentIndex = 0;
@@ -204,8 +205,8 @@ void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer&
* 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 Vector<Vertex>& perimeter,
- VertexBuffer& vertexBuffer) {
+void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo,
+ const std::vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) {
Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2);
int currentIndex = 0;
@@ -263,7 +264,7 @@ static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& cente
* 2 - can zig-zag across 'extra' vertices at either end, to create round caps
*/
void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
- const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
+ 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);
@@ -342,8 +343,9 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
*
* 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices)
*/
-void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
- VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) {
+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
@@ -401,7 +403,7 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver
* For explanation of constants and general methodoloyg, see comments for
* getStrokeVerticesFromUnclosedVerticesAA() below.
*/
-inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
+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;
@@ -426,8 +428,8 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>&
}
// determine referencePoint, the center point for the 4 primary cap vertices
- const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1);
- Vector2 referencePoint = {point->x, point->y};
+ 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)
@@ -572,7 +574,7 @@ or, for rounded caps:
= 2 + 6 * pts + 6 * roundDivs
*/
void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
- const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
+ const std::vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
const int extra = paintInfo.capExtraDivisions();
const int allocSize = 6 * vertices.size() + 2 + 6 * extra;
@@ -645,8 +647,8 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo,
}
-void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter,
- VertexBuffer& vertexBuffer) {
+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;
@@ -724,7 +726,7 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
const PaintInfo paintInfo(paint, transform);
- Vector<Vertex> tempVertices;
+ std::vector<Vertex> tempVertices;
float threshInvScaleX = paintInfo.inverseScaleX;
float threshInvScaleY = paintInfo.inverseScaleY;
if (paintInfo.style == SkPaint::kStroke_Style) {
@@ -819,7 +821,7 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP
}
// calculate outline
- Vector<Vertex> outlineVertices;
+ std::vector<Vertex> outlineVertices;
PathApproximationInfo approximationInfo(paintInfo.inverseScaleX, paintInfo.inverseScaleY,
OUTLINE_REFINE_THRESHOLD);
approximatePathOutlineVertices(path, true, approximationInfo, outlineVertices);
@@ -861,10 +863,8 @@ void PathTessellator::tessellateLines(const float* points, int count, const SkPa
vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2);
}
- Vector<Vertex> tempVertices;
- tempVertices.push();
- tempVertices.push();
- Vertex* tempVerticesData = tempVertices.editArray();
+ 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) {
@@ -900,18 +900,11 @@ void PathTessellator::tessellateLines(const float* points, int count, const SkPa
///////////////////////////////////////////////////////////////////////////////
bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float threshold,
- Vector<Vertex>& outputVertices) {
+ std::vector<Vertex>& outputVertices) {
PathApproximationInfo approximationInfo(1.0f, 1.0f, threshold);
return approximatePathOutlineVertices(path, true, approximationInfo, outputVertices);
}
-void pushToVector(Vector<Vertex>& vertices, float x, float y) {
- // TODO: make this not yuck
- vertices.push();
- Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]);
- Vertex::set(newVertex, x, y);
-}
-
class ClockwiseEnforcer {
public:
void addPoint(const SkPoint& point) {
@@ -927,15 +920,15 @@ public:
lastX = x;
lastY = y;
}
- void reverseVectorIfNotClockwise(Vector<Vertex>& vertices) {
+ 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.replaceAt(vertices[k], i);
- vertices.replaceAt(tmp, k);
+ vertices[i] = vertices[k];
+ vertices[k] = tmp;
}
}
}
@@ -947,7 +940,7 @@ private:
};
bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
- const PathApproximationInfo& approximationInfo, Vector<Vertex>& outputVertices) {
+ const PathApproximationInfo& approximationInfo, std::vector<Vertex>& outputVertices) {
ATRACE_CALL();
// TODO: to support joins other than sharp miter, join vertices should be labelled in the
@@ -959,7 +952,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
while (SkPath::kDone_Verb != (v = iter.next(pts))) {
switch (v) {
case SkPath::kMove_Verb:
- pushToVector(outputVertices, pts[0].x(), pts[0].y());
+ 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;
@@ -969,7 +962,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
break;
case SkPath::kLine_Verb:
ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
- pushToVector(outputVertices, 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:
@@ -1020,7 +1013,7 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
int size = outputVertices.size();
if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
outputVertices[0].y == outputVertices[size - 1].y) {
- outputVertices.pop();
+ outputVertices.pop_back();
wasClosed = true;
}
@@ -1048,7 +1041,7 @@ void PathTessellator::recursiveCubicBezierVertices(
float p1x, float p1y, float c1x, float c1y,
float p2x, float p2y, float c2x, float c2y,
const PathApproximationInfo& approximationInfo,
- Vector<Vertex>& outputVertices, int depth) {
+ std::vector<Vertex>& outputVertices, int depth) {
float dx = p2x - p1x;
float dy = p2y - p1y;
float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
@@ -1058,7 +1051,7 @@ void PathTessellator::recursiveCubicBezierVertices(
if (depth >= MAX_DEPTH
|| d * d <= getThreshold(approximationInfo, dx, dy)) {
// below thresh, draw line by adding endpoint
- pushToVector(outputVertices, p2x, p2y);
+ outputVertices.push_back(Vertex{p2x, p2y});
} else {
float p1c1x = (p1x + c1x) * 0.5f;
float p1c1y = (p1y + c1y) * 0.5f;
@@ -1093,7 +1086,7 @@ void PathTessellator::recursiveQuadraticBezierVertices(
float bx, float by,
float cx, float cy,
const PathApproximationInfo& approximationInfo,
- Vector<Vertex>& outputVertices, int depth) {
+ 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).
@@ -1102,7 +1095,7 @@ void PathTessellator::recursiveQuadraticBezierVertices(
if (depth >= MAX_DEPTH
|| d * d <= getThreshold(approximationInfo, dx, dy)) {
// below thresh, draw line by adding endpoint
- pushToVector(outputVertices, bx, by);
+ outputVertices.push_back(Vertex{bx, by});
} else {
float acx = (ax + cx) * 0.5f;
float bcx = (bx + cx) * 0.5f;
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index 16c8b36a6a9d..cddfb049212c 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -17,13 +17,17 @@
#ifndef ANDROID_HWUI_PATH_TESSELLATOR_H
#define ANDROID_HWUI_PATH_TESSELLATOR_H
-#include <utils/Vector.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 {
@@ -38,7 +42,7 @@ struct PathApproximationInfo {
: thresholdSquared(pixelThreshold * pixelThreshold)
, sqrInvScaleX(invScaleX * invScaleX)
, sqrInvScaleY(invScaleY * invScaleY)
- , thresholdForConicQuads(pixelThreshold * MathUtils::min(invScaleX, invScaleY) / 2.0f) {
+ , thresholdForConicQuads(pixelThreshold * std::min(invScaleX, invScaleY) / 2.0f) {
};
const float thresholdSquared;
@@ -109,11 +113,11 @@ public:
* @param outputVertices An empty Vector which will be populated with the output
*/
static bool approximatePathOutlineVertices(const SkPath &path, float threshold,
- Vector<Vertex> &outputVertices);
+ std::vector<Vertex> &outputVertices);
private:
static bool approximatePathOutlineVertices(const SkPath &path, bool forceClose,
- const PathApproximationInfo& approximationInfo, Vector<Vertex> &outputVertices);
+ const PathApproximationInfo& approximationInfo, std::vector<Vertex> &outputVertices);
/*
endpoints a & b,
@@ -124,7 +128,7 @@ private:
float bx, float by,
float cx, float cy,
const PathApproximationInfo& approximationInfo,
- Vector<Vertex> &outputVertices, int depth = 0);
+ std::vector<Vertex> &outputVertices, int depth = 0);
/*
endpoints p1, p2
@@ -136,7 +140,7 @@ private:
float p2x, float p2y,
float c2x, float c2y,
const PathApproximationInfo& approximationInfo,
- Vector<Vertex> &outputVertices, int depth = 0);
+ std::vector<Vertex> &outputVertices, int depth = 0);
};
}; // namespace uirenderer
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
index 9665a68b0e77..96247260f0f4 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include "PixelBuffer.h"
#include "Debug.h"
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 32713e9b36f3..e43b80d440e7 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
#include <utils/Trace.h>
#include "Program.h"
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index af1e4a74d46e..e5200a516777 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -78,14 +78,12 @@ namespace uirenderer {
#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
-#define PROGRAM_HAS_GAMMA_CORRECTION 40
+#define PROGRAM_IS_SIMPLE_GRADIENT 40
-#define PROGRAM_IS_SIMPLE_GRADIENT 41
+#define PROGRAM_HAS_COLORS 41
-#define PROGRAM_HAS_COLORS 42
-
-#define PROGRAM_HAS_DEBUG_HIGHLIGHT 43
-#define PROGRAM_HAS_ROUND_RECT_CLIP 44
+#define PROGRAM_HAS_DEBUG_HIGHLIGHT 42
+#define PROGRAM_HAS_ROUND_RECT_CLIP 43
///////////////////////////////////////////////////////////////////////////////
// Types
@@ -103,10 +101,10 @@ typedef uint64_t programid;
* A ProgramDescription must be used in conjunction with a ProgramCache.
*/
struct ProgramDescription {
- enum ColorFilterMode {
- kColorNone = 0,
- kColorMatrix,
- kColorBlend
+ enum class ColorFilterMode {
+ None = 0,
+ Matrix,
+ Blend
};
enum Gradient {
@@ -157,9 +155,6 @@ struct ProgramDescription {
SkXfermode::Mode framebufferMode;
bool swapSrcDst;
- bool hasGammaCorrection;
- float gamma;
-
bool hasDebugHighlight;
bool hasRoundRectClip;
@@ -193,15 +188,12 @@ struct ProgramDescription {
bitmapWrapS = GL_CLAMP_TO_EDGE;
bitmapWrapT = GL_CLAMP_TO_EDGE;
- colorOp = kColorNone;
+ colorOp = ColorFilterMode::None;
colorMode = SkXfermode::kClear_Mode;
framebufferMode = SkXfermode::kClear_Mode;
swapSrcDst = false;
- hasGammaCorrection = false;
- gamma = 2.2f;
-
hasDebugHighlight = false;
hasRoundRectClip = false;
}
@@ -249,14 +241,14 @@ struct ProgramDescription {
key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
}
switch (colorOp) {
- case kColorMatrix:
+ case ColorFilterMode::Matrix:
key |= PROGRAM_KEY_COLOR_MATRIX;
break;
- case kColorBlend:
+ case ColorFilterMode::Blend:
key |= PROGRAM_KEY_COLOR_BLEND;
key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
break;
- case kColorNone:
+ case ColorFilterMode::None:
break;
}
key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
@@ -266,7 +258,6 @@ struct ProgramDescription {
if (useShadowAlphaInterp) key |= programid(0x1) << PROGRAM_USE_SHADOW_ALPHA_INTERP_SHIFT;
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
- if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS;
if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 41adda15f367..05be48822fb2 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <utils/String8.h>
#include "Caches.h"
@@ -40,7 +38,8 @@ namespace uirenderer {
// Vertex shaders snippets
///////////////////////////////////////////////////////////////////////////////
-const char* gVS_Header_Attributes =
+const char* gVS_Header_Start =
+ "#version 100\n"
"attribute vec4 position;\n";
const char* gVS_Header_Attributes_TexCoords =
"attribute vec2 texCoords;\n";
@@ -134,6 +133,8 @@ const char* gVS_Footer =
// 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 =
@@ -166,8 +167,6 @@ const char* gFS_Uniforms_ColorOp[3] = {
// PorterDuff
"uniform vec4 colorBlend;\n"
};
-const char* gFS_Uniforms_Gamma =
- "uniform float gamma;\n";
const char* gFS_Uniforms_HasRoundRectClip =
"uniform vec4 roundRectInnerRectLTRB;\n"
@@ -203,18 +202,10 @@ const char* gFS_Fast_SingleA8Texture =
"\nvoid main(void) {\n"
" gl_FragColor = texture2D(baseSampler, outTexCoords);\n"
"}\n\n";
-const char* gFS_Fast_SingleA8Texture_ApplyGamma =
- "\nvoid main(void) {\n"
- " gl_FragColor = vec4(0.0, 0.0, 0.0, pow(texture2D(baseSampler, outTexCoords).a, gamma));\n"
- "}\n\n";
const char* gFS_Fast_SingleModulateA8Texture =
"\nvoid main(void) {\n"
" gl_FragColor = color * texture2D(baseSampler, outTexCoords).a;\n"
"}\n\n";
-const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
- "\nvoid main(void) {\n"
- " gl_FragColor = color * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
- "}\n\n";
const char* gFS_Fast_SingleGradient[2] = {
"\nvoid main(void) {\n"
" gl_FragColor = %s + texture2D(gradientSampler, linear);\n"
@@ -249,13 +240,11 @@ const char* gFS_Main_FetchTexture[2] = {
// Modulate
" fragColor = color * texture2D(baseSampler, outTexCoords);\n"
};
-const char* gFS_Main_FetchA8Texture[4] = {
+const char* gFS_Main_FetchA8Texture[2] = {
// Don't modulate
" fragColor = texture2D(baseSampler, outTexCoords);\n",
- " fragColor = texture2D(baseSampler, outTexCoords);\n",
// Modulate
" fragColor = color * texture2D(baseSampler, outTexCoords).a;\n",
- " fragColor = color * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
const char* gFS_Main_FetchGradient[6] = {
// Linear
@@ -283,38 +272,29 @@ 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] = {
+const char* gFS_Main_BlendShaders_Modulate[3] = {
// Don't modulate
";\n",
- ";\n",
// Modulate
" * color.a;\n",
- " * color.a;\n",
// Modulate with alpha 8 texture
" * texture2D(baseSampler, outTexCoords).a;\n",
- " * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
-const char* gFS_Main_GradientShader_Modulate[6] = {
+const char* gFS_Main_GradientShader_Modulate[3] = {
// 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 * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
-const char* gFS_Main_BitmapShader_Modulate[6] = {
+const char* gFS_Main_BitmapShader_Modulate[3] = {
// 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 * pow(texture2D(baseSampler, outTexCoords).a, gamma);\n"
};
const char* gFS_Main_FragColor =
" gl_FragColor = fragColor;\n";
@@ -459,7 +439,7 @@ static inline size_t gradientIndex(const ProgramDescription& description) {
String8 ProgramCache::generateVertexShader(const ProgramDescription& description) {
// Add attributes
- String8 shader(gVS_Header_Attributes);
+ String8 shader(gVS_Header_Start);
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Attributes_TexCoords);
}
@@ -539,13 +519,12 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
static bool shaderOp(const ProgramDescription& description, String8& shader,
const int modulateOp, const char** snippets) {
int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
- op = op * 2 + description.hasGammaCorrection;
shader.append(snippets[op]);
return description.hasAlpha8Texture;
}
String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
- String8 shader;
+ String8 shader(gFS_Header_Start);
const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode;
if (blendFramebuffer) {
@@ -595,9 +574,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
gFS_Uniforms_Dither);
}
- if (description.hasGammaCorrection) {
- shader.append(gFS_Uniforms_Gamma);
- }
if (description.hasRoundRectClip) {
shader.append(gFS_Uniforms_HasRoundRectClip);
}
@@ -606,7 +582,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (!description.hasVertexAlpha
&& !blendFramebuffer
&& !description.hasColors
- && description.colorOp == ProgramDescription::kColorNone
+ && description.colorOp == ProgramDescription::ColorFilterMode::None
&& !description.hasDebugHighlight
&& !description.hasRoundRectClip) {
bool fast = false;
@@ -632,17 +608,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
fast = true;
} else if (singleA8Texture) {
if (!description.modulate) {
- if (description.hasGammaCorrection) {
- shader.append(gFS_Fast_SingleA8Texture_ApplyGamma);
- } else {
- shader.append(gFS_Fast_SingleA8Texture);
- }
+ shader.append(gFS_Fast_SingleA8Texture);
} else {
- if (description.hasGammaCorrection) {
- shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma);
- } else {
- shader.append(gFS_Fast_SingleModulateA8Texture);
- }
+ shader.append(gFS_Fast_SingleModulateA8Texture);
}
fast = true;
} else if (singleGradient) {
@@ -670,13 +638,13 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasBitmap) {
shader.append(gFS_Uniforms_BitmapSampler);
}
- shader.append(gFS_Uniforms_ColorOp[description.colorOp]);
+ shader.append(gFS_Uniforms_ColorOp[static_cast<int>(description.colorOp)]);
// Generate required functions
if (description.hasGradient && description.hasBitmap) {
generateBlend(shader, "blendShaders", description.shadersMode);
}
- if (description.colorOp == ProgramDescription::kColorBlend) {
+ if (description.colorOp == ProgramDescription::ColorFilterMode::Blend) {
generateBlend(shader, "blendColors", description.colorMode);
}
if (blendFramebuffer) {
@@ -692,8 +660,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasTexture || description.hasExternalTexture) {
if (description.hasAlpha8Texture) {
if (!description.hasGradient && !description.hasBitmap) {
- shader.append(gFS_Main_FetchA8Texture[modulateOp * 2 +
- description.hasGammaCorrection]);
+ shader.append(gFS_Main_FetchA8Texture[modulateOp]);
}
} else {
shader.append(gFS_Main_FetchTexture[modulateOp]);
@@ -739,7 +706,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
}
// Apply the color op if needed
- shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
+ shader.append(gFS_Main_ApplyColorOp[static_cast<int>(description.colorOp)]);
if (description.hasVertexAlpha) {
if (description.useShadowAlphaInterp) {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 2e63793f6ffe..36a8dac9d0c1 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -30,6 +30,10 @@ bool Properties::debugOverdraw = false;
bool Properties::showDirtyRegions = false;
bool Properties::skipEmptyFrames = true;
bool Properties::swapBuffersWithDamage = true;
+bool Properties::useBufferAge = true;
+bool Properties::enablePartialUpdates = true;
+
+float Properties::textGamma = DEFAULT_TEXT_GAMMA;
DebugLevel Properties::debugLevel = kDebugDisabled;
OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
@@ -45,6 +49,15 @@ int Properties::overrideSpotShadowStrength = -1;
ProfileType Properties::sProfileType = ProfileType::None;
bool Properties::sDisableProfileBars = false;
+static float property_get_float(const char* key, float defaultValue) {
+ char buf[PROPERTY_VALUE_MAX] = {'\0',};
+
+ if (property_get(PROPERTY_PROFILE, buf, "") > 0) {
+ return atof(buf);
+ }
+ return defaultValue;
+}
+
bool Properties::load() {
char property[PROPERTY_VALUE_MAX];
bool prevDebugLayersUpdates = debugLayersUpdates;
@@ -105,6 +118,10 @@ bool Properties::load() {
skipEmptyFrames = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE, true);
swapBuffersWithDamage = property_get_bool(PROPERTY_SWAP_WITH_DAMAGE, true);
+ useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true);
+ enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true);
+
+ textGamma = property_get_float(PROPERTY_TEXT_GAMMA, DEFAULT_TEXT_GAMMA);
return (prevDebugLayersUpdates != debugLayersUpdates)
|| (prevDebugOverdraw != debugOverdraw)
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 26d8bf754ddb..3512c36417e1 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -36,9 +36,6 @@ namespace uirenderer {
// If turned on, text is interpreted as glyphs instead of UTF-16
#define RENDER_TEXT_AS_GLYPHS 1
-// Indicates whether to remove the biggest layers first, or the smaller ones
-#define LAYER_REMOVE_BIGGEST_FIRST 0
-
// Textures used by layers must have dimensions multiples of this number
#define LAYER_SIZE 64
@@ -87,12 +84,6 @@ enum DebugLevel {
#define PROPERTY_DEBUG_OVERDRAW "debug.hwui.overdraw"
/**
- * Used to enable/disable PerfHUD ES profiling. The accepted values
- * are "true" and "false". The default value is "false".
- */
-#define PROPERTY_DEBUG_NV_PROFILING "debug.hwui.nv_profiling"
-
-/**
* System property used to enable or disable hardware rendering profiling.
* The default value of this property is assumed to be false.
*
@@ -151,14 +142,27 @@ enum DebugLevel {
#define PROPERTY_SKIP_EMPTY_DAMAGE "debug.hwui.skip_empty_damage"
/**
- * Setting this property will enable usage of EGL_KHR_swap_buffers_with_damage
+ * Setting this property will enable or disable usage of EGL_KHR_swap_buffers_with_damage
* See: https://www.khronos.org/registry/egl/extensions/KHR/EGL_KHR_swap_buffers_with_damage.txt
- * Default is "false" temporarily
- * TODO: Change to "true", make sure to remove the log in EglManager::swapBuffers
- * before changing this to default to true!
+ * Default is "true"
*/
#define PROPERTY_SWAP_WITH_DAMAGE "debug.hwui.swap_with_damage"
+/**
+ * Controls whether or not HWUI will use the EGL_EXT_buffer_age extension
+ * to do partial invalidates. Setting this to "false" will fall back to
+ * using BUFFER_PRESERVED instead
+ * Default is "true"
+ */
+#define PROPERTY_USE_BUFFER_AGE "debug.hwui.use_buffer_age"
+
+/**
+ * Setting this to "false" will force HWUI to always do full-redraws of the surface.
+ * This will disable the use of EGL_EXT_buffer_age and BUFFER_PRESERVED.
+ * Default is "true"
+ */
+#define PROPERTY_ENABLE_PARTIAL_UPDATES "debug.hwui.enable_partial_updates"
+
///////////////////////////////////////////////////////////////////////////////
// Runtime configuration properties
///////////////////////////////////////////////////////////////////////////////
@@ -204,30 +208,8 @@ enum DebugLevel {
#define PROPERTY_TEXT_LARGE_CACHE_WIDTH "ro.hwui.text_large_cache_width"
#define PROPERTY_TEXT_LARGE_CACHE_HEIGHT "ro.hwui.text_large_cache_height"
-// Indicates whether gamma correction should be applied in the shaders
-// or in lookup tables. Accepted values:
-//
-// - "lookup3", correction based on lookup tables. Gamma correction
-// is different for black and white text (see thresholds below)
-//
-// - "lookup", correction based on a single lookup table
-//
-// - "shader3", correction applied by a GLSL shader. Gamma correction
-// is different for black and white text (see thresholds below)
-//
-// - "shader", correction applied by a GLSL shader
-//
-// See PROPERTY_TEXT_GAMMA, PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD and
-// PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD for more control.
-#define PROPERTY_TEXT_GAMMA_METHOD "hwui.text_gamma_correction"
-#define DEFAULT_TEXT_GAMMA_METHOD "lookup"
-
// Gamma (>= 1.0, <= 10.0)
#define PROPERTY_TEXT_GAMMA "hwui.text_gamma"
-// Luminance threshold below which black gamma correction is applied. Range: [0..255]
-#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "hwui.text_gamma.black_threshold"
-// Lumincance threshold above which white gamma correction is applied. Range: [0..255]
-#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "hwui.text_gamma.white_threshold"
///////////////////////////////////////////////////////////////////////////////
// Default property values
@@ -238,7 +220,7 @@ enum DebugLevel {
#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
#define DEFAULT_PATH_CACHE_SIZE 4.0f
#define DEFAULT_VERTEX_CACHE_SIZE 1.0f
-#define DEFAULT_PATCH_CACHE_SIZE 128 // in kB
+#define DEFAULT_PATCH_CACHE_SIZE 128.0f // in kB
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
#define DEFAULT_FBO_CACHE_SIZE 0
@@ -246,8 +228,6 @@ enum DebugLevel {
#define DEFAULT_TEXTURE_CACHE_FLUSH_RATE 0.6f
#define DEFAULT_TEXT_GAMMA 1.4f
-#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
-#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192
///////////////////////////////////////////////////////////////////////////////
// Misc
@@ -293,6 +273,10 @@ public:
static bool skipEmptyFrames;
// TODO: Remove after stabilization period
static bool swapBuffersWithDamage;
+ static bool useBufferAge;
+ static bool enablePartialUpdates;
+
+ static float textGamma;
static DebugLevel debugLevel;
static OverdrawColorSet overdrawColorSet;
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
new file mode 100644
index 000000000000..a69f0308ebc5
--- /dev/null
+++ b/libs/hwui/RecordedOp.h
@@ -0,0 +1,118 @@
+/*
+ * 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_RECORDED_OP_H
+#define ANDROID_HWUI_RECORDED_OP_H
+
+#include "utils/LinearAllocator.h"
+#include "Rect.h"
+#include "Matrix.h"
+
+#include "SkXfermode.h"
+
+class SkBitmap;
+class SkPaint;
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+struct Vertex;
+
+/**
+ * The provided macro is executed for each op type in order, with the results separated by commas.
+ *
+ * This serves as the authoritative list of ops, used for generating ID enum, and ID based LUTs.
+ */
+#define MAP_OPS(OP_FN) \
+ OP_FN(BitmapOp) \
+ OP_FN(RectOp) \
+ OP_FN(RenderNodeOp) \
+ OP_FN(SimpleRectsOp)
+
+// Generate OpId enum
+#define IDENTITY_FN(Type) Type,
+namespace RecordedOpId {
+ enum {
+ MAP_OPS(IDENTITY_FN)
+ Count,
+ };
+}
+static_assert(RecordedOpId::BitmapOp == 0,
+ "First index must be zero for LUTs to work");
+
+#define BASE_PARAMS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint
+#define BASE_PARAMS_PAINTLESS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect
+#define SUPER(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClipRect, paint)
+#define SUPER_PAINTLESS(Type) RecordedOp(RecordedOpId::Type, unmappedBounds, localMatrix, localClipRect, 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 */
+ const Rect unmappedBounds;
+
+ /* transform in recording space (vs DisplayList origin) */
+ const Matrix4 localMatrix;
+
+ /* clip in recording space */
+ const Rect localClipRect;
+
+ /* 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)
+ , localClipRect(localClipRect)
+ , paint(paint) {}
+};
+
+struct RenderNodeOp : RecordedOp {
+ RenderNodeOp(BASE_PARAMS_PAINTLESS, RenderNode* renderNode)
+ : SUPER_PAINTLESS(RenderNodeOp)
+ , renderNode(renderNode) {}
+ RenderNode * renderNode; // not const, since drawing modifies it (somehow...)
+};
+
+struct BitmapOp : RecordedOp {
+ BitmapOp(BASE_PARAMS, const SkBitmap* bitmap)
+ : SUPER(BitmapOp)
+ , bitmap(bitmap) {}
+ const SkBitmap* bitmap;
+ // TODO: asset atlas/texture id lookup?
+};
+
+struct RectOp : RecordedOp {
+ RectOp(BASE_PARAMS)
+ : SUPER(RectOp) {}
+};
+
+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;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_RECORDED_OP_H
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
new file mode 100644
index 000000000000..3b413aaec0e0
--- /dev/null
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -0,0 +1,402 @@
+/*
+ * 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 "RecordingCanvas.h"
+
+#include "RecordedOp.h"
+#include "RenderNode.h"
+
+namespace android {
+namespace uirenderer {
+
+RecordingCanvas::RecordingCanvas(size_t width, size_t height)
+ : mState(*this)
+ , mResourceCache(ResourceCache::getInstance()) {
+ reset(width, height);
+}
+
+RecordingCanvas::~RecordingCanvas() {
+ LOG_ALWAYS_FATAL_IF(mDisplayList,
+ "Destroyed a RecordingCanvas during a record!");
+}
+
+void RecordingCanvas::reset(int width, int height) {
+ LOG_ALWAYS_FATAL_IF(mDisplayList,
+ "prepareDirty called a second time during a recording!");
+ mDisplayList = new DisplayList();
+
+ mState.initializeSaveStack(width, height, 0, 0, width, height, Vector3());
+
+ mDeferredBarrierType = kBarrier_InOrder;
+ mState.setDirtyClip(false);
+ mRestoreSaveCount = -1;
+}
+
+DisplayList* RecordingCanvas::finishRecording() {
+ mPaintMap.clear();
+ mRegionMap.clear();
+ mPathMap.clear();
+ DisplayList* displayList = mDisplayList;
+ mDisplayList = nullptr;
+ mSkiaCanvasProxy.reset(nullptr);
+ return displayList;
+}
+
+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));
+ }
+
+ // SkCanvas instances default to identity transform, but should inherit
+ // the state of this Canvas; if this code was in the SkiaCanvasProxy
+ // constructor, we couldn't cache mSkiaCanvasProxy.
+ SkMatrix parentTransform;
+ getMatrix(&parentTransform);
+ mSkiaCanvasProxy.get()->setMatrix(parentTransform);
+
+ return mSkiaCanvasProxy.get();
+}
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas state operations
+// ----------------------------------------------------------------------------
+// Save (layer)
+int RecordingCanvas::save(SkCanvas::SaveFlags flags) {
+ return mState.save((int) flags);
+}
+
+void RecordingCanvas::RecordingCanvas::restore() {
+ if (mRestoreSaveCount < 0) {
+ restoreToCount(getSaveCount() - 1);
+ return;
+ }
+
+ mRestoreSaveCount--;
+ mState.restore();
+}
+
+void RecordingCanvas::restoreToCount(int saveCount) {
+ mRestoreSaveCount = saveCount;
+ mState.restoreToCount(saveCount);
+}
+
+int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
+ SkCanvas::SaveFlags flags) {
+ LOG_ALWAYS_FATAL("TODO");
+ return 0;
+}
+
+// Matrix
+void RecordingCanvas::rotate(float degrees) {
+ if (degrees == 0) return;
+
+ mState.rotate(degrees);
+}
+
+void RecordingCanvas::scale(float sx, float sy) {
+ if (sx == 1 && sy == 1) return;
+
+ mState.scale(sx, sy);
+}
+
+void RecordingCanvas::skew(float sx, float sy) {
+ mState.skew(sx, sy);
+}
+
+void RecordingCanvas::translate(float dx, float dy) {
+ if (dx == 0 && dy == 0) return;
+
+ mState.translate(dx, dy, 0);
+}
+
+// Clip
+bool RecordingCanvas::getClipBounds(SkRect* outRect) const {
+ Rect bounds = mState.getLocalClipBounds();
+ *outRect = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ return !(outRect->isEmpty());
+}
+bool RecordingCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
+ return mState.quickRejectConservative(left, top, right, bottom);
+}
+bool RecordingCanvas::quickRejectPath(const SkPath& path) const {
+ SkRect bounds = path.getBounds();
+ return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
+}
+bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+ return mState.clipRect(left, top, right, bottom, op);
+}
+bool RecordingCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
+ return mState.clipPath(path, op);
+}
+bool RecordingCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
+ return mState.clipRegion(region, op);
+}
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas draw operations
+// ----------------------------------------------------------------------------
+void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) {
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setXfermodeMode(mode);
+ drawPaint(paint);
+}
+
+void RecordingCanvas::drawPaint(const SkPaint& paint) {
+ // TODO: more efficient recording?
+ Matrix4 identity;
+ identity.loadIdentity();
+
+ addOp(new (alloc()) RectOp(
+ mState.getRenderTargetClipBounds(),
+ identity,
+ mState.getRenderTargetClipBounds(),
+ refPaint(&paint)));
+}
+
+// Geometry
+void RecordingCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
+ addOp(new (alloc()) RectOp(
+ Rect(left, top, right, bottom),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(&paint)));
+}
+
+void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) {
+ if (rects == nullptr) return;
+
+ Vertex* rectData = (Vertex*) mDisplayList->allocator.alloc(vertexCount * sizeof(Vertex));
+ 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(new (alloc()) SimpleRectsOp(
+ Rect(left, top, right, bottom),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(paint), rectData, vertexCount));
+}
+
+void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
+ 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();
+ }
+ 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();
+ }
+ }
+}
+void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+
+// Bitmap-based
+void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
+ save(SkCanvas::kMatrix_SaveFlag);
+ translate(left, top);
+ drawBitmap(&bitmap, paint);
+ restore();
+}
+
+void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) {
+ if (matrix.isIdentity()) {
+ drawBitmap(&bitmap, paint);
+ } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))
+ && MathUtils::isPositive(matrix.getScaleX())
+ && MathUtils::isPositive(matrix.getScaleY())) {
+ // SkMatrix::isScaleTranslate() not available in L
+ SkRect src;
+ SkRect dst;
+ bitmap.getBounds(&src);
+ matrix.mapRect(&dst, src);
+ drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+ dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
+ } else {
+ save(SkCanvas::kMatrix_SaveFlag);
+ concat(matrix);
+ drawBitmap(&bitmap, paint);
+ restore();
+ }
+}
+void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) {
+ if (srcLeft == 0 && srcTop == 0
+ && srcRight == bitmap.width()
+ && srcBottom == bitmap.height()
+ && (srcBottom - srcTop == dstBottom - dstTop)
+ && (srcRight - srcLeft == dstRight - dstLeft)) {
+ // transform simple rect to rect drawing case into position bitmap ops, since they merge
+ save(SkCanvas::kMatrix_SaveFlag);
+ translate(dstLeft, dstTop);
+ drawBitmap(&bitmap, paint);
+ restore();
+ } else {
+ LOG_ALWAYS_FATAL("TODO!");
+ }
+}
+void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+
+// Text
+void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, int count,
+ const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
+ float boundsRight, float boundsBottom, float totalAdvance) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawPosText(const uint16_t* text, const float* positions, int count,
+ int posCount, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) {
+ LOG_ALWAYS_FATAL("TODO!");
+}
+
+void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
+ addOp(new (alloc()) BitmapOp(
+ Rect(0, 0, bitmap->width(), bitmap->height()),
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ refPaint(paint), refBitmap(*bitmap)));
+}
+void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
+ RenderNodeOp* op = new (alloc()) RenderNodeOp(
+ Rect(0, 0, renderNode->getWidth(), renderNode->getHeight()), // are these safe? they're theoretically dynamic
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ renderNode);
+ int opIndex = addOp(op);
+ 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;
+ }
+}
+
+size_t RecordingCanvas::addOp(RecordedOp* op) {
+ // TODO: validate if "addDrawOp" quickrejection logic is useful before adding
+ int insertIndex = mDisplayList->ops.size();
+ mDisplayList->ops.push_back(op);
+ if (mDeferredBarrierType != kBarrier_None) {
+ // op is first in new chunk
+ mDisplayList->chunks.emplace_back();
+ DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
+ newChunk.beginOpIndex = insertIndex;
+ newChunk.endOpIndex = insertIndex + 1;
+ newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
+
+ int nextChildIndex = mDisplayList->children.size();
+ newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
+ mDeferredBarrierType = kBarrier_None;
+ } else {
+ // standard case - append to existing chunk
+ mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
+ }
+ return insertIndex;
+}
+
+void RecordingCanvas::refBitmapsInShader(const SkShader* shader) {
+ if (!shader) return;
+
+ // If this paint has an SkShader that has an SkBitmap add
+ // it to the bitmap pile
+ SkBitmap bitmap;
+ SkShader::TileMode xy[2];
+ if (shader->asABitmap(&bitmap, nullptr, xy) == SkShader::kDefault_BitmapType) {
+ refBitmap(bitmap);
+ return;
+ }
+ SkShader::ComposeRec rec;
+ if (shader->asACompose(&rec)) {
+ refBitmapsInShader(rec.fShaderA);
+ refBitmapsInShader(rec.fShaderB);
+ return;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
new file mode 100644
index 000000000000..2179e4c24580
--- /dev/null
+++ b/libs/hwui/RecordingCanvas.h
@@ -0,0 +1,300 @@
+/*
+ * 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_RECORDING_CANVAS_H
+#define ANDROID_HWUI_RECORDING_CANVAS_H
+
+#include "Canvas.h"
+#include "CanvasState.h"
+#include "DisplayList.h"
+#include "utils/LinearAllocator.h"
+#include "utils/NinePatch.h"
+#include "ResourceCache.h"
+#include "SkiaCanvasProxy.h"
+#include "Snapshot.h"
+
+#include "SkDrawFilter.h"
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+class OpReceiver;
+struct RecordedOp;
+
+class RecordingCanvas: public Canvas, public CanvasStateClient {
+public:
+ RecordingCanvas(size_t width, size_t height);
+ virtual ~RecordingCanvas();
+
+ void reset(int width, int height);
+ __attribute__((warn_unused_result)) DisplayList* finishRecording();
+
+// ----------------------------------------------------------------------------
+// MISC HWUI OPERATIONS - TODO: CATEGORIZE
+// ----------------------------------------------------------------------------
+ void insertReorderBarrier(bool enableReorder) {}
+ void drawRenderNode(RenderNode* renderNode);
+
+// ----------------------------------------------------------------------------
+// CanvasStateClient interface
+// ----------------------------------------------------------------------------
+ virtual void onViewportInitialized() override {}
+ virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override {}
+ virtual GLuint getTargetFbo() const override { return -1; }
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas interface
+// ----------------------------------------------------------------------------
+ virtual SkCanvas* asSkCanvas() override;
+
+ virtual void setBitmap(const SkBitmap& bitmap) override {
+ LOG_ALWAYS_FATAL("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(); }
+
+ virtual void setHighContrastText(bool highContrastText) override {
+ mHighContrastText = highContrastText;
+ }
+ virtual bool isHighContrastText() override { return mHighContrastText; }
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas state operations
+// ----------------------------------------------------------------------------
+ // Save (layer)
+ virtual int getSaveCount() const override { return mState.getSaveCount(); }
+ virtual int save(SkCanvas::SaveFlags 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,
+ SkCanvas::SaveFlags flags) override;
+ virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, SkCanvas::SaveFlags 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, SkRegion::Op op) override;
+ virtual bool clipPath(const SkPath* path, SkRegion::Op op) override;
+ virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override;
+
+ // Misc
+ virtual SkDrawFilter* getDrawFilter() override { return mDrawFilter.get(); }
+ virtual void setDrawFilter(SkDrawFilter* filter) override {
+ mDrawFilter.reset(SkSafeRef(filter));
+ }
+
+// ----------------------------------------------------------------------------
+// android/graphics/Canvas draw operations
+// ----------------------------------------------------------------------------
+ virtual void drawColor(int color, SkXfermode::Mode mode) override;
+ virtual void drawPaint(const SkPaint& paint) override;
+
+ // Geometry
+ virtual void drawPoint(float x, float y, const SkPaint& paint) override {
+ float points[2] = { x, y };
+ drawPoints(points, 2, paint);
+ }
+ virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawLine(float startX, float startY, float stopX, float stopY,
+ const SkPaint& paint) override {
+ float points[4] = { startX, startY, stopX, stopY };
+ drawLines(points, 4, paint);
+ }
+ virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
+ virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
+ virtual void drawRoundRect(float left, float top, float right, float bottom,
+ float rx, float ry, const SkPaint& paint) override;
+ virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
+ virtual void drawOval(float left, float top, float right, float bottom, const SkPaint& paint) override;
+ virtual void drawArc(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) override;
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) override;
+ virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
+ const float* verts, const float* tex, const int* colors,
+ const uint16_t* indices, int indexCount, const SkPaint& paint) override
+ { /* RecordingCanvas does not support drawVertices(); ignore */ }
+
+ // Bitmap-based
+ virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override;
+ virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) override;
+ virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+ float srcRight, float srcBottom, float dstLeft, float dstTop,
+ float dstRight, float dstBottom, const SkPaint* paint) override;
+ virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
+ const float* vertices, const int* colors, const SkPaint* paint) override;
+ virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) override;
+
+ // Text
+ virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
+ const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
+ float boundsRight, float boundsBottom, float totalAdvance) override;
+ virtual void drawPosText(const uint16_t* text, const float* positions, int count,
+ int posCount, const SkPaint& paint) override;
+ virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) override;
+ virtual bool drawTextAbsolutePos() const override { return false; }
+
+private:
+ enum DeferredBarrierType {
+ kBarrier_None,
+ kBarrier_InOrder,
+ kBarrier_OutOfOrder,
+ };
+
+ void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint);
+ void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint);
+
+
+ size_t 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(count * sizeof(T));
+ memcpy(dstBuffer, srcBuffer, count * sizeof(T));
+ return dstBuffer;
+ }
+
+ inline char* refText(const char* text, size_t byteLength) {
+ return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
+ }
+
+ inline const SkPath* refPath(const SkPath* path) {
+ if (!path) return nullptr;
+
+ // The points/verbs within the path are refcounted so this copy operation
+ // is inexpensive and maintains the generationID of the original path.
+ const SkPath* cachedPath = new SkPath(*path);
+ mDisplayList->pathResources.push_back(cachedPath);
+ return cachedPath;
+ }
+
+ inline const SkPaint* refPaint(const SkPaint* paint) {
+ if (!paint) return nullptr;
+
+ // If there is a draw filter apply it here and store the modified paint
+ // so that we don't need to modify the paint every time we access it.
+ SkTLazy<SkPaint> filteredPaint;
+ if (mDrawFilter.get()) {
+ filteredPaint.set(*paint);
+ mDrawFilter->filter(filteredPaint.get(), SkDrawFilter::kPaint_Type);
+ paint = filteredPaint.get();
+ }
+
+ // compute the hash key for the paint and check the cache.
+ const uint32_t key = paint->getHash();
+ const SkPaint* cachedPaint = mPaintMap.valueFor(key);
+ // In the unlikely event that 2 unique paints have the same hash we do a
+ // object equality check to ensure we don't erroneously dedup them.
+ if (cachedPaint == nullptr || *cachedPaint != *paint) {
+ cachedPaint = new SkPaint(*paint);
+ std::unique_ptr<const SkPaint> copy(cachedPaint);
+ mDisplayList->paints.push_back(std::move(copy));
+
+ // replaceValueFor() performs an add if the entry doesn't exist
+ mPaintMap.replaceValueFor(key, cachedPaint);
+ refBitmapsInShader(cachedPaint->getShader());
+ }
+
+ return cachedPaint;
+ }
+
+ inline const SkRegion* refRegion(const SkRegion* region) {
+ if (!region) {
+ return region;
+ }
+
+ const SkRegion* cachedRegion = mRegionMap.valueFor(region);
+ // TODO: Add generation ID to SkRegion
+ if (cachedRegion == nullptr) {
+ std::unique_ptr<const SkRegion> copy(new SkRegion(*region));
+ cachedRegion = copy.get();
+ mDisplayList->regions.push_back(std::move(copy));
+
+ // replaceValueFor() performs an add if the entry doesn't exist
+ mRegionMap.replaceValueFor(region, cachedRegion);
+ }
+
+ return cachedRegion;
+ }
+
+ inline const SkBitmap* refBitmap(const SkBitmap& bitmap) {
+ // Note that this assumes the bitmap is immutable. There are cases this won't handle
+ // correctly, such as creating the bitmap from scratch, drawing with it, changing its
+ // contents, and drawing again. The only fix would be to always copy it the first time,
+ // which doesn't seem worth the extra cycles for this unlikely case.
+ SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
+ alloc().autoDestroy(localBitmap);
+ mDisplayList->bitmapResources.push_back(localBitmap);
+ return localBitmap;
+ }
+
+ inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
+ mDisplayList->patchResources.push_back(patch);
+ mResourceCache.incrementRefcount(patch);
+ return patch;
+ }
+
+ DefaultKeyedVector<uint32_t, const SkPaint*> mPaintMap;
+ DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
+ DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
+
+ CanvasState mState;
+ std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy;
+ ResourceCache& mResourceCache;
+ DeferredBarrierType mDeferredBarrierType = kBarrier_None;
+ DisplayList* mDisplayList = nullptr;
+ bool mHighContrastText = false;
+ SkAutoTUnref<SkDrawFilter> mDrawFilter;
+ int mRestoreSaveCount = -1;
+}; // class RecordingCanvas
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_RECORDING_CANVAS_H
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 4c4cd3da3be4..50199db75640 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -125,25 +125,32 @@ public:
}
bool intersects(float l, float t, float r, float b) const {
- return !intersectWith(l, t, r, b).isEmpty();
+ float tempLeft = std::max(left, l);
+ float tempTop = std::max(top, t);
+ float tempRight = std::min(right, r);
+ float tempBottom = std::min(bottom, b);
+
+ return ((tempLeft < tempRight) && (tempTop < tempBottom)); // !isEmpty
}
bool intersects(const Rect& r) const {
return intersects(r.left, r.top, r.right, r.bottom);
}
- bool intersect(float l, float t, float r, float b) {
- Rect tmp(l, t, r, b);
- intersectWith(tmp);
- if (!tmp.isEmpty()) {
- set(tmp);
- return true;
- }
- return false;
+ /**
+ * This method is named 'doIntersect' instead of 'intersect' so as not to be confused with
+ * SkRect::intersect / android.graphics.Rect#intersect behavior, which do not modify the object
+ * if the intersection of the rects would be empty.
+ */
+ void doIntersect(float l, float t, float r, float b) {
+ left = std::max(left, l);
+ top = std::max(top, t);
+ right = std::min(right, r);
+ bottom = std::min(bottom, b);
}
- bool intersect(const Rect& r) {
- return intersect(r.left, r.top, r.right, r.bottom);
+ void doIntersect(const Rect& r) {
+ doIntersect(r.left, r.top, r.right, r.bottom);
}
inline bool contains(float l, float t, float r, float b) const {
@@ -271,24 +278,6 @@ public:
void dump(const char* label = nullptr) const {
ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom);
}
-
-private:
- void intersectWith(Rect& tmp) const {
- tmp.left = std::max(left, tmp.left);
- tmp.top = std::max(top, tmp.top);
- tmp.right = std::min(right, tmp.right);
- tmp.bottom = std::min(bottom, tmp.bottom);
- }
-
- Rect intersectWith(float l, float t, float r, float b) const {
- Rect tmp;
- tmp.left = std::max(left, l);
- tmp.top = std::max(top, t);
- tmp.right = std::min(right, r);
- tmp.bottom = std::min(bottom, b);
- return tmp;
- }
-
}; // class Rect
}; // namespace uirenderer
diff --git a/libs/hwui/RenderBufferCache.cpp b/libs/hwui/RenderBufferCache.cpp
index d0812c96afd7..8beed2540e1c 100644
--- a/libs/hwui/RenderBufferCache.cpp
+++ b/libs/hwui/RenderBufferCache.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <utils/Log.h>
#include "Debug.h"
@@ -100,9 +98,8 @@ void RenderBufferCache::deleteBuffer(RenderBuffer* buffer) {
}
void RenderBufferCache::clear() {
- size_t count = mCache.size();
- for (size_t i = 0; i < count; i++) {
- deleteBuffer(mCache.itemAt(i).mBuffer);
+ for (auto entry : mCache) {
+ deleteBuffer(entry.mBuffer);
}
mCache.clear();
}
@@ -111,11 +108,11 @@ RenderBuffer* RenderBufferCache::get(GLenum format, const uint32_t width, const
RenderBuffer* buffer = nullptr;
RenderBufferEntry entry(format, width, height);
- ssize_t index = mCache.indexOf(entry);
+ auto iter = mCache.find(entry);
- if (index >= 0) {
- entry = mCache.itemAt(index);
- mCache.removeAt(index);
+ if (iter != mCache.end()) {
+ entry = *iter;
+ mCache.erase(iter);
buffer = entry.mBuffer;
mSize -= buffer->getSize();
@@ -141,16 +138,14 @@ bool RenderBufferCache::put(RenderBuffer* buffer) {
const uint32_t size = buffer->getSize();
if (size < mMaxSize) {
while (mSize + size > mMaxSize) {
- size_t position = 0;
-
- RenderBuffer* victim = mCache.itemAt(position).mBuffer;
+ RenderBuffer* victim = mCache.begin()->mBuffer;
deleteBuffer(victim);
- mCache.removeAt(position);
+ mCache.erase(mCache.begin());
}
RenderBufferEntry entry(buffer);
- mCache.add(entry);
+ mCache.insert(entry);
mSize += size;
RENDER_BUFFER_LOGD("Added %s render buffer (%dx%d)",
diff --git a/libs/hwui/RenderBufferCache.h b/libs/hwui/RenderBufferCache.h
index 6c668b09c40d..7f59ec1c48b1 100644
--- a/libs/hwui/RenderBufferCache.h
+++ b/libs/hwui/RenderBufferCache.h
@@ -20,7 +20,8 @@
#include <GLES2/gl2.h>
#include "RenderBuffer.h"
-#include "utils/SortedList.h"
+
+#include <set>
namespace android {
namespace uirenderer {
@@ -100,14 +101,8 @@ private:
return compare(*this, other) != 0;
}
- friend inline int strictly_order_type(const RenderBufferEntry& lhs,
- const RenderBufferEntry& rhs) {
- return RenderBufferEntry::compare(lhs, rhs) < 0;
- }
-
- friend inline int compare_type(const RenderBufferEntry& lhs,
- const RenderBufferEntry& rhs) {
- return RenderBufferEntry::compare(lhs, rhs);
+ bool operator<(const RenderBufferEntry& other) const {
+ return RenderBufferEntry::compare(*this, other) < 0;
}
RenderBuffer* mBuffer;
@@ -118,7 +113,7 @@ private:
void deleteBuffer(RenderBuffer* buffer);
- SortedList<RenderBufferEntry> mCache;
+ std::multiset<RenderBufferEntry> mCache;
uint32_t mSize;
uint32_t mMaxSize;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 0951fc158ace..894a2bdf19df 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#define ATRACE_TAG ATRACE_TAG_VIEW
-#define LOG_TAG "OpenGLRenderer"
-
#include "RenderNode.h"
#include <algorithm>
@@ -28,6 +25,9 @@
#include "DamageAccumulator.h"
#include "Debug.h"
+#if HWUI_NEW_OPS
+#include "RecordedOp.h"
+#endif
#include "DisplayListOp.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
@@ -36,6 +36,9 @@
#include "utils/TraceUtils.h"
#include "renderthread/CanvasContext.h"
+#include "protos/hwui.pb.h"
+#include "protos/ProtoHelpers.h"
+
namespace android {
namespace uirenderer {
@@ -45,26 +48,26 @@ void RenderNode::debugDumpLayers(const char* prefix) {
prefix, this, getName(), mLayer, mLayer->getFbo(),
mLayer->wasBuildLayered ? "true" : "false");
}
- if (mDisplayListData) {
- for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
- mDisplayListData->children()[i]->mRenderNode->debugDumpLayers(prefix);
+ if (mDisplayList) {
+ for (auto&& child : mDisplayList->getChildren()) {
+ child->renderNode->debugDumpLayers(prefix);
}
}
}
RenderNode::RenderNode()
: mDirtyPropertyFields(0)
- , mNeedsDisplayListDataSync(false)
- , mDisplayListData(nullptr)
- , mStagingDisplayListData(nullptr)
+ , mNeedsDisplayListSync(false)
+ , mDisplayList(nullptr)
+ , mStagingDisplayList(nullptr)
, mAnimatorManager(*this)
, mLayer(nullptr)
, mParentCount(0) {
}
RenderNode::~RenderNode() {
- deleteDisplayListData();
- delete mStagingDisplayListData;
+ deleteDisplayList();
+ delete mStagingDisplayList;
if (mLayer) {
ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer);
mLayer->postDecStrong();
@@ -72,10 +75,10 @@ RenderNode::~RenderNode() {
}
}
-void RenderNode::setStagingDisplayList(DisplayListData* data) {
- mNeedsDisplayListDataSync = true;
- delete mStagingDisplayListData;
- mStagingDisplayListData = data;
+void RenderNode::setStagingDisplayList(DisplayList* displayList) {
+ mNeedsDisplayListSync = true;
+ delete mStagingDisplayList;
+ mStagingDisplayList = displayList;
}
/**
@@ -94,24 +97,100 @@ void RenderNode::output(uint32_t level) {
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
properties().debugOutputProperties(level);
- int flags = DisplayListOp::kOpLogFlag_Recurse;
- if (mDisplayListData) {
+
+ if (mDisplayList) {
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("op dumping unsupported");
+#else
// TODO: consider printing the chunk boundaries here
- for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
- mDisplayListData->displayListOps[i]->output(level, flags);
+ for (auto&& op : mDisplayList->getOps()) {
+ op->output(level, DisplayListOp::kOpLogFlag_Recurse);
}
+#endif
}
ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
}
+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 (mStagingDisplayListData) {
- size += mStagingDisplayListData->getUsedSize();
+ if (mStagingDisplayList) {
+ size += mStagingDisplayList->getUsedSize();
}
- if (mDisplayListData && mDisplayListData != mStagingDisplayListData) {
- size += mDisplayListData->getUsedSize();
+ if (mDisplayList && mDisplayList != mStagingDisplayList) {
+ size += mDisplayList->getUsedSize();
}
return size;
}
@@ -137,7 +216,7 @@ void RenderNode::damageSelf(TreeInfo& info) {
} else {
// Hope this is big enough?
// TODO: Get this from the display list ops or something
- info.damageAccumulator->dirty(INT_MIN, INT_MIN, INT_MAX, INT_MAX);
+ info.damageAccumulator->dirty(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
}
}
}
@@ -242,10 +321,10 @@ void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
}
bool willHaveFunctor = false;
- if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayListData) {
- willHaveFunctor = !mStagingDisplayListData->functors.isEmpty();
- } else if (mDisplayListData) {
- willHaveFunctor = !mDisplayListData->functors.isEmpty();
+ if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayList) {
+ willHaveFunctor = !mStagingDisplayList->getFunctors().empty();
+ } else if (mDisplayList) {
+ willHaveFunctor = !mDisplayList->getFunctors().empty();
}
bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
willHaveFunctor, functorsNeedLayer);
@@ -254,12 +333,16 @@ void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
if (info.mode == TreeInfo::MODE_FULL) {
pushStagingDisplayListChanges(info);
}
- prepareSubTree(info, childFunctorsNeedLayer, mDisplayListData);
+ prepareSubTree(info, childFunctorsNeedLayer, mDisplayList);
pushLayerUpdate(info);
info.damageAccumulator->popTransform();
}
+void RenderNode::syncProperties() {
+ mProperties = mStagingProperties;
+}
+
void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
// Push the animators first so that setupStartValueIfNecessary() is called
// before properties() is trampled by stagingProperties(), as they are
@@ -271,7 +354,7 @@ void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
mDirtyPropertyFields = 0;
damageSelf(info);
info.damageAccumulator->popTransform();
- mProperties = mStagingProperties;
+ syncProperties();
applyLayerPropertiesToLayer(info);
// We could try to be clever and only re-damage if the matrix changed.
// However, we don't need to worry about that. The cost of over-damaging
@@ -292,63 +375,63 @@ void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
mLayer->setBlend(props.needsBlending());
}
-void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
- if (mNeedsDisplayListDataSync) {
- mNeedsDisplayListDataSync = false;
- // Make sure we inc first so that we don't fluctuate between 0 and 1,
- // which would thrash the layer cache
- if (mStagingDisplayListData) {
- for (size_t i = 0; i < mStagingDisplayListData->children().size(); i++) {
- mStagingDisplayListData->children()[i]->mRenderNode->incParentRefCount();
- }
+void RenderNode::syncDisplayList() {
+ // Make sure we inc first so that we don't fluctuate between 0 and 1,
+ // which would thrash the layer cache
+ if (mStagingDisplayList) {
+ for (auto&& child : mStagingDisplayList->getChildren()) {
+ child->renderNode->incParentRefCount();
+ }
+ }
+ deleteDisplayList();
+ mDisplayList = mStagingDisplayList;
+ mStagingDisplayList = nullptr;
+ if (mDisplayList) {
+ for (size_t i = 0; i < mDisplayList->getFunctors().size(); i++) {
+ (*mDisplayList->getFunctors()[i])(DrawGlInfo::kModeSync, nullptr);
}
+ }
+}
+
+void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
+ if (mNeedsDisplayListSync) {
+ mNeedsDisplayListSync = false;
// Damage with the old display list first then the new one to catch any
// changes in isRenderable or, in the future, bounds
damageSelf(info);
- deleteDisplayListData();
- // TODO: Remove this caches stuff
- if (mStagingDisplayListData && mStagingDisplayListData->functors.size()) {
- Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size());
- }
- mDisplayListData = mStagingDisplayListData;
- mStagingDisplayListData = nullptr;
- if (mDisplayListData) {
- for (size_t i = 0; i < mDisplayListData->functors.size(); i++) {
- (*mDisplayListData->functors[i])(DrawGlInfo::kModeSync, nullptr);
- }
- }
+ syncDisplayList();
damageSelf(info);
}
}
-void RenderNode::deleteDisplayListData() {
- if (mDisplayListData) {
- for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
- mDisplayListData->children()[i]->mRenderNode->decParentRefCount();
- }
- if (mDisplayListData->functors.size()) {
- Caches::getInstance().unregisterFunctors(mDisplayListData->functors.size());
+void RenderNode::deleteDisplayList() {
+ if (mDisplayList) {
+ for (auto&& child : mDisplayList->getChildren()) {
+ child->renderNode->decParentRefCount();
}
}
- delete mDisplayListData;
- mDisplayListData = nullptr;
+ delete mDisplayList;
+ mDisplayList = nullptr;
}
-void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayListData* subtree) {
+void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree) {
if (subtree) {
TextureCache& cache = Caches::getInstance().textureCache;
- info.out.hasFunctors |= subtree->functors.size();
- for (size_t i = 0; info.prepareTextures && i < subtree->bitmapResources.size(); i++) {
- info.prepareTextures = cache.prefetchAndMarkInUse(
- info.canvasContext, subtree->bitmapResources[i]);
+ info.out.hasFunctors |= subtree->getFunctors().size();
+ for (auto&& bitmapResource : subtree->getBitmapResources()) {
+ info.prepareTextures = cache.prefetchAndMarkInUse(info.canvasContext, bitmapResource);
}
- for (size_t i = 0; i < subtree->children().size(); i++) {
- DrawRenderNodeOp* op = subtree->children()[i];
- RenderNode* childNode = op->mRenderNode;
+ for (auto&& op : subtree->getChildren()) {
+ RenderNode* childNode = op->renderNode;
+#if HWUI_NEW_OPS
+ info.damageAccumulator->pushTransform(&op->localMatrix);
+ bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip;
+#else
info.damageAccumulator->pushTransform(&op->mTransformFromParent);
bool childFunctorsNeedLayer = functorsNeedLayer
// Recorded with non-rect clip, or canvas-rotated by parent
|| op->mRecordedWithPotentialStencilClip;
+#endif
childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
info.damageAccumulator->popTransform();
}
@@ -360,14 +443,14 @@ void RenderNode::destroyHardwareResources() {
LayerRenderer::destroyLayer(mLayer);
mLayer = nullptr;
}
- if (mDisplayListData) {
- for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
- mDisplayListData->children()[i]->mRenderNode->destroyHardwareResources();
+ if (mDisplayList) {
+ for (auto&& child : mDisplayList->getChildren()) {
+ child->renderNode->destroyHardwareResources();
}
- if (mNeedsDisplayListDataSync) {
+ if (mNeedsDisplayListSync) {
// Next prepare tree we are going to push a new display list, so we can
// drop our current one now
- deleteDisplayListData();
+ deleteDisplayList();
}
}
}
@@ -384,6 +467,34 @@ void RenderNode::decParentRefCount() {
}
}
+bool RenderNode::applyViewProperties(CanvasState& canvasState) const {
+ const Outline& outline = properties().getOutline();
+ if (properties().getAlpha() <= 0
+ || (outline.getShouldClip() && outline.isEmpty())
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0) {
+ return false; // rejected
+ }
+
+ if (properties().getLeft() != 0 || properties().getTop() != 0) {
+ canvasState.translate(properties().getLeft(), properties().getTop());
+ }
+ if (properties().getStaticMatrix()) {
+ canvasState.concatMatrix(*properties().getStaticMatrix());
+ } else if (properties().getAnimationMatrix()) {
+ canvasState.concatMatrix(*properties().getAnimationMatrix());
+ }
+ if (properties().hasTransformMatrix()) {
+ if (properties().isTransformTranslateOnly()) {
+ canvasState.translate(properties().getTranslationX(), properties().getTranslationY());
+ } else {
+ canvasState.concatMatrix(*properties().getTransformMatrix());
+ }
+ }
+ return !canvasState.quickRejectConservative(
+ 0, 0, properties().getWidth(), properties().getHeight());
+}
+
/*
* For property operations, we pass a savecount of 0, since the operations aren't part of the
* displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
@@ -515,26 +626,27 @@ void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform)
* which are flagged to not draw in the standard draw loop.
*/
void RenderNode::computeOrdering() {
+#if !HWUI_NEW_OPS
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 (mDisplayListData == nullptr) return;
- for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
- DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
- childOp->mRenderNode->computeOrderingImpl(childOp,
- properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity());
+ if (mDisplayList == nullptr) return;
+ for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
+ DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
+ childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity());
}
+#endif
}
void RenderNode::computeOrderingImpl(
DrawRenderNodeOp* opState,
- const SkPath* outlineOfProjectionSurface,
- Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
+ std::vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface) {
+#if !HWUI_NEW_OPS
mProjectedNodes.clear();
- if (mDisplayListData == nullptr || mDisplayListData->isEmpty()) return;
+ 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
@@ -544,22 +656,21 @@ void RenderNode::computeOrderingImpl(
if (properties().getProjectBackwards()) {
// composited projectee, flag for out of order draw, save matrix, and store in proj surface
opState->mSkipInOrderDraw = true;
- opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
- compositedChildrenOfProjectionSurface->add(opState);
+ opState->mTransformFromCompositingAncestor = localTransformFromProjectionSurface;
+ compositedChildrenOfProjectionSurface->push_back(opState);
} else {
// standard in order draw
opState->mSkipInOrderDraw = false;
}
- if (mDisplayListData->children().size() > 0) {
- const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
+ if (mDisplayList->getChildren().size() > 0) {
+ const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0;
bool haveAppliedPropertiesToProjection = false;
- for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
- DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
- RenderNode* child = childOp->mRenderNode;
+ for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) {
+ DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
+ RenderNode* child = childOp->renderNode;
- const SkPath* projectionOutline = nullptr;
- Vector<DrawRenderNodeOp*>* projectionChildren = nullptr;
+ std::vector<DrawRenderNodeOp*>* projectionChildren = nullptr;
const mat4* projectionTransform = nullptr;
if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
// if receiving projections, collect projecting descendant
@@ -567,7 +678,6 @@ void RenderNode::computeOrderingImpl(
// 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.
- projectionOutline = properties().getOutline().getPath();
projectionChildren = &mProjectedNodes;
projectionTransform = &mat4::identity();
} else {
@@ -575,14 +685,13 @@ void RenderNode::computeOrderingImpl(
applyViewPropertyTransforms(localTransformFromProjectionSurface);
haveAppliedPropertiesToProjection = true;
}
- projectionOutline = outlineOfProjectionSurface;
projectionChildren = compositedChildrenOfProjectionSurface;
projectionTransform = &localTransformFromProjectionSurface;
}
- child->computeOrderingImpl(childOp,
- projectionOutline, projectionChildren, projectionTransform);
+ child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
}
}
+#endif
}
class DeferOperationHandler {
@@ -640,17 +749,18 @@ void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
}
-void RenderNode::buildZSortedChildList(const DisplayListData::Chunk& chunk,
- Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
+void RenderNode::buildZSortedChildList(const DisplayList::Chunk& chunk,
+ std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
+#if !HWUI_NEW_OPS
if (chunk.beginChildIndex == chunk.endChildIndex) return;
for (unsigned int i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) {
- DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
- RenderNode* child = childOp->mRenderNode;
+ DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i];
+ RenderNode* child = childOp->renderNode;
float childZ = child->properties().getZ();
if (!MathUtils::isZero(childZ) && chunk.reorderChildren) {
- zTranslatedNodes.add(ZDrawRenderNodeOpPair(childZ, childOp));
+ zTranslatedNodes.push_back(ZDrawRenderNodeOpPair(childZ, childOp));
childOp->mSkipInOrderDraw = true;
} else if (!child->properties().getProjectBackwards()) {
// regular, in order drawing DisplayList
@@ -660,13 +770,16 @@ void RenderNode::buildZSortedChildList(const DisplayListData::Chunk& chunk,
// Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order)
std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
+#endif
}
template <class T>
void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
if (properties().getAlpha() <= 0.0f
|| properties().getOutline().getAlpha() <= 0.0f
- || !properties().getOutline().getPath()) {
+ || !properties().getOutline().getPath()
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0) {
// no shadow to draw
return;
}
@@ -693,7 +806,7 @@ void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T&
if (revealClipPath) {
frameAllocatedPath = handler.allocPathForFrame();
- Op(*outlinePath, *revealClipPath, kIntersect_PathOp, frameAllocatedPath);
+ Op(*outlinePath, *revealClipPath, kIntersect_SkPathOp, frameAllocatedPath);
outlinePath = frameAllocatedPath;
}
@@ -709,7 +822,7 @@ void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T&
clipBoundsPath.addRect(clipBounds.left, clipBounds.top,
clipBounds.right, clipBounds.bottom);
- Op(*outlinePath, clipBoundsPath, kIntersect_PathOp, frameAllocatedPath);
+ Op(*outlinePath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath);
outlinePath = frameAllocatedPath;
}
@@ -722,12 +835,12 @@ void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T&
template <class T>
void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
- const Matrix4& initialTransform, const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ const Matrix4& initialTransform, const std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
OpenGLRenderer& renderer, T& handler) {
const int size = zTranslatedNodes.size();
if (size == 0
- || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
- || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
+ || (mode == ChildrenSelectMode::NegativeZChildren && zTranslatedNodes[0].key > 0.0f)
+ || (mode == ChildrenSelectMode::PositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
// no 3d children to draw
return;
}
@@ -735,7 +848,7 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
// Apply the base transform of the parent of the 3d children. This isolates
// 3d children of the current chunk from transformations made in previous chunks.
int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
- renderer.setMatrix(initialTransform);
+ renderer.setGlobalMatrix(initialTransform);
/**
* Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
@@ -746,7 +859,7 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
*/
const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
size_t drawIndex, shadowIndex, endIndex;
- if (mode == kNegativeZChildren) {
+ if (mode == ChildrenSelectMode::NegativeZChildren) {
drawIndex = 0;
endIndex = nonNegativeIndex;
shadowIndex = endIndex; // draw no shadows
@@ -763,7 +876,7 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode,
while (shadowIndex < endIndex || drawIndex < endIndex) {
if (shadowIndex < endIndex) {
DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value;
- RenderNode* caster = casterOp->mRenderNode;
+ RenderNode* caster = casterOp->renderNode;
const float casterZ = zTranslatedNodes[shadowIndex].key;
// attempt to render the shadow if the caster about to be drawn is its caster,
// OR if its caster's Z value is similar to the previous potential caster
@@ -806,9 +919,14 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
// Transform renderer to match background we're projecting onto
// (by offsetting canvas by translationX/Y of background rendernode, since only those are set)
const DisplayListOp* op =
- (mDisplayListData->displayListOps[mDisplayListData->projectionReceiveIndex]);
+#if HWUI_NEW_OPS
+ nullptr;
+ LOG_ALWAYS_FATAL("unsupported");
+#else
+ (mDisplayList->getOps()[mDisplayList->projectionReceiveIndex]);
+#endif
const DrawRenderNodeOp* backgroundOp = reinterpret_cast<const DrawRenderNodeOp*>(op);
- const RenderProperties& backgroundProps = backgroundOp->mRenderNode->properties();
+ const RenderProperties& backgroundProps = backgroundOp->renderNode->properties();
renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
// If the projection reciever has an outline, we mask projected content to it
@@ -843,7 +961,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
*/
template <class T>
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
- if (mDisplayListData->isEmpty()) {
+ if (mDisplayList->isEmpty()) {
DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", handler.level() * 2, "",
this, getName());
return;
@@ -856,7 +974,10 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
const bool useViewProperties = (!mLayer || drawLayer);
if (useViewProperties) {
const Outline& outline = properties().getOutline();
- if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) {
+ if (properties().getAlpha() <= 0
+ || (outline.getShouldClip() && outline.isEmpty())
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0) {
DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "",
this, getName());
return;
@@ -884,6 +1005,9 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
setViewProperties<T>(renderer, handler);
}
+#if HWUI_NEW_OPS
+ LOG_ALWAYS_FATAL("legacy op traversal not supported");
+#else
bool quickRejected = properties().getClipToBounds()
&& renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
if (!quickRejected) {
@@ -891,39 +1015,39 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
renderer.setBaseTransform(initialTransform);
if (drawLayer) {
- handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
+ handler(new (alloc) DrawLayerOp(mLayer),
renderer.getSaveCount() - 1, properties().getClipToBounds());
} else {
const int saveCountOffset = renderer.getSaveCount() - 1;
- const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
- for (size_t chunkIndex = 0; chunkIndex < mDisplayListData->getChunks().size(); chunkIndex++) {
- const DisplayListData::Chunk& chunk = mDisplayListData->getChunks()[chunkIndex];
+ const int projectionReceiveIndex = mDisplayList->projectionReceiveIndex;
+ for (size_t chunkIndex = 0; chunkIndex < mDisplayList->getChunks().size(); chunkIndex++) {
+ const DisplayList::Chunk& chunk = mDisplayList->getChunks()[chunkIndex];
- Vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
+ std::vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
buildZSortedChildList(chunk, zTranslatedNodes);
- issueOperationsOf3dChildren(kNegativeZChildren,
+ issueOperationsOf3dChildren(ChildrenSelectMode::NegativeZChildren,
initialTransform, zTranslatedNodes, renderer, handler);
-
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
- DisplayListOp *op = mDisplayListData->displayListOps[opIndex];
+ DisplayListOp *op = mDisplayList->getOps()[opIndex];
#if DEBUG_DISPLAY_LIST
op->output(handler.level() + 1);
#endif
handler(op, saveCountOffset, properties().getClipToBounds());
- if (CC_UNLIKELY(!mProjectedNodes.isEmpty() && projectionReceiveIndex >= 0 &&
+ if (CC_UNLIKELY(!mProjectedNodes.empty() && projectionReceiveIndex >= 0 &&
opIndex == static_cast<size_t>(projectionReceiveIndex))) {
issueOperationsOfProjectedChildren(renderer, handler);
}
}
- issueOperationsOf3dChildren(kPositiveZChildren,
+ issueOperationsOf3dChildren(ChildrenSelectMode::PositiveZChildren,
initialTransform, zTranslatedNodes, renderer, handler);
}
}
}
+#endif
DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (handler.level() + 1) * 2, "", restoreTo);
handler(new (alloc) RestoreToCountOp(restoreTo),
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 025a4a416e4c..57e41c611547 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -16,17 +16,12 @@
#ifndef RENDERNODE_H
#define RENDERNODE_H
-#ifndef LOG_TAG
- #define LOG_TAG "OpenGLRenderer"
-#endif
-
#include <SkCamera.h>
#include <SkMatrix.h>
#include <utils/LinearAllocator.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
-#include <utils/Vector.h>
#include <cutils/compiler.h>
@@ -34,10 +29,12 @@
#include "AnimatorManager.h"
#include "Debug.h"
-#include "Matrix.h"
#include "DisplayList.h"
+#include "Matrix.h"
#include "RenderProperties.h"
+#include <vector>
+
class SkBitmap;
class SkPaint;
class SkPath;
@@ -46,6 +43,7 @@ class SkRegion;
namespace android {
namespace uirenderer {
+class CanvasState;
class DisplayListOp;
class DisplayListCanvas;
class OpenGLRenderer;
@@ -60,19 +58,24 @@ class RestoreToCountOp;
class DrawRenderNodeOp;
class TreeInfo;
+namespace proto {
+class RenderNode;
+}
+
/**
* Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
*
* Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
- * functionality is split between DisplayListCanvas (which manages the recording), DisplayListData
+ * functionality is split between DisplayListCanvas (which manages the recording), DisplayList
* (which holds the actual data), and DisplayList (which holds properties and performs playback onto
* a renderer).
*
- * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's
- * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
+ * Note that DisplayList is swapped out from beneath an individual RenderNode when a view's
+ * recorded stream of canvas operations is refreshed. The RenderNode (and its properties) stay
* attached.
*/
class RenderNode : public VirtualLightRefBase {
+friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties
public:
enum DirtyPropertyMask {
GENERIC = 1 << 1,
@@ -99,10 +102,9 @@ public:
kReplayFlag_ClipChildren = 0x1
};
- static void outputLogBuffer(int fd);
void debugDumpLayers(const char* prefix);
- ANDROID_API void setStagingDisplayList(DisplayListData* newData);
+ ANDROID_API void setStagingDisplayList(DisplayList* newData);
void computeOrdering();
@@ -111,13 +113,14 @@ public:
ANDROID_API void output(uint32_t level = 1);
ANDROID_API int getDebugSize();
+ void copyTo(proto::RenderNode* node);
bool isRenderable() const {
- return mDisplayListData && !mDisplayListData->isEmpty();
+ return mDisplayList && !mDisplayList->isEmpty();
}
bool hasProjectionReceiver() const {
- return mDisplayListData && mDisplayListData->projectionReceiveIndex >= 0;
+ return mDisplayList && mDisplayList->projectionReceiveIndex >= 0;
}
const char* getName() const {
@@ -175,40 +178,56 @@ public:
AnimatorManager& animators() { return mAnimatorManager; }
+ // Returns false if the properties dictate the subtree contained in this RenderNode won't render
+ bool applyViewProperties(CanvasState& canvasState) const;
+
void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false) const;
+ bool nothingToDraw() const {
+ const Outline& outline = properties().getOutline();
+ return mDisplayList == nullptr
+ || properties().getAlpha() <= 0
+ || (outline.getShouldClip() && outline.isEmpty())
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0;
+ }
+
+ // Only call if RenderNode has DisplayList...
+ const DisplayList& getDisplayList() const {
+ return *mDisplayList;
+ }
+
private:
typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
- static size_t findNonNegativeIndex(const Vector<ZDrawRenderNodeOpPair>& nodes) {
+ static size_t findNonNegativeIndex(const std::vector<ZDrawRenderNodeOpPair>& nodes) {
for (size_t i = 0; i < nodes.size(); i++) {
if (nodes[i].key >= 0.0f) return i;
}
return nodes.size();
}
- enum ChildrenSelectMode {
- kNegativeZChildren,
- kPositiveZChildren
+ enum class ChildrenSelectMode {
+ NegativeZChildren,
+ PositiveZChildren
};
void computeOrderingImpl(DrawRenderNodeOp* opState,
- const SkPath* outlineOfProjectionSurface,
- Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
+ std::vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface);
template <class T>
inline void setViewProperties(OpenGLRenderer& renderer, T& handler);
- void buildZSortedChildList(const DisplayListData::Chunk& chunk,
- Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes);
+ void buildZSortedChildList(const DisplayList::Chunk& chunk,
+ std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes);
template<class T>
inline void issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler);
template <class T>
inline void issueOperationsOf3dChildren(ChildrenSelectMode mode,
- const Matrix4& initialTransform, const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ const Matrix4& initialTransform, const std::vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
OpenGLRenderer& renderer, T& handler);
template <class T>
@@ -235,14 +254,18 @@ private:
const char* mText;
};
+
+ void syncProperties();
+ void syncDisplayList();
+
void prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer);
void pushStagingPropertiesChanges(TreeInfo& info);
void pushStagingDisplayListChanges(TreeInfo& info);
- void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayListData* subtree);
+ void prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayList* subtree);
void applyLayerPropertiesToLayer(TreeInfo& info);
void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
void pushLayerUpdate(TreeInfo& info);
- void deleteDisplayListData();
+ void deleteDisplayList();
void damageSelf(TreeInfo& info);
void incParentRefCount() { mParentCount++; }
@@ -254,10 +277,10 @@ private:
RenderProperties mProperties;
RenderProperties mStagingProperties;
- bool mNeedsDisplayListDataSync;
- // WARNING: Do not delete this directly, you must go through deleteDisplayListData()!
- DisplayListData* mDisplayListData;
- DisplayListData* mStagingDisplayListData;
+ bool mNeedsDisplayListSync;
+ // WARNING: Do not delete this directly, you must go through deleteDisplayList()!
+ DisplayList* mDisplayList;
+ DisplayList* mStagingDisplayList;
friend class AnimatorManager;
AnimatorManager mAnimatorManager;
@@ -271,14 +294,14 @@ private:
*/
// for projection surfaces, contains a list of all children items
- Vector<DrawRenderNodeOp*> mProjectedNodes;
+ std::vector<DrawRenderNodeOp*> mProjectedNodes;
// How many references our parent(s) have to us. Typically this should alternate
// between 2 and 1 (when a staging push happens we inc first then dec)
// When this hits 0 we are no longer in the tree, so any hardware resources
// (specifically Layers) should be released.
// This is *NOT* thread-safe, and should therefore only be tracking
- // mDisplayListData, not mStagingDisplayListData.
+ // mDisplayList, not mStagingDisplayList.
uint32_t mParentCount;
}; // class RenderNode
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 4f6ef4ef9e3d..ce1bd6ab8b03 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include "RenderProperties.h"
#include <utils/Trace.h>
@@ -54,11 +52,8 @@ bool LayerProperties::setColorFilter(SkColorFilter* filter) {
bool LayerProperties::setFromPaint(const SkPaint* paint) {
bool changed = false;
- SkXfermode::Mode mode;
- int alpha;
- OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
- changed |= setAlpha(static_cast<uint8_t>(alpha));
- changed |= setXferMode(mode);
+ changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
+ changed |= setXferMode(PaintUtils::getXfermodeDirect(paint));
changed |= setColorFilter(paint ? paint->getColorFilter() : nullptr);
return changed;
}
@@ -72,23 +67,6 @@ LayerProperties& LayerProperties::operator=(const LayerProperties& other) {
return *this;
}
-RenderProperties::PrimitiveFields::PrimitiveFields()
- : mClippingFlags(CLIP_TO_BOUNDS)
- , mProjectBackwards(false)
- , mProjectionReceiver(false)
- , mAlpha(1)
- , mHasOverlappingRendering(true)
- , mElevation(0)
- , mTranslationX(0), mTranslationY(0), mTranslationZ(0)
- , mRotation(0), mRotationX(0), mRotationY(0)
- , mScaleX(1), mScaleY(1)
- , mPivotX(0), mPivotY(0)
- , mLeft(0), mTop(0), mRight(0), mBottom(0)
- , mWidth(0), mHeight(0)
- , mPivotExplicitlySet(false)
- , mMatrixOrPivotDirty(false) {
-}
-
RenderProperties::ComputedFields::ComputedFields()
: mTransformMatrix(nullptr) {
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 11abd701b9d8..f824cc020196 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -417,7 +417,7 @@ public:
return false;
}
- float getLeft() const {
+ int getLeft() const {
return mPrimitiveFields.mLeft;
}
@@ -432,7 +432,7 @@ public:
return false;
}
- float getTop() const {
+ int getTop() const {
return mPrimitiveFields.mTop;
}
@@ -447,7 +447,7 @@ public:
return false;
}
- float getRight() const {
+ int getRight() const {
return mPrimitiveFields.mRight;
}
@@ -462,7 +462,7 @@ public:
return false;
}
- float getBottom() const {
+ int getBottom() const {
return mPrimitiveFields.mBottom;
}
@@ -541,11 +541,15 @@ public:
return mPrimitiveFields.mClippingFlags & CLIP_TO_BOUNDS;
}
+ const Rect& getClipBounds() const {
+ return mPrimitiveFields.mClipBounds;
+ }
+
void getClippingRectForFlags(uint32_t flags, Rect* outRect) const {
if (flags & CLIP_TO_BOUNDS) {
outRect->set(0, 0, getWidth(), getHeight());
if (flags & CLIP_TO_CLIP_BOUNDS) {
- outRect->intersect(mPrimitiveFields.mClipBounds);
+ outRect->doIntersect(mPrimitiveFields.mClipBounds);
}
} else {
outRect->set(mPrimitiveFields.mClipBounds);
@@ -621,25 +625,23 @@ public:
private:
// Rendering properties
struct PrimitiveFields {
- PrimitiveFields();
-
+ int mLeft = 0, mTop = 0, mRight = 0, mBottom = 0;
+ int mWidth = 0, mHeight = 0;
+ int mClippingFlags = CLIP_TO_BOUNDS;
+ float mAlpha = 1;
+ float mTranslationX = 0, mTranslationY = 0, mTranslationZ = 0;
+ float mElevation = 0;
+ float mRotation = 0, mRotationX = 0, mRotationY = 0;
+ float mScaleX = 1, mScaleY = 1;
+ float mPivotX = 0, mPivotY = 0;
+ bool mHasOverlappingRendering = false;
+ bool mPivotExplicitlySet = false;
+ bool mMatrixOrPivotDirty = false;
+ bool mProjectBackwards = false;
+ bool mProjectionReceiver = false;
+ Rect mClipBounds;
Outline mOutline;
RevealClip mRevealClip;
- int mClippingFlags;
- bool mProjectBackwards;
- bool mProjectionReceiver;
- float mAlpha;
- bool mHasOverlappingRendering;
- float mElevation;
- float mTranslationX, mTranslationY, mTranslationZ;
- float mRotation, mRotationX, mRotationY;
- float mScaleX, mScaleY;
- float mPivotX, mPivotY;
- int mLeft, mTop, mRight, mBottom;
- int mWidth, mHeight;
- bool mPivotExplicitlySet;
- bool mMatrixOrPivotDirty;
- Rect mClipBounds;
} mPrimitiveFields;
SkMatrix* mStaticMatrix;
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 75d81346d62a..b26e433cfa4c 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include "ResourceCache.h"
#include "Caches.h"
diff --git a/libs/hwui/RevealClip.h b/libs/hwui/RevealClip.h
index 0084a8edccfc..63821dddd369 100644
--- a/libs/hwui/RevealClip.h
+++ b/libs/hwui/RevealClip.h
@@ -51,7 +51,10 @@ public:
outBounds->set(mX - mRadius, mY - mRadius,
mX + mRadius, mY + mRadius);
}
+
float getRadius() const { return mRadius; }
+ float getX() const { return mX; }
+ float getY() const { return mY; }
const SkPath* getPath() const {
if (!mShouldClip) return nullptr;
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 09d125839a68..eb0fa74f5af0 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -14,13 +14,9 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
#include <math.h>
#include <utils/Log.h>
#include <utils/Trace.h>
-#include <utils/Vector.h>
#include <utils/MathUtils.h>
#include "AmbientShadow.h"
@@ -77,8 +73,8 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
}
#if DEBUG_SHADOW
- ALOGD("light center %f %f %f",
- adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z);
+ ALOGD("light center %f %f %f %d",
+ adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius);
#endif
// light position (because it's in local space) needs to compensate for receiver transform
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 644a4f305a2e..36633b5205a1 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -26,6 +26,8 @@
#include <SkTArray.h>
#include <SkTemplates.h>
+#include <memory>
+
namespace android {
// Holds an SkCanvas reference plus additional native data.
@@ -55,6 +57,11 @@ public:
virtual int width() override;
virtual int height() override;
+ virtual void setHighContrastText(bool highContrastText) override {
+ mHighContrastText = highContrastText;
+ }
+ virtual bool isHighContrastText() override { return mHighContrastText; }
+
virtual int getSaveCount() const override;
virtual int save(SkCanvas::SaveFlags flags) override;
virtual void restore() override;
@@ -67,7 +74,6 @@ public:
virtual void getMatrix(SkMatrix* outMatrix) const override;
virtual void setMatrix(const SkMatrix& matrix) override;
- virtual void setLocalMatrix(const SkMatrix& matrix) override { this->setMatrix(matrix); }
virtual void concat(const SkMatrix& matrix) override;
virtual void rotate(float degrees) override;
virtual void scale(float sx, float sy) override;
@@ -95,6 +101,7 @@ public:
virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
virtual void drawRect(float left, float top, float right, float bottom,
const SkPaint& paint) override;
+ virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
virtual void drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint& paint) override;
virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
@@ -116,6 +123,9 @@ public:
float dstRight, float dstBottom, const SkPaint* paint) override;
virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const SkPaint* paint) override;
+ virtual void drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) override;
virtual void drawText(const uint16_t* text, const float* positions, int count,
const SkPaint& paint, float x, float y,
@@ -134,6 +144,8 @@ private:
SkCanvas::SaveFlags saveFlags;
};
+ bool mHighContrastText = false;
+
void recordPartialSave(SkCanvas::SaveFlags flags);
void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, int frameSaveCount);
void applyClips(const SkTArray<SkClipStack::Element>& clips);
@@ -143,7 +155,7 @@ private:
void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
SkAutoTUnref<SkCanvas> mCanvas;
- SkAutoTDelete<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
+ std::unique_ptr<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
};
Canvas* Canvas::create_canvas(const SkBitmap& bitmap) {
@@ -308,7 +320,7 @@ void SkiaCanvas::recordPartialSave(SkCanvas::SaveFlags flags) {
}
if (NULL == mSaveStack.get()) {
- mSaveStack.reset(SkNEW_ARGS(SkDeque, (sizeof(struct SaveRec), 8)));
+ mSaveStack.reset(new SkDeque(sizeof(struct SaveRec), 8));
}
SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
@@ -474,13 +486,12 @@ void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint
SkCanvas::PointMode mode) {
// convert the floats into SkPoints
count >>= 1; // now it is the number of points
- SkAutoSTMalloc<32, SkPoint> storage(count);
- SkPoint* pts = storage.get();
+ std::unique_ptr<SkPoint[]> pts(new SkPoint[count]);
for (int i = 0; i < count; i++) {
pts[i].set(points[0], points[1]);
points += 2;
}
- mCanvas->drawPoints(mode, count, pts, paint);
+ mCanvas->drawPoints(mode, count, pts.get(), paint);
}
@@ -507,6 +518,14 @@ void SkiaCanvas::drawRect(float left, float top, float right, float bottom,
}
+void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
+ SkRegion::Iterator it(region);
+ while (!it.done()) {
+ mCanvas->drawRect(SkRect::Make(it.rect()), paint);
+ it.next();
+ }
+}
+
void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint& paint) {
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
@@ -584,7 +603,7 @@ void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshH
#ifndef SK_SCALAR_IS_FLOAT
SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid");
#endif
- SkAutoMalloc storage(storageSize);
+ std::unique_ptr<char[]> storage(new char[storageSize]);
SkPoint* texs = (SkPoint*)storage.get();
uint16_t* indices = (uint16_t*)(texs + ptCount);
@@ -662,6 +681,12 @@ void SkiaCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshH
indexCount, tmpPaint);
}
+void SkiaCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& chunk,
+ float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
+ SkRect bounds = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
+ NinePatch::Draw(mCanvas, bounds, bitmap, chunk, paint, nullptr);
+}
+
// ----------------------------------------------------------------------------
// Canvas draw operations: Text
// ----------------------------------------------------------------------------
@@ -676,7 +701,7 @@ void SkiaCanvas::drawText(const uint16_t* text, const float* positions, int coun
SkPaint paintCopy(paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
- SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paintCopy);
}
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index d96ca2afed00..c3f5eb27e236 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -18,6 +18,13 @@
#include <cutils/log.h>
#include <SkPatchUtils.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkPixelRef.h>
+#include <SkRect.h>
+#include <SkRRect.h>
+
+#include <memory>
namespace android {
namespace uirenderer {
@@ -38,7 +45,7 @@ void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPo
}
// convert the SkPoints into floats
- SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_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;
@@ -96,12 +103,31 @@ void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
const SkPaint* paint) {
- mCanvas->drawBitmap(bitmap, left, top, paint);
+ SkPixelRef* pxRef = bitmap.pixelRef();
+
+ // HWUI doesn't support extractSubset(), so convert any subsetted bitmap into
+ // a drawBitmapRect(); pass through an un-subsetted bitmap.
+ if (pxRef && bitmap.dimensions() != pxRef->info().dimensions()) {
+ SkBitmap fullBitmap;
+ fullBitmap.setInfo(pxRef->info());
+ fullBitmap.setPixelRef(pxRef, 0, 0);
+ SkIPoint origin = bitmap.pixelRefOrigin();
+ mCanvas->drawBitmap(fullBitmap, 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(bitmap, left, top, paint);
+ }
}
void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr,
const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags) {
SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height());
+ // TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
}
@@ -114,8 +140,9 @@ void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& ce
void SkiaCanvasProxy::onDrawSprite(const SkBitmap& bitmap, int left, int top,
const SkPaint* paint) {
+ // TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
- mCanvas->setLocalMatrix(SkMatrix::I());
+ mCanvas->setMatrix(SkMatrix::I());
mCanvas->drawBitmap(bitmap, left, top, paint);
mCanvas->restore();
}
@@ -127,7 +154,7 @@ void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkP
return;
}
// convert the SkPoints into floats
- SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
const int floatCount = vertexCount << 1;
const float* vArray = &vertices[0].fX;
const float* tArray = (texs) ? &texs[0].fX : NULL;
@@ -165,9 +192,7 @@ void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
}
void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
- // SkCanvas setMatrix() is relative to the Canvas origin, but OpenGLRenderer's
- // setMatrix() is relative to device origin; call setLocalMatrix() instead.
- mCanvas->setLocalMatrix(matrix);
+ mCanvas->setMatrix(matrix);
}
void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
@@ -191,7 +216,8 @@ public:
glyphIDs = (uint16_t*)text;
count = byteLength >> 1;
} else {
- storage.reset(byteLength); // ensures space for one glyph per ID given UTF8 encoding.
+ // 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);
@@ -202,7 +228,7 @@ public:
uint16_t* glyphIDs;
int count;
private:
- SkAutoSTMalloc<32, uint16_t> storage;
+ std::unique_ptr<uint16_t[]> storage;
};
void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
@@ -211,8 +237,8 @@ void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x
GlyphIDConverter glyphs(text, byteLength, origPaint);
// compute the glyph positions
- SkAutoSTMalloc<32, SkPoint> pointStorage(glyphs.count);
- SkAutoSTMalloc<32, SkScalar> glyphWidths(glyphs.count);
+ std::unique_ptr<SkPoint[]> pointStorage(new SkPoint[glyphs.count]);
+ std::unique_ptr<SkScalar[]> glyphWidths(new SkScalar[glyphs.count]);
glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
// compute conservative bounds
@@ -258,7 +284,7 @@ void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x
}
}
- SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
mCanvas->drawText(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint,
x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
}
@@ -271,7 +297,7 @@ void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const S
// convert to relative positions if necessary
int x, y;
const SkPoint* posArray;
- SkAutoSTMalloc<32, SkPoint> pointStorage;
+ std::unique_ptr<SkPoint[]> pointStorage;
if (mCanvas->drawTextAbsolutePos()) {
x = 0;
y = 0;
@@ -279,11 +305,12 @@ void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const S
} else {
x = pos[0].fX;
y = pos[0].fY;
- posArray = pointStorage.reset(glyphs.count);
+ pointStorage.reset(new SkPoint[glyphs.count]);
for (int i = 0; i < glyphs.count; i++) {
- pointStorage[i].fX = pos[i].fX- x;
- pointStorage[i].fY = pos[i].fY- y;
+ pointStorage[i].fX = pos[i].fX - x;
+ pointStorage[i].fY = pos[i].fY - y;
}
+ posArray = pointStorage.get();
}
// compute conservative bounds
@@ -293,7 +320,7 @@ void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const S
glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
bounds.offset(x, y);
- SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
mCanvas->drawText(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y,
bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
}
@@ -301,12 +328,11 @@ void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const S
void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
SkScalar constY, const SkPaint& paint) {
const size_t pointCount = byteLength >> 1;
- SkAutoSTMalloc<32, SkPoint> storage(pointCount);
- SkPoint* pts = storage.get();
+ 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, paint);
+ this->onDrawPosText(text, byteLength, pts.get(), paint);
}
void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 81d8516168dd..6c105cfc7b00 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include "SkiaShader.h"
#include "Caches.h"
@@ -34,12 +32,19 @@ namespace uirenderer {
// Support
///////////////////////////////////////////////////////////////////////////////
-static const GLenum gTileModes[] = {
+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.
*/
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index beb2e1d0481c..0a58f4b42e4c 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include "Snapshot.h"
#include <SkCanvas.h>
@@ -46,7 +44,7 @@ Snapshot::Snapshot()
* Copies the specified snapshot/ The specified snapshot is stored as
* the previous snapshot.
*/
-Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
+Snapshot::Snapshot(Snapshot* s, int saveFlags)
: flags(0)
, previous(s)
, layer(s->layer)
@@ -60,7 +58,7 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
, mViewportData(s->mViewportData)
, mRelativeLightCenter(s->mRelativeLightCenter) {
if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
- mTransformRoot.load(*s->transform);
+ mTransformRoot = *s->transform;
transform = &mTransformRoot;
} else {
transform = s->transform;
@@ -85,24 +83,24 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
// Clipping
///////////////////////////////////////////////////////////////////////////////
-bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) {
+void Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) {
flags |= Snapshot::kFlagClipSet;
- return mClipArea->clipRegion(region, op);
+ mClipArea->clipRegion(region, op);
}
-bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
+void Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
flags |= Snapshot::kFlagClipSet;
- return mClipArea->clipRectWithTransform(left, top, right, bottom, transform, op);
+ mClipArea->clipRectWithTransform(left, top, right, bottom, transform, op);
}
-bool Snapshot::clipPath(const SkPath& path, SkRegion::Op op) {
+void Snapshot::clipPath(const SkPath& path, SkRegion::Op op) {
flags |= Snapshot::kFlagClipSet;
- return mClipArea->clipPathWithTransform(path, transform, op);
+ mClipArea->clipPathWithTransform(path, transform, op);
}
void Snapshot::setClip(float left, float top, float right, float bottom) {
- mClipArea->setClip(left, top, right, bottom);
flags |= Snapshot::kFlagClipSet;
+ mClipArea->setClip(left, top, right, bottom);
}
bool Snapshot::hasPerspectiveTransform() const {
@@ -150,7 +148,7 @@ void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const {
const Snapshot* current = this;
do {
snapshotList.push(current);
- current = current->previous.get();
+ current = current->previous;
} while (current);
// traverse the list, adding in each transform that contributes to the total transform
@@ -192,8 +190,7 @@ void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& boun
state->highPriority = highPriority;
// store the inverse drawing matrix
- Matrix4 roundRectDrawingMatrix;
- roundRectDrawingMatrix.load(getOrthoMatrix());
+ Matrix4 roundRectDrawingMatrix = getOrthoMatrix();
roundRectDrawingMatrix.multiply(*transform);
state->matrix.loadInverse(roundRectDrawingMatrix);
@@ -243,7 +240,7 @@ bool Snapshot::isIgnored() const {
void Snapshot::dump() const {
ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
- this, flags, previous.get(), getViewportHeight(), isIgnored(), !mClipArea->isSimple());
+ this, flags, previous, getViewportHeight(), isIgnored(), !mClipArea->isSimple());
const Rect& clipRect(mClipArea->getClipRect());
ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d",
clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple());
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index af6ad727da85..aeeda965c48f 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -83,11 +83,11 @@ public:
* Each snapshot has a link to a previous snapshot, indicating the previous
* state of the renderer.
*/
-class Snapshot: public LightRefBase<Snapshot> {
+class Snapshot {
public:
Snapshot();
- Snapshot(const sp<Snapshot>& s, int saveFlags);
+ Snapshot(Snapshot* s, int saveFlags);
/**
* Various flags set on ::flags.
@@ -124,26 +124,25 @@ public:
* the specified operation. The specified rectangle is transformed
* by this snapshot's trasnformation.
*/
- bool clip(float left, float top, float right, float bottom,
- SkRegion::Op op = SkRegion::kIntersect_Op);
+ void clip(float left, float top, float right, float bottom, SkRegion::Op op);
/**
* Modifies the current clip with the new clip rectangle and
* the specified operation. The specified rectangle is considered
* already transformed.
*/
- bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op);
+ void clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op);
/**
* Modifies the current clip with the specified region and operation.
* The specified region is considered already transformed.
*/
- bool clipRegionTransformed(const SkRegion& region, SkRegion::Op op);
+ void clipRegionTransformed(const SkRegion& region, SkRegion::Op op);
/**
* Modifies the current clip with the specified path and operation.
*/
- bool clipPath(const SkPath& path, SkRegion::Op op);
+ void clipPath(const SkPath& path, SkRegion::Op op);
/**
* Sets the current clip.
@@ -230,7 +229,7 @@ public:
/**
* Previous snapshot.
*/
- sp<Snapshot> previous;
+ Snapshot* previous;
/**
* A pointer to the currently active layer.
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index b8c98041a6bf..bdce73c79993 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
// The highest z value can't be higher than (CASTER_Z_CAP_RATIO * light.z)
#define CASTER_Z_CAP_RATIO 0.95f
@@ -48,17 +46,18 @@
#define TRANSFORMED_PENUMBRA_ALPHA 1.0f
#define TRANSFORMED_UMBRA_ALPHA 0.0f
-#include <algorithm>
-#include <math.h>
-#include <stdlib.h>
-#include <utils/Log.h>
+#include "SpotShadow.h"
#include "ShadowTessellator.h"
-#include "SpotShadow.h"
#include "Vertex.h"
#include "VertexBuffer.h"
#include "utils/MathUtils.h"
+#include <algorithm>
+#include <math.h>
+#include <stdlib.h>
+#include <utils/Log.h>
+
// 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.
@@ -545,7 +544,7 @@ void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCente
}
float ratioVI = outlineData[i].radius / distOutline;
- minRaitoVI = MathUtils::min(minRaitoVI, ratioVI);
+ minRaitoVI = std::min(minRaitoVI, ratioVI);
if (ratioVI >= (1 - FAKE_UMBRA_SIZE_RATIO)) {
ratioVI = (1 - FAKE_UMBRA_SIZE_RATIO);
}
@@ -1052,7 +1051,7 @@ void SpotShadow::dumpPolygon(const Vector2* poly, int polyLength, const char* po
*/
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", polyName, i, poly[i].x, poly[i].y);
+ ALOGD("polygon %s i %d x %f y %f z %f", polyName, i, poly[i].x, poly[i].y, poly[i].z);
}
}
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 17cb3a7fd6fd..12a3e76c9387 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -225,13 +225,13 @@ static void tessellateShadows(
VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) {
// tessellate caster outline into a 2d polygon
- Vector<Vertex> casterVertices2d;
+ 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.editArray(), casterVertices2d.size());
+ reverseVertexArray(&casterVertices2d.front(), casterVertices2d.size());
if (casterVertices2d.size() == 0) return;
@@ -250,7 +250,7 @@ static void tessellateShadows(
// map the centroid of the caster into 3d
Vector2 centroid = ShadowTessellator::centroid2d(
- reinterpret_cast<const Vector2*>(casterVertices2d.array()),
+ reinterpret_cast<const Vector2*>(&casterVertices2d.front()),
casterVertexCount);
Vector3 centroid3d = {centroid.x, centroid.y, 0};
mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
@@ -312,10 +312,10 @@ TessellationCache::TessellationCache()
, mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
char property[PROPERTY_VALUE_MAX];
if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, nullptr) > 0) {
- INIT_LOGD(" Setting %s cache size to %sMB", name, property);
+ INIT_LOGD(" Setting tessellation cache size to %sMB", property);
setMaxSize(MB(atof(property)));
} else {
- INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_VERTEX_CACHE_SIZE);
+ INIT_LOGD(" Using default tessellation cache size of %.2fMB", DEFAULT_VERTEX_CACHE_SIZE);
}
mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
index 3efeaf64d486..b54666b7d595 100644
--- a/libs/hwui/TessellationCache.h
+++ b/libs/hwui/TessellationCache.h
@@ -19,7 +19,6 @@
#include <utils/LruCache.h>
#include <utils/Mutex.h>
-#include <utils/Vector.h>
#include "Debug.h"
#include "utils/Macros.h"
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 8b1d4cb2196c..b7a76baadff5 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <utils/JenkinsHash.h>
#include "Caches.h"
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 593e91818093..5195b457af2b 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <utils/Log.h>
#include "Caches.h"
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index fda009108aba..a6c72a380805 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -14,14 +14,8 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
#include <GLES2/gl2.h>
-#include <SkCanvas.h>
-#include <SkPixelRef.h>
-
#include <utils/Mutex.h>
#include "AssetAtlas.h"
@@ -172,7 +166,7 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a
if (canCache) {
texture = new Texture(Caches::getInstance());
texture->bitmapSize = size;
- generateTexture(bitmap, texture, false);
+ Caches::getInstance().textureState().generateTexture(bitmap, texture, false);
mSize += size;
TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
@@ -185,7 +179,7 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a
} 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
- generateTexture(bitmap, texture, true);
+ Caches::getInstance().textureState().generateTexture(bitmap, texture, true);
}
return texture;
@@ -210,7 +204,7 @@ Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType
const uint32_t size = bitmap->rowBytes() * bitmap->height();
texture = new Texture(Caches::getInstance());
texture->bitmapSize = size;
- generateTexture(bitmap, texture, false);
+ Caches::getInstance().textureState().generateTexture(bitmap, texture, false);
texture->cleanup = true;
}
@@ -219,14 +213,14 @@ Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType
void TextureCache::releaseTexture(uint32_t pixelRefStableID) {
Mutex::Autolock _l(mLock);
- mGarbage.push(pixelRefStableID);
+ mGarbage.push_back(pixelRefStableID);
}
void TextureCache::clearGarbage() {
Mutex::Autolock _l(mLock);
size_t count = mGarbage.size();
for (size_t i = 0; i < count; i++) {
- uint32_t pixelRefId = mGarbage.itemAt(i);
+ uint32_t pixelRefId = mGarbage[i];
mCache.remove(pixelRefId);
}
mGarbage.clear();
@@ -252,133 +246,5 @@ void TextureCache::flush() {
}
}
-void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate) {
- SkAutoLockPixels alp(*bitmap);
-
- if (!bitmap->readyToDraw()) {
- ALOGE("Cannot generate texture from bitmap");
- return;
- }
-
- 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 = Caches::getInstance().extensions().hasNPot();
-
- // If the texture had mipmap enabled but not anymore,
- // force a glTexImage2D to discard the mipmap levels
- const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
- bitmap->height() != int(texture->height) ||
- (regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap());
-
- if (!regenerate) {
- glGenTextures(1, &texture->id);
- }
-
- texture->generation = bitmap->getGenerationID();
- texture->width = bitmap->width();
- texture->height = bitmap->height();
-
- Caches::getInstance().textureState().bindTexture(texture->id);
-
- switch (bitmap->colorType()) {
- case kAlpha_8_SkColorType:
- uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
- texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
- texture->blend = true;
- break;
- case kRGB_565_SkColorType:
- uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
- texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
- texture->blend = false;
- break;
- case kN32_SkColorType:
- uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
- texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
- // Do this after calling getPixels() to make sure Skia's deferred
- // decoding happened
- texture->blend = !bitmap->isOpaque();
- break;
- case kARGB_4444_SkColorType:
- case kIndex_8_SkColorType:
- uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
- texture->blend = !bitmap->isOpaque();
- break;
- default:
- ALOGW("Unsupported bitmap colorType: %d", bitmap->colorType());
- break;
- }
-
- if (canMipMap) {
- texture->mipMap = bitmap->hasHardwareMipMap();
- if (texture->mipMap) {
- glGenerateMipmap(GL_TEXTURE_2D);
- }
- }
-
- if (!regenerate) {
- texture->setFilter(GL_NEAREST);
- texture->setWrap(GL_CLAMP_TO_EDGE);
- }
-}
-
-void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap,
- uint32_t width, uint32_t height) {
- SkBitmap rgbaBitmap;
- rgbaBitmap.allocPixels(SkImageInfo::MakeN32(width, height, bitmap->alphaType()));
- rgbaBitmap.eraseColor(0);
-
- SkCanvas canvas(rgbaBitmap);
- canvas.drawBitmap(*bitmap, 0.0f, 0.0f, nullptr);
-
- uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(),
- width, height, GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
-}
-
-void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
- GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
- glPixelStorei(GL_UNPACK_ALIGNMENT, bpp);
- 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, format, 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, format, width, height, 0, format, type, temp);
- } else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
- }
-
- free(temp);
- }
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 7a7ee5aeb554..191c8a81fec6 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -21,10 +21,11 @@
#include <utils/LruCache.h>
#include <utils/Mutex.h>
-#include <utils/Vector.h>
#include "Debug.h"
+#include <vector>
+
namespace android {
namespace uirenderer {
@@ -143,18 +144,6 @@ private:
Texture* get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType);
Texture* getCachedTexture(const SkBitmap* bitmap, AtlasUsageType atlasUsageType);
- /**
- * Generates the texture from a bitmap into the specified texture structure.
- *
- * @param regenerate If true, the bitmap data is reuploaded into the texture, but
- * no new texture is generated.
- */
- void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate = false);
-
- void uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height);
- void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
- GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
-
LruCache<uint32_t, Texture*> mCache;
uint32_t mSize;
@@ -165,7 +154,7 @@ private:
bool mDebugEnabled;
- Vector<uint32_t> mGarbage;
+ std::vector<uint32_t> mGarbage;
mutable Mutex mLock;
AssetAtlas* mAssetAtlas;
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index ed853f72539d..98e61468ea70 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -77,7 +77,7 @@ public:
, canvasContext(clone.canvasContext)
{}
- const TraversalMode mode;
+ TraversalMode mode;
// TODO: Remove this? Currently this is used to signal to stop preparing
// textures if we run out of cache space.
bool prepareTextures;
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index 11d0c4bef84f..c1bf980658b2 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -37,7 +37,6 @@ struct Vertex {
*/
static float GeometryFudgeFactor() { return 0.0656f; }
-
float x, y;
static inline void set(Vertex* vertex, float x, float y) {
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 9be4d8487505..c0373aceebba 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_HWUI_VERTEX_BUFFER_H
#define ANDROID_HWUI_VERTEX_BUFFER_H
-#include "utils/MathUtils.h"
+#include <algorithm>
namespace android {
namespace uirenderer {
@@ -129,10 +129,10 @@ public:
unsigned int getSize() const { return mByteCount; }
unsigned int getIndexCount() const { return mIndexCount; }
void updateIndexCount(unsigned int newCount) {
- mIndexCount = MathUtils::min(newCount, mAllocatedIndexCount);
+ mIndexCount = std::min(newCount, mAllocatedIndexCount);
}
void updateVertexCount(unsigned int newCount) {
- mVertexCount = MathUtils::min(newCount, mAllocatedVertexCount);
+ mVertexCount = std::min(newCount, mAllocatedVertexCount);
}
MeshFeatureFlags getMeshFeatureFlags() const { return mMeshFeatureFlags; }
void setMeshFeatureFlags(int flags) {
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 762f2bba3340..d680f990a0be 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -14,15 +14,12 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
#include <cutils/compiler.h>
#include <utils/JenkinsHash.h>
#include <utils/Trace.h>
-#include <SkDeviceProperties.h>
+#include <SkSurfaceProps.h>
#include <SkGlyph.h>
#include <SkGlyphCache.h>
#include <SkUtils.h>
@@ -282,8 +279,8 @@ CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bo
if (cachedGlyph) {
// Is the glyph still in texture cache?
if (!cachedGlyph->mIsValid) {
- SkDeviceProperties deviceProperties(kUnknown_SkPixelGeometry, 1.0f);
- SkAutoGlyphCache autoCache(*paint, &deviceProperties, &mDescription.mLookupTransform);
+ 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);
}
@@ -473,8 +470,8 @@ CachedGlyphInfo* Font::cacheGlyph(const SkPaint* paint, glyph_t glyph, bool prec
CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
mCachedGlyphs.add(glyph, newGlyph);
- SkDeviceProperties deviceProperties(kUnknown_SkPixelGeometry, 1.0f);
- SkAutoGlyphCache autoCache(*paint, &deviceProperties, &mDescription.mLookupTransform);
+ 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;
diff --git a/libs/hwui/microbench/DisplayListCanvasBench.cpp b/libs/hwui/microbench/DisplayListCanvasBench.cpp
new file mode 100644
index 000000000000..7a620379d7b7
--- /dev/null
+++ b/libs/hwui/microbench/DisplayListCanvasBench.cpp
@@ -0,0 +1,190 @@
+/*
+ * 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 "DisplayList.h"
+#if HWUI_NEW_OPS
+#include "RecordingCanvas.h"
+#else
+#include "DisplayListCanvas.h"
+#endif
+#include "microbench/MicroBench.h"
+#include "unit_tests/TestUtils.h"
+
+using namespace android;
+using namespace android::uirenderer;
+
+#if HWUI_NEW_OPS
+typedef RecordingCanvas TestCanvas;
+#else
+typedef DisplayListCanvas TestCanvas;
+#endif
+
+BENCHMARK_NO_ARG(BM_DisplayList_alloc);
+void BM_DisplayList_alloc::Run(int iters) {
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ auto displayList = new DisplayList();
+ MicroBench::DoNotOptimize(displayList);
+ delete displayList;
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_DisplayList_alloc_theoretical);
+void BM_DisplayList_alloc_theoretical::Run(int iters) {
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ auto displayList = new char[sizeof(DisplayList)];
+ MicroBench::DoNotOptimize(displayList);
+ delete[] displayList;
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_empty);
+void BM_DisplayListCanvas_record_empty::Run(int iters) {
+ TestCanvas canvas(100, 100);
+ delete canvas.finishRecording();
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ canvas.reset(100, 100);
+ MicroBench::DoNotOptimize(&canvas);
+ delete canvas.finishRecording();
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_saverestore);
+void BM_DisplayListCanvas_record_saverestore::Run(int iters) {
+ TestCanvas canvas(100, 100);
+ delete canvas.finishRecording();
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ canvas.reset(100, 100);
+ canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+ MicroBench::DoNotOptimize(&canvas);
+ canvas.restore();
+ canvas.restore();
+ delete canvas.finishRecording();
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_translate);
+void BM_DisplayListCanvas_record_translate::Run(int iters) {
+ TestCanvas canvas(100, 100);
+ delete canvas.finishRecording();
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ canvas.reset(100, 100);
+ canvas.scale(10, 10);
+ MicroBench::DoNotOptimize(&canvas);
+ delete canvas.finishRecording();
+ }
+ StopBenchmarkTiming();
+}
+
+/**
+ * Simulate a simple view drawing a background, overlapped by an image.
+ *
+ * Note that the recording commands are intentionally not perfectly efficient, as the
+ * View system frequently produces unneeded save/restores.
+ */
+BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_simpleBitmapView);
+void BM_DisplayListCanvas_record_simpleBitmapView::Run(int iters) {
+ TestCanvas canvas(100, 100);
+ delete canvas.finishRecording();
+
+ SkPaint rectPaint;
+ SkBitmap iconBitmap = TestUtils::createSkBitmap(80, 80);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ canvas.reset(100, 100);
+ {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.drawRect(0, 0, 100, 100, rectPaint);
+ canvas.restore();
+ }
+ {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(10, 10);
+ canvas.drawBitmap(iconBitmap, 0, 0, nullptr);
+ canvas.restore();
+ }
+ MicroBench::DoNotOptimize(&canvas);
+ delete canvas.finishRecording();
+ }
+ StopBenchmarkTiming();
+}
+
+class NullClient: public CanvasStateClient {
+ void onViewportInitialized() override {}
+ void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+ GLuint getTargetFbo() const override { return 0; }
+};
+
+BENCHMARK_NO_ARG(BM_CanvasState_saverestore);
+void BM_CanvasState_saverestore::Run(int iters) {
+ NullClient client;
+ CanvasState state(client);
+ state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ state.save(SkCanvas::kMatrixClip_SaveFlag);
+ state.save(SkCanvas::kMatrixClip_SaveFlag);
+ MicroBench::DoNotOptimize(&state);
+ state.restore();
+ state.restore();
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_CanvasState_init);
+void BM_CanvasState_init::Run(int iters) {
+ NullClient client;
+ CanvasState state(client);
+ state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
+ MicroBench::DoNotOptimize(&state);
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_CanvasState_translate);
+void BM_CanvasState_translate::Run(int iters) {
+ NullClient client;
+ CanvasState state(client);
+ state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; ++i) {
+ state.translate(5, 5, 0);
+ MicroBench::DoNotOptimize(&state);
+ state.translate(-5, -5, 0);
+ }
+ StopBenchmarkTiming();
+}
diff --git a/libs/hwui/microbench/LinearAllocatorBench.cpp b/libs/hwui/microbench/LinearAllocatorBench.cpp
new file mode 100644
index 000000000000..75f57cbd6021
--- /dev/null
+++ b/libs/hwui/microbench/LinearAllocatorBench.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "utils/LinearAllocator.h"
+#include "microbench/MicroBench.h"
+
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+
+BENCHMARK_NO_ARG(BM_LinearStdAllocator_vectorBaseline);
+void BM_LinearStdAllocator_vectorBaseline::Run(int iters) {
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ std::vector<char> v;
+ for (int j = 0; j < 200; j++) {
+ v.push_back(j);
+ }
+ MicroBench::DoNotOptimize(&v);
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_LinearStdAllocator_vector);
+void BM_LinearStdAllocator_vector::Run(int iters) {
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ LinearAllocator la;
+ LinearStdAllocator<void*> stdAllocator(la);
+ std::vector<char, LinearStdAllocator<char> > v(stdAllocator);
+ for (int j = 0; j < 200; j++) {
+ v.push_back(j);
+ }
+ MicroBench::DoNotOptimize(&v);
+ }
+ StopBenchmarkTiming();
+}
diff --git a/libs/hwui/microbench/MicroBench.h b/libs/hwui/microbench/MicroBench.h
new file mode 100644
index 000000000000..f05e92cd86d1
--- /dev/null
+++ b/libs/hwui/microbench/MicroBench.h
@@ -0,0 +1,35 @@
+/*
+ * 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 MICROBENCH_MICROBENCH_H
+#define MICROBENCH_MICROBENCH_H
+
+namespace android {
+namespace uirenderer {
+
+#define NO_INLINE __attribute__ ((noinline))
+
+class MicroBench {
+public:
+ template <class Tp>
+ static inline void DoNotOptimize(Tp const& value) {
+ asm volatile("" : "+rm" (const_cast<Tp&>(value)));
+ }
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* MICROBENCH_MICROBENCH_H */
diff --git a/libs/hwui/microbench/OpReordererBench.cpp b/libs/hwui/microbench/OpReordererBench.cpp
new file mode 100644
index 000000000000..4c8dedfa5a15
--- /dev/null
+++ b/libs/hwui/microbench/OpReordererBench.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "BakedOpState.h"
+#include "BakedOpRenderer.h"
+#include "OpReorderer.h"
+#include "RecordedOp.h"
+#include "RecordingCanvas.h"
+#include "unit_tests/TestUtils.h"
+#include "microbench/MicroBench.h"
+
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+
+auto sReorderingDisplayList = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(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(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ 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();
+});
+
+BENCHMARK_NO_ARG(BM_OpReorderer_defer);
+void BM_OpReorderer_defer::Run(int iters) {
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ OpReorderer reorderer;
+ reorderer.defer(200, 200, *sReorderingDisplayList);
+ MicroBench::DoNotOptimize(&reorderer);
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender);
+void BM_OpReorderer_deferAndRender::Run(int iters) {
+ TestUtils::runOnRenderThread([this, iters](RenderState& renderState, Caches& caches) {
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ OpReorderer reorderer;
+ reorderer.defer(200, 200, *sReorderingDisplayList);
+ MicroBench::DoNotOptimize(&reorderer);
+
+ BakedOpRenderer::Info info(caches, renderState, 200, 200, true);
+ reorderer.replayBakedOps<BakedOpRenderer>(&info);
+ }
+ StopBenchmarkTiming();
+ });
+}
diff --git a/libs/hwui/protos/ProtoHelpers.h b/libs/hwui/protos/ProtoHelpers.h
new file mode 100644
index 000000000000..832e31200eb6
--- /dev/null
+++ b/libs/hwui/protos/ProtoHelpers.h
@@ -0,0 +1,41 @@
+/*
+ * 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
new file mode 100644
index 000000000000..dcff80a24974
--- /dev/null
+++ b/libs/hwui/protos/hwui.proto
@@ -0,0 +1,99 @@
+/*
+ * 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
index 29927ed8667b..93f787d31745 100644
--- a/libs/hwui/renderstate/Blend.cpp
+++ b/libs/hwui/renderstate/Blend.cpp
@@ -30,6 +30,26 @@ struct Blender {
GLenum dst;
};
+// assumptions made by lookup tables in either this file or ProgramCache
+static_assert(0 == SkXfermode::kClear_Mode, "SkXfermode enums have changed");
+static_assert(1 == SkXfermode::kSrc_Mode, "SkXfermode enums have changed");
+static_assert(2 == SkXfermode::kDst_Mode, "SkXfermode enums have changed");
+static_assert(3 == SkXfermode::kSrcOver_Mode, "SkXfermode enums have changed");
+static_assert(4 == SkXfermode::kDstOver_Mode, "SkXfermode enums have changed");
+static_assert(5 == SkXfermode::kSrcIn_Mode, "SkXfermode enums have changed");
+static_assert(6 == SkXfermode::kDstIn_Mode, "SkXfermode enums have changed");
+static_assert(7 == SkXfermode::kSrcOut_Mode, "SkXfermode enums have changed");
+static_assert(8 == SkXfermode::kDstOut_Mode, "SkXfermode enums have changed");
+static_assert(9 == SkXfermode::kSrcATop_Mode, "SkXfermode enums have changed");
+static_assert(10 == SkXfermode::kDstATop_Mode, "SkXfermode enums have changed");
+static_assert(11 == SkXfermode::kXor_Mode, "SkXfermode enums have changed");
+static_assert(12 == SkXfermode::kPlus_Mode, "SkXfermode enums have changed");
+static_assert(13 == SkXfermode::kModulate_Mode, "SkXfermode enums have changed");
+static_assert(14 == SkXfermode::kScreen_Mode, "SkXfermode enums have changed");
+static_assert(15 == SkXfermode::kOverlay_Mode, "SkXfermode enums have changed");
+static_assert(16 == SkXfermode::kDarken_Mode, "SkXfermode enums have changed");
+static_assert(17 == SkXfermode::kLighten_Mode, "SkXfermode enums have changed");
+
// In this array, the index of each Blender equals the value of the first
// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
const Blender kBlends[] = {
@@ -78,20 +98,6 @@ Blend::Blend()
// gl blending off by default
}
-void Blend::enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage) {
- GLenum srcMode;
- GLenum dstMode;
- getFactors(mode, modeUsage, &srcMode, &dstMode);
- setFactors(srcMode, dstMode);
-}
-
-void Blend::disable() {
- if (mEnabled) {
- glDisable(GL_BLEND);
- mEnabled = false;
- }
-}
-
void Blend::invalidate() {
syncEnabled();
mSrcMode = mDstMode = GL_ZERO;
@@ -112,8 +118,13 @@ void Blend::getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage, GLenum* o
void Blend::setFactors(GLenum srcMode, GLenum dstMode) {
if (srcMode == GL_ZERO && dstMode == GL_ZERO) {
- disable();
+ // disable blending
+ if (mEnabled) {
+ glDisable(GL_BLEND);
+ mEnabled = false;
+ }
} else {
+ // enable blending
if (!mEnabled) {
glEnable(GL_BLEND);
mEnabled = true;
diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h
index dcc681d4aa50..df9e5a8af879 100644
--- a/libs/hwui/renderstate/Blend.h
+++ b/libs/hwui/renderstate/Blend.h
@@ -34,9 +34,6 @@ public:
NoSwap,
Swap,
};
-
- void enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage);
- void disable();
void syncEnabled();
static void getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage,
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 1e39bfa4b583..dfa70ace2f44 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -19,6 +19,8 @@
#include "renderthread/EglManager.h"
#include "utils/GLUtils.h"
+#include <algorithm>
+
namespace android {
namespace uirenderer {
@@ -206,7 +208,7 @@ void RenderState::postDecStrong(VirtualLightRefBase* object) {
// Render
///////////////////////////////////////////////////////////////////////////////
-void RenderState::render(const Glop& glop) {
+void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
const Glop::Mesh& mesh = glop.mesh;
const Glop::Mesh::Vertices& vertices = mesh.vertices;
const Glop::Mesh::Indices& indices = mesh.indices;
@@ -221,17 +223,17 @@ void RenderState::render(const Glop& glop) {
fill.program->setColor(fill.color);
}
- fill.program->set(glop.transform.ortho,
+ fill.program->set(orthoMatrix,
glop.transform.modelView,
glop.transform.meshTransform(),
glop.transform.transformFlags & TransformFlags::OffsetByFudgeFactor);
// Color filter uniforms
- if (fill.filterMode == ProgramDescription::kColorBlend) {
+ 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::kColorMatrix) {
+ } 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,
@@ -320,7 +322,7 @@ void RenderState::render(const Glop& glop) {
GLsizei elementsCount = mesh.elementCount;
const GLbyte* vertexData = static_cast<const GLbyte*>(vertices.position);
while (elementsCount > 0) {
- GLsizei drawCount = MathUtils::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
+ GLsizei drawCount = std::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
// rebind pointers without forcing, since initial bind handled above
meshState().bindPositionVertexPointer(false, vertexData, vertices.stride);
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 4fd792c1b503..9ae084506f1d 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -84,7 +84,7 @@ public:
// more thinking...
void postDecStrong(VirtualLightRefBase* object);
- void render(const Glop& glop);
+ void render(const Glop& glop, const Matrix4& orthoMatrix);
AssetAtlas& assetAtlas() { return mAssetAtlas; }
Blend& blend() { return *mBlend; }
diff --git a/libs/hwui/renderstate/Stencil.cpp b/libs/hwui/renderstate/Stencil.cpp
index 319cfe4ba0d0..d25ad514e892 100644
--- a/libs/hwui/renderstate/Stencil.cpp
+++ b/libs/hwui/renderstate/Stencil.cpp
@@ -34,10 +34,6 @@ namespace uirenderer {
#define STENCIL_MASK_VALUE 0x1
#endif
-Stencil::Stencil()
- : mState(kDisabled) {
-}
-
uint8_t Stencil::getStencilSize() {
return STENCIL_BUFFER_SIZE;
}
@@ -64,14 +60,14 @@ void Stencil::clear() {
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
- if (mState == kTest) {
+ if (mState == StencilState::Test) {
// reset to test state, with immutable stencil
glStencilMask(0);
}
}
void Stencil::enableTest(int incrementThreshold) {
- if (mState != kTest) {
+ if (mState != StencilState::Test) {
enable();
if (incrementThreshold > 0) {
glStencilFunc(GL_EQUAL, incrementThreshold, 0xff);
@@ -82,12 +78,12 @@ void Stencil::enableTest(int incrementThreshold) {
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glStencilMask(0);
- mState = kTest;
+ mState = StencilState::Test;
}
}
void Stencil::enableWrite(int incrementThreshold) {
- if (mState != kWrite) {
+ if (mState != StencilState::Write) {
enable();
if (incrementThreshold > 0) {
glStencilFunc(GL_ALWAYS, 1, 0xff);
@@ -100,7 +96,7 @@ void Stencil::enableWrite(int incrementThreshold) {
}
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glStencilMask(0xff);
- mState = kWrite;
+ mState = StencilState::Write;
}
}
@@ -109,7 +105,7 @@ void Stencil::enableDebugTest(GLint value, bool greater) {
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 = kTest;
+ mState = StencilState::Test;
glStencilMask(0);
}
@@ -119,20 +115,20 @@ void Stencil::enableDebugWrite() {
// 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 = kWrite;
+ mState = StencilState::Write;
glStencilMask(0xff);
}
void Stencil::enable() {
- if (mState == kDisabled) {
+ if (mState == StencilState::Disabled) {
glEnable(GL_STENCIL_TEST);
}
}
void Stencil::disable() {
- if (mState != kDisabled) {
+ if (mState != StencilState::Disabled) {
glDisable(GL_STENCIL_TEST);
- mState = kDisabled;
+ mState = StencilState::Disabled;
}
}
diff --git a/libs/hwui/renderstate/Stencil.h b/libs/hwui/renderstate/Stencil.h
index 3a8f8ebad48d..5f7d4056b51d 100644
--- a/libs/hwui/renderstate/Stencil.h
+++ b/libs/hwui/renderstate/Stencil.h
@@ -17,10 +17,6 @@
#ifndef ANDROID_HWUI_STENCIL_H
#define ANDROID_HWUI_STENCIL_H
-#ifndef LOG_TAG
- #define LOG_TAG "OpenGLRenderer"
-#endif
-
#include <GLES2/gl2.h>
#include <cutils/compiler.h>
@@ -34,8 +30,6 @@ namespace uirenderer {
class ANDROID_API Stencil {
public:
- Stencil();
-
/**
* Returns the desired size for the stencil buffer. If the returned value
* is 0, then no stencil buffer is required.
@@ -85,32 +79,31 @@ public:
* Indicates whether either test or write is enabled.
*/
bool isEnabled() {
- return mState != kDisabled;
+ return mState != StencilState::Disabled;
}
/**
* Indicates whether testing only is enabled.
*/
bool isTestEnabled() {
- return mState == kTest;
+ return mState == StencilState::Test;
}
bool isWriteEnabled() {
- return mState == kWrite;
+ return mState == StencilState::Write;
}
void dump();
private:
- void enable();
-
- enum StencilState {
- kDisabled,
- kTest,
- kWrite
+ enum class StencilState {
+ Disabled,
+ Test,
+ Write
};
- StencilState mState;
+ void enable();
+ StencilState mState = StencilState::Disabled;
}; // class Stencil
diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp
index 987d4cd55a5e..1f50f712c267 100644
--- a/libs/hwui/renderstate/TextureState.cpp
+++ b/libs/hwui/renderstate/TextureState.cpp
@@ -15,6 +15,14 @@
*/
#include "renderstate/TextureState.h"
+#include "Caches.h"
+#include "utils/TraceUtils.h"
+
+#include <GLES3/gl3.h>
+#include <memory>
+#include <SkCanvas.h>
+#include <SkBitmap.h>
+
namespace android {
namespace uirenderer {
@@ -26,6 +34,134 @@ const GLenum kTextureUnits[] = {
GL_TEXTURE3
};
+static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp,
+ GLsizei width, GLsizei height, const GLvoid * data) {
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, bpp);
+ 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, format, 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, format, width, height, 0, format, type, temp);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
+ }
+
+ free(temp);
+ }
+}
+
+static void uploadSkBitmapToTexture(const SkBitmap& bitmap,
+ bool resize, GLenum format, GLenum type) {
+ uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(),
+ bitmap.width(), bitmap.height(), bitmap.getPixels());
+}
+
+void TextureState::generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate) {
+ SkAutoLockPixels alp(*bitmap);
+
+ if (!bitmap->readyToDraw()) {
+ ALOGE("Cannot generate texture from bitmap");
+ return;
+ }
+
+ 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 = Caches::getInstance().extensions().hasNPot();
+
+ // If the texture had mipmap enabled but not anymore,
+ // force a glTexImage2D to discard the mipmap levels
+ const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
+ bitmap->height() != int(texture->height) ||
+ (regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap());
+
+ if (!regenerate) {
+ glGenTextures(1, &texture->id);
+ }
+
+ texture->generation = bitmap->getGenerationID();
+ texture->width = bitmap->width();
+ texture->height = bitmap->height();
+
+ bindTexture(texture->id);
+
+ switch (bitmap->colorType()) {
+ case kAlpha_8_SkColorType:
+ uploadSkBitmapToTexture(*bitmap, resize, GL_ALPHA, GL_UNSIGNED_BYTE);
+ texture->blend = true;
+ break;
+ case kRGB_565_SkColorType:
+ uploadSkBitmapToTexture(*bitmap, resize, GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
+ texture->blend = false;
+ break;
+ case kN32_SkColorType:
+ uploadSkBitmapToTexture(*bitmap, resize, GL_RGBA, GL_UNSIGNED_BYTE);
+ // Do this after calling getPixels() to make sure Skia's deferred
+ // decoding happened
+ texture->blend = !bitmap->isOpaque();
+ break;
+ case kARGB_4444_SkColorType:
+ case kIndex_8_SkColorType: {
+ SkBitmap rgbaBitmap;
+ rgbaBitmap.allocPixels(SkImageInfo::MakeN32(texture->width, texture->height,
+ bitmap->alphaType()));
+ rgbaBitmap.eraseColor(0);
+
+ SkCanvas canvas(rgbaBitmap);
+ canvas.drawBitmap(*bitmap, 0.0f, 0.0f, nullptr);
+
+ uploadSkBitmapToTexture(rgbaBitmap, resize, GL_RGBA, GL_UNSIGNED_BYTE);
+ texture->blend = !bitmap->isOpaque();
+ break;
+ }
+ default:
+ ALOGW("Unsupported bitmap colorType: %d", bitmap->colorType());
+ break;
+ }
+
+ if (canMipMap) {
+ texture->mipMap = bitmap->hasHardwareMipMap();
+ if (texture->mipMap) {
+ glGenerateMipmap(GL_TEXTURE_2D);
+ }
+ }
+
+ if (!regenerate) {
+ texture->setFilter(GL_NEAREST);
+ texture->setWrap(GL_CLAMP_TO_EDGE);
+ }
+}
+
TextureState::TextureState()
: mTextureUnit(0) {
glActiveTexture(kTextureUnits[0]);
diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h
index d3c014c00bce..3a2b85ae2886 100644
--- a/libs/hwui/renderstate/TextureState.h
+++ b/libs/hwui/renderstate/TextureState.h
@@ -23,9 +23,13 @@
#include <SkXfermode.h>
#include <memory>
+class SkBitmap;
+
namespace android {
namespace uirenderer {
+class Texture;
+
class TextureState {
friend class Caches; // TODO: move to RenderState
public:
@@ -71,6 +75,14 @@ public:
* Clear the cache of bound textures.
*/
void unbindTexture(GLuint texture);
+
+ /**
+ * Generates the texture from a bitmap into the specified texture structure.
+ *
+ * @param regenerate If true, the bitmap data is reuploaded into the texture, but
+ * no new texture is generated.
+ */
+ void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate);
private:
// total number of texture units available for use
static const int kTextureUnitsCount = 4;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 6dfb6e811e60..238cf06e77f6 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -26,15 +26,35 @@
#include "RenderThread.h"
#include "renderstate/RenderState.h"
#include "renderstate/Stencil.h"
+#include "protos/hwui.pb.h"
+
+#if HWUI_NEW_OPS
+#include "BakedOpRenderer.h"
+#include "OpReorderer.h"
+#endif
-#include <algorithm>
-#include <strings.h>
#include <cutils/properties.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <private/hwui/DrawGlInfo.h>
+#include <strings.h>
+
+#include <algorithm>
+#include <fcntl.h>
+#include <sys/stat.h>
#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
+static float sBenchMma = 0;
+static int sFrameCount = 0;
+static const float NANOS_PER_MILLIS_F = 1000000.0f;
+#endif
+
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -45,9 +65,10 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
, mEglManager(thread.eglManager())
, mOpaque(!translucent)
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
- , mRootRenderNode(rootRenderNode)
, mJankTracker(thread.timeLord().frameIntervalNanos())
- , mProfiler(mFrames) {
+ , mProfiler(mFrames)
+ , mContentDrawBounds(0, 0, 0, 0) {
+ mRenderNodes.emplace_back(rootRenderNode);
mRenderThread.renderState().registerCanvasContext(this);
mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
}
@@ -93,13 +114,6 @@ void CanvasContext::setSurface(ANativeWindow* window) {
}
}
-void CanvasContext::swapBuffers(const SkRect& dirty, EGLint width, EGLint height) {
- if (CC_UNLIKELY(!mEglManager.swapBuffers(mEglSurface, dirty, width, height))) {
- setSurface(nullptr);
- }
- mHaveNewSurface = false;
-}
-
void CanvasContext::requireSurface() {
LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
"requireSurface() called but no surface set!");
@@ -112,9 +126,11 @@ void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
bool CanvasContext::initialize(ANativeWindow* window) {
setSurface(window);
+#if !HWUI_NEW_OPS
if (mCanvas) return false;
mCanvas = new OpenGLRenderer(mRenderThread.renderState());
mCanvas->initProperties();
+#endif
return true;
}
@@ -153,18 +169,21 @@ void CanvasContext::makeCurrent() {
}
void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
+#if !HWUI_NEW_OPS
bool success = layerUpdater->apply();
LOG_ALWAYS_FATAL_IF(!success, "Failed to update layer!");
if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
}
+#endif
}
static bool wasSkipped(FrameInfo* info) {
return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
}
-void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued) {
+void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
+ int64_t syncQueued, RenderNode* target) {
mRenderThread.removeFrameCallback(this);
// If the previous frame was dropped we don't need to hold onto it, so
@@ -181,7 +200,13 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
info.canvasContext = this;
mAnimationContext->startFrame(info.mode);
- mRootRenderNode->prepareTree(info);
+ for (const sp<RenderNode>& node : mRenderNodes) {
+ // Only the primary target node will be drawn full - all other nodes would get drawn in
+ // real time mode. In case of a window, the primary node is the window content and the other
+ // node(s) are non client / filler nodes.
+ info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY);
+ node->prepareTree(info);
+ }
mAnimationContext->runRemainingAnimations(info);
freePrefetechedLayers();
@@ -223,8 +248,10 @@ void CanvasContext::notifyFramePending() {
}
void CanvasContext::draw() {
+#if !HWUI_NEW_OPS
LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
"drawRenderNode called on a context with no canvas or surface!");
+#endif
SkRect dirty;
mDamageAccumulator.finish(&dirty);
@@ -237,46 +264,210 @@ void CanvasContext::draw() {
mCurrentFrameInfo->markIssueDrawCommandsStart();
- EGLint width, height;
- mEglManager.beginFrame(mEglSurface, &width, &height);
- if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
- mCanvas->setViewport(width, height);
+ Frame frame = mEglManager.beginFrame(mEglSurface);
+
+ if (frame.width() != lastFrameWidth || frame.height() != lastFrameHeight) {
+ // can't rely on prior content of window if viewport size changes
dirty.setEmpty();
- } else if (!mBufferPreserved || mHaveNewSurface) {
+ lastFrameWidth = frame.width();
+ lastFrameHeight = frame.height();
+ } else if (mHaveNewSurface || frame.bufferAge() == 0) {
+ // New surface needs a full draw
dirty.setEmpty();
} else {
- if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
+ if (!dirty.isEmpty() && !dirty.intersect(0, 0, frame.width(), frame.height())) {
ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
- SK_RECT_ARGS(dirty), width, height);
+ SK_RECT_ARGS(dirty), frame.width(), frame.height());
dirty.setEmpty();
}
profiler().unionDirty(&dirty);
}
- if (!dirty.isEmpty()) {
- mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
- dirty.fRight, dirty.fBottom, mOpaque);
- } else {
- mCanvas->prepare(mOpaque);
+ if (dirty.isEmpty()) {
+ dirty.set(0, 0, frame.width(), frame.height());
}
+ // At this point dirty is the area of the screen to update. However,
+ // the area of the frame we need to repaint is potentially different, so
+ // stash the screen area for later
+ SkRect screenDirty(dirty);
+
+ // If the buffer age is 0 we do a full-screen repaint (handled above)
+ // If the buffer age is 1 the buffer contents are the same as they were
+ // last frame so there's nothing to union() against
+ // Therefore we only care about the > 1 case.
+ if (frame.bufferAge() > 1) {
+ if (frame.bufferAge() > (int) mDamageHistory.size()) {
+ // We don't have enough history to handle this old of a buffer
+ // Just do a full-draw
+ dirty.set(0, 0, frame.width(), frame.height());
+ } else {
+ // At this point we haven't yet added the latest frame
+ // to the damage history (happens below)
+ // So we need to damage
+ for (int i = mDamageHistory.size() - 1;
+ i > ((int) mDamageHistory.size()) - frame.bufferAge(); i--) {
+ dirty.join(mDamageHistory[i]);
+ }
+ }
+ }
+
+ // Add the screen damage to the ring buffer.
+ mDamageHistory.next() = screenDirty;
+
+ mEglManager.damageFrame(frame, dirty);
+
+#if HWUI_NEW_OPS
+ OpReorderer reorderer;
+ reorderer.defer(dirty, frame.width(), frame.height(), mRenderNodes);
+ BakedOpRenderer::Info info(Caches::getInstance(), mRenderThread.renderState(),
+ frame.width(), frame.height(), mOpaque);
+ // TODO: profiler().draw(mCanvas);
+ reorderer.replayBakedOps<BakedOpRenderer>(&info);
+
+ bool drew = info.didDraw;
+
+#else
+ mCanvas->prepareDirty(frame.width(), frame.height(),
+ dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque);
+
Rect outBounds;
- mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
+ // It there are multiple render nodes, they are as follows:
+ // #0 - backdrop
+ // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
+ // #2 - frame
+ // 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 crop the content
+ // against the backdrop (since that indicates a shrinking of the window) and then the frame
+ // around everything.
+ // The bounds of the backdrop against which the content should be clipped.
+ Rect backdropBounds = mContentDrawBounds;
+ // Usually the contents bounds should be mContentDrawBounds - however - we will
+ // move it towards the fixed edge to give it a more stable appearance (for the moment).
+ Rect contentBounds;
+ // If there is no content bounds we ignore the layering as stated above and start with 2.
+ int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() <= 2) ? 2 : 0;
+ // Draw all render nodes. Note that
+ for (const sp<RenderNode>& node : mRenderNodes) {
+ if (layer == 0) { // Backdrop.
+ // Draw the backdrop clipped to the inverse content bounds, but assume that the content
+ // was moved to the upper left corner.
+ const RenderProperties& properties = node->properties();
+ Rect targetBounds(properties.getLeft(), properties.getTop(),
+ properties.getRight(), properties.getBottom());
+ // Move the content bounds towards the fixed corner of the backdrop.
+ const int x = targetBounds.left;
+ const int y = targetBounds.top;
+ contentBounds.set(x, y, x + mContentDrawBounds.getWidth(),
+ y + mContentDrawBounds.getHeight());
+ // Remember the intersection of the target bounds and the intersection bounds against
+ // which we have to crop the content.
+ backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
+ backdropBounds.doIntersect(targetBounds);
+ // Check if we have to draw something on the left side ...
+ if (targetBounds.left < contentBounds.left) {
+ mCanvas->save(SkCanvas::kClip_SaveFlag);
+ if (mCanvas->clipRect(targetBounds.left, targetBounds.top,
+ contentBounds.left, targetBounds.bottom,
+ SkRegion::kIntersect_Op)) {
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ // Reduce the target area by the area we have just painted.
+ targetBounds.left = std::min(contentBounds.left, targetBounds.right);
+ mCanvas->restore();
+ }
+ // ... or on the right side ...
+ if (targetBounds.right > contentBounds.right &&
+ !targetBounds.isEmpty()) {
+ mCanvas->save(SkCanvas::kClip_SaveFlag);
+ if (mCanvas->clipRect(contentBounds.right, targetBounds.top,
+ targetBounds.right, targetBounds.bottom,
+ SkRegion::kIntersect_Op)) {
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ // Reduce the target area by the area we have just painted.
+ targetBounds.right = std::max(targetBounds.left, contentBounds.right);
+ mCanvas->restore();
+ }
+ // ... or at the top ...
+ if (targetBounds.top < contentBounds.top &&
+ !targetBounds.isEmpty()) {
+ mCanvas->save(SkCanvas::kClip_SaveFlag);
+ if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right,
+ contentBounds.top,
+ SkRegion::kIntersect_Op)) {
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ // Reduce the target area by the area we have just painted.
+ targetBounds.top = std::min(contentBounds.top, targetBounds.bottom);
+ mCanvas->restore();
+ }
+ // ... or at the bottom.
+ if (targetBounds.bottom > contentBounds.bottom &&
+ !targetBounds.isEmpty()) {
+ mCanvas->save(SkCanvas::kClip_SaveFlag);
+ if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right,
+ targetBounds.bottom, SkRegion::kIntersect_Op)) {
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ mCanvas->restore();
+ }
+ } else if (layer == 1) { // Content
+ // It gets cropped against the bounds of the backdrop to stay inside.
+ mCanvas->save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+
+ // We shift and clip the content to match its final location in the window.
+ const float left = mContentDrawBounds.left;
+ const float top = mContentDrawBounds.top;
+ const float dx = backdropBounds.left - left;
+ const float dy = backdropBounds.top - top;
+ const float width = backdropBounds.getWidth();
+ const float height = backdropBounds.getHeight();
+ mCanvas->translate(dx, dy);
+ if (mCanvas->clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op)) {
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ mCanvas->restore();
+ } else { // draw the rest on top at will!
+ mCanvas->drawRenderNode(node.get(), outBounds);
+ }
+ layer++;
+ }
profiler().draw(mCanvas);
bool drew = mCanvas->finish();
-
+#endif
// Even if we decided to cancel the frame, from the perspective of jank
// metrics the frame was swapped at this point
mCurrentFrameInfo->markSwapBuffers();
if (drew) {
- swapBuffers(dirty, width, height);
+ if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) {
+ setSurface(nullptr);
+ }
+ mHaveNewSurface = false;
}
// TODO: Use a fence for real completion?
mCurrentFrameInfo->markFrameCompleted();
+
+#if LOG_FRAMETIME_MMA
+ float thisFrame = mCurrentFrameInfo->duration(
+ FrameInfoIndex::IssueDrawCommandsStart,
+ FrameInfoIndex::FrameCompleted) / NANOS_PER_MILLIS_F;
+ if (sFrameCount) {
+ sBenchMma = ((9 * sBenchMma) + thisFrame) / 10;
+ } else {
+ sBenchMma = thisFrame;
+ }
+ if (++sFrameCount == 10) {
+ sFrameCount = 1;
+ ALOGD("Average frame time: %.4f", sBenchMma);
+ }
+#endif
+
mJankTracker.addFrame(*mCurrentFrameInfo);
mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
}
@@ -286,7 +477,10 @@ void CanvasContext::doFrame() {
if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
return;
}
+ prepareAndDraw(nullptr);
+}
+void CanvasContext::prepareAndDraw(RenderNode* node) {
ATRACE_CALL();
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
@@ -296,7 +490,7 @@ void CanvasContext::doFrame() {
mRenderThread.timeLord().latestVsync());
TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
- prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC));
+ prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node);
if (info.out.canDrawThisFrame) {
draw();
}
@@ -366,12 +560,14 @@ void CanvasContext::destroyHardwareResources() {
stopDrawing();
if (mEglManager.hasEglContext()) {
freePrefetechedLayers();
- mRootRenderNode->destroyHardwareResources();
+ for (const sp<RenderNode>& node : mRenderNodes) {
+ node->destroyHardwareResources();
+ }
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);
- caches.flush(Caches::kFlushMode_Layers);
+ caches.flush(Caches::FlushMode::Layers);
}
}
@@ -381,10 +577,10 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) {
ATRACE_CALL();
if (level >= TRIM_MEMORY_COMPLETE) {
- Caches::getInstance().flush(Caches::kFlushMode_Full);
+ Caches::getInstance().flush(Caches::FlushMode::Full);
thread.eglManager().destroy();
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
- Caches::getInstance().flush(Caches::kFlushMode_Moderate);
+ Caches::getInstance().flush(Caches::FlushMode::Moderate);
}
}
@@ -430,6 +626,40 @@ void CanvasContext::resetFrameStats() {
mRenderThread.jankTracker().reset();
}
+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
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index f2fa9cdcbefd..16956e6f1e47 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -31,10 +31,10 @@
#include <SkBitmap.h>
#include <SkRect.h>
#include <utils/Functor.h>
-#include <utils/Vector.h>
#include <set>
#include <string>
+#include <vector>
namespace android {
namespace uirenderer {
@@ -78,12 +78,14 @@ public:
void setOpaque(bool opaque);
void makeCurrent();
void processLayerUpdate(DeferredLayerUpdater* layerUpdater);
- void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued);
+ void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
+ int64_t syncQueued, RenderNode* target);
void draw();
void destroy();
// IFrameCallback, Chroreographer-driven frame callback entry point
virtual void doFrame() override;
+ void prepareAndDraw(RenderNode* node);
void buildLayer(RenderNode* node);
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
@@ -112,6 +114,22 @@ public:
void setName(const std::string&& name) { mName = name; }
const std::string& name() { return mName; }
+ void serializeDisplayListTree();
+
+ void addRenderNode(RenderNode* node, bool placeFront) {
+ int pos = placeFront ? 0 : static_cast<int>(mRenderNodes.size());
+ mRenderNodes.emplace( mRenderNodes.begin() + pos, node);
+ }
+
+ void removeRenderNode(RenderNode* node) {
+ mRenderNodes.erase(std::remove(mRenderNodes.begin(), mRenderNodes.end(), node),
+ mRenderNodes.end());
+ }
+
+ void setContentDrawBounds(int left, int top, int right, int bottom) {
+ mContentDrawBounds.set(left, top, right, bottom);
+ }
+
private:
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
@@ -119,17 +137,20 @@ private:
friend class android::uirenderer::RenderState;
void setSurface(ANativeWindow* window);
- void swapBuffers(const SkRect& dirty, EGLint width, EGLint height);
void requireSurface();
void freePrefetechedLayers();
+ int lastFrameWidth = 0;
+ int lastFrameHeight = 0;
+
RenderThread& mRenderThread;
EglManager& mEglManager;
sp<ANativeWindow> mNativeWindow;
EGLSurface mEglSurface = EGL_NO_SURFACE;
bool mBufferPreserved = false;
SwapBehavior mSwapBehavior = kSwap_default;
+ RingBuffer<SkRect, 3> mDamageHistory;
bool mOpaque;
OpenGLRenderer* mCanvas = nullptr;
@@ -137,7 +158,7 @@ private:
DamageAccumulator mDamageAccumulator;
std::unique_ptr<AnimationContext> mAnimationContext;
- const sp<RenderNode> mRootRenderNode;
+ std::vector< sp<RenderNode> > mRenderNodes;
FrameInfo* mCurrentFrameInfo = nullptr;
// Ring buffer large enough for 2 seconds worth of frames
@@ -147,6 +168,9 @@ private:
FrameInfoVisualizer mProfiler;
std::set<RenderNode*> mPrefetechedLayers;
+
+ // Stores the bounds of the main content.
+ Rect mContentDrawBounds;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index a4ac13bb95a1..a47c9ecf8b31 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
#include "DrawFrameTask.h"
#include <utils/Log.h>
@@ -40,9 +38,11 @@ DrawFrameTask::DrawFrameTask()
DrawFrameTask::~DrawFrameTask() {
}
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
+ RenderNode* targetNode) {
mRenderThread = thread;
mContext = context;
+ mTargetNode = targetNode;
}
void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -120,7 +120,7 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) {
mContext->processLayerUpdate(mLayers[i].get());
}
mLayers.clear();
- mContext->prepareTree(info, mFrameInfo, mSyncQueued);
+ mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
// This is after the prepareTree so that any pending operations
// (RenderNode tree state, prefetched layers, etc...) will be flushed.
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index ebefcba9f6a5..cae251a980df 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -32,7 +32,7 @@ namespace android {
namespace uirenderer {
class DeferredLayerUpdater;
-class DisplayListData;
+class DisplayList;
class RenderNode;
namespace renderthread {
@@ -48,7 +48,7 @@ enum SyncResult {
/*
* This is a special Super Task. It is re-used multiple times by RenderProxy,
- * and contains state (such as layer updaters & new DisplayListDatas) that is
+ * and contains state (such as layer updaters & new DisplayLists) that is
* tracked across many frames not just a single frame.
* It is the sync-state task, and will kick off the post-sync draw
*/
@@ -57,7 +57,7 @@ public:
DrawFrameTask();
virtual ~DrawFrameTask();
- void setContext(RenderThread* thread, CanvasContext* context);
+ void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
void pushLayerUpdate(DeferredLayerUpdater* layer);
void removeLayerUpdate(DeferredLayerUpdater* layer);
@@ -78,6 +78,7 @@ private:
RenderThread* mRenderThread;
CanvasContext* mContext;
+ RenderNode* mTargetNode = nullptr;
/*********************************************
* Single frame data
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index eb332d59fee3..c9b9637dd52e 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -20,12 +20,14 @@
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
+#include "utils/StringUtils.h"
#include <cutils/log.h>
#include <cutils/properties.h>
#include <EGL/eglext.h>
-#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
+#include <string>
+
#define GLES_VERSION 2
#define WAIT_FOR_GPU_COMPLETION 0
@@ -63,10 +65,27 @@ static const char* egl_error_str() {
return egl_error_str(eglGetError());
}
-static bool load_dirty_regions_property() {
- char buf[PROPERTY_VALUE_MAX];
- int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
- return !strncasecmp("true", buf, len);
+static struct {
+ bool bufferAge = false;
+ bool setDamage = false;
+} EglExtensions;
+
+void Frame::map(const SkRect& in, EGLint* out) const {
+ /* The rectangles are specified relative to the bottom-left of the surface
+ * and the x and y components of each rectangle specify the bottom-left
+ * position of that rectangle.
+ *
+ * HWUI does everything with 0,0 being top-left, so need to map
+ * the rect
+ */
+ SkIRect idirty;
+ in.roundOut(&idirty);
+ EGLint y = mHeight - (idirty.y() + idirty.height());
+ // layout: {x, y, width, height}
+ out[0] = idirty.x();
+ out[1] = y;
+ out[2] = idirty.width();
+ out[3] = idirty.height();
}
EglManager::EglManager(RenderThread& thread)
@@ -75,12 +94,9 @@ EglManager::EglManager(RenderThread& thread)
, mEglConfig(nullptr)
, mEglContext(EGL_NO_CONTEXT)
, mPBufferSurface(EGL_NO_SURFACE)
- , mAllowPreserveBuffer(load_dirty_regions_property())
, mCurrentSurface(EGL_NO_SURFACE)
, mAtlasMap(nullptr)
, mAtlasMapSize(0) {
- mCanSetPreserveBuffer = mAllowPreserveBuffer;
- ALOGD("Use EGL_SWAP_BEHAVIOR_PRESERVED: %s", mAllowPreserveBuffer ? "true" : "false");
}
void EglManager::initialize() {
@@ -98,6 +114,17 @@ void EglManager::initialize() {
ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
+ initExtensions();
+
+ // Now that extensions are loaded, pick a swap behavior
+ if (Properties::enablePartialUpdates) {
+ if (Properties::useBufferAge && EglExtensions.bufferAge) {
+ mSwapBehavior = SwapBehavior::BufferAge;
+ } else {
+ mSwapBehavior = SwapBehavior::Preserved;
+ }
+ }
+
loadConfig();
createContext();
createPBufferSurface();
@@ -106,12 +133,20 @@ void EglManager::initialize() {
initAtlas();
}
+void EglManager::initExtensions() {
+ StringCollection extensions(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
+ EglExtensions.bufferAge = extensions.has("EGL_EXT_buffer_age");
+ EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update");
+}
+
bool EglManager::hasEglContext() {
return mEglDisplay != EGL_NO_DISPLAY;
}
void EglManager::loadConfig() {
- EGLint swapBehavior = mCanSetPreserveBuffer ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+ ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
+ EGLint swapBehavior = (mSwapBehavior == SwapBehavior::Preserved)
+ ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
EGLint attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
@@ -128,13 +163,13 @@ void EglManager::loadConfig() {
EGLint num_configs = 1;
if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
|| num_configs != 1) {
- // Failed to get a valid config
- if (mCanSetPreserveBuffer) {
- ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
+ if (mSwapBehavior == SwapBehavior::Preserved) {
// Try again without dirty regions enabled
- mCanSetPreserveBuffer = false;
+ ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
+ mSwapBehavior = SwapBehavior::Discard;
loadConfig();
} else {
+ // Failed to get a valid config
LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
}
}
@@ -238,21 +273,47 @@ bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
return true;
}
-void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
+EGLint EglManager::queryBufferAge(EGLSurface surface) {
+ switch (mSwapBehavior) {
+ case SwapBehavior::Discard:
+ return 0;
+ case SwapBehavior::Preserved:
+ return 1;
+ case SwapBehavior::BufferAge:
+ EGLint bufferAge;
+ eglQuerySurface(mEglDisplay, surface, EGL_BUFFER_AGE_EXT, &bufferAge);
+ return bufferAge;
+ }
+ return 0;
+}
+
+Frame EglManager::beginFrame(EGLSurface surface) {
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
"Tried to beginFrame on EGL_NO_SURFACE!");
makeCurrent(surface);
- if (width) {
- eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
- }
- if (height) {
- eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
- }
+ Frame frame;
+ frame.mSurface = surface;
+ eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, &frame.mWidth);
+ eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, &frame.mHeight);
+ frame.mBufferAge = queryBufferAge(surface);
eglBeginFrame(mEglDisplay, surface);
+ return frame;
}
-bool EglManager::swapBuffers(EGLSurface surface, const SkRect& dirty,
- EGLint width, EGLint height) {
+void EglManager::damageFrame(const Frame& frame, const SkRect& dirty) {
+#ifdef EGL_KHR_partial_update
+ if (EglExtensions.setDamage && mSwapBehavior == SwapBehavior::BufferAge) {
+ EGLint rects[4];
+ frame.map(dirty, rects);
+ if (!eglSetDamageRegionKHR(mEglDisplay, frame.mSurface, rects, 1)) {
+ LOG_ALWAYS_FATAL("Failed to set damage region on surface %p, error=%s",
+ (void*)frame.mSurface, egl_error_str());
+ }
+ }
+#endif
+}
+
+bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) {
#if WAIT_FOR_GPU_COMPLETION
{
@@ -263,28 +324,15 @@ bool EglManager::swapBuffers(EGLSurface surface, const SkRect& dirty,
#ifdef EGL_KHR_swap_buffers_with_damage
if (CC_LIKELY(Properties::swapBuffersWithDamage)) {
- SkIRect idirty;
- dirty.roundOut(&idirty);
- /*
- * EGL_KHR_swap_buffers_with_damage spec states:
- *
- * The rectangles are specified relative to the bottom-left of the surface
- * and the x and y components of each rectangle specify the bottom-left
- * position of that rectangle.
- *
- * HWUI does everything with 0,0 being top-left, so need to map
- * the rect
- */
- EGLint y = height - (idirty.y() + idirty.height());
- // layout: {x, y, width, height}
- EGLint rects[4] = { idirty.x(), y, idirty.width(), idirty.height() };
- EGLint numrects = dirty.isEmpty() ? 0 : 1;
- eglSwapBuffersWithDamageKHR(mEglDisplay, surface, rects, numrects);
+ EGLint rects[4];
+ frame.map(screenDirty, rects);
+ eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects,
+ screenDirty.isEmpty() ? 0 : 1);
} else {
- eglSwapBuffers(mEglDisplay, surface);
+ eglSwapBuffers(mEglDisplay, frame.mSurface);
}
#else
- eglSwapBuffers(mEglDisplay, surface);
+ eglSwapBuffers(mEglDisplay, frame.mSurface);
#endif
EGLint err = eglGetError();
@@ -295,7 +343,8 @@ bool EglManager::swapBuffers(EGLSurface surface, const SkRect& dirty,
// For some reason our surface was destroyed out from under us
// This really shouldn't happen, but if it does we can recover easily
// by just not trying to use the surface anymore
- ALOGW("swapBuffers encountered EGL_BAD_SURFACE on %p, halting rendering...", surface);
+ ALOGW("swapBuffers encountered EGL_BAD_SURFACE on %p, halting rendering...",
+ frame.mSurface);
return false;
}
LOG_ALWAYS_FATAL("Encountered EGL error %d %s during rendering",
@@ -312,18 +361,13 @@ void EglManager::fence() {
}
bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) {
- if (CC_UNLIKELY(!mAllowPreserveBuffer)) return false;
-
- bool preserved = false;
- if (mCanSetPreserveBuffer) {
- preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR,
- preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
- if (CC_UNLIKELY(!preserved)) {
- ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
- (void*) surface, egl_error_str());
- }
- }
- if (CC_UNLIKELY(!preserved)) {
+ if (mSwapBehavior != SwapBehavior::Preserved) return false;
+
+ bool preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR,
+ preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
+ if (!preserved) {
+ ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
+ (void*) surface, egl_error_str());
// Maybe it's already set?
EGLint swapBehavior;
if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) {
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 0a8cfd30df71..62b5b99a6e30 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -27,6 +27,29 @@ namespace uirenderer {
namespace renderthread {
class RenderThread;
+class EglManager;
+
+class Frame {
+public:
+ EGLint width() const { return mWidth; }
+ EGLint height() const { return mHeight; }
+
+ // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt
+ // for what this means
+ EGLint bufferAge() const { return mBufferAge; }
+
+private:
+ friend class EglManager;
+
+ EGLSurface mSurface;
+ EGLint mWidth;
+ EGLint mHeight;
+ EGLint mBufferAge;
+
+ // Maps from 0,0 in top-left to 0,0 in bottom-left
+ // If out is not an EGLint[4] you're going to have a bad time
+ void map(const SkRect& in, EGLint* out) const;
+};
// This class contains the shared global EGL objects, such as EGLDisplay
// and EGLConfig, which are re-used by CanvasContext
@@ -45,8 +68,9 @@ public:
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);
- void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
- bool swapBuffers(EGLSurface surface, const SkRect& dirty, EGLint width, EGLint height);
+ Frame beginFrame(EGLSurface surface);
+ void damageFrame(const Frame& frame, const SkRect& dirty);
+ bool swapBuffers(const Frame& frame, const SkRect& screenDirty);
// Returns true iff the surface is now preserving buffers.
bool setPreserveBuffer(EGLSurface surface, bool preserve);
@@ -62,10 +86,12 @@ private:
// EglContext is never destroyed, method is purposely not implemented
~EglManager();
+ void initExtensions();
void createPBufferSurface();
void loadConfig();
void createContext();
void initAtlas();
+ EGLint queryBufferAge(EGLSurface surface);
RenderThread& mRenderThread;
@@ -74,14 +100,18 @@ private:
EGLContext mEglContext;
EGLSurface mPBufferSurface;
- const bool mAllowPreserveBuffer;
- bool mCanSetPreserveBuffer;
-
EGLSurface mCurrentSurface;
sp<GraphicBuffer> mAtlasBuffer;
int64_t* mAtlasMap;
size_t mAtlasMapSize;
+
+ enum class SwapBehavior {
+ Discard,
+ Preserved,
+ BufferAge,
+ };
+ SwapBehavior mSwapBehavior = SwapBehavior::Discard;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 6d9acd429279..15ccd6ac5b6b 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -74,7 +74,7 @@ RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextF
args->thread = &mRenderThread;
args->contextFactory = contextFactory;
mContext = (CanvasContext*) postAndWait(task);
- mDrawFrameTask.setContext(&mRenderThread, mContext);
+ mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}
RenderProxy::~RenderProxy() {
@@ -91,7 +91,7 @@ void RenderProxy::destroyContext() {
SETUP_TASK(destroyContext);
args->context = mContext;
mContext = nullptr;
- mDrawFrameTask.setContext(nullptr, nullptr);
+ mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
// This is also a fence as we need to be certain that there are no
// outstanding mDrawFrame tasks posted before it is destroyed
postAndWait(task);
@@ -271,16 +271,15 @@ void RenderProxy::runWithGlContext(RenderTask* gltask) {
postAndWait(task);
}
-CREATE_BRIDGE2(createTextureLayer, RenderThread* thread, CanvasContext* context) {
+CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) {
Layer* layer = args->context->createTextureLayer();
if (!layer) return nullptr;
- return new DeferredLayerUpdater(*args->thread, layer);
+ return new DeferredLayerUpdater(layer);
}
DeferredLayerUpdater* RenderProxy::createTextureLayer() {
SETUP_TASK(createTextureLayer);
args->context = mContext;
- args->thread = &mRenderThread;
void* retval = postAndWait(task);
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(retval);
return layer;
@@ -462,7 +461,8 @@ void RenderProxy::dumpGraphicsMemory(int fd) {
staticPostAndWait(task);
}
-CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) {
+CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map,
+ size_t size) {
CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size);
args->buffer->decStrong(nullptr);
return nullptr;
@@ -491,6 +491,71 @@ void RenderProxy::setProcessStatsBuffer(int fd) {
post(task);
}
+CREATE_BRIDGE3(addRenderNode, CanvasContext* context, RenderNode* node, bool placeFront) {
+ args->context->addRenderNode(args->node, args->placeFront);
+ return nullptr;
+}
+
+void RenderProxy::addRenderNode(RenderNode* node, bool placeFront) {
+ SETUP_TASK(addRenderNode);
+ args->context = mContext;
+ args->node = node;
+ args->placeFront = placeFront;
+ post(task);
+}
+
+CREATE_BRIDGE2(removeRenderNode, CanvasContext* context, RenderNode* node) {
+ args->context->removeRenderNode(args->node);
+ return nullptr;
+}
+
+void RenderProxy::removeRenderNode(RenderNode* node) {
+ SETUP_TASK(removeRenderNode);
+ args->context = mContext;
+ args->node = node;
+ post(task);
+}
+
+CREATE_BRIDGE2(drawRenderNode, CanvasContext* context, RenderNode* node) {
+ args->context->prepareAndDraw(args->node);
+ return nullptr;
+}
+
+void RenderProxy::drawRenderNode(RenderNode* node) {
+ SETUP_TASK(drawRenderNode);
+ args->context = mContext;
+ args->node = node;
+ // Be pseudo-thread-safe and don't use any member variables
+ staticPostAndWait(task);
+}
+
+CREATE_BRIDGE5(setContentDrawBounds, CanvasContext* context, int left, int top,
+ int right, int bottom) {
+ args->context->setContentDrawBounds(args->left, args->top, args->right, args->bottom);
+ return nullptr;
+}
+
+void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) {
+ SETUP_TASK(setContentDrawBounds);
+ args->context = mContext;
+ args->left = left;
+ args->top = top;
+ args->right = right;
+ args->bottom = bottom;
+ staticPostAndWait(task);
+}
+
+CREATE_BRIDGE1(serializeDisplayListTree, CanvasContext* context) {
+ args->context->serializeDisplayListTree();
+ return nullptr;
+}
+
+void RenderProxy::serializeDisplayListTree() {
+ SETUP_TASK(serializeDisplayListTree);
+ args->context = mContext;
+ post(task);
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
@@ -509,12 +574,7 @@ void* RenderProxy::staticPostAndWait(MethodInvokeRenderTask* task) {
RenderThread& thread = RenderThread::getInstance();
void* retval;
task->setReturnPtr(&retval);
- Mutex mutex;
- Condition condition;
- SignalingRenderTask syncTask(task, &mutex, &condition);
- AutoMutex _lock(mutex);
- thread.queue(&syncTask);
- condition.wait(mutex);
+ thread.queueAndWait(task);
return retval;
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 5febbe0ab26c..338fab650876 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -27,7 +27,6 @@
#include <utils/Mutex.h>
#include <utils/Timers.h>
#include <utils/StrongPointer.h>
-#include <utils/Vector.h>
#include "../Caches.h"
#include "../IContextFactory.h"
@@ -39,7 +38,7 @@ namespace uirenderer {
class DeferredLayerUpdater;
class RenderNode;
-class DisplayListData;
+class DisplayList;
class Layer;
class Rect;
@@ -105,6 +104,13 @@ public:
ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
ANDROID_API void setProcessStatsBuffer(int fd);
+ ANDROID_API void serializeDisplayListTree();
+
+ ANDROID_API void addRenderNode(RenderNode* node, bool placeFront);
+ ANDROID_API void removeRenderNode(RenderNode* node);
+ ANDROID_API void drawRenderNode(RenderNode* node);
+ ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom);
+
private:
RenderThread& mRenderThread;
CanvasContext* mContext;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 64075f1c346a..8fcd10967e17 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -312,6 +312,16 @@ void RenderThread::queue(RenderTask* task) {
}
}
+void RenderThread::queueAndWait(RenderTask* task) {
+ Mutex mutex;
+ Condition condition;
+ SignalingRenderTask syncTask(task, &mutex, &condition);
+
+ AutoMutex _lock(mutex);
+ queue(&syncTask);
+ condition.wait(mutex);
+}
+
void RenderThread::queueAtFront(RenderTask* task) {
AutoMutex _lock(mLock);
mQueue.queueAtFront(task);
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 80960999ef53..f3444a85a336 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -39,6 +39,7 @@ class DisplayEventReceiver;
namespace uirenderer {
class RenderState;
+class TestUtils;
namespace renderthread {
@@ -76,6 +77,7 @@ public:
// RenderThread takes complete ownership of tasks that are queued
// and will delete them after they are run
ANDROID_API void queue(RenderTask* task);
+ ANDROID_API void queueAndWait(RenderTask* task);
ANDROID_API void queueAtFront(RenderTask* task);
void queueAt(RenderTask* task, nsecs_t runAtNs);
void remove(RenderTask* task);
@@ -101,6 +103,7 @@ private:
friend class Singleton<RenderThread>;
friend class DispatchFrameCallbacks;
friend class RenderProxy;
+ friend class android::uirenderer::TestUtils;
RenderThread();
virtual ~RenderThread();
diff --git a/libs/hwui/tests/Android.mk b/libs/hwui/tests/Android.mk
deleted file mode 100644
index b6f0baf4bf3e..000000000000
--- a/libs/hwui/tests/Android.mk
+++ /dev/null
@@ -1,36 +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.
-#
-
-local_target_dir := $(TARGET_OUT_DATA)/local/tmp
-LOCAL_PATH:= $(call my-dir)/..
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_PATH := $(local_target_dir)
-LOCAL_MODULE:= hwuitest
-LOCAL_MODULE_TAGS := tests
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := hwuitest
-LOCAL_MODULE_STEM_64 := hwuitest64
-
-HWUI_NULL_GPU := false
-
-include $(LOCAL_PATH)/Android.common.mk
-
-LOCAL_SRC_FILES += \
- tests/TestContext.cpp \
- tests/main.cpp
-
-include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/tests/Benchmark.h b/libs/hwui/tests/Benchmark.h
new file mode 100644
index 000000000000..e16310e034be
--- /dev/null
+++ b/libs/hwui/tests/Benchmark.h
@@ -0,0 +1,54 @@
+/*
+ * 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 TESTS_BENCHMARK_H
+#define TESTS_BENCHMARK_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+struct BenchmarkOptions {
+ int count;
+};
+
+typedef void (*BenchmarkFunctor)(const BenchmarkOptions&);
+
+struct BenchmarkInfo {
+ std::string name;
+ std::string description;
+ BenchmarkFunctor functor;
+};
+
+class Benchmark {
+public:
+ Benchmark(const BenchmarkInfo& info) {
+ registerBenchmark(info);
+ }
+
+private:
+ Benchmark() = delete;
+ Benchmark(const Benchmark&) = delete;
+ Benchmark& operator=(const Benchmark&) = delete;
+
+ static void registerBenchmark(const BenchmarkInfo& info);
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TESTS_BENCHMARK_H */
diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp
index 3687a5003471..ba763a8def62 100644
--- a/libs/hwui/tests/TestContext.cpp
+++ b/libs/hwui/tests/TestContext.cpp
@@ -22,16 +22,35 @@ namespace test {
static const int IDENT_DISPLAYEVENT = 1;
-static DisplayInfo getBuiltInDisplay() {
+static android::DisplayInfo DUMMY_DISPLAY {
+ 1080, //w
+ 1920, //h
+ 320.0, // xdpi
+ 320.0, // ydpi
+ 60.0, // fps
+ 2.0, // density
+ 0, // orientation
+ false, // secure?
+ 0, // appVsyncOffset
+ 0, // presentationDeadline
+ 0, // colorTransform
+};
+
+DisplayInfo getBuiltInDisplay() {
+#if !HWUI_NULL_GPU
DisplayInfo display;
sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display);
LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
return display;
+#else
+ return DUMMY_DISPLAY;
+#endif
}
-android::DisplayInfo gDisplay = getBuiltInDisplay();
+// Initialize to a dummy default
+android::DisplayInfo gDisplay = DUMMY_DISPLAY;
TestContext::TestContext() {
mLooper = new Looper(true);
@@ -57,10 +76,7 @@ sp<Surface> TestContext::surface() {
}
void TestContext::waitForVsync() {
-#if HWUI_NULL_GPU
- return;
-#endif
-
+#if !HWUI_NULL_GPU
// Request vsync
mDisplayEventReceiver.requestNextVsync();
@@ -70,6 +86,7 @@ void TestContext::waitForVsync() {
// Drain it
DisplayEventReceiver::Event buf[100];
while (mDisplayEventReceiver.getEvents(buf, 100) > 0) { }
+#endif
}
} // namespace test
diff --git a/libs/hwui/tests/TestContext.h b/libs/hwui/tests/TestContext.h
index 7b30fc1dc7ce..2bbe5dffd9b8 100644
--- a/libs/hwui/tests/TestContext.h
+++ b/libs/hwui/tests/TestContext.h
@@ -32,6 +32,8 @@ namespace test {
extern DisplayInfo gDisplay;
#define dp(x) ((x) * android::uirenderer::test::gDisplay.density)
+DisplayInfo getBuiltInDisplay();
+
class TestContext {
public:
TestContext();
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
new file mode 100644
index 000000000000..891af9171518
--- /dev/null
+++ b/libs/hwui/tests/TreeContentAnimation.cpp
@@ -0,0 +1,433 @@
+/*
+ * 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 <cutils/log.h>
+#include <gui/Surface.h>
+#include <ui/PixelFormat.h>
+
+#include <AnimationContext.h>
+#include <DisplayListCanvas.h>
+#include <RecordingCanvas.h>
+#include <RenderNode.h>
+#include <renderthread/RenderProxy.h>
+#include <renderthread/RenderTask.h>
+
+#include "Benchmark.h"
+#include "TestContext.h"
+
+#include "protos/hwui.pb.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
+
+#if HWUI_NEW_OPS
+typedef RecordingCanvas TestCanvas;
+#else
+typedef DisplayListCanvas TestCanvas;
+#endif
+
+
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+ return new AnimationContext(clock);
+ }
+};
+
+static TestCanvas* startRecording(RenderNode* node) {
+ TestCanvas* renderer = new TestCanvas(
+ node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
+ return renderer;
+}
+
+static void endRecording(TestCanvas* renderer, RenderNode* node) {
+ node->setStagingDisplayList(renderer->finishRecording());
+ delete renderer;
+}
+
+class TreeContentAnimation {
+public:
+ virtual ~TreeContentAnimation() {}
+ int frameCount = 150;
+ virtual int getFrameCount() { return frameCount; }
+ virtual void setFrameCount(int fc) {
+ if (fc > 0) {
+ frameCount = fc;
+ }
+ }
+ virtual void createContent(int width, int height, TestCanvas* renderer) = 0;
+ virtual void doFrame(int frameNr) = 0;
+
+ template <class T>
+ static void run(const BenchmarkOptions& opts) {
+ // Switch to the real display
+ gDisplay = getBuiltInDisplay();
+
+ T animation;
+ animation.setFrameCount(opts.count);
+
+ TestContext testContext;
+
+ // create the native surface
+ const int width = gDisplay.w;
+ const int height = gDisplay.h;
+ sp<Surface> surface = testContext.surface();
+
+ RenderNode* rootNode = new RenderNode();
+ rootNode->incStrong(nullptr);
+ rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
+ rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ rootNode->mutateStagingProperties().setClipToBounds(false);
+ rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+
+ ContextFactory factory;
+ std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
+ proxy->loadSystemProperties();
+ proxy->initialize(surface);
+ float lightX = width / 2.0;
+ proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
+ proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
+
+ android::uirenderer::Rect DUMMY;
+
+ TestCanvas* renderer = startRecording(rootNode);
+ animation.createContent(width, height, renderer);
+ endRecording(renderer, rootNode);
+
+ // Do a few cold runs then reset the stats so that the caches are all hot
+ for (int i = 0; i < 3; i++) {
+ testContext.waitForVsync();
+ proxy->syncAndDrawFrame();
+ }
+ proxy->resetProfileInfo();
+
+ for (int i = 0; i < animation.getFrameCount(); i++) {
+ testContext.waitForVsync();
+
+ ATRACE_NAME("UI-Draw Frame");
+ nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
+ UiFrameInfoBuilder(proxy->frameInfo())
+ .setVsync(vsync, vsync);
+ animation.doFrame(i);
+ proxy->syncAndDrawFrame();
+ }
+
+ proxy->dumpProfileInfo(STDOUT_FILENO, 0);
+ rootNode->decStrong(nullptr);
+ }
+};
+
+class ShadowGridAnimation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, TestCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ TestCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _ShadowGrid(BenchmarkInfo{
+ "shadowgrid",
+ "A grid of rounded rects that cast a shadow. Simplified scenario of an "
+ "Android TV-style launcher interface. High CPU/GPU load.",
+ TreeContentAnimation::run<ShadowGridAnimation>
+});
+
+class ShadowGrid2Animation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, TestCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
+ for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
+ sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ TestCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _ShadowGrid2(BenchmarkInfo{
+ "shadowgrid2",
+ "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
+ "variant of shadowgrid. Very high CPU load, high GPU load.",
+ TreeContentAnimation::run<ShadowGrid2Animation>
+});
+
+class RectGridAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, TestCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ card = createCard(40, 40, 200, 200);
+ renderer->drawRenderNode(card.get());
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ TestCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+
+ SkRegion region;
+ for (int xOffset = 0; xOffset < width; xOffset+=2) {
+ for (int yOffset = 0; yOffset < height; yOffset+=2) {
+ region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
+ }
+ }
+
+ SkPaint paint;
+ paint.setColor(0xff00ffff);
+ renderer->drawRegion(region, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _RectGrid(BenchmarkInfo{
+ "rectgrid",
+ "A dense grid of 1x1 rects that should visually look like a single rect. "
+ "Low CPU/GPU load.",
+ TreeContentAnimation::run<RectGridAnimation>
+});
+
+class OvalAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, TestCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ card = createCard(40, 40, 400, 400);
+ renderer->drawRenderNode(card.get());
+
+ renderer->insertReorderBarrier(false);
+ }
+
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ TestCanvas* renderer = startRecording(node.get());
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(0xFF000000);
+ renderer->drawOval(0, 0, width, height, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _Oval(BenchmarkInfo{
+ "oval",
+ "Draws 1 oval.",
+ TreeContentAnimation::run<OvalAnimation>
+});
+
+class PartialDamageTest : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, TestCanvas* renderer) override {
+ static SkColor COLORS[] = {
+ 0xFFF44336,
+ 0xFF9C27B0,
+ 0xFF2196F3,
+ 0xFF4CAF50,
+ };
+
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
+ COLORS[static_cast<int>((y / dp(116))) % 4]);
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ cards[0]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[0]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ TestCanvas* renderer = startRecording(cards[0].get());
+ renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
+ SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, cards[0].get());
+ }
+
+ static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
+ int startA = (start >> 24) & 0xff;
+ int startR = (start >> 16) & 0xff;
+ int startG = (start >> 8) & 0xff;
+ int startB = start & 0xff;
+
+ int endA = (end >> 24) & 0xff;
+ int endR = (end >> 16) & 0xff;
+ int endG = (end >> 8) & 0xff;
+ int endB = end & 0xff;
+
+ return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
+ (int)((startR + (int)(fraction * (endR - startR))) << 16) |
+ (int)((startG + (int)(fraction * (endG - startG))) << 8) |
+ (int)((startB + (int)(fraction * (endB - startB))));
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ TestCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _PartialDamage(BenchmarkInfo{
+ "partialdamage",
+ "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
+ "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
+ "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
+ TreeContentAnimation::run<PartialDamageTest>
+});
+
+
+class SimpleRectGridAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, TestCanvas* renderer) override {
+ SkPaint paint;
+ paint.setColor(0xFF00FFFF);
+ renderer->drawRect(0, 0, width, height, paint);
+
+ card = createCard(40, 40, 200, 200);
+ renderer->drawRenderNode(card.get());
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ TestCanvas* renderer = startRecording(node.get());
+ SkPaint paint;
+ paint.setColor(0xFFFF00FF);
+ renderer->drawRect(0, 0, width, height, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _SimpleRectGrid(BenchmarkInfo{
+ "simplerectgrid",
+ "A simple collection of rects. "
+ "Low CPU/GPU load.",
+ TreeContentAnimation::run<SimpleRectGridAnimation>
+});
diff --git a/libs/hwui/tests/how_to_run.txt b/libs/hwui/tests/how_to_run.txt
index 686cd7878c50..b051768f3262 100644
--- a/libs/hwui/tests/how_to_run.txt
+++ b/libs/hwui/tests/how_to_run.txt
@@ -1,17 +1,5 @@
-mmm -j8 frameworks/base/libs/hwui/tests/ &&
+mmm -j8 frameworks/base/libs/hwui/ &&
adb push $OUT/data/local/tmp/hwuitest /data/local/tmp/hwuitest &&
adb shell /data/local/tmp/hwuitest
-
-Command arguments:
-hwuitest [testname]
-
-Default test is 'shadowgrid'
-
-List of tests:
-
-shadowgrid: creates a grid of rounded rects that cast shadows, high CPU & GPU load
-
-rectgrid: creates a grid of 1x1 rects
-
-oval: draws 1 oval
+Pass --help to get help
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index 80d7029857c4..aee84de3ae7b 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -14,327 +14,181 @@
* limitations under the License.
*/
-#include <cutils/log.h>
-#include <gui/Surface.h>
-#include <ui/PixelFormat.h>
+#include "Benchmark.h"
-#include <AnimationContext.h>
-#include <DisplayListCanvas.h>
-#include <RenderNode.h>
-#include <renderthread/RenderProxy.h>
-#include <renderthread/RenderTask.h>
-
-#include "TestContext.h"
+#include "protos/hwui.pb.h"
+#include <getopt.h>
#include <stdio.h>
+#include <string>
#include <unistd.h>
+#include <unordered_map>
+#include <vector>
using namespace android;
using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-using namespace android::uirenderer::test;
-
-class ContextFactory : public IContextFactory {
-public:
- virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
- return new AnimationContext(clock);
- }
-};
-static DisplayListCanvas* startRecording(RenderNode* node) {
- DisplayListCanvas* renderer = new DisplayListCanvas();
- renderer->setViewport(node->stagingProperties().getWidth(),
- node->stagingProperties().getHeight());
- renderer->prepare();
- return renderer;
+// Not a static global because we need to force the map to be constructed
+// before we try to add things to it.
+std::unordered_map<std::string, BenchmarkInfo>& testMap() {
+ static std::unordered_map<std::string, BenchmarkInfo> testMap;
+ return testMap;
}
-static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
- renderer->finish();
- node->setStagingDisplayList(renderer->finishRecording());
- delete renderer;
+void Benchmark::registerBenchmark(const BenchmarkInfo& info) {
+ testMap()[info.name] = info;
}
-class TreeContentAnimation {
-public:
- virtual ~TreeContentAnimation() {}
- int frameCount = 150;
- virtual int getFrameCount() { return frameCount; }
- virtual void setFrameCount(int fc) {
- if (fc > 0) {
- frameCount = fc;
- }
- }
- virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
- virtual void doFrame(int frameNr) = 0;
-
- template <class T>
- static void run(int frameCount) {
- T animation;
- animation.setFrameCount(frameCount);
-
- TestContext testContext;
-
- // create the native surface
- const int width = gDisplay.w;
- const int height = gDisplay.h;
- sp<Surface> surface = testContext.surface();
-
- RenderNode* rootNode = new RenderNode();
- rootNode->incStrong(nullptr);
- rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
- rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- rootNode->mutateStagingProperties().setClipToBounds(false);
- rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
-
- ContextFactory factory;
- std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
- proxy->loadSystemProperties();
- proxy->initialize(surface);
- float lightX = width / 2.0;
- proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
- proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
-
- android::uirenderer::Rect DUMMY;
-
- DisplayListCanvas* renderer = startRecording(rootNode);
- animation.createContent(width, height, renderer);
- endRecording(renderer, rootNode);
-
- // Do a few cold runs then reset the stats so that the caches are all hot
- for (int i = 0; i < 3; i++) {
- testContext.waitForVsync();
- proxy->syncAndDrawFrame();
- }
- proxy->resetProfileInfo();
-
- for (int i = 0; i < animation.getFrameCount(); i++) {
- testContext.waitForVsync();
-
- ATRACE_NAME("UI-Draw Frame");
- nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
- UiFrameInfoBuilder(proxy->frameInfo())
- .setVsync(vsync, vsync);
- animation.doFrame(i);
- proxy->syncAndDrawFrame();
- }
-
- proxy->dumpProfileInfo(STDOUT_FILENO, 0);
- rootNode->decStrong(nullptr);
- }
-};
-
-class ShadowGridAnimation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+static int gFrameCount = 150;
+static int gRepeatCount = 1;
+static std::vector<BenchmarkInfo> gRunTests;
+
+static void printHelp() {
+ printf("\
+USAGE: hwuitest [OPTIONS] <TESTNAME>\n\
+\n\
+OPTIONS:\n\
+ -c, --count=NUM NUM loops a test should run (example, number of frames)\n\
+ -r, --runs=NUM Repeat the test(s) NUM times\n\
+ -h, --help Display this help\n\
+ --list List all tests\n\
+\n");
+}
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
+static void listTests() {
+ printf("Tests: \n");
+ for (auto&& test : testMap()) {
+ auto&& info = test.second;
+ const char* col1 = info.name.c_str();
+ int dlen = info.description.length();
+ const char* col2 = info.description.c_str();
+ // World's best line breaking algorithm.
+ do {
+ int toPrint = dlen;
+ if (toPrint > 50) {
+ char* found = (char*) memrchr(col2, ' ', 50);
+ if (found) {
+ toPrint = found - col2;
+ } else {
+ toPrint = 50;
+ }
}
- }
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
+ printf("%-20s %.*s\n", col1, toPrint, col2);
+ col1 = "";
+ col2 += toPrint;
+ dlen -= toPrint;
+ while (*col2 == ' ') {
+ col2++; dlen--;
+ }
+ } while (dlen > 0);
+ printf("\n");
}
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+}
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
+static const struct option LONG_OPTIONS[] = {
+ { "frames", required_argument, nullptr, 'f' },
+ { "repeat", required_argument, nullptr, 'r' },
+ { "help", no_argument, nullptr, 'h' },
+ { "list", no_argument, nullptr, 'l' },
+ { 0, 0, 0, 0 }
};
-class ShadowGrid2Animation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+static const char* SHORT_OPTIONS = "c:r:h";
- for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
- for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
- sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
- }
- }
+void parseOptions(int argc, char* argv[]) {
+ int c;
+ // temporary variable
+ int count;
+ bool error = false;
+ opterr = 0;
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+ while (true) {
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
-class RectGridAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+ c = getopt_long(argc, argv, SHORT_OPTIONS, LONG_OPTIONS, &option_index);
- card = createCard(40, 40, 200, 200);
- renderer->drawRenderNode(card.get());
+ if (c == -1)
+ break;
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ switch (c) {
+ case 0:
+ // Option set a flag, don't need to do anything
+ // (although none of the current LONG_OPTIONS do this...)
+ break;
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+ case 'l':
+ listTests();
+ exit(EXIT_SUCCESS);
+ break;
- float rects[width * height];
- int index = 0;
- for (int xOffset = 0; xOffset < width; xOffset+=2) {
- for (int yOffset = 0; yOffset < height; yOffset+=2) {
- rects[index++] = xOffset;
- rects[index++] = yOffset;
- rects[index++] = xOffset + 1;
- rects[index++] = yOffset + 1;
+ case 'c':
+ count = atoi(optarg);
+ if (!count) {
+ fprintf(stderr, "Invalid frames argument '%s'\n", optarg);
+ error = true;
+ } else {
+ gFrameCount = (count > 0 ? count : INT_MAX);
+ }
+ break;
+
+ case 'r':
+ count = atoi(optarg);
+ if (!count) {
+ fprintf(stderr, "Invalid repeat argument '%s'\n", optarg);
+ error = true;
+ } else {
+ gRepeatCount = (count > 0 ? count : INT_MAX);
}
+ break;
+
+ case 'h':
+ printHelp();
+ exit(EXIT_SUCCESS);
+ break;
+
+ case '?':
+ fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]);
+ // fall-through
+ default:
+ error = true;
+ break;
}
- int count = width * height;
-
- SkPaint paint;
- paint.setColor(0xff00ffff);
- renderer->drawRects(rects, count, &paint);
-
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class OvalAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- card = createCard(40, 40, 400, 400);
- renderer->drawRenderNode(card.get());
-
- renderer->insertReorderBarrier(false);
- }
-
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setColor(0xFF000000);
- renderer->drawOval(0, 0, width, height, paint);
-
- endRecording(renderer, node.get());
- return node;
+ if (error) {
+ fprintf(stderr, "Try 'hwuitest --help' for more information.\n");
+ exit(EXIT_FAILURE);
}
-};
-struct cstr_cmp {
- bool operator()(const char *a, const char *b) const {
- return std::strcmp(a, b) < 0;
+ /* Print any remaining command line arguments (not options). */
+ if (optind < argc) {
+ do {
+ const char* test = argv[optind++];
+ auto pos = testMap().find(test);
+ if (pos == testMap().end()) {
+ fprintf(stderr, "Unknown test '%s'\n", test);
+ exit(EXIT_FAILURE);
+ } else {
+ gRunTests.push_back(pos->second);
+ }
+ } while (optind < argc);
+ } else {
+ gRunTests.push_back(testMap()["shadowgrid"]);
}
-};
-
-typedef void (*testProc)(int);
-
-std::map<const char*, testProc, cstr_cmp> gTestMap {
- {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>},
- {"shadowgrid2", TreeContentAnimation::run<ShadowGrid2Animation>},
- {"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
- {"oval", TreeContentAnimation::run<OvalAnimation> },
-};
+}
int main(int argc, char* argv[]) {
- const char* testName = argc > 1 ? argv[1] : "shadowgrid";
- testProc proc = gTestMap[testName];
- if(!proc) {
- printf("Error: couldn't find test %s\n", testName);
- return 1;
- }
- int loopCount = 1;
- if (argc > 2) {
- loopCount = atoi(argv[2]);
- if (!loopCount) {
- printf("Invalid loop count!\n");
- return 1;
- }
- }
- int frameCount = 150;
- if (argc > 3) {
- frameCount = atoi(argv[3]);
- if (frameCount < 1) {
- printf("Invalid frame count!\n");
- return 1;
+ parseOptions(argc, argv);
+
+ BenchmarkOptions opts;
+ opts.count = gFrameCount;
+ for (int i = 0; i < gRepeatCount; i++) {
+ for (auto&& test : gRunTests) {
+ test.functor(opts);
}
}
- if (loopCount < 0) {
- loopCount = INT_MAX;
- }
- for (int i = 0; i < loopCount; i++) {
- proc(frameCount);
- }
printf("Success!\n");
return 0;
}
diff --git a/libs/hwui/tests/nullgles.cpp b/libs/hwui/tests/nullgles.cpp
index 8ca7598a91a0..f8e8c98c20ba 100644
--- a/libs/hwui/tests/nullgles.cpp
+++ b/libs/hwui/tests/nullgles.cpp
@@ -261,8 +261,6 @@ void glInsertEventMarkerEXT(GLsizei length, const GLchar *marker) {}
void glPushGroupMarkerEXT(GLsizei length, const GLchar *marker) {}
void glPopGroupMarkerEXT(void) {}
void glDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, const GLenum *attachments) {}
-void glStartTilingQCOM(GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) {}
-void glEndTilingQCOM(GLbitfield preserveMask) {}
void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) {}
// GLES3
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index e9dde294b2aa..a07845ecf659 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -39,7 +39,7 @@ TaskManager::TaskManager() {
for (int i = 0; i < workerCount; i++) {
String8 name;
name.appendFormat("hwuiTask%d", i + 1);
- mThreads.add(new WorkerThread(name));
+ mThreads.push_back(new WorkerThread(name));
}
}
@@ -89,15 +89,14 @@ status_t TaskManager::WorkerThread::readyToRun() {
bool TaskManager::WorkerThread::threadLoop() {
mSignal.wait();
- Vector<TaskWrapper> tasks;
+ std::vector<TaskWrapper> tasks;
{
Mutex::Autolock l(mLock);
- tasks = mTasks;
- mTasks.clear();
+ tasks.swap(mTasks);
}
for (size_t i = 0; i < tasks.size(); i++) {
- const TaskWrapper& task = tasks.itemAt(i);
+ const TaskWrapper& task = tasks[i];
task.mProcessor->process(task.mTask);
}
@@ -111,14 +110,13 @@ bool TaskManager::WorkerThread::addTask(TaskWrapper task) {
return false;
}
- ssize_t index;
{
Mutex::Autolock l(mLock);
- index = mTasks.add(task);
+ mTasks.push_back(task);
}
mSignal.signal();
- return index >= 0;
+ return true;
}
size_t TaskManager::WorkerThread::getTaskCount() const {
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index 10e8b9e0bead..d0eb3049ae37 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -20,10 +20,11 @@
#include <utils/Mutex.h>
#include <utils/String8.h>
#include <utils/Thread.h>
-#include <utils/Vector.h>
#include "Signal.h"
+#include <vector>
+
namespace android {
namespace uirenderer {
@@ -89,7 +90,7 @@ private:
// Lock for the list of tasks
mutable Mutex mLock;
- Vector<TaskWrapper> mTasks;
+ std::vector<TaskWrapper> mTasks;
// Signal used to wake up the thread when a new
// task is available in the list
@@ -98,7 +99,7 @@ private:
const String8 mName;
};
- Vector<sp<WorkerThread> > mThreads;
+ std::vector<sp<WorkerThread> > mThreads;
};
}; // namespace uirenderer
diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk
deleted file mode 100644
index 917e646e2303..000000000000
--- a/libs/hwui/unit_tests/Android.mk
+++ /dev/null
@@ -1,35 +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.
-#
-
-local_target_dir := $(TARGET_OUT_DATA)/local/tmp
-LOCAL_PATH:= $(call my-dir)/..
-
-include $(CLEAR_VARS)
-
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.common.mk
-LOCAL_MODULE := hwui_unit_tests
-LOCAL_MODULE_TAGS := tests
-
-include $(LOCAL_PATH)/Android.common.mk
-
-LOCAL_SRC_FILES += \
- unit_tests/ClipAreaTests.cpp \
- unit_tests/DamageAccumulatorTests.cpp \
- unit_tests/LinearAllocatorTests.cpp \
- unit_tests/main.cpp
-
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libs/hwui/unit_tests/BakedOpStateTests.cpp b/libs/hwui/unit_tests/BakedOpStateTests.cpp
new file mode 100644
index 000000000000..82aebeabe038
--- /dev/null
+++ b/libs/hwui/unit_tests/BakedOpStateTests.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <RecordedOp.h>
+#include <unit_tests/TestUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+TEST(ResolvedRenderState, resolution) {
+ Matrix4 identity;
+ identity.loadIdentity();
+
+ Matrix4 translate10x20;
+ translate10x20.loadTranslate(10, 20, 0);
+
+ SkPaint paint;
+ RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, Rect(0, 0, 100, 200), &paint);
+ {
+ // recorded with transform, no parent transform
+ auto parentSnapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
+ ResolvedRenderState state(*parentSnapshot, recordedOp);
+ EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20);
+ EXPECT_EQ(state.clipRect, Rect(0, 0, 100, 200));
+ EXPECT_EQ(state.clippedBounds, Rect(40, 60, 100, 200)); // translated and also clipped
+ }
+ {
+ // recorded with transform and parent transform
+ auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(0, 0, 100, 200));
+ ResolvedRenderState state(*parentSnapshot, recordedOp);
+
+ Matrix4 expectedTranslate;
+ expectedTranslate.loadTranslate(20, 40, 0);
+ EXPECT_MATRIX_APPROX_EQ(state.transform, expectedTranslate);
+
+ // intersection of parent & transformed child clip
+ EXPECT_EQ(state.clipRect, Rect(10, 20, 100, 200));
+
+ // translated and also clipped
+ EXPECT_EQ(state.clippedBounds, Rect(50, 80, 100, 200));
+ }
+}
+
+TEST(BakedOpState, constructAndReject) {
+ LinearAllocator allocator;
+
+ Matrix4 identity;
+ identity.loadIdentity();
+
+ Matrix4 translate100x0;
+ translate100x0.loadTranslate(100, 0, 0);
+
+ SkPaint paint;
+ {
+ RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, Rect(0, 0, 100, 200), &paint);
+ auto snapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
+ BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp);
+
+ EXPECT_EQ(bakedOp, nullptr); // rejected by clip, so not constructed
+ EXPECT_LE(allocator.usedSize(), 8u); // no significant allocation space used for rejected op
+ }
+ {
+ RectOp successOp(Rect(30, 40, 100, 200), identity, Rect(0, 0, 100, 200), &paint);
+ auto snapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
+ BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, successOp);
+
+ EXPECT_NE(bakedOp, nullptr); // NOT rejected by clip, so will be constructed
+ EXPECT_GT(allocator.usedSize(), 64u); // relatively large alloc for non-rejected op
+ }
+}
+
+#define UNSUPPORTED_OP(Info, Type) \
+ static void on##Type(Info*, const Type&, const BakedOpState&) { FAIL(); }
+
+class Info {
+public:
+ int index = 0;
+};
+
+}
+}
diff --git a/libs/hwui/unit_tests/CanvasStateTests.cpp b/libs/hwui/unit_tests/CanvasStateTests.cpp
new file mode 100644
index 000000000000..dfbf6d358ce5
--- /dev/null
+++ b/libs/hwui/unit_tests/CanvasStateTests.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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 "utils/LinearAllocator.h"
+
+#include <gtest/gtest.h>
+#include <SkPath.h>
+#include <SkRegion.h>
+#include <SkCanvas.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(0, 0, 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, SkRegion::kIntersect_Op);
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 100, 100));
+
+ state.clipRect(10, 10, 200, 200, SkRegion::kIntersect_Op);
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100));
+
+ state.clipRect(50, 50, 150, 150, SkRegion::kReplace_Op);
+ 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(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ {
+ // rotated clip causes complex clip
+ state.rotate(10);
+ EXPECT_TRUE(state.clipIsSimple());
+ state.clipRect(0, 0, 200, 200, SkRegion::kIntersect_Op);
+ EXPECT_FALSE(state.clipIsSimple());
+ }
+ state.restore();
+
+ state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ {
+ // subtracted clip causes complex clip
+ EXPECT_TRUE(state.clipIsSimple());
+ state.clipRect(50, 50, 150, 150, SkRegion::kDifference_Op);
+ EXPECT_FALSE(state.clipIsSimple());
+ }
+ state.restore();
+
+ state.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ {
+ // complex path causes complex clip
+ SkPath path;
+ path.addOval(SkRect::MakeWH(200, 200));
+ EXPECT_TRUE(state.clipIsSimple());
+ state.clipPath(&path, SkRegion::kDifference_Op);
+ EXPECT_FALSE(state.clipIsSimple());
+ }
+ state.restore();
+}
+
+TEST(CanvasState, saveAndRestore) {
+ CanvasState state(sNullClient);
+ state.initializeSaveStack(200, 200,
+ 0, 0, 200, 200, Vector3());
+
+ state.save(SkCanvas::kClip_SaveFlag);
+ {
+ state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 10, 10));
+ }
+ state.restore();
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 200, 200)); // verify restore
+
+ Matrix4 simpleTranslate;
+ simpleTranslate.loadTranslate(10, 10, 0);
+ state.save(SkCanvas::kMatrix_SaveFlag);
+ {
+ 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(SkCanvas::kMatrix_SaveFlag); // NOTE: clip not saved
+ {
+ state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 10, 10));
+ }
+ state.restore();
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 10, 10)); // verify not restored
+
+ Matrix4 simpleTranslate;
+ simpleTranslate.loadTranslate(10, 10, 0);
+ state.save(SkCanvas::kClip_SaveFlag); // 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/unit_tests/ClipAreaTests.cpp b/libs/hwui/unit_tests/ClipAreaTests.cpp
index 0c5e5e715dea..d6192df08bc3 100644
--- a/libs/hwui/unit_tests/ClipAreaTests.cpp
+++ b/libs/hwui/unit_tests/ClipAreaTests.cpp
@@ -101,10 +101,9 @@ TEST(ClipArea, paths) {
EXPECT_FALSE(area.isEmpty());
EXPECT_FALSE(area.isSimple());
EXPECT_FALSE(area.isRectangleList());
+
Rect clipRect(area.getClipRect());
- clipRect.dump("clipRect");
Rect expected(0, 0, r * 2, r * 2);
- expected.dump("expected");
EXPECT_EQ(expected, clipRect);
SkRegion clipRegion(area.getClipRegion());
auto skRect(clipRegion.getBounds());
diff --git a/libs/hwui/unit_tests/DamageAccumulatorTests.cpp b/libs/hwui/unit_tests/DamageAccumulatorTests.cpp
index c8d6004e11ec..29354a79efbd 100644
--- a/libs/hwui/unit_tests/DamageAccumulatorTests.cpp
+++ b/libs/hwui/unit_tests/DamageAccumulatorTests.cpp
@@ -18,6 +18,7 @@
#include <DamageAccumulator.h>
#include <Matrix.h>
+#include <RenderNode.h>
#include <utils/LinearAllocator.h>
#include <SkRect.h>
@@ -35,10 +36,12 @@ TEST(DamageAccumulator, identity) {
identity.loadIdentity();
da.pushTransform(&identity);
da.dirty(50, 50, 100, 100);
- da.pushTransform(&identity);
- da.peekAtDirty(&curDirty);
- ASSERT_EQ(SkRect(), curDirty);
- da.popTransform();
+ {
+ da.pushTransform(&identity);
+ da.peekAtDirty(&curDirty);
+ ASSERT_EQ(SkRect(), curDirty);
+ da.popTransform();
+ }
da.peekAtDirty(&curDirty);
ASSERT_EQ(SkRect::MakeLTRB(50, 50, 100, 100), curDirty);
da.popTransform();
@@ -69,13 +72,62 @@ TEST(DamageAccumulator, union) {
SkRect curDirty;
identity.loadIdentity();
da.pushTransform(&identity);
- da.pushTransform(&identity);
- da.dirty(50, 50, 100, 100);
- da.popTransform();
- da.pushTransform(&identity);
- da.dirty(150, 50, 200, 125);
- da.popTransform();
+ {
+ da.pushTransform(&identity);
+ da.dirty(50, 50, 100, 100);
+ da.popTransform();
+ da.pushTransform(&identity);
+ da.dirty(150, 50, 200, 125);
+ da.popTransform();
+ }
da.popTransform();
da.finish(&curDirty);
ASSERT_EQ(SkRect::MakeLTRB(50, 50, 200, 125), curDirty);
}
+
+TEST(DamageAccumulator, basicRenderNode) {
+ DamageAccumulator da;
+ RenderNode node1;
+ node1.animatorProperties().setLeftTopRightBottom(50, 50, 500, 500);
+ node1.animatorProperties().updateMatrix();
+ da.pushTransform(&node1);
+ {
+ RenderNode node2;
+ node2.animatorProperties().setLeftTopRightBottom(50, 50, 100, 100);
+ node2.animatorProperties().updateMatrix();
+ da.pushTransform(&node2);
+ da.dirty(0, 0, 25, 25);
+ da.popTransform();
+ }
+ da.popTransform();
+ SkRect dirty;
+ da.finish(&dirty);
+ ASSERT_EQ(SkRect::MakeLTRB(100, 100, 125, 125), dirty);
+}
+
+TEST(DamageAccumulator, perspectiveTransform) {
+ DamageAccumulator da;
+ RenderNode node1;
+ node1.animatorProperties().setLeftTopRightBottom(50, 50, 500, 500);
+ node1.animatorProperties().setClipToBounds(true);
+ node1.animatorProperties().updateMatrix();
+ da.pushTransform(&node1);
+ {
+ RenderNode node2;
+ node2.animatorProperties().setLeftTopRightBottom(50, 50, 100, 100);
+ node2.animatorProperties().setClipToBounds(false);
+ node2.animatorProperties().setRotationX(1.0f);
+ node2.animatorProperties().setRotationY(1.0f);
+ node2.animatorProperties().setRotation(20.0f);
+ node2.animatorProperties().setCameraDistance(500.0f);
+ node2.animatorProperties().setTranslationZ(30.0f);
+ node2.animatorProperties().updateMatrix();
+ da.pushTransform(&node2);
+ da.dirty(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
+ da.popTransform();
+ }
+ da.popTransform();
+ SkRect dirty;
+ da.finish(&dirty);
+ ASSERT_EQ(SkRect::MakeLTRB(50, 50, 500, 500), dirty);
+}
diff --git a/libs/hwui/unit_tests/LinearAllocatorTests.cpp b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
index b3959d169e1d..02cd77ae93db 100644
--- a/libs/hwui/unit_tests/LinearAllocatorTests.cpp
+++ b/libs/hwui/unit_tests/LinearAllocatorTests.cpp
@@ -106,3 +106,31 @@ TEST(LinearAllocator, rewind) {
// Checking for a double-destroy case
EXPECT_EQ(destroyed, false);
}
+
+TEST(LinearStdAllocator, simpleAllocate) {
+ LinearAllocator la;
+ LinearStdAllocator<void*> stdAllocator(la);
+
+ std::vector<char, LinearStdAllocator<char> > v(stdAllocator);
+ v.push_back(0);
+ char* initialLocation = &v[0];
+ v.push_back(10);
+ v.push_back(20);
+ v.push_back(30);
+
+ // expect to have allocated (since no space reserved), so [0] will have moved to
+ // slightly further down in the same LinearAllocator page
+ EXPECT_LT(initialLocation, &v[0]);
+ EXPECT_GT(initialLocation + 20, &v[0]);
+
+ // expect to have allocated again inserting 4 more entries
+ char* lastLocation = &v[0];
+ v.push_back(40);
+ v.push_back(50);
+ v.push_back(60);
+ v.push_back(70);
+
+ EXPECT_LT(lastLocation, &v[0]);
+ EXPECT_GT(lastLocation + 20, &v[0]);
+
+}
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
new file mode 100644
index 000000000000..e1249fbd2e96
--- /dev/null
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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 <OpReorderer.h>
+#include <RecordedOp.h>
+#include <RecordingCanvas.h>
+#include <unit_tests/TestUtils.h>
+
+#include <unordered_map>
+
+namespace android {
+namespace uirenderer {
+
+#define UNSUPPORTED_OP(Info, Type) \
+ static void on##Type(Info*, const Type&, const BakedOpState&) { FAIL(); }
+
+class Info {
+public:
+ int index = 0;
+};
+
+class SimpleReceiver {
+public:
+ static void onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
+ EXPECT_EQ(1, info->index++);
+ }
+ static void onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
+ EXPECT_EQ(0, info->index++);
+ }
+ UNSUPPORTED_OP(Info, RenderNodeOp)
+ UNSUPPORTED_OP(Info, SimpleRectsOp)
+ static void startFrame(Info& info) {}
+ static void endFrame(Info& info) {}
+};
+TEST(OpReorderer, simple) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
+ canvas.drawRect(0, 0, 100, 200, SkPaint());
+ canvas.drawBitmap(bitmap, 10, 10, nullptr);
+ });
+
+ OpReorderer reorderer;
+ reorderer.defer(200, 200, *dl);
+
+ Info info;
+ reorderer.replayBakedOps<SimpleReceiver>(&info);
+}
+
+
+static int SIMPLE_BATCHING_LOOPS = 5;
+class SimpleBatchingReceiver {
+public:
+ static void onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
+ EXPECT_TRUE(info->index++ >= SIMPLE_BATCHING_LOOPS);
+ }
+ static void onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
+ EXPECT_TRUE(info->index++ < SIMPLE_BATCHING_LOOPS);
+ }
+ UNSUPPORTED_OP(Info, RenderNodeOp)
+ UNSUPPORTED_OP(Info, SimpleRectsOp)
+ static void startFrame(Info& info) {}
+ static void endFrame(Info& info) {}
+};
+TEST(OpReorderer, simpleBatching) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
+
+ // 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(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ for (int i = 0; i < SIMPLE_BATCHING_LOOPS; i++) {
+ canvas.translate(0, 10);
+ canvas.drawRect(0, 0, 10, 10, SkPaint());
+ canvas.drawBitmap(bitmap, 5, 0, nullptr);
+ }
+ canvas.restore();
+ });
+
+ OpReorderer reorderer;
+ reorderer.defer(200, 200, *dl);
+
+ Info info;
+ reorderer.replayBakedOps<SimpleBatchingReceiver>(&info);
+ EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, info.index); // 2 x loops ops, because no merging (TODO: force no merging)
+}
+
+class RenderNodeReceiver {
+public:
+ UNSUPPORTED_OP(Info, BitmapOp)
+ static void onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
+ switch(info->index++) {
+ case 0:
+ EXPECT_EQ(Rect(0, 0, 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:
+ FAIL();
+ }
+ }
+ UNSUPPORTED_OP(Info, RenderNodeOp)
+ UNSUPPORTED_OP(Info, SimpleRectsOp)
+ static void startFrame(Info& info) {}
+ static void endFrame(Info& info) {}
+};
+TEST(OpReorderer, renderNode) {
+ sp<RenderNode> child = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+
+ RenderNode* childPtr = child.get();
+ sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [childPtr](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorDKGRAY);
+ canvas.drawRect(0, 0, 200, 200, paint);
+
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(40, 40);
+ canvas.drawRenderNode(childPtr);
+ canvas.restore();
+ });
+
+ TestUtils::syncNodePropertiesAndDisplayList(child);
+ TestUtils::syncNodePropertiesAndDisplayList(parent);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(parent.get());
+
+ OpReorderer reorderer;
+ reorderer.defer(SkRect::MakeWH(200, 200), 200, 200, nodes);
+
+ Info info;
+ reorderer.replayBakedOps<RenderNodeReceiver>(&info);
+}
+
+class ClippedReceiver {
+public:
+ static void onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
+ EXPECT_EQ(0, info->index++);
+ 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());
+ }
+ UNSUPPORTED_OP(Info, RectOp)
+ UNSUPPORTED_OP(Info, RenderNodeOp)
+ UNSUPPORTED_OP(Info, SimpleRectsOp)
+ static void startFrame(Info& info) {}
+ static void endFrame(Info& info) {}
+};
+TEST(OpReorderer, clipped) {
+ sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RecordingCanvas& canvas) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
+ canvas.drawBitmap(bitmap, 0, 0, nullptr);
+ });
+ TestUtils::syncNodePropertiesAndDisplayList(node);
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(node.get());
+
+ OpReorderer reorderer;
+ reorderer.defer(SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
+ 200, 200, nodes);
+
+ Info info;
+ reorderer.replayBakedOps<ClippedReceiver>(&info);
+}
+
+}
+}
diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
new file mode 100644
index 000000000000..ce25fc6189b4
--- /dev/null
+++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 <RecordedOp.h>
+#include <RecordingCanvas.h>
+#include <unit_tests/TestUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+static void playbackOps(const DisplayList& displayList,
+ std::function<void(const RecordedOp&)> opReciever) {
+ for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
+ for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
+ RecordedOp* op = displayList.getOps()[opIndex];
+ opReciever(*op);
+ }
+ }
+}
+
+TEST(RecordingCanvas, emptyPlayback) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.restore();
+ });
+ playbackOps(*dl, [](const RecordedOp& op) { FAIL(); });
+}
+
+TEST(RecordingCanvas, testSimpleRectRecord) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ canvas.drawRect(10, 20, 90, 180, SkPaint());
+ });
+
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ count++;
+ ASSERT_EQ(RecordedOpId::RectOp, op.opId);
+ ASSERT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
+ ASSERT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
+ });
+ ASSERT_EQ(1, count); // only one observed
+}
+
+TEST(RecordingCanvas, backgroundAndImage) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ SkBitmap bitmap;
+ bitmap.setInfo(SkImageInfo::MakeUnknown(25, 25));
+ SkPaint paint;
+ paint.setColor(SK_ColorBLUE);
+
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ {
+ // a background!
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.drawRect(0, 0, 100, 200, paint);
+ canvas.restore();
+ }
+ {
+ // an image!
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ 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(0, 0, 100, 200), op.unmappedBounds);
+ EXPECT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
+
+ 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(0, 0, 25, 25), op.unmappedBounds);
+ EXPECT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
+
+ Matrix4 expectedMatrix;
+ expectedMatrix.loadTranslate(25, 25, 0);
+ expectedMatrix.scale(2, 2, 1);
+ EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+ }
+ count++;
+ });
+ ASSERT_EQ(2, count); // two draws observed
+}
+
+}
+}
diff --git a/libs/hwui/unit_tests/StringUtilsTests.cpp b/libs/hwui/unit_tests/StringUtilsTests.cpp
new file mode 100644
index 000000000000..5174ae99e71e
--- /dev/null
+++ b/libs/hwui/unit_tests/StringUtilsTests.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "utils/StringUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+TEST(StringUtils, simpleBuildSet) {
+ StringCollection collection("a b c");
+
+ EXPECT_TRUE(collection.has("a"));
+ EXPECT_TRUE(collection.has("b"));
+ EXPECT_TRUE(collection.has("c"));
+ EXPECT_FALSE(collection.has("d"));
+}
+
+TEST(StringUtils, advancedBuildSet) {
+ StringCollection collection("GL_ext1 GL_ext2 GL_ext3");
+
+ EXPECT_TRUE(collection.has("GL_ext1"));
+ EXPECT_FALSE(collection.has("GL_ext")); // string present, but not in list
+}
+
+};
+};
diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/unit_tests/TestUtils.h
new file mode 100644
index 000000000000..80d83a2e47a1
--- /dev/null
+++ b/libs/hwui/unit_tests/TestUtils.h
@@ -0,0 +1,117 @@
+/*
+ * 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 TEST_UTILS_H
+#define TEST_UTILS_H
+
+#include <Matrix.h>
+#include <Rect.h>
+#include <RenderNode.h>
+#include <renderstate/RenderState.h>
+#include <renderthread/RenderThread.h>
+#include <Snapshot.h>
+
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+
+#define EXPECT_MATRIX_APPROX_EQ(a, b) \
+ EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b))
+
+class TestUtils {
+public:
+ static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) {
+ for (int i = 0; i < 16; i++) {
+ if (!MathUtils::areEqual(a[i], b[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) {
+ std::unique_ptr<Snapshot> snapshot(new Snapshot());
+ snapshot->clip(clip.left, clip.top, clip.right, clip.bottom, SkRegion::kReplace_Op);
+ *(snapshot->transform) = transform;
+ return snapshot;
+ }
+
+ static SkBitmap createSkBitmap(int width, int height) {
+ SkBitmap bitmap;
+ SkImageInfo info = SkImageInfo::MakeUnknown(width, height);
+ bitmap.setInfo(info);
+ bitmap.allocPixels(info);
+ return bitmap;
+ }
+
+ template<class CanvasType>
+ static std::unique_ptr<DisplayList> createDisplayList(int width, int height,
+ std::function<void(CanvasType& canvas)> canvasCallback) {
+ CanvasType canvas(width, height);
+ canvasCallback(canvas);
+ return std::unique_ptr<DisplayList>(canvas.finishRecording());
+ }
+
+ template<class CanvasType>
+ static sp<RenderNode> createNode(int left, int top, int right, int bottom,
+ std::function<void(CanvasType& canvas)> canvasCallback) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ CanvasType canvas(
+ node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
+ canvasCallback(canvas);
+ node->setStagingDisplayList(canvas.finishRecording());
+ return node;
+ }
+
+ static void syncNodePropertiesAndDisplayList(sp<RenderNode>& node) {
+ node->syncProperties();
+ node->syncDisplayList();
+ }
+
+ typedef std::function<void(RenderState& state, Caches& caches)> RtCallback;
+
+ class TestTask : public renderthread::RenderTask {
+ public:
+ TestTask(RtCallback rtCallback)
+ : rtCallback(rtCallback) {}
+ virtual ~TestTask() {}
+ virtual void run() override {
+ // RenderState only valid once RenderThread is running, so queried here
+ RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
+
+ renderState.onGLContextCreated();
+ rtCallback(renderState, Caches::getInstance());
+ renderState.onGLContextDestroyed();
+ };
+ RtCallback rtCallback;
+ };
+
+ /**
+ * NOTE: requires surfaceflinger to run, otherwise this method will wait indefinitely.
+ */
+ static void runOnRenderThread(RtCallback rtCallback) {
+ TestTask task(rtCallback);
+ renderthread::RenderThread::getInstance().queueAndWait(&task);
+ }
+}; // class TestUtils
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TEST_UTILS_H */
diff --git a/libs/hwui/unit_tests/how_to_run.txt b/libs/hwui/unit_tests/how_to_run.txt
index a2d6a34726df..c11d6eb33358 100755
--- a/libs/hwui/unit_tests/how_to_run.txt
+++ b/libs/hwui/unit_tests/how_to_run.txt
@@ -1,4 +1,4 @@
-mmm -j8 $ANDROID_BUILD_TOP/frameworks/base/libs/hwui/unit_tests &&
+mmm -j8 frameworks/base/libs/hwui &&
adb push $ANDROID_PRODUCT_OUT/data/nativetest/hwui_unit_tests/hwui_unit_tests \
/data/nativetest/hwui_unit_tests/hwui_unit_tests &&
adb shell /data/nativetest/hwui_unit_tests/hwui_unit_tests
diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp
index 877a42216c27..9b70765ee8ad 100644
--- a/libs/hwui/utils/Blur.cpp
+++ b/libs/hwui/utils/Blur.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-
#include <math.h>
#include "Blur.h"
@@ -60,7 +58,9 @@ static float legacyConvertRadiusToSigma(float radius) {
return radius > 0 ? 0.3f * radius + 0.6f : 0.0f;
}
-void Blur::generateGaussianWeights(float* weights, int32_t radius) {
+void Blur::generateGaussianWeights(float* weights, float radius) {
+ int32_t intRadius = convertRadiusToInt(radius);
+
// Compute gaussian weights for the blur
// e is the euler's number
static float e = 2.718281828459045f;
@@ -68,7 +68,7 @@ void Blur::generateGaussianWeights(float* weights, int32_t radius) {
// g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
// x is of the form [-radius .. 0 .. radius]
// and sigma varies with radius.
- float sigma = legacyConvertRadiusToSigma((float) radius);
+ float sigma = legacyConvertRadiusToSigma(radius);
// Now compute the coefficints
// We will store some redundant values to save some math during
@@ -78,16 +78,16 @@ void Blur::generateGaussianWeights(float* weights, int32_t radius) {
float coeff2 = - 1.0f / (2.0f * sigma * sigma);
float normalizeFactor = 0.0f;
- for (int32_t r = -radius; r <= radius; r ++) {
+ for (int32_t r = -intRadius; r <= intRadius; r ++) {
float floatR = (float) r;
- weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
- normalizeFactor += weights[r + radius];
+ weights[r + intRadius] = coeff1 * pow(e, floatR * floatR * coeff2);
+ normalizeFactor += weights[r + intRadius];
}
//Now we need to normalize the weights because all our coefficients need to add up to one
normalizeFactor = 1.0f / normalizeFactor;
- for (int32_t r = -radius; r <= radius; r ++) {
- weights[r + radius] *= normalizeFactor;
+ for (int32_t r = -intRadius; r <= intRadius; r ++) {
+ weights[r + intRadius] *= normalizeFactor;
}
}
diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h
index b14533312719..3f21832bf2b5 100644
--- a/libs/hwui/utils/Blur.h
+++ b/libs/hwui/utils/Blur.h
@@ -34,7 +34,7 @@ public:
// accounts for that error and snaps to the appropriate integer boundary.
static uint32_t convertRadiusToInt(float radius);
- static void generateGaussianWeights(float* weights, int32_t radius);
+ static void generateGaussianWeights(float* weights, float radius);
static void horizontal(float* weights, int32_t radius, const uint8_t* source,
uint8_t* dest, int32_t width, int32_t height);
static void vertical(float* weights, int32_t radius, const uint8_t* source,
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index 59b12cf66a89..e6a4c03156b4 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -32,7 +32,7 @@
// The ideal size of a page allocation (these need to be multiples of 8)
-#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb
+#define INITIAL_PAGE_SIZE ((size_t)512) // 512b
#define MAX_PAGE_SIZE ((size_t)131072) // 128kb
// The maximum amount of wasted space we can have per page
@@ -40,7 +40,7 @@
// If this is too low, we will malloc too much
// Too high, and we may waste too much space
// Must be smaller than INITIAL_PAGE_SIZE
-#define MAX_WASTE_SIZE ((size_t)1024)
+#define MAX_WASTE_RATIO (0.5f)
#if ALIGN_DOUBLE
#define ALIGN_SZ (sizeof(double))
@@ -52,8 +52,8 @@
#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p)))
#if LOG_NDEBUG
-#define ADD_ALLOCATION(size)
-#define RM_ALLOCATION(size)
+#define ADD_ALLOCATION()
+#define RM_ALLOCATION()
#else
#include <utils/Thread.h>
#include <utils/Timers.h>
@@ -65,18 +65,18 @@ static void _logUsageLocked() {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now > s_nextLog) {
s_nextLog = now + milliseconds_to_nanoseconds(10);
- ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024);
+ ALOGV("Total pages allocated: %zu", s_totalAllocations);
}
}
-static void _addAllocation(size_t size) {
+static void _addAllocation(int count) {
android::AutoMutex lock(s_mutex);
- s_totalAllocations += size;
+ s_totalAllocations += count;
_logUsageLocked();
}
-#define ADD_ALLOCATION(size) _addAllocation(size);
-#define RM_ALLOCATION(size) _addAllocation(-size);
+#define ADD_ALLOCATION(size) _addAllocation(1);
+#define RM_ALLOCATION(size) _addAllocation(-1);
#endif
#define min(x,y) (((x) < (y)) ? (x) : (y))
@@ -114,7 +114,7 @@ private:
LinearAllocator::LinearAllocator()
: mPageSize(INITIAL_PAGE_SIZE)
- , mMaxAllocSize(MAX_WASTE_SIZE)
+ , mMaxAllocSize(INITIAL_PAGE_SIZE * MAX_WASTE_RATIO)
, mNext(0)
, mCurrentPage(0)
, mPages(0)
@@ -134,13 +134,13 @@ LinearAllocator::~LinearAllocator(void) {
Page* next = p->next();
p->~Page();
free(p);
- RM_ALLOCATION(mPageSize);
+ RM_ALLOCATION();
p = next;
}
}
void* LinearAllocator::start(Page* p) {
- return ALIGN_PTR(((size_t*)p) + sizeof(Page));
+ return ALIGN_PTR((size_t)p + sizeof(Page));
}
void* LinearAllocator::end(Page* p) {
@@ -156,6 +156,7 @@ void LinearAllocator::ensureNext(size_t size) {
if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) {
mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2);
+ mMaxAllocSize = mPageSize * MAX_WASTE_RATIO;
mPageSize = ALIGN(mPageSize);
}
mWastedSpace += mPageSize;
@@ -237,7 +238,7 @@ void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) {
LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) {
pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page));
- ADD_ALLOCATION(pageSize);
+ ADD_ALLOCATION();
mTotalAllocated += pageSize;
mPageCount++;
void* buf = malloc(pageSize);
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index d90dd825ea1d..e1c6f6c70428 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -29,6 +29,8 @@
#include <stddef.h>
#include <type_traits>
+#include <vector>
+
namespace android {
namespace uirenderer {
@@ -134,6 +136,54 @@ private:
size_t mDedicatedPageCount;
};
+template <class T>
+class LinearStdAllocator {
+public:
+ typedef T value_type; // needed to implement std::allocator
+ typedef T* pointer; // needed to implement std::allocator
+
+ LinearStdAllocator(LinearAllocator& allocator)
+ : linearAllocator(allocator) {}
+ LinearStdAllocator(const LinearStdAllocator& other)
+ : linearAllocator(other.linearAllocator) {}
+ ~LinearStdAllocator() {}
+
+ // rebind marks that allocators can be rebound to different types
+ template <class U>
+ struct rebind {
+ typedef LinearStdAllocator<U> other;
+ };
+ // enable allocators to be constructed from other templated types
+ template <class U>
+ LinearStdAllocator(const LinearStdAllocator<U>& other)
+ : linearAllocator(other.linearAllocator) {}
+
+ T* allocate(size_t num, const void* = 0) {
+ return (T*)(linearAllocator.alloc(num * sizeof(T)));
+ }
+
+ void deallocate(pointer p, size_t num) {
+ // attempt to rewind, but no guarantees
+ linearAllocator.rewindIfLastAlloc(p, num * sizeof(T));
+ }
+
+ // public so template copy constructor can access
+ LinearAllocator& linearAllocator;
+};
+
+// return that all specializations of LinearStdAllocator are interchangeable
+template <class T1, class T2>
+bool operator== (const LinearStdAllocator<T1>&, const LinearStdAllocator<T2>&) { return true; }
+template <class T1, class T2>
+bool operator!= (const LinearStdAllocator<T1>&, const LinearStdAllocator<T2>&) { return false; }
+
+template <class T>
+class LsaVector : public std::vector<T, LinearStdAllocator<T>> {
+public:
+ LsaVector(const LinearStdAllocator<T>& allocator)
+ : std::vector<T, LinearStdAllocator<T>>(allocator) {}
+};
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 9c3787cd1e7f..8d20f2142e73 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -16,6 +16,7 @@
#ifndef MATHUTILS_H
#define MATHUTILS_H
+#include <algorithm>
#include <math.h>
namespace android {
@@ -82,18 +83,8 @@ public:
}
template<typename T>
- static inline T max(T a, T b) {
- return a > b ? a : b;
- }
-
- template<typename T>
- static inline T min(T a, T b) {
- return a < b ? a : b;
- }
-
- template<typename T>
static inline T clamp(T a, T minValue, T maxValue) {
- return min(max(a, minValue), maxValue);
+ return std::min(std::max(a, minValue), maxValue);
}
inline static float lerp(float v1, float v2, float t) {
diff --git a/libs/hwui/utils/NinePatch.h b/libs/hwui/utils/NinePatch.h
new file mode 100644
index 000000000000..323e56312fb7
--- /dev/null
+++ b/libs/hwui/utils/NinePatch.h
@@ -0,0 +1,37 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_GRAPHICS_NINEPATCH_H
+#define ANDROID_GRAPHICS_NINEPATCH_H
+
+#include <androidfw/ResourceTypes.h>
+#include <cutils/compiler.h>
+
+#include "SkCanvas.h"
+#include "SkRegion.h"
+
+namespace android {
+
+class ANDROID_API NinePatch {
+public:
+ static void Draw(SkCanvas* canvas, const SkRect& bounds, const SkBitmap& bitmap,
+ const Res_png_9patch& chunk, const SkPaint* paint, SkRegion** outRegion);
+};
+
+} // namespace android
+
+#endif // ANDROID_GRAPHICS_NINEPATCH_H
diff --git a/libs/hwui/utils/NinePatchImpl.cpp b/libs/hwui/utils/NinePatchImpl.cpp
new file mode 100644
index 000000000000..f51f5df2402c
--- /dev/null
+++ b/libs/hwui/utils/NinePatchImpl.cpp
@@ -0,0 +1,326 @@
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "utils/NinePatch.h"
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkNinePatch.h"
+#include "SkPaint.h"
+#include "SkUnPreMultiply.h"
+
+#include <utils/Log.h>
+
+namespace android {
+
+static const bool kUseTrace = true;
+static bool gTrace = false;
+
+static bool getColor(const SkBitmap& bitmap, int x, int y, SkColor* c) {
+ switch (bitmap.colorType()) {
+ case kN32_SkColorType:
+ *c = SkUnPreMultiply::PMColorToColor(*bitmap.getAddr32(x, y));
+ break;
+ case kRGB_565_SkColorType:
+ *c = SkPixel16ToPixel32(*bitmap.getAddr16(x, y));
+ break;
+ case kARGB_4444_SkColorType:
+ *c = SkUnPreMultiply::PMColorToColor(
+ SkPixel4444ToPixel32(*bitmap.getAddr16(x, y)));
+ break;
+ case kIndex_8_SkColorType: {
+ SkColorTable* ctable = bitmap.getColorTable();
+ *c = SkUnPreMultiply::PMColorToColor(
+ (*ctable)[*bitmap.getAddr8(x, y)]);
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+static SkColor modAlpha(SkColor c, int alpha) {
+ int scale = alpha + (alpha >> 7);
+ int a = SkColorGetA(c) * scale >> 8;
+ return SkColorSetA(c, a);
+}
+
+static void drawStretchyPatch(SkCanvas* canvas, SkIRect& src, const SkRect& dst,
+ const SkBitmap& bitmap, const SkPaint& paint,
+ SkColor initColor, uint32_t colorHint,
+ bool hasXfer) {
+ if (colorHint != android::Res_png_9patch::NO_COLOR) {
+ ((SkPaint*)&paint)->setColor(modAlpha(colorHint, paint.getAlpha()));
+ canvas->drawRect(dst, paint);
+ ((SkPaint*)&paint)->setColor(initColor);
+ } else if (src.width() == 1 && src.height() == 1) {
+ SkColor c;
+ if (!getColor(bitmap, src.fLeft, src.fTop, &c)) {
+ goto SLOW_CASE;
+ }
+ if (0 != c || hasXfer) {
+ SkColor prev = paint.getColor();
+ ((SkPaint*)&paint)->setColor(c);
+ canvas->drawRect(dst, paint);
+ ((SkPaint*)&paint)->setColor(prev);
+ }
+ } else {
+ SLOW_CASE:
+ canvas->drawBitmapRect(bitmap, &src, dst, &paint);
+ }
+}
+
+SkScalar calculateStretch(SkScalar boundsLimit, SkScalar startingPoint,
+ int srcSpace, int numStrechyPixelsRemaining,
+ int numFixedPixelsRemaining) {
+ SkScalar spaceRemaining = boundsLimit - startingPoint;
+ SkScalar stretchySpaceRemaining =
+ spaceRemaining - SkIntToScalar(numFixedPixelsRemaining);
+ return srcSpace * stretchySpaceRemaining / numStrechyPixelsRemaining;
+}
+
+void NinePatch::Draw(SkCanvas* canvas, const SkRect& bounds,
+ const SkBitmap& bitmap, const Res_png_9patch& chunk,
+ const SkPaint* paint, SkRegion** outRegion) {
+ if (canvas && canvas->quickReject(bounds)) {
+ return;
+ }
+
+ SkPaint defaultPaint;
+ if (NULL == paint) {
+ // matches default dither in NinePatchDrawable.java.
+ defaultPaint.setDither(true);
+ paint = &defaultPaint;
+ }
+
+ const int32_t* xDivs = chunk.getXDivs();
+ const int32_t* yDivs = chunk.getYDivs();
+ // if our SkCanvas were back by GL we should enable this and draw this as
+ // a mesh, which will be faster in most cases.
+ if ((false)) {
+ SkNinePatch::DrawMesh(canvas, bounds, bitmap,
+ xDivs, chunk.numXDivs,
+ yDivs, chunk.numYDivs,
+ paint);
+ return;
+ }
+
+ if (kUseTrace) {
+ gTrace = true;
+ }
+
+ SkASSERT(canvas || outRegion);
+
+ if (kUseTrace) {
+ if (canvas) {
+ const SkMatrix& m = canvas->getTotalMatrix();
+ ALOGV("ninepatch [%g %g %g] [%g %g %g]\n",
+ SkScalarToFloat(m[0]), SkScalarToFloat(m[1]), SkScalarToFloat(m[2]),
+ SkScalarToFloat(m[3]), SkScalarToFloat(m[4]), SkScalarToFloat(m[5]));
+ }
+
+ ALOGV("======== ninepatch bounds [%g %g]\n", SkScalarToFloat(bounds.width()),
+ SkScalarToFloat(bounds.height()));
+ ALOGV("======== ninepatch paint bm [%d,%d]\n", bitmap.width(), bitmap.height());
+ ALOGV("======== ninepatch xDivs [%d,%d]\n", xDivs[0], xDivs[1]);
+ ALOGV("======== ninepatch yDivs [%d,%d]\n", yDivs[0], yDivs[1]);
+ }
+
+ if (bounds.isEmpty() ||
+ bitmap.width() == 0 || bitmap.height() == 0 ||
+ (paint && paint->getXfermode() == NULL && paint->getAlpha() == 0))
+ {
+ if (kUseTrace) {
+ ALOGV("======== abort ninepatch draw\n");
+ }
+ return;
+ }
+
+ // should try a quick-reject test before calling lockPixels
+
+ SkAutoLockPixels alp(bitmap);
+ // after the lock, it is valid to check getPixels()
+ if (bitmap.getPixels() == NULL)
+ return;
+
+ const bool hasXfer = paint->getXfermode() != NULL;
+ SkRect dst;
+ SkIRect src;
+
+ const int32_t x0 = xDivs[0];
+ const int32_t y0 = yDivs[0];
+ const SkColor initColor = ((SkPaint*)paint)->getColor();
+ const uint8_t numXDivs = chunk.numXDivs;
+ const uint8_t numYDivs = chunk.numYDivs;
+ int i;
+ int j;
+ int colorIndex = 0;
+ uint32_t color;
+ bool xIsStretchable;
+ const bool initialXIsStretchable = (x0 == 0);
+ bool yIsStretchable = (y0 == 0);
+ const int bitmapWidth = bitmap.width();
+ const int bitmapHeight = bitmap.height();
+
+ // Number of bytes needed for dstRights array.
+ // Need to cast numXDivs to a larger type to avoid overflow.
+ const size_t dstBytes = ((size_t) numXDivs + 1) * sizeof(SkScalar);
+ SkScalar* dstRights = (SkScalar*) alloca(dstBytes);
+ bool dstRightsHaveBeenCached = false;
+
+ int numStretchyXPixelsRemaining = 0;
+ for (i = 0; i < numXDivs; i += 2) {
+ numStretchyXPixelsRemaining += xDivs[i + 1] - xDivs[i];
+ }
+ int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining;
+ int numStretchyYPixelsRemaining = 0;
+ for (i = 0; i < numYDivs; i += 2) {
+ numStretchyYPixelsRemaining += yDivs[i + 1] - yDivs[i];
+ }
+ int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining;
+
+ if (kUseTrace) {
+ ALOGV("NinePatch [%d %d] bounds [%g %g %g %g] divs [%d %d]\n",
+ bitmap.width(), bitmap.height(),
+ SkScalarToFloat(bounds.fLeft), SkScalarToFloat(bounds.fTop),
+ SkScalarToFloat(bounds.width()), SkScalarToFloat(bounds.height()),
+ numXDivs, numYDivs);
+ }
+
+ src.fTop = 0;
+ dst.fTop = bounds.fTop;
+ // The first row always starts with the top being at y=0 and the bottom
+ // being either yDivs[1] (if yDivs[0]=0) or yDivs[0]. In the former case
+ // the first row is stretchable along the Y axis, otherwise it is fixed.
+ // The last row always ends with the bottom being bitmap.height and the top
+ // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
+ // yDivs[numYDivs-1]. In the former case the last row is stretchable along
+ // the Y axis, otherwise it is fixed.
+ //
+ // The first and last columns are similarly treated with respect to the X
+ // axis.
+ //
+ // The above is to help explain some of the special casing that goes on the
+ // code below.
+
+ // The initial yDiv and whether the first row is considered stretchable or
+ // not depends on whether yDiv[0] was zero or not.
+ for (j = yIsStretchable ? 1 : 0;
+ j <= numYDivs && src.fTop < bitmapHeight;
+ j++, yIsStretchable = !yIsStretchable) {
+ src.fLeft = 0;
+ dst.fLeft = bounds.fLeft;
+ if (j == numYDivs) {
+ src.fBottom = bitmapHeight;
+ dst.fBottom = bounds.fBottom;
+ } else {
+ src.fBottom = yDivs[j];
+ const int srcYSize = src.fBottom - src.fTop;
+ if (yIsStretchable) {
+ dst.fBottom = dst.fTop + calculateStretch(bounds.fBottom, dst.fTop,
+ srcYSize,
+ numStretchyYPixelsRemaining,
+ numFixedYPixelsRemaining);
+ numStretchyYPixelsRemaining -= srcYSize;
+ } else {
+ dst.fBottom = dst.fTop + SkIntToScalar(srcYSize);
+ numFixedYPixelsRemaining -= srcYSize;
+ }
+ }
+
+ xIsStretchable = initialXIsStretchable;
+ // The initial xDiv and whether the first column is considered
+ // stretchable or not depends on whether xDiv[0] was zero or not.
+ const uint32_t* colors = chunk.getColors();
+ for (i = xIsStretchable ? 1 : 0;
+ i <= numXDivs && src.fLeft < bitmapWidth;
+ i++, xIsStretchable = !xIsStretchable) {
+ color = colors[colorIndex++];
+ if (i == numXDivs) {
+ src.fRight = bitmapWidth;
+ dst.fRight = bounds.fRight;
+ } else {
+ src.fRight = xDivs[i];
+ if (dstRightsHaveBeenCached) {
+ dst.fRight = dstRights[i];
+ } else {
+ const int srcXSize = src.fRight - src.fLeft;
+ if (xIsStretchable) {
+ dst.fRight = dst.fLeft + calculateStretch(bounds.fRight, dst.fLeft,
+ srcXSize,
+ numStretchyXPixelsRemaining,
+ numFixedXPixelsRemaining);
+ numStretchyXPixelsRemaining -= srcXSize;
+ } else {
+ dst.fRight = dst.fLeft + SkIntToScalar(srcXSize);
+ numFixedXPixelsRemaining -= srcXSize;
+ }
+ dstRights[i] = dst.fRight;
+ }
+ }
+ // If this horizontal patch is too small to be displayed, leave
+ // the destination left edge where it is and go on to the next patch
+ // in the source.
+ if (src.fLeft >= src.fRight) {
+ src.fLeft = src.fRight;
+ continue;
+ }
+ // Make sure that we actually have room to draw any bits
+ if (dst.fRight <= dst.fLeft || dst.fBottom <= dst.fTop) {
+ goto nextDiv;
+ }
+ // If this patch is transparent, skip and don't draw.
+ if (color == android::Res_png_9patch::TRANSPARENT_COLOR && !hasXfer) {
+ if (outRegion) {
+ if (*outRegion == NULL) {
+ *outRegion = new SkRegion();
+ }
+ SkIRect idst;
+ dst.round(&idst);
+ //ALOGI("Adding trans rect: (%d,%d)-(%d,%d)\n",
+ // idst.fLeft, idst.fTop, idst.fRight, idst.fBottom);
+ (*outRegion)->op(idst, SkRegion::kUnion_Op);
+ }
+ goto nextDiv;
+ }
+ if (canvas) {
+ if (kUseTrace) {
+ ALOGV("-- src [%d %d %d %d] dst [%g %g %g %g]\n",
+ src.fLeft, src.fTop, src.width(), src.height(),
+ SkScalarToFloat(dst.fLeft), SkScalarToFloat(dst.fTop),
+ SkScalarToFloat(dst.width()), SkScalarToFloat(dst.height()));
+ if (2 == src.width() && SkIntToScalar(5) == dst.width()) {
+ ALOGV("--- skip patch\n");
+ }
+ }
+ drawStretchyPatch(canvas, src, dst, bitmap, *paint, initColor,
+ color, hasXfer);
+ }
+
+nextDiv:
+ src.fLeft = src.fRight;
+ dst.fLeft = dst.fRight;
+ }
+ src.fTop = src.fBottom;
+ dst.fTop = dst.fBottom;
+ dstRightsHaveBeenCached = true;
+ }
+}
+
+} // namespace android
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index ba02f5f1a77d..db537130e12e 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -16,12 +16,20 @@
#ifndef PAINT_UTILS_H
#define PAINT_UTILS_H
+#include <utils/Blur.h>
+
#include <SkColorFilter.h>
+#include <SkDrawLooper.h>
+#include <SkShader.h>
#include <SkXfermode.h>
namespace android {
namespace uirenderer {
+/**
+ * Utility methods for accessing data within SkPaint, and providing defaults
+ * with optional SkPaint pointers.
+ */
class PaintUtils {
public:
@@ -73,6 +81,39 @@ public:
return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
}
+ struct TextShadow {
+ SkScalar radius;
+ float dx;
+ float dy;
+ SkColor color;
+ };
+
+ static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
+ SkDrawLooper::BlurShadowRec blur;
+ if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
+ if (textShadow) {
+ textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
+ textShadow->dx = blur.fOffset.fX;
+ textShadow->dy = blur.fOffset.fY;
+ textShadow->color = blur.fColor;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ static inline bool hasTextShadow(const SkPaint* paint) {
+ return getTextShadow(paint, nullptr);
+ }
+
+ static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
+ return paint ? getXfermode(paint->getXfermode()) : SkXfermode::kSrcOver_Mode;
+ }
+
+ static inline int getAlphaDirect(const SkPaint* paint) {
+ return paint ? paint->getAlpha() : 255;
+ }
+
}; // class PaintUtils
} /* namespace uirenderer */
diff --git a/libs/hwui/utils/SortedList.h b/libs/hwui/utils/SortedList.h
deleted file mode 100644
index a2c8c52fcbc7..000000000000
--- a/libs/hwui/utils/SortedList.h
+++ /dev/null
@@ -1,242 +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_SORTED_LIST_H
-#define ANDROID_HWUI_SORTED_LIST_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Vector.h>
-#include <utils/TypeHelpers.h>
-
-#include "SortedListImpl.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Sorted list
-///////////////////////////////////////////////////////////////////////////////
-
-template<class TYPE>
-class SortedList: private SortedListImpl {
-public:
- typedef TYPE value_type;
-
- SortedList();
- SortedList(const SortedList<TYPE>& rhs);
- virtual ~SortedList();
-
- const SortedList<TYPE>& operator =(const SortedList<TYPE>& rhs) const;
- SortedList<TYPE>& operator =(const SortedList<TYPE>& rhs);
-
- inline void clear() {
- VectorImpl::clear();
- }
-
- inline size_t size() const {
- return VectorImpl::size();
- }
-
- inline bool isEmpty() const {
- return VectorImpl::isEmpty();
- }
-
- inline size_t capacity() const {
- return VectorImpl::capacity();
- }
-
- inline ssize_t setCapacity(size_t size) {
- return VectorImpl::setCapacity(size);
- }
-
- inline const TYPE* array() const;
-
- TYPE* editArray();
-
- ssize_t indexOf(const TYPE& item) const;
- size_t orderOf(const TYPE& item) const;
-
- inline const TYPE& operator [](size_t index) const;
- inline const TYPE& itemAt(size_t index) const;
- const TYPE& top() const;
- const TYPE& mirrorItemAt(ssize_t index) const;
-
- ssize_t add(const TYPE& item);
-
- TYPE& editItemAt(size_t index) {
- return *(static_cast<TYPE *> (VectorImpl::editItemLocation(index)));
- }
-
- ssize_t merge(const Vector<TYPE>& vector);
- ssize_t merge(const SortedList<TYPE>& vector);
-
- ssize_t remove(const TYPE&);
-
- inline ssize_t removeItemsAt(size_t index, size_t count = 1);
- inline ssize_t removeAt(size_t index) {
- return removeItemsAt(index);
- }
-
-protected:
- virtual void do_construct(void* storage, size_t num) const override;
- virtual void do_destroy(void* storage, size_t num) const override;
- virtual void do_copy(void* dest, const void* from, size_t num) const override;
- virtual void do_splat(void* dest, const void* item, size_t num) const override;
- virtual void do_move_forward(void* dest, const void* from, size_t num) const override;
- virtual void do_move_backward(void* dest, const void* from, size_t num) const override;
- virtual int do_compare(const void* lhs, const void* rhs) const override;
-}; // class SortedList
-
-///////////////////////////////////////////////////////////////////////////////
-// Implementation
-///////////////////////////////////////////////////////////////////////////////
-
-template<class TYPE>
-inline SortedList<TYPE>::SortedList():
- SortedListImpl(sizeof(TYPE), ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
- | (traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
- | (traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))) {
-}
-
-template<class TYPE>
-inline SortedList<TYPE>::SortedList(const SortedList<TYPE>& rhs): SortedListImpl(rhs) {
-}
-
-template<class TYPE> inline SortedList<TYPE>::~SortedList() {
- finish_vector();
-}
-
-template<class TYPE>
-inline SortedList<TYPE>& SortedList<TYPE>::operator =(const SortedList<TYPE>& rhs) {
- SortedListImpl::operator =(rhs);
- return *this;
-}
-
-template<class TYPE>
-inline const SortedList<TYPE>& SortedList<TYPE>::operator =(
- const SortedList<TYPE>& rhs) const {
- SortedListImpl::operator =(rhs);
- return *this;
-}
-
-template<class TYPE>
-inline const TYPE* SortedList<TYPE>::array() const {
- return static_cast<const TYPE *> (arrayImpl());
-}
-
-template<class TYPE>
-inline TYPE* SortedList<TYPE>::editArray() {
- return static_cast<TYPE *> (editArrayImpl());
-}
-
-template<class TYPE>
-inline const TYPE& SortedList<TYPE>::operator[](size_t index) const {
- assert( index<size() );
- return *(array() + index);
-}
-
-template<class TYPE>
-inline const TYPE& SortedList<TYPE>::itemAt(size_t index) const {
- return operator[](index);
-}
-
-template<class TYPE>
-inline const TYPE& SortedList<TYPE>::mirrorItemAt(ssize_t index) const {
- assert( (index>0 ? index : -index)<size() );
- return *(array() + ((index < 0) ? (size() - index) : index));
-}
-
-template<class TYPE>
-inline const TYPE& SortedList<TYPE>::top() const {
- return *(array() + size() - 1);
-}
-
-template<class TYPE>
-inline ssize_t SortedList<TYPE>::add(const TYPE& item) {
- return SortedListImpl::add(&item);
-}
-
-template<class TYPE>
-inline ssize_t SortedList<TYPE>::indexOf(const TYPE& item) const {
- return SortedListImpl::indexOf(&item);
-}
-
-template<class TYPE>
-inline size_t SortedList<TYPE>::orderOf(const TYPE& item) const {
- return SortedListImpl::orderOf(&item);
-}
-
-template<class TYPE>
-inline ssize_t SortedList<TYPE>::merge(const Vector<TYPE>& vector) {
- return SortedListImpl::merge(reinterpret_cast<const VectorImpl&> (vector));
-}
-
-template<class TYPE>
-inline ssize_t SortedList<TYPE>::merge(const SortedList<TYPE>& vector) {
- return SortedListImpl::merge(reinterpret_cast<const SortedListImpl&> (vector));
-}
-
-template<class TYPE>
-inline ssize_t SortedList<TYPE>::remove(const TYPE& item) {
- return SortedListImpl::remove(&item);
-}
-
-template<class TYPE>
-inline ssize_t SortedList<TYPE>::removeItemsAt(size_t index, size_t count) {
- return VectorImpl::removeItemsAt(index, count);
-}
-
-template<class TYPE>
-void SortedList<TYPE>::do_construct(void* storage, size_t num) const {
- construct_type(reinterpret_cast<TYPE*> (storage), num);
-}
-
-template<class TYPE>
-void SortedList<TYPE>::do_destroy(void* storage, size_t num) const {
- destroy_type(reinterpret_cast<TYPE*> (storage), num);
-}
-
-template<class TYPE>
-void SortedList<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
- copy_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num);
-}
-
-template<class TYPE>
-void SortedList<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
- splat_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (item), num);
-}
-
-template<class TYPE>
-void SortedList<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
- move_forward_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num);
-}
-
-template<class TYPE>
-void SortedList<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
- move_backward_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num);
-}
-
-template<class TYPE>
-int SortedList<TYPE>::do_compare(const void* lhs, const void* rhs) const {
- return compare_type(*reinterpret_cast<const TYPE*> (lhs), *reinterpret_cast<const TYPE*> (rhs));
-}
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_SORTED_LIST_H
diff --git a/libs/hwui/utils/SortedListImpl.cpp b/libs/hwui/utils/SortedListImpl.cpp
deleted file mode 100644
index 35171d5b1a5b..000000000000
--- a/libs/hwui/utils/SortedListImpl.cpp
+++ /dev/null
@@ -1,126 +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 "SortedListImpl.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Sorted list implementation, not for direct use
-///////////////////////////////////////////////////////////////////////////////
-
-SortedListImpl::SortedListImpl(size_t itemSize, uint32_t flags): VectorImpl(itemSize, flags) {
-}
-
-SortedListImpl::SortedListImpl(const VectorImpl& rhs): VectorImpl(rhs) {
-}
-
-SortedListImpl::~SortedListImpl() {
-}
-
-SortedListImpl& SortedListImpl::operator =(const SortedListImpl& rhs) {
- return static_cast<SortedListImpl&>
- (VectorImpl::operator =(static_cast<const VectorImpl&> (rhs)));
-}
-
-ssize_t SortedListImpl::indexOf(const void* item) const {
- return _indexOrderOf(item);
-}
-
-size_t SortedListImpl::orderOf(const void* item) const {
- size_t o;
- _indexOrderOf(item, &o);
- return o;
-}
-
-ssize_t SortedListImpl::_indexOrderOf(const void* item, size_t* order) const {
- // binary search
- ssize_t err = NAME_NOT_FOUND;
- ssize_t l = 0;
- ssize_t h = size() - 1;
- ssize_t mid;
- const void* a = arrayImpl();
- const size_t s = itemSize();
- while (l <= h) {
- mid = l + (h - l) / 2;
- const void* const curr = reinterpret_cast<const char *> (a) + (mid * s);
- const int c = do_compare(curr, item);
- if (c == 0) {
- err = l = mid;
- break;
- } else if (c < 0) {
- l = mid + 1;
- } else {
- h = mid - 1;
- }
- }
- if (order) {
- *order = l;
- }
- return err;
-}
-
-ssize_t SortedListImpl::add(const void* item) {
- size_t order;
- ssize_t index = _indexOrderOf(item, &order);
- index = VectorImpl::insertAt(item, order, 1);
- return index;
-}
-
-ssize_t SortedListImpl::merge(const VectorImpl& vector) {
- // naive merge...
- if (!vector.isEmpty()) {
- const void* buffer = vector.arrayImpl();
- const size_t is = itemSize();
- size_t s = vector.size();
- for (size_t i = 0; i < s; i++) {
- ssize_t err = add(reinterpret_cast<const char*> (buffer) + i * is);
- if (err < 0) {
- return err;
- }
- }
- }
- return NO_ERROR;
-}
-
-ssize_t SortedListImpl::merge(const SortedListImpl& vector) {
- // we've merging a sorted vector... nice!
- ssize_t err = NO_ERROR;
- if (!vector.isEmpty()) {
- // first take care of the case where the vectors are sorted together
- if (do_compare(vector.itemLocation(vector.size() - 1), arrayImpl()) <= 0) {
- err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&> (vector), 0);
- } else if (do_compare(vector.arrayImpl(), itemLocation(size() - 1)) >= 0) {
- err = VectorImpl::appendVector(static_cast<const VectorImpl&> (vector));
- } else {
- // this could be made a little better
- err = merge(static_cast<const VectorImpl&> (vector));
- }
- }
- return err;
-}
-
-ssize_t SortedListImpl::remove(const void* item) {
- ssize_t i = indexOf(item);
- if (i >= 0) {
- VectorImpl::removeItemsAt(i, 1);
- }
- return i;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/utils/SortedListImpl.h b/libs/hwui/utils/SortedListImpl.h
deleted file mode 100644
index b1018265566a..000000000000
--- a/libs/hwui/utils/SortedListImpl.h
+++ /dev/null
@@ -1,65 +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_SORTED_LIST_IMPL_H
-#define ANDROID_HWUI_SORTED_LIST_IMPL_H
-
-#include <utils/VectorImpl.h>
-
-namespace android {
-namespace uirenderer {
-
-class SortedListImpl: public VectorImpl {
-public:
- SortedListImpl(size_t itemSize, uint32_t flags);
- SortedListImpl(const VectorImpl& rhs);
- virtual ~SortedListImpl();
-
- SortedListImpl& operator =(const SortedListImpl& rhs);
-
- ssize_t indexOf(const void* item) const;
- size_t orderOf(const void* item) const;
- ssize_t add(const void* item);
- ssize_t merge(const VectorImpl& vector);
- ssize_t merge(const SortedListImpl& vector);
- ssize_t remove(const void* item);
-
-protected:
- virtual int do_compare(const void* lhs, const void* rhs) const = 0;
-
-private:
- ssize_t _indexOrderOf(const void* item, size_t* order = nullptr) const;
-
- // these are made private, because they can't be used on a SortedVector
- // (they don't have an implementation either)
- ssize_t add();
- void pop();
- void push();
- void push(const void* item);
- ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
- ssize_t appendVector(const VectorImpl& vector);
- ssize_t insertArrayAt(const void* array, size_t index, size_t length);
- ssize_t appendArray(const void* array, size_t length);
- ssize_t insertAt(size_t where, size_t numItems = 1);
- ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
- ssize_t replaceAt(size_t index);
- ssize_t replaceAt(const void* item, size_t index);
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_SORTED_LIST_IMPL_H
diff --git a/libs/hwui/utils/StringUtils.cpp b/libs/hwui/utils/StringUtils.cpp
new file mode 100644
index 000000000000..a1df0e7966a9
--- /dev/null
+++ b/libs/hwui/utils/StringUtils.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "StringUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+StringCollection::StringCollection(const char* spacedList) {
+ const char* current = spacedList;
+ const char* head = current;
+ do {
+ head = strchr(current, ' ');
+ std::string s(current, head ? head - current : strlen(current));
+ if (s.length()) {
+ mSet.insert(s);
+ }
+ current = head + 1;
+ } while (head);
+}
+
+bool StringCollection::has(const char* s) {
+ return mSet.find(std::string(s)) != mSet.end();
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h
new file mode 100644
index 000000000000..ef2a6d5c031a
--- /dev/null
+++ b/libs/hwui/utils/StringUtils.h
@@ -0,0 +1,36 @@
+/*
+ * 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 STRING_UTILS_H
+#define STRING_UTILS_H
+
+#include <string>
+#include <unordered_set>
+
+namespace android {
+namespace uirenderer {
+
+class StringCollection {
+public:
+ StringCollection(const char* spacedList);
+ bool has(const char* string);
+private:
+ std::unordered_set<std::string> mSet;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* GLUTILS_H */
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 11527378f586..0f86bc614b2b 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -78,6 +78,7 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>&
mLocked.pointerAlpha = 0.0f; // pointer is initially faded
mLocked.pointerSprite = mSpriteController->createSprite();
mLocked.pointerIconChanged = false;
+ mLocked.requestedPointerShape = 0;
mLocked.buttonState = 0;
@@ -231,6 +232,10 @@ void PointerController::unfade(Transition transition) {
void PointerController::setPresentation(Presentation presentation) {
AutoMutex _l(mLock);
+ if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) {
+ mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources);
+ }
+
if (mLocked.presentation != presentation) {
mLocked.presentation = presentation;
mLocked.presentationChanged = true;
@@ -391,6 +396,15 @@ void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_
updatePointerLocked();
}
+void PointerController::updatePointerShape(int iconId) {
+ AutoMutex _l(mLock);
+ if (mLocked.requestedPointerShape != iconId) {
+ mLocked.requestedPointerShape = iconId;
+ mLocked.presentationChanged = true;
+ updatePointerLocked();
+ }
+}
+
void PointerController::setPointerIcon(const SpriteIcon& icon) {
AutoMutex _l(mLock);
@@ -497,8 +511,22 @@ void PointerController::updatePointerLocked() {
}
if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
- mLocked.pointerSprite->setIcon(mLocked.presentation == PRESENTATION_POINTER
- ? mLocked.pointerIcon : mResources.spotAnchor);
+ if (mLocked.presentation == PRESENTATION_POINTER) {
+ if (mLocked.requestedPointerShape == 0) {
+ mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
+ } else {
+ std::map<int, SpriteIcon>::const_iterator iter =
+ mLocked.additionalMouseResources.find(mLocked.requestedPointerShape);
+ if (iter != mLocked.additionalMouseResources.end()) {
+ mLocked.pointerSprite->setIcon(iter->second);
+ } else {
+ ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerShape);
+ mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
+ }
+ }
+ } else {
+ mLocked.pointerSprite->setIcon(mResources.spotAnchor);
+ }
mLocked.pointerIconChanged = false;
mLocked.presentationChanged = false;
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index b9e4ce7e7ed0..308ff1242064 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -19,6 +19,8 @@
#include "SpriteController.h"
+#include <map>
+
#include <ui/DisplayInfo.h>
#include <input/Input.h>
#include <inputflinger/PointerControllerInterface.h>
@@ -40,7 +42,6 @@ struct PointerResources {
SpriteIcon spotAnchor;
};
-
/*
* Pointer controller policy interface.
*
@@ -57,6 +58,7 @@ protected:
public:
virtual void loadPointerResources(PointerResources* outResources) = 0;
+ virtual void loadAdditionalMouseResources(std::map<int, SpriteIcon>* outResources) = 0;
};
@@ -93,6 +95,7 @@ public:
const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
virtual void clearSpots();
+ void updatePointerShape(int iconId);
void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
void setPointerIcon(const SpriteIcon& icon);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
@@ -155,6 +158,10 @@ private:
SpriteIcon pointerIcon;
bool pointerIconChanged;
+ std::map<int, SpriteIcon> additionalMouseResources;
+
+ int32_t requestedPointerShape;
+
int32_t buttonState;
Vector<Spot*> spots;