diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/androidfw/AssetManager2.cpp | 68 | ||||
| -rw-r--r-- | libs/androidfw/tests/Theme_test.cpp | 73 | ||||
| -rw-r--r-- | libs/androidfw/tests/data/styles/R.h | 1 | ||||
| -rwxr-xr-x | libs/androidfw/tests/data/styles/build | 4 | ||||
| -rw-r--r-- | libs/androidfw/tests/data/styles/res/values/styles.xml | 10 | ||||
| -rw-r--r-- | libs/androidfw/tests/data/styles/styles.apk | bin | 2550 -> 2774 bytes | |||
| -rw-r--r-- | libs/hwui/HWUIProperties.sysprop | 7 | ||||
| -rw-r--r-- | libs/hwui/Properties.cpp | 8 | ||||
| -rw-r--r-- | libs/hwui/Properties.h | 4 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 30 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 4 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 6 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 17 | ||||
| -rw-r--r-- | libs/hwui/tests/common/TestScene.h | 1 | ||||
| -rw-r--r-- | libs/hwui/tests/macrobench/TestSceneRunner.cpp | 5 | ||||
| -rw-r--r-- | libs/hwui/tests/macrobench/main.cpp | 13 | ||||
| -rw-r--r-- | libs/hwui/tests/unit/CommonPoolTests.cpp | 123 | ||||
| -rw-r--r-- | libs/hwui/thread/CommonPool.h | 6 | ||||
| -rw-r--r-- | libs/protoutil/src/ProtoFileReader.cpp | 1 |
19 files changed, 328 insertions, 53 deletions
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 951970464bfe..1b515ad41e68 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -1319,7 +1319,7 @@ void Theme::SetTo(const Theme& o) { typedef std::map<int, int> SourceToDestinationRuntimePackageMap; std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map; - // Determine which ApkAssets are loaded in both theme AssetManagers + // Determine which ApkAssets are loaded in both theme AssetManagers. std::vector<const ApkAssets*> src_assets = o.asset_manager_->GetApkAssets(); for (size_t i = 0; i < src_assets.size(); i++) { const ApkAssets* src_asset = src_assets[i]; @@ -1328,7 +1328,7 @@ void Theme::SetTo(const Theme& o) { for (size_t j = 0; j < dest_assets.size(); j++) { const ApkAssets* dest_asset = dest_assets[j]; - // Map the runtime package of the source apk asset to the destination apk asset + // Map the runtime package of the source apk asset to the destination apk asset. if (src_asset->GetPath() == dest_asset->GetPath()) { const std::vector<std::unique_ptr<const LoadedPackage>>& src_packages = src_asset->GetLoadedArsc()->GetPackages(); @@ -1353,15 +1353,14 @@ void Theme::SetTo(const Theme& o) { package_map[src_package_id] = dest_package_id; } - src_to_dest_asset_cookies.insert(std::pair<ApkAssetsCookie, ApkAssetsCookie>(i, j)); - src_asset_cookie_id_map.insert( - std::pair<ApkAssetsCookie, SourceToDestinationRuntimePackageMap>(i, package_map)); + src_to_dest_asset_cookies.insert(std::make_pair(i, j)); + src_asset_cookie_id_map.insert(std::make_pair(i, package_map)); break; } } } - // Reset the data in the destination theme + // Reset the data in the destination theme. for (size_t p = 0; p < packages_.size(); p++) { if (packages_[p] != nullptr) { packages_[p].reset(); @@ -1387,16 +1386,17 @@ void Theme::SetTo(const Theme& o) { continue; } + bool is_reference = (entry.value.dataType == Res_value::TYPE_ATTRIBUTE + || entry.value.dataType == Res_value::TYPE_REFERENCE + || entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE + || entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE) + && entry.value.data != 0x0; + // If the attribute value represents an attribute or reference, the package id of the - // value needs to be rewritten to the package id of the value in the destination - uint32_t attribue_data = entry.value.data; - if ((entry.value.dataType == Res_value::TYPE_ATTRIBUTE - || entry.value.dataType == Res_value::TYPE_REFERENCE - || entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE - || entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE) - && attribue_data != 0x0) { - - // Determine the package id of the reference in the destination AssetManager + // value needs to be rewritten to the package id of the value in the destination. + uint32_t attribute_data = entry.value.data; + if (is_reference) { + // Determine the package id of the reference in the destination AssetManager. auto value_package_map = src_asset_cookie_id_map.find(entry.cookie); if (value_package_map == src_asset_cookie_id_map.end()) { continue; @@ -1408,14 +1408,28 @@ void Theme::SetTo(const Theme& o) { continue; } - attribue_data = fix_package_id(entry.value.data, value_dest_package->second); + attribute_data = fix_package_id(entry.value.data, value_dest_package->second); + } + + // Find the cookie of the value in the destination. If the source apk is not loaded in the + // destination, only copy resources that do not reference resources in the source. + ApkAssetsCookie data_dest_cookie; + auto value_dest_cookie = src_to_dest_asset_cookies.find(entry.cookie); + if (value_dest_cookie != src_to_dest_asset_cookies.end()) { + data_dest_cookie = value_dest_cookie->second; + } else { + if (is_reference || entry.value.dataType == Res_value::TYPE_STRING) { + continue; + } else { + data_dest_cookie = 0x0; + } } // The package id of the attribute needs to be rewritten to the package id of the - // attribute in the destination + // attribute in the destination. int attribute_dest_package_id = p; if (attribute_dest_package_id != 0x01) { - // Find the cookie of the attribute resource id + // Find the cookie of the attribute resource id in the source AssetManager FindEntryResult attribute_entry_result; ApkAssetsCookie attribute_cookie = o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , @@ -1423,7 +1437,7 @@ void Theme::SetTo(const Theme& o) { true /* ignore_configuration */, &attribute_entry_result); - // Determine the package id of the attribute in the destination AssetManager + // Determine the package id of the attribute in the destination AssetManager. auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie); if (attribute_package_map == src_asset_cookie_id_map.end()) { continue; @@ -1436,13 +1450,13 @@ void Theme::SetTo(const Theme& o) { attribute_dest_package_id = attribute_dest_package->second; } - // Lazily instantiate the destination package + // Lazily instantiate the destination package. std::unique_ptr<Package>& dest_package = packages_[attribute_dest_package_id]; if (dest_package == nullptr) { dest_package.reset(new Package()); } - // Lazily instantiate and resize the destination type + // Lazily instantiate and resize the destination type. util::unique_cptr<ThemeType>& dest_type = dest_package->types[t]; if (dest_type == nullptr || dest_type->entry_count < type->entry_count) { const size_t type_alloc_size = sizeof(ThemeType) @@ -1450,7 +1464,7 @@ void Theme::SetTo(const Theme& o) { void* dest_data = malloc(type_alloc_size); memset(dest_data, 0, type->entry_count * sizeof(ThemeEntry)); - // Copy the existing destination type values if the type is resized + // Copy the existing destination type values if the type is resized. if (dest_type != nullptr) { memcpy(dest_data, type, sizeof(ThemeType) + (dest_type->entry_count * sizeof(ThemeEntry))); @@ -1460,15 +1474,9 @@ void Theme::SetTo(const Theme& o) { dest_type->entry_count = type->entry_count; } - // Find the cookie of the value in the destination - auto value_dest_cookie = src_to_dest_asset_cookies.find(entry.cookie); - if (value_dest_cookie == src_to_dest_asset_cookies.end()) { - continue; - } - - dest_type->entries[e].cookie = value_dest_cookie->second; + dest_type->entries[e].cookie = data_dest_cookie; dest_type->entries[e].value.dataType = entry.value.dataType; - dest_type->entries[e].value.data = attribue_data; + dest_type->entries[e].value.data = attribute_data; dest_type->entries[e].type_spec_flags = entry.type_spec_flags; } } diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index 2c39ceead123..be5ecd94a588 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -282,48 +282,81 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) { } TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) { - AssetManager2 assetmanager_one; - assetmanager_one.SetApkAssets({system_assets_.get(), lib_one_assets_.get(), style_assets_.get(), + AssetManager2 assetmanager_dst; + assetmanager_dst.SetApkAssets({system_assets_.get(), lib_one_assets_.get(), style_assets_.get(), libclient_assets_.get()}); - AssetManager2 assetmanager_two; - assetmanager_two.SetApkAssets({system_assets_.get(), lib_two_assets_.get(), lib_one_assets_.get(), + AssetManager2 assetmanager_src; + assetmanager_src.SetApkAssets({system_assets_.get(), lib_two_assets_.get(), lib_one_assets_.get(), style_assets_.get()}); - auto theme_one = assetmanager_one.NewTheme(); - ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne)); + auto theme_dst = assetmanager_dst.NewTheme(); + ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne)); - auto theme_two = assetmanager_two.NewTheme(); - ASSERT_TRUE(theme_two->ApplyStyle(R::style::Theme_One)); - ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleTwo)); - ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03), + auto theme_src = assetmanager_src.NewTheme(); + ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One)); + ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo)); + ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03), false /*force*/)); - ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02), + ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02), false /*force*/)); - theme_one->SetTo(*theme_two); + theme_dst->SetTo(*theme_src); Res_value value; uint32_t flags; - // System resources (present in destination asset manager) - EXPECT_EQ(0, theme_one->GetAttribute(R::attr::foreground, &value, &flags)); + // System resources (present in destination asset manager). + EXPECT_EQ(0, theme_dst->GetAttribute(R::attr::foreground, &value, &flags)); // The cookie of the style asset is 3 in the source and 2 in the destination. - // Check that the cookie has been rewritten to the destination values - EXPECT_EQ(2, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags)); + // Check that the cookie has been rewritten to the destination values. + EXPECT_EQ(2, theme_dst->GetAttribute(app::R::attr::attr_one, &value, &flags)); // The cookie of the lib_one asset is 2 in the source and 1 in the destination. // The package id of the lib_one package is 0x03 in the source and 0x02 in the destination - // Check that the cookie and packages have been rewritten to the destination values - EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value, + // Check that the cookie and packages have been rewritten to the destination values. + EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value, &flags)); - EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value, + EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value, &flags)); // attr2 references an attribute in lib_one. Check that the resolution of the attribute value is - // correct after the value of attr2 had its package id rewritten to the destination package id + // correct after the value of attr2 had its package id rewritten to the destination package id. EXPECT_EQ(700, value.data); } +TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) { + AssetManager2 assetmanager_dst; + assetmanager_dst.SetApkAssets({system_assets_.get()}); + + AssetManager2 assetmanager_src; + assetmanager_src.SetApkAssets({system_assets_.get(), style_assets_.get()}); + + auto theme_dst = assetmanager_dst.NewTheme(); + auto theme_src = assetmanager_src.NewTheme(); + ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven)); + theme_dst->SetTo(*theme_src); + + Res_value value; + uint32_t flags; + + // Allow inline resource values to be copied even if the source apk asset is not present in the + // destination. + EXPECT_EQ(0, theme_dst->GetAttribute(0x0101021b /* android:versionCode */, &value, &flags)); + + // Do not copy strings since the data is an index into the values string pool of the source apk + // asset. + EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010001 /* android:label */, &value, &flags)); + + // Do not copy values that reference another resource if the resource is not present in the + // destination. + EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010002 /* android:icon */, &value, &flags)); + EXPECT_EQ(-1, theme_dst->GetAttribute(0x010100d1 /* android:tag */, &value, &flags)); + + // Allow @empty to and @null to be copied. + EXPECT_EQ(0, theme_dst->GetAttribute(0x010100d0 /* android:id */, &value, &flags)); + EXPECT_EQ(0, theme_dst->GetAttribute(0x01010000 /* android:theme */, &value, &flags)); +} + } // namespace android diff --git a/libs/androidfw/tests/data/styles/R.h b/libs/androidfw/tests/data/styles/R.h index 538a84717176..f11486fe924a 100644 --- a/libs/androidfw/tests/data/styles/R.h +++ b/libs/androidfw/tests/data/styles/R.h @@ -51,6 +51,7 @@ struct R { StyleFour = 0x7f020003u, StyleFive = 0x7f020004u, StyleSix = 0x7f020005u, + StyleSeven = 0x7f020006u, }; }; }; diff --git a/libs/androidfw/tests/data/styles/build b/libs/androidfw/tests/data/styles/build index 1ef8e6e19208..7b7c1f7aa962 100755 --- a/libs/androidfw/tests/data/styles/build +++ b/libs/androidfw/tests/data/styles/build @@ -2,5 +2,7 @@ set -e +PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar + aapt2 compile -o compiled.flata --dir res -aapt2 link -o styles.apk --manifest AndroidManifest.xml compiled.flata +aapt2 link -o styles.apk --manifest AndroidManifest.xml -I $PATH_TO_FRAMEWORK_RES compiled.flata diff --git a/libs/androidfw/tests/data/styles/res/values/styles.xml b/libs/androidfw/tests/data/styles/res/values/styles.xml index 1a231768dade..06774a8a6005 100644 --- a/libs/androidfw/tests/data/styles/res/values/styles.xml +++ b/libs/androidfw/tests/data/styles/res/values/styles.xml @@ -79,4 +79,14 @@ <item name="attr_three">3</item> </style> + <public type="style" name="StyleSeven" id="0x7f020006" /> + <style name="StyleSeven" > + <item name="android:versionCode">3</item> + <item name="android:label">"string"</item> + <item name="android:icon">?attr/attr_one</item> + <item name="android:tag">@string/string_one</item> + <item name="android:id">@null</item> + <item name="android:theme">@empty</item> + </style> + </resources> diff --git a/libs/androidfw/tests/data/styles/styles.apk b/libs/androidfw/tests/data/styles/styles.apk Binary files differindex cd5c7a1c6c12..92e9bf90101e 100644 --- a/libs/androidfw/tests/data/styles/styles.apk +++ b/libs/androidfw/tests/data/styles/styles.apk diff --git a/libs/hwui/HWUIProperties.sysprop b/libs/hwui/HWUIProperties.sysprop index 42191ca6f514..34aeaae867f9 100644 --- a/libs/hwui/HWUIProperties.sysprop +++ b/libs/hwui/HWUIProperties.sysprop @@ -7,3 +7,10 @@ prop { scope: Public access: Readonly } +prop { + api_name: "render_ahead" + type: Integer + prop_name: "ro.hwui.render_ahead" + scope: Public + access: Readonly +}
\ No newline at end of file diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 046ffc4da5ea..9b1f25986d56 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -67,6 +67,7 @@ bool Properties::debuggingEnabled = false; bool Properties::isolatedProcess = false; int Properties::contextPriority = 0; +int Properties::defaultRenderAhead = 0; static int property_get_int(const char* key, int defaultValue) { char buf[PROPERTY_VALUE_MAX] = { @@ -129,6 +130,13 @@ bool Properties::load() { enableForceDarkSupport = property_get_bool(PROPERTY_ENABLE_FORCE_DARK, true); + defaultRenderAhead = std::max(0, std::min(2, property_get_int(PROPERTY_RENDERAHEAD, + render_ahead().value_or(0)))); + + if (defaultRenderAhead && sRenderPipelineType == RenderPipelineType::SkiaVulkan) { + ALOGW("hwui.render_ahead of %d ignored because pipeline is skiavk", defaultRenderAhead); + } + return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 0a7f4e7eb41c..3e91c63fcbde 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -167,6 +167,8 @@ enum DebugLevel { #define PROPERTY_ENABLE_FORCE_DARK "debug.hwui.force_dark_enabled" +#define PROPERTY_RENDERAHEAD "debug.hwui.render_ahead" + /////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////// @@ -251,6 +253,8 @@ public: ANDROID_API static int contextPriority; + static int defaultRenderAhead; + private: static ProfileType sProfileType; static bool sDisableProfileBars; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index baa41c1ec698..4808d68b89ab 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -111,6 +111,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); + setRenderAheadDepth(Properties::defaultRenderAhead); } CanvasContext::~CanvasContext() { @@ -159,6 +160,7 @@ void CanvasContext::setSurface(sp<Surface>&& surface) { if (hasSurface) { mHaveNewSurface = true; mSwapHistory.clear(); + applyRenderAheadSettings(); } else { mRenderThread.removeFrameCallback(this); mGenerationID++; @@ -423,6 +425,12 @@ void CanvasContext::draw() { waitOnFences(); + if (mRenderAheadDepth) { + auto presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) + + (mRenderThread.timeLord().frameIntervalNanos() * (mRenderAheadDepth + 1)); + native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime); + } + bool requireSwap = false; bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap); @@ -636,6 +644,28 @@ bool CanvasContext::surfaceRequiresRedraw() { return width == mLastFrameWidth && height == mLastFrameHeight; } +void CanvasContext::applyRenderAheadSettings() { + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + // TODO: Fix SkiaVulkan's assumptions on buffer counts. And SIGBUS crashes. + mRenderAheadDepth = 0; + return; + } + if (mNativeSurface) { + native_window_set_buffer_count(mNativeSurface.get(), 3 + mRenderAheadDepth); + if (!mRenderAheadDepth) { + native_window_set_buffers_timestamp(mNativeSurface.get(), NATIVE_WINDOW_TIMESTAMP_AUTO); + } + } +} + +void CanvasContext::setRenderAheadDepth(int renderAhead) { + if (renderAhead < 0 || renderAhead > 2 || renderAhead == mRenderAheadDepth) { + return; + } + mRenderAheadDepth = renderAhead; + applyRenderAheadSettings(); +} + SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) { // can't rely on prior content of window if viewport size changes diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index abca34225f98..4a3119a55c77 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -204,6 +204,8 @@ public: return mUseForceDark; } + void setRenderAheadDepth(int renderAhead); + private: CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline); @@ -217,6 +219,7 @@ private: bool isSwapChainStuffed(); bool surfaceRequiresRedraw(); + void applyRenderAheadSettings(); SkRect computeDirtyRect(const Frame& frame, SkRect* dirty); @@ -235,6 +238,7 @@ private: // painted onto its surface. bool mIsDirty = false; SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default; + int mRenderAheadDepth = 0; struct SwapHistory { SkRect damage; nsecs_t vsyncTime; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 16240b4e177b..b58bab1191ed 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -312,6 +312,12 @@ void RenderProxy::setForceDark(bool enable) { mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); }); } +void RenderProxy::setRenderAheadDepth(int renderAhead) { + mRenderThread.queue().post([ context = mContext, renderAhead ] { + context->setRenderAheadDepth(renderAhead); + }); +} + int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom, SkBitmap* bitmap) { auto& thread = RenderThread::getInstance(); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index a1a5551722bc..a0f08cbd26f9 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -123,6 +123,23 @@ public: ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer); ANDROID_API void setForceDark(bool enable); + /** + * Sets a render-ahead depth on the backing renderer. This will increase latency by + * <swapInterval> * renderAhead and increase memory usage by (3 + renderAhead) * <resolution>. + * In return the renderer will be less susceptible to jitter, resulting in a smoother animation. + * + * Not recommended to use in response to anything touch driven, but for canned animations + * where latency is not a concern careful use may be beneficial. + * + * Note that when increasing this there will be a frame gap of N frames where N is + * renderAhead - <current renderAhead>. When decreasing this if there are any pending + * frames they will retain their prior renderAhead value, so it will take a few frames + * for the decrease to flush through. + * + * @param renderAhead How far to render ahead, must be in the range [0..2] + */ + ANDROID_API void setRenderAheadDepth(int renderAhead); + ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom, SkBitmap* bitmap); ANDROID_API static void prepareToDraw(Bitmap& bitmap); diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h index 91022cfe734b..74a039b3d090 100644 --- a/libs/hwui/tests/common/TestScene.h +++ b/libs/hwui/tests/common/TestScene.h @@ -38,6 +38,7 @@ public: int count = 0; int reportFrametimeWeight = 0; bool renderOffscreen = true; + int renderAhead = 0; }; template <class T> diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index b45dbc8b832b..aa579adfb2ce 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -154,6 +154,11 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, proxy->resetProfileInfo(); proxy->fence(); + if (opts.renderAhead) { + usleep(33000); + } + proxy->setRenderAheadDepth(opts.renderAhead); + ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight); nsecs_t start = systemTime(CLOCK_MONOTONIC); diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp index 174a14080eff..88d33c315a09 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -69,6 +69,7 @@ OPTIONS: are offscreen rendered --benchmark_format Set output format. Possible values are tabular, json, csv --renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk + --render-ahead=NUM Sets how far to render-ahead. Must be 0 (default), 1, or 2. )"); } @@ -170,6 +171,7 @@ enum { Onscreen, Offscreen, Renderer, + RenderAhead, }; } @@ -185,6 +187,7 @@ static const struct option LONG_OPTIONS[] = { {"onscreen", no_argument, nullptr, LongOpts::Onscreen}, {"offscreen", no_argument, nullptr, LongOpts::Offscreen}, {"renderer", required_argument, nullptr, LongOpts::Renderer}, + {"render-ahead", required_argument, nullptr, LongOpts::RenderAhead}, {0, 0, 0, 0}}; static const char* SHORT_OPTIONS = "c:r:h"; @@ -283,6 +286,16 @@ void parseOptions(int argc, char* argv[]) { gOpts.renderOffscreen = true; break; + case LongOpts::RenderAhead: + if (!optarg) { + error = true; + } + gOpts.renderAhead = atoi(optarg); + if (gOpts.renderAhead < 0 || gOpts.renderAhead > 2) { + error = true; + } + break; + case 'h': printHelp(); exit(EXIT_SUCCESS); diff --git a/libs/hwui/tests/unit/CommonPoolTests.cpp b/libs/hwui/tests/unit/CommonPoolTests.cpp index c564ed632786..9c885ef0e29f 100644 --- a/libs/hwui/tests/unit/CommonPoolTests.cpp +++ b/libs/hwui/tests/unit/CommonPoolTests.cpp @@ -135,4 +135,127 @@ TEST(CommonPool, fullQueue) { for (auto& f : futures) { f.get(); } +} + +struct DestructorObserver { + DestructorObserver(int* destroyCount) : mDestroyCount(destroyCount) {} + DestructorObserver(const DestructorObserver& other) : mDestroyCount(other.mDestroyCount) {} + DestructorObserver(DestructorObserver&& other) { + mDestroyCount = other.mDestroyCount; + other.mDestroyCount = nullptr; + } + ~DestructorObserver() { + if (mDestroyCount) { + (*mDestroyCount)++; + } + } + DestructorObserver& operator=(DestructorObserver&& other) { + mDestroyCount = other.mDestroyCount; + other.mDestroyCount = nullptr; + return *this; + } + int* mDestroyCount; +}; + +struct CopyObserver { + CopyObserver(int* copyCount) : mCopyCount(copyCount) {} + CopyObserver(CopyObserver&& other) = default; + CopyObserver& operator=(CopyObserver&& other) = default; + + CopyObserver(const CopyObserver& other) { + mCopyCount = other.mCopyCount; + if (mCopyCount) { + (*mCopyCount)++; + } + } + + CopyObserver& operator=(const CopyObserver& other) { + mCopyCount = other.mCopyCount; + if (mCopyCount) { + (*mCopyCount)++; + } + return *this; + } + + int* mCopyCount; +}; + +TEST(CommonPool, asyncLifecycleCheck) { + std::vector<std::future<void>> mFrameFences; + int destroyCount = 0; + int runCount = 0; + { + DestructorObserver observer{&destroyCount}; + auto func = [observer = std::move(observer), count = &runCount]() { + if (observer.mDestroyCount) { + (*count)++; + } + }; + mFrameFences.push_back(CommonPool::async(std::move(func))); + } + for (auto& fence : mFrameFences) { + EXPECT_TRUE(fence.valid()); + fence.get(); + EXPECT_FALSE(fence.valid()); + } + mFrameFences.clear(); + EXPECT_EQ(1, runCount); + EXPECT_EQ(1, destroyCount); +} + +TEST(CommonPool, asyncCopyCheck) { + std::vector<std::future<void>> mFrameFences; + int copyCount = 0; + int runCount = 0; + { + CopyObserver observer{©Count}; + auto func = [observer = std::move(observer), count = &runCount]() { + if (observer.mCopyCount) { + (*count)++; + } + }; + mFrameFences.push_back(CommonPool::async(std::move(func))); + } + for (auto& fence : mFrameFences) { + EXPECT_TRUE(fence.valid()); + fence.get(); + EXPECT_FALSE(fence.valid()); + } + mFrameFences.clear(); + EXPECT_EQ(1, runCount); + // We expect std::move all the way + EXPECT_EQ(0, copyCount); +} + +TEST(CommonPool, syncLifecycleCheck) { + int destroyCount = 0; + int runCount = 0; + { + DestructorObserver observer{&destroyCount}; + auto func = [observer = std::move(observer), count = &runCount]() { + if (observer.mDestroyCount) { + (*count)++; + } + }; + CommonPool::runSync(std::move(func)); + } + EXPECT_EQ(1, runCount); + EXPECT_EQ(1, destroyCount); +} + +TEST(CommonPool, syncCopyCheck) { + int copyCount = 0; + int runCount = 0; + { + CopyObserver observer{©Count}; + auto func = [observer = std::move(observer), count = &runCount]() { + if (observer.mCopyCount) { + (*count)++; + } + }; + CommonPool::runSync(std::move(func)); + } + EXPECT_EQ(1, runCount); + // We expect std::move all the way + EXPECT_EQ(0, copyCount); }
\ No newline at end of file diff --git a/libs/hwui/thread/CommonPool.h b/libs/hwui/thread/CommonPool.h index aef2990d6343..51628259d2a8 100644 --- a/libs/hwui/thread/CommonPool.h +++ b/libs/hwui/thread/CommonPool.h @@ -57,11 +57,13 @@ public: mHead = newHead; } - constexpr T&& pop() { + constexpr T pop() { LOG_ALWAYS_FATAL_IF(mTail == mHead, "empty"); int index = mTail; mTail = (mTail + 1) % SIZE; - return std::move(mBuffer[index]); + T ret = std::move(mBuffer[index]); + mBuffer[index] = nullptr; + return ret; } private: diff --git a/libs/protoutil/src/ProtoFileReader.cpp b/libs/protoutil/src/ProtoFileReader.cpp index 074170a6e2c3..4017979fd4fc 100644 --- a/libs/protoutil/src/ProtoFileReader.cpp +++ b/libs/protoutil/src/ProtoFileReader.cpp @@ -55,6 +55,7 @@ ProtoFileReader::ProtoFileReader(int fd) mSize(get_file_size(fd)), mPos(0), mOffset(0), + mMaxOffset(0), mChunkSize(sizeof(mBuffer)) { } |