summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/AssetManager2.cpp68
-rw-r--r--libs/androidfw/tests/Theme_test.cpp73
-rw-r--r--libs/androidfw/tests/data/styles/R.h1
-rwxr-xr-xlibs/androidfw/tests/data/styles/build4
-rw-r--r--libs/androidfw/tests/data/styles/res/values/styles.xml10
-rw-r--r--libs/androidfw/tests/data/styles/styles.apkbin2550 -> 2774 bytes
-rw-r--r--libs/hwui/HWUIProperties.sysprop7
-rw-r--r--libs/hwui/Properties.cpp8
-rw-r--r--libs/hwui/Properties.h4
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp30
-rw-r--r--libs/hwui/renderthread/CanvasContext.h4
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp6
-rw-r--r--libs/hwui/renderthread/RenderProxy.h17
-rw-r--r--libs/hwui/tests/common/TestScene.h1
-rw-r--r--libs/hwui/tests/macrobench/TestSceneRunner.cpp5
-rw-r--r--libs/hwui/tests/macrobench/main.cpp13
-rw-r--r--libs/hwui/tests/unit/CommonPoolTests.cpp123
-rw-r--r--libs/hwui/thread/CommonPool.h6
-rw-r--r--libs/protoutil/src/ProtoFileReader.cpp1
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
index cd5c7a1c6c12..92e9bf90101e 100644
--- a/libs/androidfw/tests/data/styles/styles.apk
+++ b/libs/androidfw/tests/data/styles/styles.apk
Binary files differ
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{&copyCount};
+ 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{&copyCount};
+ 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)) {
}