Merge "Skip BufferHub_test if BufferHub service is not running"
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 5fb3f0b..d87228f 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -59,6 +59,13 @@
}
}
+void BufferQueue::ProxyConsumerListener::onBufferAllocated(const BufferItem& item) {
+ sp<ConsumerListener> listener(mConsumerListener.promote());
+ if (listener != nullptr) {
+ listener->onBufferAllocated(item);
+ }
+}
+
void BufferQueue::ProxyConsumerListener::onBuffersReleased() {
sp<ConsumerListener> listener(mConsumerListener.promote());
if (listener != nullptr) {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a462b03..e657488 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -530,6 +530,13 @@
return NO_INIT;
}
+ if (mCore->mConsumerListener != nullptr) {
+ BufferItem item;
+ item.mGraphicBuffer = graphicBuffer;
+ item.mSlot = *outSlot;
+ mCore->mConsumerListener->onBufferAllocated(item);
+ }
+
VALIDATE_CONSISTENCY();
} // Autolock scope
}
@@ -1414,6 +1421,13 @@
BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d",
*slot);
+ if (mCore->mConsumerListener != nullptr) {
+ BufferItem item;
+ item.mGraphicBuffer = buffers[i];
+ item.mSlot = *slot;
+ mCore->mConsumerListener->onBufferAllocated(item);
+ }
+
// Make sure the erase is done after all uses of the slot
// iterator since it will be invalid after this point.
mCore->mFreeSlots.erase(slot);
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index abd9921..1e94cc1 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -131,6 +131,8 @@
}
}
+void ConsumerBase::onBufferAllocated(const BufferItem& /*item*/) {}
+
void ConsumerBase::onBuffersReleased() {
Mutex::Autolock lock(mMutex);
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 85ac304..ea9045c 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -28,7 +28,8 @@
ON_FRAME_REPLACED,
ON_BUFFERS_RELEASED,
ON_SIDEBAND_STREAM_CHANGED,
- LAST = ON_SIDEBAND_STREAM_CHANGED,
+ ON_BUFFER_ALLOCATED,
+ LAST = ON_BUFFER_ALLOCATED,
};
} // Anonymous namespace
@@ -54,6 +55,11 @@
item);
}
+ void onBufferAllocated(const BufferItem& item) override {
+ callRemoteAsync<decltype(&IConsumerListener::onBufferAllocated)>(Tag::ON_BUFFER_ALLOCATED,
+ item);
+ }
+
void onBuffersReleased() override {
callRemoteAsync<decltype(&IConsumerListener::onBuffersReleased)>(Tag::ON_BUFFERS_RELEASED);
}
@@ -89,6 +95,8 @@
return callLocalAsync(data, reply, &IConsumerListener::onFrameAvailable);
case Tag::ON_FRAME_REPLACED:
return callLocalAsync(data, reply, &IConsumerListener::onFrameReplaced);
+ case Tag::ON_BUFFER_ALLOCATED:
+ return callLocalAsync(data, reply, &IConsumerListener::onBufferAllocated);
case Tag::ON_BUFFERS_RELEASED:
return callLocalAsync(data, reply, &IConsumerListener::onBuffersReleased);
case Tag::ON_SIDEBAND_STREAM_CHANGED:
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index ce88d7b..74cd4f1 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -76,7 +76,11 @@
}
status_t TransactionStats::writeToParcel(Parcel* output) const {
- status_t err = output->writeInt64(latchTime);
+ status_t err = output->writeInt64Vector(callbackIds);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = output->writeInt64(latchTime);
if (err != NO_ERROR) {
return err;
}
@@ -96,7 +100,11 @@
}
status_t TransactionStats::readFromParcel(const Parcel* input) {
- status_t err = input->readInt64(&latchTime);
+ status_t err = input->readInt64Vector(&callbackIds);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ err = input->readInt64(&latchTime);
if (err != NO_ERROR) {
return err;
}
@@ -120,16 +128,11 @@
if (err != NO_ERROR) {
return err;
}
-
- for (const auto& [callbackIds, stats] : transactionStats) {
+ for (const auto& stats : transactionStats) {
err = output->writeParcelable(stats);
if (err != NO_ERROR) {
return err;
}
- err = output->writeInt64Vector(callbackIds);
- if (err != NO_ERROR) {
- return err;
- }
}
return NO_ERROR;
}
@@ -139,18 +142,11 @@
for (int i = 0; i < transactionStats_size; i++) {
TransactionStats stats;
- std::vector<CallbackId> callbackIds;
-
status_t err = input->readParcelable(&stats);
if (err != NO_ERROR) {
return err;
}
- err = input->readInt64Vector(&callbackIds);
- if (err != NO_ERROR) {
- return err;
- }
-
- transactionStats.emplace(callbackIds, stats);
+ transactionStats.push_back(stats);
}
return NO_ERROR;
}
@@ -159,11 +155,8 @@
const std::unordered_set<CallbackId>& callbackIds) {
ListenerStats listenerStats;
listenerStats.listener = listener;
- TransactionStats transactionStats;
- listenerStats.transactionStats.emplace(std::piecewise_construct,
- std::forward_as_tuple(callbackIds.begin(),
- callbackIds.end()),
- std::forward_as_tuple(transactionStats));
+ listenerStats.transactionStats.emplace_back(callbackIds);
+
return listenerStats;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index c627898..83cf40c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -203,15 +203,15 @@
* that could possibly exist for the callbacks.
*/
std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
- for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
- for (auto callbackId : callbackIds) {
+ for (const auto& transactionStats : listenerStats.transactionStats) {
+ for (auto callbackId : transactionStats.callbackIds) {
auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
}
}
- for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
- for (auto callbackId : callbackIds) {
+ for (const auto& transactionStats : listenerStats.transactionStats) {
+ for (auto callbackId : transactionStats.callbackIds) {
auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
if (!callbackFunction) {
ALOGE("cannot call null callback function, skipping");
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index da95274..721427b 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -61,6 +61,7 @@
void onDisconnect() override;
void onFrameAvailable(const BufferItem& item) override;
void onFrameReplaced(const BufferItem& item) override;
+ void onBufferAllocated(const BufferItem& item) override;
void onBuffersReleased() override;
void onSidebandStreamChanged() override;
void addAndGetFrameTimestamps(
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index 366ced3..7c26482 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -141,6 +141,7 @@
// classes if they want the notification.
virtual void onFrameAvailable(const BufferItem& item) override;
virtual void onFrameReplaced(const BufferItem& item) override;
+ virtual void onBufferAllocated(const BufferItem& item) override;
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index c082882..03fefbe 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -61,6 +61,13 @@
// This is called without any lock held and can be called concurrently by multiple threads.
virtual void onFrameReplaced(const BufferItem& /* item */) {} /* Asynchronous */
+ // onBufferAllocated is called to notify the buffer consumer that the BufferQueue has allocated
+ // a GraphicBuffer for a particular slot. Only the GraphicBuffer pointer and the slot ID will
+ // be populated.
+ //
+ // This is called without any lock held and can be called concurrently by multiple threads.
+ virtual void onBufferAllocated(const BufferItem& /* item */) {} /* Asynchronous */
+
// onBuffersReleased is called to notify the buffer consumer that the BufferQueue has released
// its references to one or more GraphicBuffers contained in its slots. The buffer consumer
// should then call BufferQueue::getReleasedBuffers to retrieve the list of buffers.
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 29ab026..774ad46 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -34,18 +34,6 @@
using CallbackId = int64_t;
-struct CallbackIdsHash {
- // CallbackId vectors have several properties that let us get away with this simple hash.
- // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
- // empty we can still hash 0.
- // 2) CallbackId vectors for the same listener either are identical or contain none of the
- // same members. It is sufficient to just check the first CallbackId in the vectors. If
- // they match, they are the same. If they do not match, they are not the same.
- std::size_t operator()(const std::vector<CallbackId> callbackIds) const {
- return std::hash<CallbackId>{}((callbackIds.size() == 0) ? 0 : callbackIds.front());
- }
-};
-
class SurfaceStats : public Parcelable {
public:
status_t writeToParcel(Parcel* output) const override;
@@ -65,6 +53,15 @@
status_t writeToParcel(Parcel* output) const override;
status_t readFromParcel(const Parcel* input) override;
+ TransactionStats() = default;
+ TransactionStats(const std::vector<CallbackId>& ids) : callbackIds(ids) {}
+ TransactionStats(const std::unordered_set<CallbackId>& ids)
+ : callbackIds(ids.begin(), ids.end()) {}
+ TransactionStats(const std::vector<CallbackId>& ids, nsecs_t latch, const sp<Fence>& present,
+ const std::vector<SurfaceStats>& surfaces)
+ : callbackIds(ids), latchTime(latch), presentFence(present), surfaceStats(surfaces) {}
+
+ std::vector<CallbackId> callbackIds;
nsecs_t latchTime = -1;
sp<Fence> presentFence = nullptr;
std::vector<SurfaceStats> surfaceStats;
@@ -79,7 +76,7 @@
const std::unordered_set<CallbackId>& callbackIds);
sp<ITransactionCompletedListener> listener;
- std::unordered_map<std::vector<CallbackId>, TransactionStats, CallbackIdsHash> transactionStats;
+ std::vector<TransactionStats> transactionStats;
};
class ITransactionCompletedListener : public IInterface {
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 5847402..d0127b8 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -626,23 +626,26 @@
}
}
-status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
- sp<Fence> bufferFence) {
+status_t GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ return cacheExternalTextureBufferLocked(buffer);
+}
+
+status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName,
+ const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& bufferFence) {
std::lock_guard<std::mutex> lock(mRenderingMutex);
return bindExternalTextureBufferLocked(texName, buffer, bufferFence);
}
-status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
- sp<GraphicBuffer> buffer,
- sp<Fence> bufferFence) {
+status_t GLESRenderEngine::cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) {
if (buffer == nullptr) {
return BAD_VALUE;
}
- ATRACE_CALL();
- auto cachedImage = mImageCache.find(buffer->getId());
- if (cachedImage != mImageCache.end()) {
- bindExternalTextureImage(texName, *cachedImage->second);
+ ATRACE_CALL();
+
+ if (mImageCache.count(buffer->getId()) > 0) {
return NO_ERROR;
}
@@ -654,11 +657,32 @@
ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
buffer->getPixelFormat());
+ return NO_INIT;
+ }
+ mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
+
+ return NO_ERROR;
+}
+
+status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
+ const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& bufferFence) {
+ ATRACE_CALL();
+ status_t cacheResult = cacheExternalTextureBufferLocked(buffer);
+
+ if (cacheResult != NO_ERROR) {
+ return cacheResult;
+ }
+
+ auto cachedImage = mImageCache.find(buffer->getId());
+
+ if (cachedImage == mImageCache.end()) {
+ // We failed creating the image if we got here, so bail out.
bindExternalTextureImage(texName, *createImage());
return NO_INIT;
}
- bindExternalTextureImage(texName, *newImage);
+ bindExternalTextureImage(texName, *cachedImage->second);
// Wait for the new buffer to be ready.
if (bufferFence != nullptr && bufferFence->isValid()) {
@@ -680,7 +704,6 @@
}
}
}
- mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
return NO_ERROR;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index cbde5f4..de793c2 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -72,8 +72,9 @@
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
void bindExternalTextureImage(uint32_t texName, const Image& image) override;
- status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence)
- EXCLUDES(mRenderingMutex);
+ status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
+ status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
status_t bindFrameBuffer(Framebuffer* framebuffer) override;
void unbindFrameBuffer(Framebuffer* framebuffer) override;
@@ -221,8 +222,12 @@
// See bindExternalTextureBuffer above, but requiring that mRenderingMutex
// is held.
- status_t bindExternalTextureBufferLocked(uint32_t texName, sp<GraphicBuffer> buffer,
- sp<Fence> fence) REQUIRES(mRenderingMutex);
+ status_t bindExternalTextureBufferLocked(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) REQUIRES(mRenderingMutex);
+ // See cacheExternalTextureBuffer above, but requiring that mRenderingMutex
+ // is held.
+ status_t cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer)
+ REQUIRES(mRenderingMutex);
std::unique_ptr<Framebuffer> mDrawingBuffer;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index fa57215..bf614fd 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -106,8 +106,14 @@
virtual void genTextures(size_t count, uint32_t* names) = 0;
virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
- virtual status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer,
- sp<Fence> fence) = 0;
+ // Legacy public method used by devices that don't support native fence
+ // synchronization in their GPU driver, as this method provides implicit
+ // synchronization for latching buffers.
+ virtual status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& fence) = 0;
+ // Caches Image resources for this buffer, but does not bind the buffer to
+ // a particular texture.
+ virtual status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0;
// Removes internal resources referenced by the bufferId. This method should be
// invoked when the caller will no longer hold a reference to a GraphicBuffer
// and needs to clean up its resources.
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 7393f7a..e33bcfd 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -51,7 +51,9 @@
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
- MOCK_METHOD3(bindExternalTextureBuffer, status_t(uint32_t, sp<GraphicBuffer>, sp<Fence>));
+ MOCK_METHOD1(cacheExternalTextureBuffer, status_t(const sp<GraphicBuffer>&));
+ MOCK_METHOD3(bindExternalTextureBuffer,
+ status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&));
MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
MOCK_CONST_METHOD0(checkErrors, void());
MOCK_METHOD4(setViewportAndProjection,
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 351ca6b..7acaecf 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -1023,4 +1023,18 @@
EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
}
+TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferWithNullBuffer) {
+ status_t result = sRE->cacheExternalTextureBuffer(nullptr);
+ ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferCachesImages) {
+ sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+ uint64_t bufferId = buf->getId();
+ sRE->cacheExternalTextureBuffer(buf);
+ EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+ sRE->unbindExternalTextureBuffer(bufferId);
+ EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
} // namespace android
diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
index ed72577..7b3717e 100644
--- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
+++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp
@@ -28,6 +28,8 @@
namespace {
+using ::testing::Test;
+
DvrSurfaceAttribute MakeAttribute(DvrSurfaceAttributeKey key, nullptr_t) {
DvrSurfaceAttribute attribute;
attribute.key = key;
@@ -141,6 +143,23 @@
return {UniqueDvrWriteBufferQueue(queue)};
}
+Status<std::vector<uint8_t>> GetConfigData(int config_type) {
+ uint8_t* data = nullptr;
+ size_t data_size = 0;
+ int error = dvrConfigurationDataGet(config_type, &data, &data_size);
+ if (error < 0) {
+ return ErrorStatus(-error);
+ }
+
+ if (!data || data_size == 0) {
+ return ErrorStatus(EINVAL);
+ }
+ std::vector<uint8_t> data_result(data, data + data_size);
+ dvrConfigurationDataDestroy(data);
+ std::string s(data, data + data_size);
+ return {std::move(data_result)};
+}
+
class TestDisplayManager {
public:
TestDisplayManager(UniqueDvrDisplayManager display_manager,
@@ -275,23 +294,6 @@
return {std::move(queue_ids)};
}
- Status<std::vector<uint8_t>> GetConfigData(int config_type) {
- uint8_t* data = nullptr;
- size_t data_size = 0;
- int error = dvrConfigurationDataGet(config_type, &data, &data_size);
- if (error < 0) {
- return ErrorStatus(-error);
- }
-
- if (!data || data_size == 0) {
- return ErrorStatus(EINVAL);
- }
- std::vector<uint8_t> data_result(data, data + data_size);
- dvrConfigurationDataDestroy(data);
- std::string s(data, data + data_size);
- return {std::move(data_result)};
- }
-
private:
UniqueDvrDisplayManager display_manager_;
UniqueDvrSurfaceState surface_state_;
@@ -303,9 +305,17 @@
void operator=(const TestDisplayManager&) = delete;
};
-class DvrDisplayManagerTest : public ::testing::Test {
+class DvrDisplayManagerTest : public Test {
protected:
void SetUp() override {
+ // dvr display manager test doesn't apply to standalone vr devices because
+ // tests cannot create display manager client on these devices.
+ if (property_get_bool("ro.boot.vr", false)) {
+ GTEST_SKIP()
+ << "All tests in DvrDisplayManagerTest test case are skipped "
+ "because the device boot to VR.";
+ }
+
int ret;
DvrDisplayManager* display_manager;
DvrSurfaceState* surface_state;
@@ -429,7 +439,7 @@
#if 0
// Verify utility predicate/macro functionality. This section is commented out
// because it is designed to fail in some cases to validate the helpers.
-TEST_F(DvrDisplayManagerTest, ExpectVoid) {
+TEST_F(Test, ExpectVoid) {
Status<void> status_error{ErrorStatus{EINVAL}};
Status<void> status_ok{};
@@ -444,7 +454,7 @@
EXPECT_STATUS_ERROR_VALUE(ENOMEM, status_ok);
}
-TEST_F(DvrDisplayManagerTest, ExpectInt) {
+TEST_F(Test, ExpectInt) {
Status<int> status_error{ErrorStatus{EINVAL}};
Status<int> status_ok{10};
@@ -480,11 +490,6 @@
#endif
TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) {
- // This test doesn't apply to standalone vr devices.
- if (property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
// Get surface state and verify there are no surfaces.
ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
@@ -524,11 +529,6 @@
}
TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) {
- // This test doesn't apply to standalone vr devices.
- if (property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
// Get surface state and verify there are no surfaces.
ASSERT_STATUS_OK(manager_->UpdateSurfaceState());
ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount());
@@ -768,11 +768,6 @@
}
TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) {
- // This test doesn't apply to standalone vr devices.
- if (property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
// Create an application surface.
auto surface_status = CreateApplicationSurface();
ASSERT_STATUS_OK(surface_status);
@@ -841,11 +836,6 @@
}
TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) {
- // This test doesn't apply to standalone vr devices.
- if (property_get_bool("ro.boot.vr", false)) {
- return;
- }
-
// Create an application surface.
auto surface_status = CreateApplicationSurface();
ASSERT_STATUS_OK(surface_status);
@@ -884,16 +874,17 @@
dvrWriteBufferDestroy(buffer);
}
-TEST_F(DvrDisplayManagerTest, ConfigurationData) {
- // TODO(hendrikw): Move this out of the display manager tests.
- auto data1 = manager_->GetConfigData(-1);
+TEST_F(Test, ConfigurationData) {
+ // TODO(hendrikw): Move this test and GetConfigData helper function out of the
+ // display manager tests.
+ auto data1 = GetConfigData(-1);
ASSERT_STATUS_ERROR(data1);
const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics";
// This should be run on devices with and without built in metrics.
bool has_metric = !base::GetProperty(kDvrLensMetricsProperty, "").empty();
- auto data2 = manager_->GetConfigData(DVR_CONFIGURATION_DATA_LENS_METRICS);
+ auto data2 = GetConfigData(DVR_CONFIGURATION_DATA_LENS_METRICS);
if (has_metric) {
ASSERT_STATUS_OK(data2);
ASSERT_NE(0u, data2.get().size());
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index f2d4c51..fc98dc8 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -217,7 +217,11 @@
// If item->mGraphicBuffer is not null, this buffer has not been acquired
// before, so we need to clean up old references.
if (item->mGraphicBuffer != nullptr) {
- mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE);
+ std::lock_guard<std::mutex> lock(mImagesMutex);
+ if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->graphicBuffer() == nullptr ||
+ mImages[item->mSlot]->graphicBuffer()->getId() != item->mGraphicBuffer->getId()) {
+ mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE);
+ }
}
return NO_ERROR;
@@ -238,7 +242,12 @@
// Hang onto the pointer so that it isn't freed in the call to
// releaseBufferLocked() if we're in shared buffer mode and both buffers are
// the same.
- std::shared_ptr<Image> nextTextureBuffer = mImages[slot];
+
+ std::shared_ptr<Image> nextTextureBuffer;
+ {
+ std::lock_guard<std::mutex> lock(mImagesMutex);
+ nextTextureBuffer = mImages[slot];
+ }
// release old buffer
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
@@ -436,6 +445,7 @@
void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+ std::lock_guard<std::mutex> lock(mImagesMutex);
if (slotIndex == mCurrentTexture) {
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
}
@@ -468,6 +478,23 @@
}
}
+void BufferLayerConsumer::onBufferAllocated(const BufferItem& item) {
+ if (item.mGraphicBuffer != nullptr) {
+ std::shared_ptr<Image> image = std::make_shared<Image>(item.mGraphicBuffer, mRE);
+ std::shared_ptr<Image> oldImage;
+ {
+ std::lock_guard<std::mutex> lock(mImagesMutex);
+ oldImage = mImages[item.mSlot];
+ if (oldImage == nullptr || oldImage->graphicBuffer() == nullptr ||
+ oldImage->graphicBuffer()->getId() != item.mGraphicBuffer->getId()) {
+ mImages[item.mSlot] = std::make_shared<Image>(item.mGraphicBuffer, mRE);
+ }
+ image = mImages[item.mSlot];
+ }
+ mRE.cacheExternalTextureBuffer(image->graphicBuffer());
+ }
+}
+
void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
sp<Layer> l = mLayer.promote();
@@ -480,6 +507,7 @@
BLC_LOGV("abandonLocked");
mCurrentTextureBuffer = nullptr;
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ std::lock_guard<std::mutex> lock(mImagesMutex);
mImages[i] = nullptr;
}
ConsumerBase::abandonLocked();
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index f4ca846..0f0655d 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_BUFFERLAYERCONSUMER_H
#define ANDROID_BUFFERLAYERCONSUMER_H
+#include <android-base/thread_annotations.h>
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
#include <gui/HdrMetadata.h>
@@ -179,7 +180,7 @@
protected:
// abandonLocked overrides the ConsumerBase method to clear
// mCurrentTextureImage in addition to the ConsumerBase behavior.
- virtual void abandonLocked();
+ virtual void abandonLocked() EXCLUDES(mImagesMutex);
// dumpLocked overrides the ConsumerBase method to dump BufferLayerConsumer-
// specific info in addition to the ConsumerBase behavior.
@@ -187,7 +188,8 @@
// See ConsumerBase::acquireBufferLocked
virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
- uint64_t maxFrameNumber = 0) override;
+ uint64_t maxFrameNumber = 0) override
+ EXCLUDES(mImagesMutex);
bool canUseImageCrop(const Rect& crop) const;
@@ -206,7 +208,8 @@
// completion of the method will instead be returned to the caller, so that
// it may call releaseBufferLocked itself later.
status_t updateAndReleaseLocked(const BufferItem& item,
- PendingRelease* pendingRelease = nullptr);
+ PendingRelease* pendingRelease = nullptr)
+ EXCLUDES(mImagesMutex);
// Binds mTexName and the current buffer to TEXTURE_EXTERNAL target.
// If the bind succeeds, this calls doFenceWait.
@@ -234,10 +237,11 @@
// that slot. Otherwise it has no effect.
//
// This method must be called with mMutex locked.
- virtual void freeBufferLocked(int slotIndex);
+ virtual void freeBufferLocked(int slotIndex) EXCLUDES(mImagesMutex);
// IConsumerListener interface
void onDisconnect() override;
+ void onBufferAllocated(const BufferItem& item) override EXCLUDES(mImagesMutex);
void onSidebandStreamChanged() override;
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) override;
@@ -344,7 +348,12 @@
int mCurrentTexture;
// Shadow buffer cache for cleaning up renderengine references.
- std::shared_ptr<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS];
+ std::shared_ptr<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
+
+ // Separate mutex guarding the shadow buffer cache.
+ // mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated)
+ // which is contentious enough that we can't just use mMutex.
+ mutable std::mutex mImagesMutex;
// A release that is pending on the receipt of a new release fence from
// presentDisplay
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index ad08a92..7927fa9 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -42,32 +42,4 @@
void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&,
const Rect&, int32_t, const ui::Dataspace) {}
-Layer::RoundedCornerState ContainerLayer::getRoundedCornerStateInternal(
- const FloatRect bounds) const {
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- RoundedCornerState parentState = p->getRoundedCornerStateInternal(bounds);
- if (parentState.radius > 0) {
- ui::Transform t = getActiveTransform(getDrawingState());
- t = t.inverse();
- parentState.cropRect = t.transform(parentState.cropRect);
- // The rounded corners shader only accepts 1 corner radius for performance reasons,
- // but a transform matrix can define horizontal and vertical scales.
- // Let's take the average between both of them and pass into the shader, practically we
- // never do this type of transformation on windows anyway.
- parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
- return parentState;
- }
- }
- const float radius = getDrawingState().cornerRadius;
- if (radius > 0) {
- const Rect crop = getCrop(getDrawingState());
- if (!crop.isEmpty()) {
- return RoundedCornerState(bounds.intersect(crop.toFloatRect()), radius);
- }
- return RoundedCornerState(bounds, radius);
- }
- return RoundedCornerState();
-}
-
} // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index cd8e722..7222a3e 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -40,7 +40,6 @@
bool isCreatedFromMainThread() const override { return true; }
bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
- Layer::RoundedCornerState getRoundedCornerStateInternal(const FloatRect bounds) const override;
protected:
bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 73f27e4..7965245 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1788,13 +1788,9 @@
}
Layer::RoundedCornerState Layer::getRoundedCornerState() const {
- return getRoundedCornerStateInternal(mSourceBounds);
-}
-
-Layer::RoundedCornerState Layer::getRoundedCornerStateInternal(const FloatRect) const {
const auto& p = mDrawingParent.promote();
if (p != nullptr) {
- RoundedCornerState parentState = p->getRoundedCornerStateInternal(mSourceBounds);
+ RoundedCornerState parentState = p->getRoundedCornerState();
if (parentState.radius > 0) {
ui::Transform t = getActiveTransform(getDrawingState());
t = t.inverse();
@@ -1808,10 +1804,7 @@
}
}
const float radius = getDrawingState().cornerRadius;
- return radius > 0
- ? RoundedCornerState(mSourceBounds.intersect(getCrop(getDrawingState()).toFloatRect()),
- radius)
- : RoundedCornerState();
+ return radius > 0 ? RoundedCornerState(getBounds(), radius) : RoundedCornerState();
}
void Layer::commitChildList() {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index b9dc7ec..8348ce1 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -452,13 +452,13 @@
virtual void setPostTime(nsecs_t /*postTime*/) {}
virtual void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/) {}
- virtual RoundedCornerState getRoundedCornerStateInternal(const FloatRect bounds) const;
protected:
virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
bool useIdentityTransform, Region& clearRegion,
const bool supportProtectedContent,
renderengine::LayerSettings& layer);
+
public:
/*
* compositionengine::LayerFE overrides
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index a2a6bd8..f72aef1 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -333,7 +333,6 @@
if (t < now) {
if (isShorterThanPeriod(now - eventListener.mLastCallbackTime)) {
eventListener.mLastEventTime = t;
- eventListener.mLastCallbackTime = now;
ALOGV("[%s] [%s] Skipping event due to model error", mName,
eventListener.mName);
continue;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c0d7f2d..0345baf 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3647,18 +3647,22 @@
transactionFlags |= setDisplayStateLocked(display);
}
+ // In case the client has sent a Transaction that should receive callbacks but without any
+ // SurfaceControls that should be included in the callback, send the listener and callbackIds
+ // to the callback thread so it can send an empty callback
+ if (!listenerCallbacks.empty()) {
+ mTransactionCompletedThread.run();
+ }
+ for (const auto& [listener, callbackIds] : listenerCallbacks) {
+ mTransactionCompletedThread.addCallback(listener, callbackIds);
+ }
+
uint32_t clientStateFlags = 0;
for (const ComposerState& state : states) {
clientStateFlags |= setClientStateLocked(state, desiredPresentTime, listenerCallbacks,
postTime, privileged);
}
- // In case the client has sent a Transaction that should receive callbacks but without any
- // SurfaceControls that should be included in the callback, send the listener and callbackIds
- // to the callback thread so it can send an empty callback
- for (const auto& [listener, callbackIds] : listenerCallbacks) {
- mTransactionCompletedThread.addCallback(listener, callbackIds);
- }
// If the state doesn't require a traversal and there are callbacks, send them now
if (!(clientStateFlags & eTraversalNeeded)) {
mTransactionCompletedThread.sendCallbacks();
@@ -4015,7 +4019,6 @@
}
std::vector<sp<CallbackHandle>> callbackHandles;
if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!listenerCallbacks.empty())) {
- mTransactionCompletedThread.run();
for (const auto& [listener, callbackIds] : listenerCallbacks) {
callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
}
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 6e0c1e2..1d2217d 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -29,6 +29,18 @@
namespace android {
+// Returns 0 if they are equal
+// <0 if the first id that doesn't match is lower in c2 or all ids match but c2 is shorter
+// >0 if the first id that doesn't match is greater in c2 or all ids match but c2 is longer
+//
+// See CallbackIdsHash for a explaniation of why this works
+static int compareCallbackIds(const std::vector<CallbackId>& c1, const std::vector<CallbackId> c2) {
+ if (c1.empty()) {
+ return !c2.empty();
+ }
+ return c1.front() - c2.front();
+}
+
TransactionCompletedThread::~TransactionCompletedThread() {
std::lock_guard lockThread(mThreadMutex);
@@ -44,8 +56,8 @@
{
std::lock_guard lock(mMutex);
- for (const auto& [listener, listenerStats] : mListenerStats) {
- listener->unlinkToDeath(mDeathRecipient);
+ for (const auto& [listener, transactionStats] : mCompletedTransactions) {
+ IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient);
}
}
}
@@ -62,85 +74,127 @@
mThread = std::thread(&TransactionCompletedThread::threadMain, this);
}
-void TransactionCompletedThread::registerPendingCallbackHandle(const sp<CallbackHandle>& handle) {
+status_t TransactionCompletedThread::addCallback(const sp<ITransactionCompletedListener>& listener,
+ const std::vector<CallbackId>& callbackIds) {
std::lock_guard lock(mMutex);
-
- sp<IBinder> listener = IInterface::asBinder(handle->listener);
- const auto& callbackIds = handle->callbackIds;
-
- mPendingTransactions[listener][callbackIds]++;
-}
-
-void TransactionCompletedThread::addPresentedCallbackHandles(
- const std::deque<sp<CallbackHandle>>& handles) {
- std::lock_guard lock(mMutex);
-
- for (const auto& handle : handles) {
- auto listener = mPendingTransactions.find(IInterface::asBinder(handle->listener));
- auto& pendingCallbacks = listener->second;
- auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
-
- if (pendingCallback != pendingCallbacks.end()) {
- auto& pendingCount = pendingCallback->second;
-
- // Decrease the pending count for this listener
- if (--pendingCount == 0) {
- pendingCallbacks.erase(pendingCallback);
- }
- } else {
- ALOGE("there are more latched callbacks than there were registered callbacks");
- }
-
- addCallbackHandle(handle);
- }
-}
-
-void TransactionCompletedThread::addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle) {
- std::lock_guard lock(mMutex);
- addCallbackHandle(handle);
-}
-
-void TransactionCompletedThread::addCallback(
- const sp<ITransactionCompletedListener>& transactionListener,
- const std::vector<CallbackId>& callbackIds) {
- std::lock_guard lock(mMutex);
- addCallbackLocked(transactionListener, callbackIds);
-}
-
-status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
- status_t err = addCallbackLocked(handle->listener, handle->callbackIds);
- if (err != NO_ERROR) {
- ALOGE("cannot add callback, err: %d", err);
- return err;
+ if (!mRunning) {
+ ALOGE("cannot add callback because the callback thread isn't running");
+ return BAD_VALUE;
}
- const sp<IBinder> listener = IInterface::asBinder(handle->listener);
- auto& listenerStats = mListenerStats[listener];
- auto& transactionStats = listenerStats.transactionStats[handle->callbackIds];
-
- transactionStats.latchTime = handle->latchTime;
- transactionStats.surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
- handle->previousReleaseFence);
- return NO_ERROR;
-}
-
-status_t TransactionCompletedThread::addCallbackLocked(
- const sp<ITransactionCompletedListener>& transactionListener,
- const std::vector<CallbackId>& callbackIds) {
- const sp<IBinder> listener = IInterface::asBinder(transactionListener);
- // If we don't already have a reference to this listener, linkToDeath so we get a notification
- // if it dies.
- if (mListenerStats.count(listener) == 0) {
- status_t err = listener->linkToDeath(mDeathRecipient);
+ if (mCompletedTransactions.count(listener) == 0) {
+ status_t err = IInterface::asBinder(listener)->linkToDeath(mDeathRecipient);
if (err != NO_ERROR) {
ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
return err;
}
}
- auto& listenerStats = mListenerStats[listener];
- listenerStats.listener = transactionListener;
- listenerStats.transactionStats[callbackIds];
+ auto& transactionStatsDeque = mCompletedTransactions[listener];
+ transactionStatsDeque.emplace_back(callbackIds);
+ return NO_ERROR;
+}
+
+status_t TransactionCompletedThread::registerPendingCallbackHandle(
+ const sp<CallbackHandle>& handle) {
+ std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGE("cannot register callback handle because the callback thread isn't running");
+ return BAD_VALUE;
+ }
+
+ // If we can't find the transaction stats something has gone wrong. The client should call
+ // addCallback before trying to register a pending callback handle.
+ TransactionStats* transactionStats;
+ status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+ if (err != NO_ERROR) {
+ ALOGE("cannot find transaction stats");
+ return err;
+ }
+
+ mPendingTransactions[handle->listener][handle->callbackIds]++;
+ return NO_ERROR;
+}
+
+status_t TransactionCompletedThread::addPresentedCallbackHandles(
+ const std::deque<sp<CallbackHandle>>& handles) {
+ std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGE("cannot add presented callback handle because the callback thread isn't running");
+ return BAD_VALUE;
+ }
+
+ for (const auto& handle : handles) {
+ auto listener = mPendingTransactions.find(handle->listener);
+ if (listener != mPendingTransactions.end()) {
+ auto& pendingCallbacks = listener->second;
+ auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
+
+ if (pendingCallback != pendingCallbacks.end()) {
+ auto& pendingCount = pendingCallback->second;
+
+ // Decrease the pending count for this listener
+ if (--pendingCount == 0) {
+ pendingCallbacks.erase(pendingCallback);
+ }
+ } else {
+ ALOGW("there are more latched callbacks than there were registered callbacks");
+ }
+ } else {
+ ALOGW("cannot find listener in mPendingTransactions");
+ }
+
+ status_t err = addCallbackHandle(handle);
+ if (err != NO_ERROR) {
+ ALOGE("could not add callback handle");
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t TransactionCompletedThread::addUnpresentedCallbackHandle(
+ const sp<CallbackHandle>& handle) {
+ std::lock_guard lock(mMutex);
+ if (!mRunning) {
+ ALOGE("cannot add unpresented callback handle because the callback thread isn't running");
+ return BAD_VALUE;
+ }
+
+ return addCallbackHandle(handle);
+}
+
+status_t TransactionCompletedThread::findTransactionStats(
+ const sp<ITransactionCompletedListener>& listener,
+ const std::vector<CallbackId>& callbackIds, TransactionStats** outTransactionStats) {
+ auto& transactionStatsDeque = mCompletedTransactions[listener];
+
+ // Search back to front because the most recent transactions are at the back of the deque
+ auto itr = transactionStatsDeque.rbegin();
+ for (; itr != transactionStatsDeque.rend(); itr++) {
+ if (compareCallbackIds(itr->callbackIds, callbackIds) == 0) {
+ *outTransactionStats = &(*itr);
+ return NO_ERROR;
+ }
+ }
+
+ ALOGE("could not find transaction stats");
+ return BAD_VALUE;
+}
+
+status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
+ // If we can't find the transaction stats something has gone wrong. The client should call
+ // addCallback before trying to add a presnted callback handle.
+ TransactionStats* transactionStats;
+ status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ transactionStats->latchTime = handle->latchTime;
+ transactionStats->surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
+ handle->previousReleaseFence);
return NO_ERROR;
}
@@ -163,40 +217,46 @@
mConditionVariable.wait(mMutex);
// For each listener
- auto it = mListenerStats.begin();
- while (it != mListenerStats.end()) {
- auto& [listener, listenerStats] = *it;
+ auto completedTransactionsItr = mCompletedTransactions.begin();
+ while (completedTransactionsItr != mCompletedTransactions.end()) {
+ auto& [listener, transactionStatsDeque] = *completedTransactionsItr;
+ ListenerStats listenerStats;
+ listenerStats.listener = listener;
// For each transaction
- bool sendCallback = true;
- for (auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
- // If we are still waiting on the callback handles for this transaction, skip it
- if (mPendingTransactions[listener].count(callbackIds) != 0) {
- sendCallback = false;
+ auto transactionStatsItr = transactionStatsDeque.begin();
+ while (transactionStatsItr != transactionStatsDeque.end()) {
+ auto& transactionStats = *transactionStatsItr;
+
+ // If we are still waiting on the callback handles for this transaction, stop
+ // here because all transaction callbacks for the same listener must come in order
+ if (mPendingTransactions[listener].count(transactionStats.callbackIds) != 0) {
break;
}
// If the transaction has been latched
if (transactionStats.latchTime >= 0) {
if (!mPresentFence) {
- sendCallback = false;
break;
}
transactionStats.presentFence = mPresentFence;
}
+
+ // Remove the transaction from completed to the callback
+ listenerStats.transactionStats.push_back(std::move(transactionStats));
+ transactionStatsItr = transactionStatsDeque.erase(transactionStatsItr);
}
- // If the listener has no pending transactions and all latched transactions have been
- // presented
- if (sendCallback) {
+ // If the listener has completed transactions
+ if (!listenerStats.transactionStats.empty()) {
// If the listener is still alive
- if (listener->isBinderAlive()) {
+ if (IInterface::asBinder(listener)->isBinderAlive()) {
// Send callback
listenerStats.listener->onTransactionCompleted(listenerStats);
- listener->unlinkToDeath(mDeathRecipient);
+ IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient);
}
- it = mListenerStats.erase(it);
+ completedTransactionsItr = mCompletedTransactions.erase(completedTransactionsItr);
} else {
- it++;
+ completedTransactionsItr++;
}
}
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index 88808fd..09feb75 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -30,6 +30,18 @@
namespace android {
+struct CallbackIdsHash {
+ // CallbackId vectors have several properties that let us get away with this simple hash.
+ // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
+ // empty we can still hash 0.
+ // 2) CallbackId vectors for the same listener either are identical or contain none of the
+ // same members. It is sufficient to just check the first CallbackId in the vectors. If
+ // they match, they are the same. If they do not match, they are not the same.
+ std::size_t operator()(const std::vector<CallbackId> callbackIds) const {
+ return std::hash<CallbackId>{}((callbackIds.empty()) ? 0 : callbackIds.front());
+ }
+};
+
class CallbackHandle : public RefBase {
public:
CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
@@ -51,23 +63,24 @@
void run();
+ // Adds listener and callbackIds in case there are no SurfaceControls that are supposed
+ // to be included in the callback. This functions should be call before attempting to add any
+ // callback handles.
+ status_t addCallback(const sp<ITransactionCompletedListener>& transactionListener,
+ const std::vector<CallbackId>& callbackIds);
+
// Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle
// that needs to be latched and presented this frame. This function should be called once the
// layer has received the CallbackHandle so the TransactionCompletedThread knows not to send
// a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
// presented.
- void registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
+ status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
// Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
- void addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
+ status_t addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
// Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
// presented this frame.
- void addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
-
- // Adds listener and callbackIds in case there are no SurfaceControls that are supposed
- // to be included in the callback.
- void addCallback(const sp<ITransactionCompletedListener>& transactionListener,
- const std::vector<CallbackId>& callbackIds);
+ status_t addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
void addPresentFence(const sp<Fence>& presentFence);
@@ -76,9 +89,11 @@
private:
void threadMain();
+ status_t findTransactionStats(const sp<ITransactionCompletedListener>& listener,
+ const std::vector<CallbackId>& callbackIds,
+ TransactionStats** outTransactionStats) REQUIRES(mMutex);
+
status_t addCallbackHandle(const sp<CallbackHandle>& handle) REQUIRES(mMutex);
- status_t addCallbackLocked(const sp<ITransactionCompletedListener>& transactionListener,
- const std::vector<CallbackId>& callbackIds) REQUIRES(mMutex);
class ThreadDeathRecipient : public IBinder::DeathRecipient {
public:
@@ -91,9 +106,10 @@
};
sp<ThreadDeathRecipient> mDeathRecipient;
- struct IBinderHash {
- std::size_t operator()(const sp<IBinder>& strongPointer) const {
- return std::hash<IBinder*>{}(strongPointer.get());
+ struct ITransactionCompletedListenerHash {
+ std::size_t operator()(const sp<ITransactionCompletedListener>& listener) const {
+ return std::hash<IBinder*>{}((listener) ? IInterface::asBinder(listener).get()
+ : nullptr);
}
};
@@ -106,12 +122,13 @@
std::condition_variable_any mConditionVariable;
std::unordered_map<
- sp<IBinder /*listener*/>,
+ sp<ITransactionCompletedListener>,
std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
- IBinderHash>
+ ITransactionCompletedListenerHash>
mPendingTransactions GUARDED_BY(mMutex);
- std::unordered_map<sp<IBinder /*listener*/>, ListenerStats, IBinderHash> mListenerStats
- GUARDED_BY(mMutex);
+ std::unordered_map<sp<ITransactionCompletedListener>, std::deque<TransactionStats>,
+ ITransactionCompletedListenerHash>
+ mCompletedTransactions GUARDED_BY(mMutex);
bool mRunning GUARDED_BY(mMutex) = false;
bool mKeepRunning GUARDED_BY(mMutex) = true;