Fix flickering with client frame composer
... by caching the DRM buffers.
Bug: b/239855014
Test: manual run
Change-Id: I212114e37f0ab7c2b678b29e67841715462a7523
diff --git a/system/hwc3/ClientFrameComposer.h b/system/hwc3/ClientFrameComposer.h
index 6961354..28199b3 100644
--- a/system/hwc3/ClientFrameComposer.h
+++ b/system/hwc3/ClientFrameComposer.h
@@ -70,7 +70,7 @@
private:
struct DisplayInfo {
- std::unique_ptr<DrmBuffer> clientTargetDrmBuffer;;
+ std::shared_ptr<DrmBuffer> clientTargetDrmBuffer;
};
std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
diff --git a/system/hwc3/DrmPresenter.cpp b/system/hwc3/DrmPresenter.cpp
index a62e17b..b512277 100644
--- a/system/hwc3/DrmPresenter.cpp
+++ b/system/hwc3/DrmPresenter.cpp
@@ -80,6 +80,18 @@
ALOGE("%s: Failed to initialize DRM backend", __FUNCTION__);
return HWC3::Error::NoResources;
}
+
+ constexpr const std::size_t kCachedBuffersPerDisplay = 3;
+ std::size_t numDisplays = 0;
+ for (const DrmConnector& connector : mConnectors) {
+ if (connector.connection == DRM_MODE_CONNECTED) {
+ ++numDisplays;
+ }
+ }
+ const std::size_t bufferCacheSize = kCachedBuffersPerDisplay * numDisplays;
+ DEBUG_LOG("%s: initializing DRM buffer cache to size %zu",
+ __FUNCTION__, bufferCacheSize);
+ mBufferCache = std::make_unique<DrmBufferCache>(bufferCacheSize);
}
mDrmEventListener = ::android::sp<DrmEventListener>::make(*this);
@@ -347,54 +359,59 @@
mPlanes.clear();
}
-std::tuple<HWC3::Error, std::unique_ptr<DrmBuffer>> DrmPresenter::create(
+std::tuple<HWC3::Error, std::shared_ptr<DrmBuffer>> DrmPresenter::create(
const native_handle_t* handle) {
- auto buffer = std::unique_ptr<DrmBuffer>(new DrmBuffer(*this));
-
cros_gralloc_handle* crosHandle = (cros_gralloc_handle*)handle;
if (crosHandle == nullptr) {
ALOGE("%s: invalid cros_gralloc_handle", __FUNCTION__);
- return std::make_tuple(HWC3::Error::NoResources,
- std::unique_ptr<DrmBuffer>());
+ return std::make_tuple(HWC3::Error::NoResources, nullptr);
}
+ DrmPrimeBufferHandle primeHandle = 0;
+ int ret = drmPrimeFDToHandle(mFd.get(), crosHandle->fds[0], &primeHandle);
+ if (ret) {
+ ALOGE("%s: drmPrimeFDToHandle failed: %s (errno %d)", __FUNCTION__,
+ strerror(errno), errno);
+ return std::make_tuple(HWC3::Error::NoResources, nullptr);
+ }
+
+ auto drmBufferPtr = mBufferCache->get(primeHandle);
+ if (drmBufferPtr != nullptr) {
+ return std::make_tuple(HWC3::Error::None,
+ std::shared_ptr<DrmBuffer>(*drmBufferPtr));
+ }
+
+ auto buffer = std::shared_ptr<DrmBuffer>(new DrmBuffer(*this));
buffer->mWidth = crosHandle->width;
buffer->mHeight = crosHandle->height;
buffer->mDrmFormat = crosHandle->format;
buffer->mPlaneFds[0] = crosHandle->fds[0];
+ buffer->mPlaneHandles[0] = primeHandle;
buffer->mPlanePitches[0] = crosHandle->strides[0];
buffer->mPlaneOffsets[0] = crosHandle->offsets[0];
- HWC3::Error error = createDrmFramebuffer(buffer.get());
- return std::make_tuple(error, std::move(buffer));
-}
-
-HWC3::Error DrmPresenter::createDrmFramebuffer(DrmBuffer* buffer) {
- int ret;
-
- ret = drmPrimeFDToHandle(mFd.get(), buffer->mPlaneFds[0],
- &buffer->mPlaneHandles[0]);
- if (ret) {
- ALOGE("%s: drmPrimeFDToHandle failed: %s (errno %d)", __FUNCTION__,
- strerror(errno), errno);
- return HWC3::Error::NoResources;
- }
-
uint32_t framebuffer = 0;
- ret = drmModeAddFB2(mFd.get(), buffer->mWidth, buffer->mHeight,
- buffer->mDrmFormat, buffer->mPlaneHandles,
- buffer->mPlanePitches, buffer->mPlaneOffsets,
- &framebuffer, 0);
+ ret = drmModeAddFB2(mFd.get(),
+ buffer->mWidth,
+ buffer->mHeight,
+ buffer->mDrmFormat,
+ buffer->mPlaneHandles,
+ buffer->mPlanePitches,
+ buffer->mPlaneOffsets,
+ &framebuffer,
+ 0);
if (ret) {
ALOGE("%s: drmModeAddFB2 failed: %s (errno %d)", __FUNCTION__,
strerror(errno), errno);
- return HWC3::Error::NoResources;
+ return std::make_tuple(HWC3::Error::NoResources, nullptr);
}
-
DEBUG_LOG("%s: created framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
+ buffer->mDrmFramebuffer = framebuffer;
- buffer->mDrmFramebuffer.emplace(framebuffer);
- return HWC3::Error::None;
+ mBufferCache->set(primeHandle, std::shared_ptr<DrmBuffer>(buffer));
+
+ return std::make_tuple(HWC3::Error::None,
+ std::shared_ptr<DrmBuffer>(buffer));
}
HWC3::Error DrmPresenter::destroyDrmFramebuffer(DrmBuffer* buffer) {
@@ -416,6 +433,8 @@
strerror(errno), errno);
return HWC3::Error::NoResources;
}
+
+ mBufferCache->remove(buffer->mPlaneHandles[0]);
}
return HWC3::Error::None;
diff --git a/system/hwc3/DrmPresenter.h b/system/hwc3/DrmPresenter.h
index c87e5aa..72e3f5a 100644
--- a/system/hwc3/DrmPresenter.h
+++ b/system/hwc3/DrmPresenter.h
@@ -29,6 +29,7 @@
#include <vector>
#include "Common.h"
+#include "LruCache.h"
#include "android/base/synchronization/AndroidLock.h"
namespace aidl::android::hardware::graphics::composer3::impl {
@@ -60,7 +61,6 @@
uint32_t mPlaneHandles[4] = {0, 0, 0, 0};
uint32_t mPlanePitches[4] = {0, 0, 0, 0};
uint32_t mPlaneOffsets[4] = {0, 0, 0, 0};
-
std::optional<uint32_t> mDrmFramebuffer;
};
@@ -101,7 +101,7 @@
uint32_t refreshRate() const { return mConnectors[0].mRefreshRateAsInteger; }
- std::tuple<HWC3::Error, std::unique_ptr<DrmBuffer>> create(
+ std::tuple<HWC3::Error, std::shared_ptr<DrmBuffer>> create(
const native_handle_t* handle);
std::tuple<HWC3::Error, ::android::base::unique_fd> flushToDisplay(
@@ -111,9 +111,13 @@
std::optional<std::vector<uint8_t>> getEdid(uint32_t id);
private:
- // Grant visibility for createDrmFramebuffer and clearDrmFB to DrmBuffer.
+ // TODO: make this cache per display when enabling hotplug support.
+ using DrmPrimeBufferHandle = uint32_t;
+ using DrmBufferCache = LruCache<DrmPrimeBufferHandle, std::shared_ptr<DrmBuffer>>;
+ std::unique_ptr<DrmBufferCache> mBufferCache;
+
+ // Grant visibility to destroyDrmFramebuffer to DrmBuffer.
friend class DrmBuffer;
- HWC3::Error createDrmFramebuffer(DrmBuffer* buffer);
HWC3::Error destroyDrmFramebuffer(DrmBuffer* buffer);
// Grant visibility for handleHotplug to DrmEventListener.
diff --git a/system/hwc3/GuestFrameComposer.h b/system/hwc3/GuestFrameComposer.h
index 2ab1a82..e37ff87 100644
--- a/system/hwc3/GuestFrameComposer.h
+++ b/system/hwc3/GuestFrameComposer.h
@@ -95,7 +95,7 @@
// Additional per display buffer for the composition result.
buffer_handle_t compositionResultBuffer = nullptr;
- std::unique_ptr<DrmBuffer> compositionResultDrmBuffer;
+ std::shared_ptr<DrmBuffer> compositionResultDrmBuffer;
};
std::unordered_map<int64_t, DisplayInfo> mDisplayInfos;
diff --git a/system/hwc3/HostFrameComposer.h b/system/hwc3/HostFrameComposer.h
index b2b042c..42eb43a 100644
--- a/system/hwc3/HostFrameComposer.h
+++ b/system/hwc3/HostFrameComposer.h
@@ -92,10 +92,10 @@
const native_handle_t* compositionResultBuffer = nullptr;
// Drm info for the additional composition result buffer.
- std::unique_ptr<DrmBuffer> compositionResultDrmBuffer;
+ std::shared_ptr<DrmBuffer> compositionResultDrmBuffer;
// Drm info for the displays client target buffer.
- std::unique_ptr<DrmBuffer> clientTargetDrmBuffer;
+ std::shared_ptr<DrmBuffer> clientTargetDrmBuffer;
};
std::unordered_map<int64_t, HostComposerDisplayInfo> mDisplayInfos;
diff --git a/system/hwc3/LruCache.h b/system/hwc3/LruCache.h
new file mode 100644
index 0000000..9ffca46
--- /dev/null
+++ b/system/hwc3/LruCache.h
@@ -0,0 +1,83 @@
+// Copyright 2022 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 <list>
+#include <unordered_map>
+
+template <typename Key, typename Value>
+class LruCache {
+ public:
+ LruCache(std::size_t maxSize) : m_maxSize(maxSize) {
+ m_table.reserve(maxSize);
+ }
+
+ Value* get(const Key& key) {
+ auto tableIt = m_table.find(key);
+ if (tableIt == m_table.end()) {
+ return nullptr;
+ }
+
+ // Move to front.
+ auto elementsIt = tableIt->second;
+ m_elements.splice(elementsIt, m_elements, m_elements.begin());
+ return &elementsIt->value;
+ }
+
+ void set(const Key& key, Value&& value) {
+ auto tableIt = m_table.find(key);
+ if (tableIt == m_table.end()) {
+ if (m_table.size() >= m_maxSize) {
+ auto& kv = m_elements.back();
+ m_table.erase(kv.key);
+ m_elements.pop_back();
+ }
+ } else {
+ auto elementsIt = tableIt->second;
+ m_elements.erase(elementsIt);
+ }
+ m_elements.emplace_front(KeyValue{
+ key,
+ std::forward<Value>(value),
+ });
+ m_table[key] = m_elements.begin();
+ }
+
+ void remove(const Key& key) {
+ auto tableIt = m_table.find(key);
+ if (tableIt == m_table.end()) {
+ return;
+ }
+ auto elementsIt = tableIt->second;
+ m_elements.erase(elementsIt);
+ m_table.erase(tableIt);
+ }
+
+ void clear() {
+ m_elements.clear();
+ m_table.clear();
+ }
+
+ private:
+ struct KeyValue {
+ Key key;
+ Value value;
+ };
+
+ const std::size_t m_maxSize;
+ // Front is the most recently used and back is the least recently used.
+ std::list<KeyValue> m_elements;
+ std::unordered_map<Key, typename std::list<KeyValue>::iterator> m_table;
+};