blast: cache buffers
Cache incoming buffers so they don't have to be imported by mapper
everytime they are set.
Amended by Change-Id: I3040f6d8886ca9b130115784c199edfdd9c85c7e
Test: Transaction_test
Bug: 80477568
Change-Id: Icd167c5e5bd59d7331b829667b1139919393d98b
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 5b3bbca..033ed66 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -114,6 +114,7 @@
"BufferLayerConsumer.cpp",
"BufferQueueLayer.cpp",
"BufferStateLayer.cpp",
+ "BufferStateLayerCache.cpp",
"Client.cpp",
"ColorLayer.cpp",
"ContainerLayer.cpp",
diff --git a/services/surfaceflinger/BufferStateLayerCache.cpp b/services/surfaceflinger/BufferStateLayerCache.cpp
new file mode 100644
index 0000000..c82ad7b
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayerCache.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "BufferStateLayerCache"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BufferStateLayerCache.h"
+
+#define MAX_CACHE_SIZE 64
+
+namespace android {
+
+int32_t BufferStateLayerCache::add(const sp<IBinder>& processToken,
+ const sp<GraphicBuffer>& buffer) {
+ std::lock_guard lock(mMutex);
+
+ auto& processCache = getProccessCache(processToken);
+
+ int32_t slot = findSlot(processCache);
+ if (slot < 0) {
+ return slot;
+ }
+
+ processCache[slot] = buffer;
+
+ return slot;
+}
+
+void BufferStateLayerCache::release(const sp<IBinder>& processToken, int32_t id) {
+ if (id < 0) {
+ ALOGE("invalid buffer id");
+ return;
+ }
+
+ std::lock_guard lock(mMutex);
+ auto& processCache = getProccessCache(processToken);
+
+ if (id >= processCache.size()) {
+ ALOGE("invalid buffer id");
+ return;
+ }
+ processCache[id] = nullptr;
+}
+
+sp<GraphicBuffer> BufferStateLayerCache::get(const sp<IBinder>& processToken, int32_t id) {
+ if (id < 0) {
+ ALOGE("invalid buffer id");
+ return nullptr;
+ }
+
+ std::lock_guard lock(mMutex);
+ auto& processCache = getProccessCache(processToken);
+
+ if (id >= processCache.size()) {
+ ALOGE("invalid buffer id");
+ return nullptr;
+ }
+ return processCache[id];
+}
+
+std::vector<sp<GraphicBuffer>>& BufferStateLayerCache::getProccessCache(
+ const sp<IBinder>& processToken) {
+ return mBuffers[processToken];
+}
+
+int32_t BufferStateLayerCache::findSlot(std::vector<sp<GraphicBuffer>>& processCache) {
+ int32_t slot = 0;
+
+ for (const sp<GraphicBuffer> buffer : processCache) {
+ if (!buffer) {
+ return slot;
+ }
+ slot++;
+ }
+
+ if (processCache.size() < MAX_CACHE_SIZE) {
+ processCache.push_back(nullptr);
+ return slot;
+ }
+
+ return -1;
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/BufferStateLayerCache.h b/services/surfaceflinger/BufferStateLayerCache.h
new file mode 100644
index 0000000..623f0c6
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayerCache.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <binder/IBinder.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
+
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+class BufferStateLayerCache {
+public:
+ int32_t add(const sp<IBinder>& processToken, const sp<GraphicBuffer>& buffer);
+ void release(const sp<IBinder>& processToken, int32_t id);
+
+ sp<GraphicBuffer> get(const sp<IBinder>& processToken, int32_t id);
+
+private:
+ std::mutex mMutex;
+
+ std::vector<sp<GraphicBuffer>>& getProccessCache(const sp<IBinder>& processToken)
+ REQUIRES(mMutex);
+
+ int32_t findSlot(std::vector<sp<GraphicBuffer>>& proccessCache) REQUIRES(mMutex);
+
+ struct IBinderHash {
+ std::size_t operator()(const sp<IBinder>& strongPointer) const {
+ return std::hash<IBinder*>{}(strongPointer.get());
+ }
+ };
+
+ std::unordered_map<sp<IBinder> /*caching process*/, std::vector<sp<GraphicBuffer>>, IBinderHash>
+ mBuffers GUARDED_BY(mMutex);
+};
+
+}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c0059d5..2640625 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1160,6 +1160,20 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+ int32_t* outBufferId) {
+ if (!outBufferId) {
+ return BAD_VALUE;
+ }
+ *outBufferId = mBufferStateLayerCache.add(token, buffer);
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) {
+ mBufferStateLayerCache.release(token, bufferId);
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
postMessageSync(new LambdaMessage([&] {
Mutex::Autolock _l(mStateLock);
@@ -3932,6 +3946,11 @@
callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
}
}
+ if (what & layer_state_t::eCachedBufferChanged) {
+ sp<GraphicBuffer> buffer =
+ mBufferStateLayerCache.get(s.cachedBuffer.token, s.cachedBuffer.bufferId);
+ if (layer->setBuffer(buffer)) flags |= eTraversalNeeded;
+ }
if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
// Do not put anything that updates layer state or modifies flags after
// setTransactionCompletedListener
@@ -5025,7 +5044,9 @@
case CREATE_CONNECTION:
case GET_COLOR_MANAGEMENT:
case GET_COMPOSITION_PREFERENCE:
- case GET_PROTECTED_CONTENT_SUPPORT: {
+ case GET_PROTECTED_CONTENT_SUPPORT:
+ case CACHE_BUFFER:
+ case UNCACHE_BUFFER: {
return OK;
}
case CAPTURE_LAYERS:
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e5b3570..1e6db06 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -48,6 +48,7 @@
#include <utils/threads.h>
#include "Barrier.h"
+#include "BufferStateLayerCache.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/HWComposer.h"
@@ -459,6 +460,9 @@
uint64_t timestamp,
DisplayedFrameStats* outStats) const override;
status_t getProtectedContentSupport(bool* outSupported) const override;
+ status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+ int32_t* outBufferId) override;
+ status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) override;
/* ------------------------------------------------------------------------
* DeathRecipient interface
@@ -1034,6 +1038,8 @@
sp<IInputFlinger> mInputFlinger;
InputWindowCommands mInputWindowCommands;
+
+ BufferStateLayerCache mBufferStateLayerCache;
};
}; // namespace android
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 0ad2711..216532a 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -2323,6 +2323,143 @@
Transaction().setSidebandStream(layer, nullptr).apply();
}
+TEST_F(LayerTransactionTest, CacheBuffer_BufferState) {
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+
+ int32_t bufferId = -1;
+ ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+ ASSERT_GE(bufferId, 0);
+
+ ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+}
+
+TEST_F(LayerTransactionTest, CacheBuffers_BufferState) {
+ std::vector<int32_t> bufferIds;
+ int32_t bufferCount = 20;
+
+ for (int i = 0; i < bufferCount; i++) {
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ int32_t bufferId = -1;
+ ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+ if (bufferId < 0) {
+ EXPECT_GE(bufferId, 0);
+ break;
+ }
+ bufferIds.push_back(bufferId);
+ }
+
+ for (int32_t bufferId : bufferIds) {
+ ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+ }
+}
+
+TEST_F(LayerTransactionTest, CacheBufferInvalid_BufferState) {
+ sp<GraphicBuffer> buffer = nullptr;
+
+ int32_t bufferId = -1;
+ ASSERT_NE(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+ ASSERT_LT(bufferId, 0);
+}
+
+TEST_F(LayerTransactionTest, UncacheBufferTwice_BufferState) {
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+
+ int32_t bufferId = -1;
+ ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+ ASSERT_GE(bufferId, 0);
+
+ ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+ mClient->uncacheBuffer(bufferId);
+}
+
+TEST_F(LayerTransactionTest, UncacheBufferInvalidId_BufferState) {
+ mClient->uncacheBuffer(-1);
+ mClient->uncacheBuffer(0);
+ mClient->uncacheBuffer(1);
+ mClient->uncacheBuffer(5);
+ mClient->uncacheBuffer(1000);
+}
+
+TEST_F(LayerTransactionTest, SetCachedBuffer_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+ int32_t bufferId = -1;
+ ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+ ASSERT_GE(bufferId, 0);
+
+ Transaction().setCachedBuffer(layer, bufferId).apply();
+
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+
+ ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+}
+
+TEST_F(LayerTransactionTest, SetCachedBufferDelayed_BufferState) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(
+ layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+ sp<GraphicBuffer> cachedBuffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ int32_t bufferId = -1;
+ ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(cachedBuffer, &bufferId));
+ ASSERT_GE(bufferId, 0);
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY,
+ "test");
+ fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::BLUE);
+ Transaction().setBuffer(layer, buffer).apply();
+ {
+ SCOPED_TRACE("Uncached buffer");
+
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ fillGraphicBufferColor(cachedBuffer, Rect(0, 0, 32, 32), Color::RED);
+ Transaction().setCachedBuffer(layer, bufferId).apply();
+ {
+ SCOPED_TRACE("Cached buffer");
+
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+ shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+ }
+
+ ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+}
+
class ColorTransformHelper {
public:
static void DegammaColorSingle(half& s) {