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