Merge "Native Side of Binder Proxy Tracking by Uid"
diff --git a/include/layerproto b/include/layerproto
new file mode 120000
index 0000000..ef21a4e
--- /dev/null
+++ b/include/layerproto
@@ -0,0 +1 @@
+../services/surfaceflinger/layerproto/include/layerproto/
\ No newline at end of file
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index a610a51..102964f 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -118,4 +118,7 @@
vendor_available: true,
}
-subdirs = ["tests"]
+subdirs = [
+ "tests",
+ "tools",
+]
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index f327200..e3cb494 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -61,10 +61,10 @@
cc_test {
tags: ["optional"],
- srcs: ["bufferhub_tests.cpp"],
+ srcs: ["buffer_hub-test.cpp"],
static_libs: ["libbufferhub"] + staticLibraries,
shared_libs: sharedLibraries,
header_libs: headerLibraries,
- name: "bufferhub_tests",
+ name: "buffer_hub-test",
}
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
similarity index 68%
rename from libs/vr/libbufferhub/bufferhub_tests.cpp
rename to libs/vr/libbufferhub/buffer_hub-test.cpp
index c4b9a8c..3c99f99 100644
--- a/libs/vr/libbufferhub/bufferhub_tests.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -20,6 +20,10 @@
using android::dvr::BufferConsumer;
using android::dvr::BufferHubDefs::kConsumerStateMask;
using android::dvr::BufferHubDefs::kProducerStateBit;
+using android::dvr::BufferHubDefs::IsBufferGained;
+using android::dvr::BufferHubDefs::IsBufferPosted;
+using android::dvr::BufferHubDefs::IsBufferAcquired;
+using android::dvr::BufferHubDefs::IsBufferReleased;
using android::dvr::BufferProducer;
using android::pdx::LocalHandle;
@@ -28,6 +32,7 @@
const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
const int kUsage = 0;
const uint64_t kContext = 42;
+const size_t kMaxConsumerCount = 63;
using LibBufferHubTest = ::testing::Test;
@@ -159,10 +164,10 @@
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- // It's ok to create up to 63 consumer buffers.
+ // It's ok to create up to kMaxConsumerCount consumer buffers.
uint64_t buffer_state_bits = p->buffer_state_bit();
- std::array<std::unique_ptr<BufferConsumer>, 63> cs;
- for (size_t i = 0; i < 63; i++) {
+ std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs;
+ for (size_t i = 0; i < kMaxConsumerCount; i++) {
cs[i] = BufferConsumer::Import(p->CreateConsumer());
ASSERT_TRUE(cs[i].get() != nullptr);
// Expect all buffers have unique state mask.
@@ -176,7 +181,7 @@
EXPECT_EQ(state.error(), E2BIG);
// Release any consumer should allow us to re-create.
- for (size_t i = 0; i < 63; i++) {
+ for (size_t i = 0; i < kMaxConsumerCount; i++) {
buffer_state_bits &= ~cs[i]->buffer_state_bit();
cs[i] = nullptr;
cs[i] = BufferConsumer::Import(p->CreateConsumer());
@@ -240,6 +245,217 @@
EXPECT_EQ(-EALREADY, p->Gain(&fence));
}
+TEST_F(LibBufferHubTest, TestAsyncStateTransitions) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+
+ DvrNativeBufferMetadata metadata;
+ LocalHandle invalid_fence;
+
+ // The producer buffer starts in gained state.
+
+ // Acquire, release, and gain in gained state should fail.
+ EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+ EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+ EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+
+ // Post in gained state should succeed.
+ EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+ EXPECT_EQ(p->buffer_state(), c->buffer_state());
+ EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+
+ // Post, release, and gain in posted state should fail.
+ EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
+ EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+ EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+
+ // Acquire in posted state should succeed.
+ EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
+ EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+ EXPECT_EQ(p->buffer_state(), c->buffer_state());
+ EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+
+ // Acquire, post, and gain in acquired state should fail.
+ EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+ EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
+ EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+
+ // Release in acquired state should succeed.
+ EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence));
+ EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+ EXPECT_EQ(p->buffer_state(), c->buffer_state());
+ EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+
+ // Release, acquire, and post in released state should fail.
+ EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+ EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+ EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
+
+ // Gain in released state should succeed.
+ EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+ EXPECT_EQ(p->buffer_state(), c->buffer_state());
+ EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+
+ // Acquire, release, and gain in gained state should fail.
+ EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+ EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+ EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
+ EXPECT_FALSE(invalid_fence.IsValid());
+}
+
+TEST_F(LibBufferHubTest, TestZeroConsumer) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+
+ DvrNativeBufferMetadata metadata;
+ LocalHandle invalid_fence;
+
+ // Newly created.
+ EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+ EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+
+ // The buffer should stay in posted stay until a consumer picks it up.
+ EXPECT_GE(0, RETRY_EINTR(p->Poll(100)));
+
+ // A new consumer should still be able to acquire the buffer immediately.
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+}
+
+TEST_F(LibBufferHubTest, TestMaxConsumers) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+
+ std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs;
+ for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ cs[i] = BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(cs[i].get() != nullptr);
+ EXPECT_TRUE(IsBufferGained(cs[i]->buffer_state()));
+ }
+
+ DvrNativeBufferMetadata metadata;
+ LocalHandle invalid_fence;
+
+ // Post the producer should trigger all consumers to be available.
+ EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+ EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+ for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ EXPECT_TRUE(IsBufferPosted(cs[i]->buffer_state(),
+ cs[i]->buffer_state_bit()));
+ EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(10)));
+ EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+ }
+
+ // All consumers have to release before the buffer is considered to be
+ // released.
+ for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ EXPECT_FALSE(IsBufferReleased(p->buffer_state()));
+ EXPECT_EQ(0, cs[i]->ReleaseAsync(&metadata, invalid_fence));
+ }
+
+ EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+ EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+
+ // Buffer state cross all clients must be consistent.
+ for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ EXPECT_EQ(p->buffer_state(), cs[i]->buffer_state());
+ }
+}
+
+TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferGained) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_TRUE(IsBufferGained(c->buffer_state()));
+
+ DvrNativeBufferMetadata metadata;
+ LocalHandle invalid_fence;
+
+ // Post the gained buffer should signal already created consumer.
+ EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+ EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+ EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
+ EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+}
+
+TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+
+ DvrNativeBufferMetadata metadata;
+ LocalHandle invalid_fence;
+
+ // Post the gained buffer before any consumer gets created.
+ EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+ EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+
+ // Newly created consumer should be automatically sigalled.
+ std::unique_ptr<BufferConsumer> c =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_TRUE(IsBufferPosted(c->buffer_state()));
+ EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+}
+
+TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) {
+ std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+
+ std::unique_ptr<BufferConsumer> c1 =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c1.get() != nullptr);
+
+ DvrNativeBufferMetadata metadata;
+ LocalHandle invalid_fence;
+
+ // Post, acquire, and release the buffer..
+ EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+ EXPECT_LT(0, RETRY_EINTR(c1->Poll(10)));
+ EXPECT_EQ(0, c1->AcquireAsync(&metadata, &invalid_fence));
+ EXPECT_EQ(0, c1->ReleaseAsync(&metadata, invalid_fence));
+
+ // Create another consumer immediately after the release, should not make the
+ // buffer un-released. This is guaranteed by IPC execution order in bufferhubd.
+ std::unique_ptr<BufferConsumer> c2 =
+ BufferConsumer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c2.get() != nullptr);
+
+ EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+ EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
+ EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+}
+
TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
struct Metadata {
int64_t field1;
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
index 1186f93..a356959 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -94,6 +94,9 @@
int id() const { return id_; }
+ // Returns the buffer buffer state.
+ uint64_t buffer_state() { return buffer_state_->load(); };
+
// A state mask which is unique to a buffer hub client among all its siblings
// sharing the same concrete graphic buffer.
uint64_t buffer_state_bit() const { return buffer_state_bit_; }
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
index 8bd1ef1..c4ffb41 100644
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -12,6 +12,7 @@
"libhardware",
"libui",
"libutils",
+ "libnativewindow",
]
static_libraries = [
@@ -20,6 +21,7 @@
"libchrome",
"libdvrcommon",
"libpdx_default_transport",
+ "libperformance",
]
cc_test {
@@ -51,3 +53,17 @@
name: "buffer_hub_queue_producer-test",
tags: ["optional"],
}
+
+cc_test {
+ srcs: ["buffer_transport_benchmark.cpp"],
+ static_libs: static_libraries,
+ shared_libs: shared_libraries,
+ header_libs: header_libraries,
+ cflags: [
+ "-DLOG_TAG=\"buffer_transport_benchmark\"",
+ "-DTRACE=0",
+ "-O2",
+ ],
+ name: "buffer_transport_benchmark",
+ tags: ["optional"],
+}
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp
new file mode 100644
index 0000000..5b580df
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp
@@ -0,0 +1,619 @@
+#include <android/native_window.h>
+#include <base/logging.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <dvr/dvr_api.h>
+#include <dvr/performance_client_api.h>
+#include <gtest/gtest.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
+#include <private/dvr/buffer_hub_queue_producer.h>
+#include <utils/Trace.h>
+
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include <poll.h>
+#include <sys/wait.h>
+#include <unistd.h> // for pipe
+
+// Use ALWAYS at the tag level. Control is performed manually during command
+// line processing.
+#ifdef ATRACE_TAG
+#undef ATRACE_TAG
+#endif
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
+
+using namespace android;
+using namespace android::dvr;
+
+static const String16 kBinderService = String16("bufferTransport");
+static const uint32_t kBufferWidth = 100;
+static const uint32_t kBufferHeight = 1;
+static const uint32_t kBufferLayerCount = 1;
+static const uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
+static const uint64_t kBufferUsage =
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
+static const int kMaxAcquiredImages = 1;
+static const size_t kMaxQueueCounts = 128;
+
+static int gConcurrency = 1; // 1 writer at a time
+static int gIterations = 1000; // 1K times
+static int gSleepIntervalUs = 16 * 1000; // 16ms
+
+enum BufferTransportServiceCode {
+ CREATE_BUFFER_QUEUE = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+// A mininal cross process helper class based on a bidirectional pipe pair. This
+// is used to signal that Binder-based BufferTransportService has finished
+// initialization.
+class Pipe {
+ public:
+ static std::tuple<Pipe, Pipe> CreatePipePair() {
+ int a[2] = {-1, -1};
+ int b[2] = {-1, -1};
+
+ pipe(a);
+ pipe(b);
+
+ return std::make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1]));
+ }
+
+ Pipe() = default;
+
+ Pipe(Pipe&& other) {
+ read_fd_ = other.read_fd_;
+ write_fd_ = other.write_fd_;
+ other.read_fd_ = 0;
+ other.write_fd_ = 0;
+ }
+
+ Pipe& operator=(Pipe&& other) {
+ Reset();
+ read_fd_ = other.read_fd_;
+ write_fd_ = other.write_fd_;
+ other.read_fd_ = 0;
+ other.write_fd_ = 0;
+ return *this;
+ }
+
+ ~Pipe() { Reset(); }
+
+ Pipe(const Pipe&) = delete;
+ Pipe& operator=(const Pipe&) = delete;
+ Pipe& operator=(const Pipe&&) = delete;
+
+ bool IsValid() { return read_fd_ > 0 && write_fd_ > 0; }
+
+ void Signal() {
+ bool val = true;
+ int error = write(write_fd_, &val, sizeof(val));
+ ASSERT_GE(error, 0);
+ };
+
+ void Wait() {
+ bool val = false;
+ int error = read(read_fd_, &val, sizeof(val));
+ ASSERT_GE(error, 0);
+ }
+
+ void Reset() {
+ if (read_fd_)
+ close(read_fd_);
+ if (write_fd_)
+ close(write_fd_);
+ }
+
+ private:
+ int read_fd_ = -1;
+ int write_fd_ = -1;
+ Pipe(int read_fd, int write_fd) : read_fd_{read_fd}, write_fd_{write_fd} {}
+};
+
+// A binder services that minics a compositor that consumes buffers. It provides
+// one Binder interface to create a new Surface for buffer producer to write
+// into; while itself will carry out no-op buffer consuming by acquiring then
+// releasing the buffer immediately.
+class BufferTransportService : public BBinder {
+ public:
+ BufferTransportService() = default;
+ ~BufferTransportService() = default;
+
+ virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+ uint32_t flags = 0) {
+ (void)flags;
+ (void)data;
+ switch (code) {
+ case CREATE_BUFFER_QUEUE: {
+ auto new_queue = std::make_shared<BufferQueueHolder>(this);
+ reply->writeStrongBinder(
+ IGraphicBufferProducer::asBinder(new_queue->producer_));
+ buffer_queues_.push_back(new_queue);
+ return NO_ERROR;
+ }
+ default:
+ return UNKNOWN_TRANSACTION;
+ };
+ }
+
+ private:
+ struct FrameListener : public ConsumerBase::FrameAvailableListener {
+ public:
+ FrameListener(BufferTransportService* service,
+ sp<BufferItemConsumer> buffer_item_consumer)
+ : service_(service),
+ buffer_item_consumer_(buffer_item_consumer) {}
+
+ void onFrameAvailable(const BufferItem& /*item*/) override {
+ std::unique_lock<std::mutex> autolock(service_->reader_mutex_);
+
+ BufferItem buffer;
+ status_t ret = 0;
+ {
+ ATRACE_NAME("AcquireBuffer");
+ ret = buffer_item_consumer_->acquireBuffer(&buffer, /*presentWhen=*/0,
+ /*waitForFence=*/false);
+ }
+
+ if (ret != NO_ERROR) {
+ LOG(ERROR) << "Failed to acquire next buffer.";
+ return;
+ }
+
+ {
+ ATRACE_NAME("ReleaseBuffer");
+ ret = buffer_item_consumer_->releaseBuffer(buffer);
+ }
+
+ if (ret != NO_ERROR) {
+ LOG(ERROR) << "Failed to release buffer.";
+ return;
+ }
+ }
+
+ private:
+ BufferTransportService* service_ = nullptr;
+ sp<BufferItemConsumer> buffer_item_consumer_;
+ };
+
+ struct BufferQueueHolder {
+ explicit BufferQueueHolder(BufferTransportService* service) {
+ BufferQueue::createBufferQueue(&producer_, &consumer_);
+
+ sp<BufferItemConsumer> buffer_item_consumer =
+ new BufferItemConsumer(consumer_, kBufferUsage, kMaxAcquiredImages,
+ /*controlledByApp=*/true);
+ buffer_item_consumer->setName(String8("BinderBufferTransport"));
+ frame_listener_ = new FrameListener(service, buffer_item_consumer);
+ buffer_item_consumer->setFrameAvailableListener(frame_listener_);
+ }
+
+ sp<IGraphicBufferProducer> producer_;
+ sp<IGraphicBufferConsumer> consumer_;
+ sp<FrameListener> frame_listener_;
+ };
+
+ std::mutex reader_mutex_;
+ std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_;
+};
+
+// A virtual interfaces that abstracts the common BufferQueue operations, so
+// that the test suite can use the same test case to drive different types of
+// transport backends.
+class BufferTransport {
+ public:
+ virtual ~BufferTransport() {}
+
+ virtual int Start() = 0;
+ virtual sp<Surface> CreateSurface() = 0;
+};
+
+// Binder-based buffer transport backend.
+//
+// On Start() a new process will be swapned to run a Binder server that
+// actually consumes the buffer.
+// On CreateSurface() a new Binder BufferQueue will be created, which the
+// service holds the concrete binder node of the IGraphicBufferProducer while
+// sending the binder proxy to the client. In another word, the producer side
+// operations are carried out process while the consumer side operations are
+// carried out within the BufferTransportService's own process.
+class BinderBufferTransport : public BufferTransport {
+ public:
+ BinderBufferTransport() {}
+
+ ~BinderBufferTransport() {
+ if (client_pipe_.IsValid()) {
+ client_pipe_.Signal();
+ LOG(INFO) << "Client signals service to shut down.";
+ }
+ }
+
+ int Start() override {
+ // Fork a process to run a binder server. The parent process will return
+ // a pipe here, and we use the pipe to signal the binder server to exit.
+ client_pipe_ = CreateBinderServer();
+
+ // Wait until service is ready.
+ LOG(INFO) << "Service is ready for client.";
+ client_pipe_.Wait();
+ return 0;
+ }
+
+ sp<Surface> CreateSurface() override {
+ sp<IServiceManager> sm = defaultServiceManager();
+ service_ = sm->getService(kBinderService);
+ if (service_ == nullptr) {
+ LOG(ERROR) << "Failed to set the benchmark service.";
+ return nullptr;
+ }
+
+ Parcel data;
+ Parcel reply;
+ int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply);
+ if (error != NO_ERROR) {
+ LOG(ERROR) << "Failed to get buffer queue over binder.";
+ return nullptr;
+ }
+
+ sp<IBinder> binder;
+ error = reply.readNullableStrongBinder(&binder);
+ if (error != NO_ERROR) {
+ LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
+ return nullptr;
+ }
+
+ auto producer = interface_cast<IGraphicBufferProducer>(binder);
+ if (producer == nullptr) {
+ LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
+ return nullptr;
+ }
+
+ sp<Surface> surface = new Surface(producer, /*controlledByApp=*/true);
+
+ // Set buffer dimension.
+ ANativeWindow* window = static_cast<ANativeWindow*>(surface.get());
+ ANativeWindow_setBuffersGeometry(window, kBufferWidth, kBufferHeight,
+ kBufferFormat);
+
+ return surface;
+ }
+
+ private:
+ static Pipe CreateBinderServer() {
+ std::tuple<Pipe, Pipe> pipe_pair = Pipe::CreatePipePair();
+ pid_t pid = fork();
+ if (pid) {
+ // parent, i.e. the client side.
+ ProcessState::self()->startThreadPool();
+ LOG(INFO) << "Binder server pid: " << pid;
+ return std::move(std::get<0>(pipe_pair));
+ } else {
+ // child, i.e. the service side.
+ Pipe service_pipe = std::move(std::get<1>(pipe_pair));
+
+ ProcessState::self()->startThreadPool();
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<BufferTransportService> service = new BufferTransportService;
+ sm->addService(kBinderService, service, false);
+
+ LOG(INFO) << "Binder Service Running...";
+
+ service_pipe.Signal();
+ service_pipe.Wait();
+
+ LOG(INFO) << "Service Exiting...";
+ exit(EXIT_SUCCESS);
+
+ /* never get here */
+ return {};
+ }
+ }
+
+ sp<IBinder> service_;
+ Pipe client_pipe_;
+};
+
+// BufferHub/PDX-based buffer transport.
+//
+// On Start() a new thread will be swapned to run an epoll polling thread which
+// minics the behavior of a compositor. Similar to Binder-based backend, the
+// buffer available handler is also a no-op: Buffer gets acquired and released
+// immediately.
+// On CreateSurface() a pair of dvr::ProducerQueue and dvr::ConsumerQueue will
+// be created. The epoll thread holds on the consumer queue and dequeues buffer
+// from it; while the producer queue will be wrapped in a Surface and returned
+// to test suite.
+class BufferHubTransport : public BufferTransport {
+ public:
+ virtual ~BufferHubTransport() {
+ stopped_.store(true);
+ if (reader_thread_.joinable()) {
+ reader_thread_.join();
+ }
+ }
+
+ int Start() override {
+ int ret = epoll_fd_.Create();
+ if (ret < 0) {
+ LOG(ERROR) << "Failed to create epoll fd: %s", strerror(-ret);
+ return -1;
+ }
+
+ // Create the reader thread.
+ reader_thread_ = std::thread([this]() {
+ int ret = dvrSetSchedulerClass(0, "graphics");
+ if (ret < 0) {
+ LOG(ERROR) << "Failed to set thread priority";
+ return;
+ }
+
+
+ ret = dvrSetCpuPartition(0, "/system/performance");
+ if (ret < 0) {
+ LOG(ERROR) << "Failed to set thread cpu partition";
+ return;
+ }
+
+ stopped_.store(false);
+ LOG(INFO) << "Reader Thread Running...";
+
+ while (!stopped_.load()) {
+ std::array<epoll_event, kMaxQueueCounts> events;
+
+ // Don't sleep forever so that we will have a chance to wake up.
+ const int ret = epoll_fd_.Wait(events.data(), events.size(),
+ /*timeout=*/100);
+ if (ret < 0) {
+ LOG(ERROR) << "Error polling consumer queues.";
+ continue;
+ }
+ if (ret == 0) {
+ continue;
+ }
+
+ const int num_events = ret;
+ for (int i = 0; i < num_events; i++) {
+ uint32_t surface_index = events[i].data.u32;
+ // LOG(INFO) << "!!! handle queue events index: " << surface_index;
+ buffer_queues_[surface_index]->consumer_queue_->HandleQueueEvents();
+ }
+ }
+
+ LOG(INFO) << "Reader Thread Exiting...";
+ });
+
+ return 0;
+ }
+
+ sp<Surface> CreateSurface() override {
+ std::lock_guard<std::mutex> autolock(queue_mutex_);
+
+ auto new_queue = std::make_shared<BufferQueueHolder>();
+ if (new_queue->producer_ == nullptr) {
+ LOG(ERROR) << "Failed to create buffer producer.";
+ return nullptr;
+ }
+
+ sp<Surface> surface =
+ new Surface(new_queue->producer_, /*controlledByApp=*/true);
+
+ // Set buffer dimension.
+ ANativeWindow* window = static_cast<ANativeWindow*>(surface.get());
+ ANativeWindow_setBuffersGeometry(window, kBufferWidth, kBufferHeight,
+ kBufferFormat);
+
+ // Use the next position as buffer_queue index.
+ uint32_t index = buffer_queues_.size();
+ epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u32 = index}};
+ const int ret = epoll_fd_.Control(
+ EPOLL_CTL_ADD, new_queue->consumer_queue_->queue_fd(), &event);
+ if (ret < 0) {
+ LOG(ERROR) << "Failed to track consumer queue: " << strerror(-ret)
+ << ", consumer queue fd: "
+ << new_queue->consumer_queue_->queue_fd();
+ return nullptr;
+ }
+
+ new_queue->queue_index_ = index;
+ buffer_queues_.push_back(new_queue);
+ return surface;
+ }
+
+ private:
+ struct BufferQueueHolder {
+ BufferQueueHolder() {
+ ProducerQueueConfigBuilder config_builder;
+ producer_queue_ =
+ ProducerQueue::Create(config_builder.SetDefaultWidth(kBufferWidth)
+ .SetDefaultHeight(kBufferHeight)
+ .SetDefaultFormat(kBufferFormat)
+ .SetMetadata<DvrNativeBufferMetadata>()
+ .Build(),
+ UsagePolicy{});
+ consumer_queue_ = producer_queue_->CreateConsumerQueue();
+ consumer_queue_->SetBufferAvailableCallback([this]() {
+ size_t index = 0;
+ pdx::LocalHandle fence;
+ DvrNativeBufferMetadata meta;
+ pdx::Status<std::shared_ptr<BufferConsumer>> status;
+
+ {
+ ATRACE_NAME("AcquireBuffer");
+ status = consumer_queue_->Dequeue(0, &index, &meta, &fence);
+ }
+ if (!status.ok()) {
+ LOG(ERROR) << "Failed to dequeue consumer buffer, error: "
+ << status.GetErrorMessage().c_str();
+ return;
+ }
+
+ auto buffer = status.take();
+
+ if (buffer) {
+ ATRACE_NAME("ReleaseBuffer");
+ buffer->ReleaseAsync();
+ }
+ });
+
+ producer_ = BufferHubQueueProducer::Create(producer_queue_);
+ }
+
+ int count_ = 0;
+ int queue_index_;
+ std::shared_ptr<ProducerQueue> producer_queue_;
+ std::shared_ptr<ConsumerQueue> consumer_queue_;
+ sp<IGraphicBufferProducer> producer_;
+ };
+
+ std::atomic<bool> stopped_;
+ std::thread reader_thread_;
+
+ // Mutex to guard epoll_fd_ and buffer_queues_.
+ std::mutex queue_mutex_;
+ EpollFileDescriptor epoll_fd_;
+ std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_;
+};
+
+enum TransportType {
+ kBinderBufferTransport,
+ kBufferHubTransport,
+};
+
+// Main test suite, which supports two transport backend: 1) BinderBufferQueue,
+// 2) BufferHubQueue. The test case drives the producer end of both transport
+// backend by queuing buffers into the buffer queue by using ANativeWindow API.
+class BufferTransportBenchmark
+ : public ::testing::TestWithParam<TransportType> {
+ public:
+ void SetUp() override {
+ switch (GetParam()) {
+ case kBinderBufferTransport:
+ transport_.reset(new BinderBufferTransport);
+ break;
+ case kBufferHubTransport:
+ transport_.reset(new BufferHubTransport);
+ break;
+ default:
+ FAIL() << "Unknown test case.";
+ break;
+ }
+ }
+
+ protected:
+ void ProduceBuffers(sp<Surface> surface, int iterations, int sleep_usec) {
+ ANativeWindow* window = static_cast<ANativeWindow*>(surface.get());
+ ANativeWindow_Buffer buffer;
+ int32_t error = 0;
+
+ for (int i = 0; i < iterations; i++) {
+ usleep(sleep_usec);
+
+ {
+ ATRACE_NAME("GainBuffer");
+ error = ANativeWindow_lock(window, &buffer,
+ /*inOutDirtyBounds=*/nullptr);
+ }
+ ASSERT_EQ(error, 0);
+
+ {
+ ATRACE_NAME("PostBuffer");
+ error = ANativeWindow_unlockAndPost(window);
+ }
+ ASSERT_EQ(error, 0);
+ }
+ }
+
+ std::unique_ptr<BufferTransport> transport_;
+};
+
+TEST_P(BufferTransportBenchmark, ContinuousLoad) {
+ ASSERT_NE(transport_, nullptr);
+ const int ret = transport_->Start();
+ ASSERT_EQ(ret, 0);
+
+ LOG(INFO) << "Start Running.";
+
+ std::vector<std::thread> writer_threads;
+ for (int i = 0; i < gConcurrency; i++) {
+ std::thread writer_thread = std::thread([this]() {
+ sp<Surface> surface = transport_->CreateSurface();
+ ASSERT_NE(surface, nullptr);
+
+ ASSERT_NO_FATAL_FAILURE(
+ ProduceBuffers(surface, gIterations, gSleepIntervalUs));
+
+ usleep(1000 * 100);
+ });
+
+ writer_threads.push_back(std::move(writer_thread));
+ }
+
+ for (auto& writer_thread : writer_threads) {
+ writer_thread.join();
+ }
+
+ LOG(INFO) << "All done.";
+};
+
+INSTANTIATE_TEST_CASE_P(BufferTransportBenchmarkInstance,
+ BufferTransportBenchmark,
+ ::testing::ValuesIn({kBinderBufferTransport,
+ kBufferHubTransport}));
+
+// To run binder-based benchmark, use:
+// adb shell buffer_transport_benchmark \
+// --gtest_filter="BufferTransportBenchmark.ContinuousLoad/0"
+//
+// To run bufferhub-based benchmark, use:
+// adb shell buffer_transport_benchmark \
+// --gtest_filter="BufferTransportBenchmark.ContinuousLoad/1"
+int main(int argc, char** argv) {
+ bool tracing_enabled = false;
+
+ // Parse arguments in addition to "--gtest_filter" paramters.
+ for (int i = 1; i < argc; i++) {
+ if (std::string(argv[i]) == "--help") {
+ std::cout << "Usage: binderThroughputTest [OPTIONS]" << std::endl;
+ std::cout << "\t-c N: Specify number of concurrent writer threads, "
+ "(default: 1, max: 128)."
+ << std::endl;
+ std::cout << "\t-i N: Specify number of iterations, (default: 1000)."
+ << std::endl;
+ std::cout << "\t-s N: Specify sleep interval in usec, (default: 16000)."
+ << std::endl;
+ std::cout << "\t--trace: Enable systrace logging."
+ << std::endl;
+ return 0;
+ }
+ if (std::string(argv[i]) == "-c") {
+ gConcurrency = atoi(argv[i + 1]);
+ i++;
+ continue;
+ }
+ if (std::string(argv[i]) == "-s") {
+ gSleepIntervalUs = atoi(argv[i + 1]);
+ i++;
+ continue;
+ }
+ if (std::string(argv[i]) == "-i") {
+ gIterations = atoi(argv[i + 1]);
+ i++;
+ continue;
+ }
+ if (std::string(argv[i]) == "--trace") {
+ tracing_enabled = true;
+ continue;
+ }
+ }
+
+ // Setup ATRACE/systrace based on command line.
+ atrace_setup();
+ atrace_set_tracing_enabled(tracing_enabled);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 64a2a50..bd7f0ea 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -5,4 +5,4 @@
export_static_lib_headers = ["libserviceutils"],
}
-subdirs = ["tests/fakehwc"]
\ No newline at end of file
+subdirs = ["tests/fakehwc", "layerproto"]
\ No newline at end of file
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 390263f..d8152e0 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -39,6 +39,7 @@
RenderEngine/RenderEngine.cpp \
RenderEngine/Texture.cpp \
RenderEngine/GLES20RenderEngine.cpp \
+ LayerProtoHelper.cpp \
LOCAL_MODULE := libsurfaceflinger
LOCAL_C_INCLUDES := \
@@ -98,7 +99,8 @@
libsync \
libprotobuf-cpp-lite \
libbase \
- android.hardware.power@1.0
+ android.hardware.power@1.0 \
+ liblayers_proto
LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
android.hardware.graphics.allocator@2.0 \
@@ -145,7 +147,8 @@
libutils \
libui \
libgui \
- libdl
+ libdl \
+ liblayers_proto
LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
LOCAL_STATIC_LIBRARIES := libtrace_proto \
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 7d6d988..c707e3c 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -223,6 +223,10 @@
mWriter.reset();
}
+Error Composer::executeCommands() {
+ return execute();
+}
+
uint32_t Composer::getMaxVirtualDisplayCount()
{
auto ret = mClient->getMaxVirtualDisplayCount();
@@ -750,6 +754,11 @@
}
}
+ if (commandLength == 0) {
+ mWriter.reset();
+ return Error::NONE;
+ }
+
Error error = kDefaultError;
auto ret = mClient->executeCommands(commandLength, commandHandles,
[&](const auto& tmpError, const auto& tmpOutChanged,
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 31a3c1d..104ca60 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -152,6 +152,9 @@
// skip a frame but have already queued some commands.
void resetCommands();
+ // Explicitly flush all pending commands in the command buffer.
+ Error executeCommands();
+
uint32_t getMaxVirtualDisplayCount();
bool isUsingVrComposer() const { return mIsUsingVrComposer; }
Error createVirtualDisplay(uint32_t width, uint32_t height,
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 78c0c85..ab4a4b2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -221,6 +221,11 @@
}
}
+Error Device::flushCommands()
+{
+ return static_cast<Error>(mComposer->executeCommands());
+}
+
// Display methods
Display::Display(android::Hwc2::Composer& composer,
@@ -632,11 +637,6 @@
return error;
}
-void Display::discardCommands()
-{
- mComposer.resetCommands();
-}
-
// For use by Device
void Display::setConnected(bool connected) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index fbe4c7e..a15c6d9 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -106,6 +106,11 @@
android::Hwc2::Composer* getComposer() { return mComposer.get(); }
+ // We buffer most state changes and flush them implicitly with
+ // Display::validate, Display::present, and Display::presentOrValidate.
+ // This method provides an explicit way to flush state changes to HWC.
+ Error flushCommands();
+
private:
// Initialization methods
@@ -244,12 +249,6 @@
uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence, uint32_t* state);
- // Most methods in this class write a command to a command buffer. The
- // command buffer is implicitly submitted in validate, present, and
- // presentOrValidate. This method provides a way to discard the commands,
- // which can be used to discard stale commands.
- void discardCommands();
-
// Other Display methods
hwc2_display_t getId() const { return mId; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index b096a3a..5328a22 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -605,8 +605,11 @@
auto& hwcDisplay = displayData.hwcDisplay;
if (displayData.validateWasSkipped) {
- hwcDisplay->discardCommands();
- auto error = displayData.presentError;
+ // explicitly flush all pending commands
+ auto error = mHwcDevice->flushCommands();
+ if (displayData.presentError != HWC2::Error::None) {
+ error = displayData.presentError;
+ }
if (error != HWC2::Error::None) {
ALOGE("skipValidate: failed for display %d: %s (%d)",
displayId, to_string(error).c_str(), static_cast<int32_t>(error));
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 956f7f6..27739ce 100755
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -56,6 +56,7 @@
#include "RenderEngine/RenderEngine.h"
#include <mutex>
+#include "LayerProtoHelper.h"
#define DEBUG_RESIZE 0
@@ -2826,6 +2827,82 @@
mDrawingParent = mCurrentParent;
}
+void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) {
+ const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
+ const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
+ const State& state = useDrawing ? mDrawingState : mCurrentState;
+
+ Transform requestedTransform = state.active.transform;
+ Transform transform = getTransform();
+
+ layerInfo->set_id(sequence);
+ layerInfo->set_name(getName().c_str());
+ layerInfo->set_type(String8(getTypeId()));
+
+ for (const auto& child : children) {
+ layerInfo->add_children(child->sequence);
+ }
+
+ for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
+ sp<Layer> strongRelative = weakRelative.promote();
+ if (strongRelative != nullptr) {
+ layerInfo->add_relatives(strongRelative->sequence);
+ }
+ }
+
+ LayerProtoHelper::writeToProto(state.activeTransparentRegion,
+ layerInfo->mutable_transparent_region());
+ LayerProtoHelper::writeToProto(visibleRegion, layerInfo->mutable_visible_region());
+ LayerProtoHelper::writeToProto(surfaceDamageRegion, layerInfo->mutable_damage_region());
+
+ layerInfo->set_layer_stack(getLayerStack());
+ layerInfo->set_z(state.z);
+
+ PositionProto* position = layerInfo->mutable_position();
+ position->set_x(transform.tx());
+ position->set_y(transform.ty());
+
+ PositionProto* requestedPosition = layerInfo->mutable_requested_position();
+ requestedPosition->set_x(requestedTransform.tx());
+ requestedPosition->set_y(requestedTransform.ty());
+
+ SizeProto* size = layerInfo->mutable_size();
+ size->set_w(state.active.w);
+ size->set_h(state.active.h);
+
+ LayerProtoHelper::writeToProto(state.crop, layerInfo->mutable_crop());
+ LayerProtoHelper::writeToProto(state.finalCrop, layerInfo->mutable_final_crop());
+
+ layerInfo->set_is_opaque(isOpaque(state));
+ layerInfo->set_invalidate(contentDirty);
+ layerInfo->set_dataspace(dataspaceDetails(getDataSpace()));
+ layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
+ LayerProtoHelper::writeToProto(getColor(), layerInfo->mutable_color());
+ LayerProtoHelper::writeToProto(state.color, layerInfo->mutable_requested_color());
+ layerInfo->set_flags(state.flags);
+
+ LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
+ LayerProtoHelper::writeToProto(requestedTransform, layerInfo->mutable_requested_transform());
+
+ auto parent = getParent();
+ if (parent != nullptr) {
+ layerInfo->set_parent(parent->sequence);
+ }
+
+ auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
+ if (zOrderRelativeOf != nullptr) {
+ layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
+ }
+
+ auto activeBuffer = getActiveBuffer();
+ if (activeBuffer != nullptr) {
+ LayerProtoHelper::writeToProto(activeBuffer, layerInfo->mutable_active_buffer());
+ }
+
+ layerInfo->set_queued_frames(getQueuedFrameCount());
+ layerInfo->set_refresh_pending(isBufferLatched());
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 47924ae..06c4863 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -49,9 +49,12 @@
#include "DisplayHardware/HWComposerBufferCache.h"
#include "RenderEngine/Mesh.h"
#include "RenderEngine/Texture.h"
+#include <layerproto/LayerProtoHeader.h>
#include <math/vec4.h>
+using namespace android::surfaceflinger;
+
namespace android {
// ---------------------------------------------------------------------------
@@ -303,6 +306,8 @@
*/
virtual bool isFixedSize() const;
+ void writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet = LayerVector::StateSet::Drawing);
+
protected:
/*
* onDraw - draws the surface.
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
new file mode 100644
index 0000000..6a33148
--- /dev/null
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "LayerProtoHelper.h"
+
+namespace android {
+namespace surfaceflinger {
+void LayerProtoHelper::writeToProto(const Region& region, RegionProto* regionProto) {
+ Region::const_iterator head = region.begin();
+ Region::const_iterator const tail = region.end();
+ uint64_t address = reinterpret_cast<uint64_t>(®ion);
+ regionProto->set_id(address);
+ while (head != tail) {
+ RectProto* rectProto = regionProto->add_rect();
+ writeToProto(*head, rectProto);
+ head++;
+ }
+}
+
+void LayerProtoHelper::writeToProto(const Rect& rect, RectProto* rectProto) {
+ rectProto->set_left(rect.left);
+ rectProto->set_top(rect.top);
+ rectProto->set_bottom(rect.bottom);
+ rectProto->set_right(rect.right);
+}
+
+void LayerProtoHelper::writeToProto(const half4 color, ColorProto* colorProto) {
+ colorProto->set_r(color.r);
+ colorProto->set_g(color.g);
+ colorProto->set_b(color.b);
+ colorProto->set_a(color.a);
+}
+
+void LayerProtoHelper::writeToProto(const Transform& transform, TransformProto* transformProto) {
+ transformProto->set_dsdx(transform[0][0]);
+ transformProto->set_dtdx(transform[0][1]);
+ transformProto->set_dsdy(transform[1][0]);
+ transformProto->set_dtdy(transform[1][1]);
+}
+
+void LayerProtoHelper::writeToProto(const sp<GraphicBuffer>& buffer,
+ ActiveBufferProto* activeBufferProto) {
+ activeBufferProto->set_width(buffer->getWidth());
+ activeBufferProto->set_height(buffer->getHeight());
+ activeBufferProto->set_stride(buffer->getStride());
+ activeBufferProto->set_format(buffer->format);
+}
+
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
new file mode 100644
index 0000000..45a0b5d
--- /dev/null
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <layerproto/LayerProtoHeader.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+#include <Transform.h>
+
+#include <math/vec4.h>
+
+namespace android {
+namespace surfaceflinger {
+class LayerProtoHelper {
+public:
+ static void writeToProto(const Rect& rect, RectProto* rectProto);
+ static void writeToProto(const Region& region, RegionProto* regionProto);
+ static void writeToProto(const half4 color, ColorProto* colorProto);
+ static void writeToProto(const Transform& transform, TransformProto* transformProto);
+ static void writeToProto(const sp<GraphicBuffer>& buffer, ActiveBufferProto* activeBufferProto);
+};
+
+} // namespace surfaceflinger
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5982422..54c040d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -90,6 +90,8 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
+#include <layerproto/LayerProtoParser.h>
+
#define DISPLAY_COUNT 1
/*
@@ -682,6 +684,12 @@
property_get("persist.sys.sf.color_saturation", value, "1.0");
mSaturation = atof(value);
ALOGV("Saturation is set to %.2f", mSaturation);
+
+ property_get("persist.sys.sf.native_mode", value, "0");
+ mForceNativeColorMode = atoi(value) == 1;
+ if (mForceNativeColorMode) {
+ ALOGV("Forcing native color mode");
+ }
}
void SurfaceFlinger::startBootAnim() {
@@ -1288,12 +1296,13 @@
break;
}
}
+ bool useWideColorMode = hasWideColorModes && hasWideColorDisplay && !mForceNativeColorMode;
sp<DisplayDevice> hw = new DisplayDevice(this, DisplayDevice::DISPLAY_PRIMARY, type, isSecure,
token, fbs, producer, mRenderEngine->getEGLConfig(),
- hasWideColorModes && hasWideColorDisplay);
+ useWideColorMode);
mDisplays.add(token, hw);
android_color_mode defaultColorMode = HAL_COLOR_MODE_NATIVE;
- if (hasWideColorModes && hasWideColorDisplay) {
+ if (useWideColorMode) {
defaultColorMode = HAL_COLOR_MODE_SRGB;
}
setActiveColorModeInternal(hw, defaultColorMode);
@@ -1810,6 +1819,10 @@
// pickColorMode translates a given dataspace into the best available color mode.
// Currently only support sRGB and Display-P3.
android_color_mode SurfaceFlinger::pickColorMode(android_dataspace dataSpace) const {
+ if (mForceNativeColorMode) {
+ return HAL_COLOR_MODE_NATIVE;
+ }
+
switch (dataSpace) {
// treat Unknown as regular SRGB buffer, since that's what the rest of the
// system expects.
@@ -2644,8 +2657,10 @@
ALOGV("hasClientComposition");
#ifdef USE_HWC2
- mRenderEngine->setWideColor(displayDevice->getWideColorSupport());
- mRenderEngine->setColorMode(displayDevice->getActiveColorMode());
+ mRenderEngine->setWideColor(
+ displayDevice->getWideColorSupport() && !mForceNativeColorMode);
+ mRenderEngine->setColorMode(mForceNativeColorMode ?
+ HAL_COLOR_MODE_NATIVE : displayDevice->getActiveColorMode());
#endif
if (!displayDevice->makeCurrent(mEGLDisplay, mEGLContext)) {
ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
@@ -3532,6 +3547,13 @@
dumpWideColorInfo(result);
dumpAll = false;
}
+
+ if ((index < numArgs) && (args[index] == String16("--proto"))) {
+ index++;
+ LayersProto layersProto = dumpProtoInfo();
+ result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
+ dumpAll = false;
+ }
}
if (dumpAll) {
@@ -3704,6 +3726,7 @@
void SurfaceFlinger::dumpWideColorInfo(String8& result) const {
result.appendFormat("hasWideColorDisplay: %d\n", hasWideColorDisplay);
+ result.appendFormat("forceNativeColorMode: %d\n", mForceNativeColorMode);
// TODO: print out if wide-color mode is active or not
@@ -3727,6 +3750,16 @@
result.append("\n");
}
+LayersProto SurfaceFlinger::dumpProtoInfo() const {
+ LayersProto layersProto;
+ mCurrentState.traverseInZOrder([&](Layer* layer) {
+ LayerProto* layerProto = layersProto.add_layers();
+ layer->writeToProto(layerProto, LayerVector::StateSet::Current);
+ });
+
+ return layersProto;
+}
+
void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
String8& result) const
{
@@ -3791,9 +3824,10 @@
colorizer.bold(result);
result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
colorizer.reset(result);
- mCurrentState.traverseInZOrder([&](Layer* layer) {
- result.append(to_string(layer->getLayerDebugInfo()).c_str());
- });
+
+ LayersProto layersProto = dumpProtoInfo();
+ auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
+ result.append(LayerProtoParser::layersToString(layerTree).c_str());
/*
* Dump Display state
@@ -4157,6 +4191,17 @@
repaintEverything();
return NO_ERROR;
}
+ case 1023: { // Set native mode
+ mForceNativeColorMode = data.readInt32() == 1;
+
+ invalidateHwcGeometry();
+ repaintEverything();
+ return NO_ERROR;
+ }
+ case 1024: { // Is wide color gamut rendering/color management supported?
+ reply->writeBool(hasWideColorDisplay);
+ return NO_ERROR;
+ }
}
}
return err;
@@ -4314,8 +4359,9 @@
WindowDisconnector disconnector(window, NATIVE_WINDOW_API_EGL);
ANativeWindowBuffer* buffer = nullptr;
- result = getWindowBuffer(window, reqWidth, reqHeight, hasWideColorDisplay,
- getRenderEngine().usesWideColor(), &buffer);
+ result = getWindowBuffer(window, reqWidth, reqHeight,
+ hasWideColorDisplay && !mForceNativeColorMode,
+ getRenderEngine().usesWideColor(), &buffer);
if (result != NO_ERROR) {
return result;
}
@@ -4417,8 +4463,8 @@
}
#ifdef USE_HWC2
- engine.setWideColor(hw->getWideColorSupport());
- engine.setColorMode(hw->getActiveColorMode());
+ engine.setWideColor(hw->getWideColorSupport() && !mForceNativeColorMode);
+ engine.setColorMode(mForceNativeColorMode ? HAL_COLOR_MODE_NATIVE : hw->getActiveColorMode());
#endif
// make sure to clear all GL error flags
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 2cba500..bd98c8f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -78,6 +78,10 @@
#include <thread>
#include <utility>
+#include <layerproto/LayerProtoHeader.h>
+
+using namespace android::surfaceflinger;
+
namespace android {
// ---------------------------------------------------------------------------
@@ -622,6 +626,7 @@
std::vector<OccupancyTracker::Segment>&& history);
void dumpBufferingStats(String8& result) const;
void dumpWideColorInfo(String8& result) const;
+ LayersProto dumpProtoInfo() const;
bool isLayerTripleBufferingDisabled() const {
return this->mLayerTripleBufferingDisabled;
@@ -830,6 +835,7 @@
#endif
float mSaturation = 1.0f;
+ bool mForceNativeColorMode = false;
};
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index b718ec8..ed7641f 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -87,6 +87,8 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
+#include <layerproto/LayerProtoParser.h>
+
#define DISPLAY_COUNT 1
/*
@@ -3073,6 +3075,13 @@
dumpFrameEventsLocked(result);
dumpAll = false;
}
+
+ if ((index < numArgs) && (args[index] == String16("--proto"))) {
+ index++;
+ LayersProto layersProto = dumpProtoInfo();
+ result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
+ dumpAll = false;
+ }
}
if (dumpAll) {
@@ -3243,6 +3252,16 @@
result.append("\n");
}
+LayersProto SurfaceFlinger::dumpProtoInfo() const {
+ LayersProto layersProto;
+ mCurrentState.traverseInZOrder([&](Layer* layer) {
+ LayerProto* layerProto = layersProto.add_layers();
+ layer->writeToProto(layerProto, LayerVector::StateSet::Current);
+ });
+
+ return layersProto;
+}
+
void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
String8& result) const
{
@@ -3302,9 +3321,10 @@
colorizer.bold(result);
result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
colorizer.reset(result);
- mCurrentState.traverseInZOrder([&](Layer* layer) {
- result.append(to_string(layer->getLayerDebugInfo()).c_str());
- });
+
+ LayersProto layersProto = dumpProtoInfo();
+ auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
+ result.append(LayerProtoParser::layersToString(layerTree).c_str());
/*
* Dump Display state
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
new file mode 100644
index 0000000..4c52bdf
--- /dev/null
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -0,0 +1,35 @@
+cc_library_shared {
+ name: "liblayers_proto",
+ vendor_available: true,
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "LayerProtoParser.cpp",
+ "layers.proto",
+ ],
+
+ shared_libs: [
+ "libui",
+ "libprotobuf-cpp-lite",
+ "libbase",
+ ],
+
+ proto: {
+ export_proto_headers: true,
+ },
+
+ cppflags: [
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wno-format",
+ "-Wno-c++98-compat-pedantic",
+ "-Wno-float-conversion",
+ "-Wno-disabled-macro-expansion",
+ "-Wno-float-equal",
+ "-Wno-sign-conversion",
+ "-Wno-padded",
+ "-Wno-old-style-cast",
+ "-Wno-undef",
+ ],
+
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
new file mode 100644
index 0000000..6554167
--- /dev/null
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <layerproto/LayerProtoParser.h>
+
+namespace android {
+namespace surfaceflinger {
+bool sortLayers(const LayerProtoParser::Layer* lhs, const LayerProtoParser::Layer* rhs) {
+ uint32_t ls = lhs->layerStack;
+ uint32_t rs = rhs->layerStack;
+ if (ls != rs) return ls < rs;
+
+ uint32_t lz = lhs->z;
+ uint32_t rz = rhs->z;
+ if (lz != rz) return lz < rz;
+
+ return lhs->id < rhs->id;
+}
+
+std::vector<const LayerProtoParser::Layer*> LayerProtoParser::generateLayerTree(
+ const LayersProto& layersProto) {
+ auto layerMap = generateMap(layersProto);
+
+ std::vector<const Layer*> layers;
+ std::for_each(layerMap.begin(), layerMap.end(),
+ [&](const std::pair<const int32_t, Layer*>& ref) {
+ if (ref.second->parent == nullptr) {
+ // only save top level layers
+ layers.push_back(ref.second);
+ }
+ });
+
+ std::sort(layers.begin(), layers.end(), sortLayers);
+ return layers;
+}
+
+std::unordered_map<int32_t, LayerProtoParser::Layer*> LayerProtoParser::generateMap(
+ const LayersProto& layersProto) {
+ std::unordered_map<int32_t, Layer*> layerMap;
+
+ for (int i = 0; i < layersProto.layers_size(); i++) {
+ const LayerProto& layerProto = layersProto.layers(i);
+ layerMap[layerProto.id()] = generateLayer(layerProto);
+ }
+
+ for (int i = 0; i < layersProto.layers_size(); i++) {
+ const LayerProto& layerProto = layersProto.layers(i);
+ updateChildrenAndRelative(layerProto, layerMap);
+ }
+
+ return layerMap;
+}
+
+LayerProtoParser::Layer* LayerProtoParser::generateLayer(const LayerProto& layerProto) {
+ Layer* layer = new Layer();
+ layer->id = layerProto.id();
+ layer->name = layerProto.name();
+ layer->type = layerProto.type();
+ layer->transparentRegion = generateRegion(layerProto.transparent_region());
+ layer->visibleRegion = generateRegion(layerProto.visible_region());
+ layer->damageRegion = generateRegion(layerProto.damage_region());
+ layer->layerStack = layerProto.layer_stack();
+ layer->z = layerProto.z();
+ layer->position = {layerProto.position().x(), layerProto.position().y()};
+ layer->requestedPosition = {layerProto.requested_position().x(),
+ layerProto.requested_position().y()};
+ layer->size = {layerProto.size().w(), layerProto.size().h()};
+ layer->crop = generateRect(layerProto.crop());
+ layer->finalCrop = generateRect(layerProto.final_crop());
+ layer->isOpaque = layerProto.is_opaque();
+ layer->invalidate = layerProto.invalidate();
+ layer->dataspace = layerProto.dataspace();
+ layer->pixelFormat = layerProto.pixel_format();
+ layer->color = {layerProto.color().r(), layerProto.color().g(), layerProto.color().b(),
+ layerProto.color().a()};
+ layer->requestedColor = {layerProto.requested_color().r(), layerProto.requested_color().g(),
+ layerProto.requested_color().b(), layerProto.requested_color().a()};
+ layer->flags = layerProto.flags();
+ layer->transform = generateTransform(layerProto.transform());
+ layer->requestedTransform = generateTransform(layerProto.requested_transform());
+ layer->activeBuffer = generateActiveBuffer(layerProto.active_buffer());
+ layer->queuedFrames = layerProto.queued_frames();
+ layer->refreshPending = layerProto.refresh_pending();
+
+ return layer;
+}
+
+LayerProtoParser::Region LayerProtoParser::generateRegion(const RegionProto& regionProto) {
+ LayerProtoParser::Region region;
+ region.id = regionProto.id();
+ for (int i = 0; i < regionProto.rect_size(); i++) {
+ const RectProto& rectProto = regionProto.rect(i);
+ region.rects.push_back(generateRect(rectProto));
+ }
+
+ return region;
+}
+
+LayerProtoParser::Rect LayerProtoParser::generateRect(const RectProto& rectProto) {
+ LayerProtoParser::Rect rect;
+ rect.left = rectProto.left();
+ rect.top = rectProto.top();
+ rect.right = rectProto.right();
+ rect.bottom = rectProto.bottom();
+
+ return rect;
+}
+
+LayerProtoParser::Transform LayerProtoParser::generateTransform(
+ const TransformProto& transformProto) {
+ LayerProtoParser::Transform transform;
+ transform.dsdx = transformProto.dsdx();
+ transform.dtdx = transformProto.dtdx();
+ transform.dsdy = transformProto.dsdy();
+ transform.dtdy = transformProto.dtdy();
+
+ return transform;
+}
+
+LayerProtoParser::ActiveBuffer LayerProtoParser::generateActiveBuffer(
+ const ActiveBufferProto& activeBufferProto) {
+ LayerProtoParser::ActiveBuffer activeBuffer;
+ activeBuffer.width = activeBufferProto.width();
+ activeBuffer.height = activeBufferProto.height();
+ activeBuffer.stride = activeBufferProto.stride();
+ activeBuffer.format = activeBufferProto.format();
+
+ return activeBuffer;
+}
+
+void LayerProtoParser::updateChildrenAndRelative(const LayerProto& layerProto,
+ std::unordered_map<int32_t, Layer*>& layerMap) {
+ auto currLayer = layerMap[layerProto.id()];
+
+ for (int i = 0; i < layerProto.children_size(); i++) {
+ if (layerMap.count(layerProto.children(i)) > 0) {
+ auto childLayer = layerMap[layerProto.children(i)];
+ currLayer->children.push_back(childLayer);
+ }
+ }
+
+ for (int i = 0; i < layerProto.relatives_size(); i++) {
+ if (layerMap.count(layerProto.relatives(i)) > 0) {
+ auto relativeLayer = layerMap[layerProto.relatives(i)];
+ currLayer->relatives.push_back(relativeLayer);
+ }
+ }
+
+ if (layerProto.has_parent()) {
+ if (layerMap.count(layerProto.parent()) > 0) {
+ auto parentLayer = layerMap[layerProto.parent()];
+ currLayer->parent = parentLayer;
+ }
+ }
+
+ if (layerProto.has_z_order_relative_of()) {
+ if (layerMap.count(layerProto.z_order_relative_of()) > 0) {
+ auto relativeLayer = layerMap[layerProto.z_order_relative_of()];
+ currLayer->zOrderRelativeOf = relativeLayer;
+ }
+ }
+}
+
+std::string LayerProtoParser::layersToString(
+ const std::vector<const LayerProtoParser::Layer*> layers) {
+ std::string result;
+ for (const LayerProtoParser::Layer* layer : layers) {
+ if (layer->zOrderRelativeOf != nullptr) {
+ continue;
+ }
+ result.append(layerToString(layer).c_str());
+ }
+
+ return result;
+}
+
+std::string LayerProtoParser::layerToString(const LayerProtoParser::Layer* layer) {
+ std::string result;
+
+ std::vector<const Layer*> traverse(layer->relatives);
+ for (const LayerProtoParser::Layer* child : layer->children) {
+ if (child->zOrderRelativeOf != nullptr) {
+ continue;
+ }
+
+ traverse.push_back(child);
+ }
+
+ std::sort(traverse.begin(), traverse.end(), sortLayers);
+
+ size_t i = 0;
+ for (; i < traverse.size(); i++) {
+ const auto& relative = traverse[i];
+ if (relative->z >= 0) {
+ break;
+ }
+ result.append(layerToString(relative).c_str());
+ }
+ result.append(layer->to_string().c_str());
+ result.append("\n");
+ for (; i < traverse.size(); i++) {
+ const auto& relative = traverse[i];
+ result.append(layerToString(relative).c_str());
+ }
+
+ return result;
+}
+
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoHeader.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoHeader.h
new file mode 100644
index 0000000..054d4f2
--- /dev/null
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoHeader.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Projectlayerproto/LayerProtoHeader.h
+ *
+ * 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 is used here to disable the warnings emitted from the protobuf
+// headers. By adding #pragma before including layer.pb.h, it supresses
+// protobuf warnings, but allows the rest of the files to continuing using
+// the current flags.
+// This file should be included instead of directly including layer.b.h
+#pragma GCC system_header
+#include <layers.pb.h>
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
new file mode 100644
index 0000000..7b94cef
--- /dev/null
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <layerproto/LayerProtoHeader.h>
+
+#include <math/vec4.h>
+
+#include <android-base/stringprintf.h>
+#include <ui/DebugUtils.h>
+#include <unordered_map>
+#include <vector>
+
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
+namespace android {
+namespace surfaceflinger {
+
+class LayerProtoParser {
+public:
+ class ActiveBuffer {
+ public:
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ int32_t format;
+
+ std::string to_string() const {
+ return StringPrintf("[%4ux%4u:%4u,%s]", width, height, stride,
+ decodePixelFormat(format).c_str());
+ }
+ };
+
+ class Transform {
+ public:
+ float dsdx;
+ float dtdx;
+ float dsdy;
+ float dtdy;
+
+ std::string to_string() const {
+ return StringPrintf("[%.2f, %.2f][%.2f, %.2f]", static_cast<double>(dsdx),
+ static_cast<double>(dtdx), static_cast<double>(dsdy),
+ static_cast<double>(dtdy));
+ }
+ };
+
+ class Rect {
+ public:
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+
+ std::string to_string() const {
+ return StringPrintf("[%3d, %3d, %3d, %3d]", left, top, right, bottom);
+ }
+ };
+
+ class Region {
+ public:
+ uint64_t id;
+ std::vector<Rect> rects;
+
+ std::string to_string(const char* what) const {
+ std::string result =
+ StringPrintf(" Region %s (this=%lx count=%d)\n", what,
+ static_cast<unsigned long>(id), static_cast<int>(rects.size()));
+
+ for (auto& rect : rects) {
+ StringAppendF(&result, " %s\n", rect.to_string().c_str());
+ }
+
+ return result;
+ }
+ };
+
+ class Layer {
+ public:
+ int32_t id;
+ std::string name;
+ std::vector<const Layer*> children;
+ std::vector<const Layer*> relatives;
+ std::string type;
+ LayerProtoParser::Region transparentRegion;
+ LayerProtoParser::Region visibleRegion;
+ LayerProtoParser::Region damageRegion;
+ uint32_t layerStack;
+ int32_t z;
+ float2 position;
+ float2 requestedPosition;
+ int2 size;
+ LayerProtoParser::Rect crop;
+ LayerProtoParser::Rect finalCrop;
+ bool isOpaque;
+ bool invalidate;
+ std::string dataspace;
+ std::string pixelFormat;
+ half4 color;
+ half4 requestedColor;
+ uint32_t flags;
+ Transform transform;
+ Transform requestedTransform;
+ Layer* parent = 0;
+ Layer* zOrderRelativeOf = 0;
+ LayerProtoParser::ActiveBuffer activeBuffer;
+ int32_t queuedFrames;
+ bool refreshPending;
+
+ std::string to_string() const {
+ std::string result;
+ StringAppendF(&result, "+ %s (%s)\n", type.c_str(), name.c_str());
+ result.append(transparentRegion.to_string("TransparentRegion").c_str());
+ result.append(visibleRegion.to_string("VisibleRegion").c_str());
+ result.append(damageRegion.to_string("SurfaceDamageRegion").c_str());
+
+ StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
+ layerStack, z, static_cast<double>(position.x),
+ static_cast<double>(position.y), size.x, size.y);
+
+ StringAppendF(&result, "crop=%s, finalCrop=%s, ", crop.to_string().c_str(),
+ finalCrop.to_string().c_str());
+ StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
+ StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
+ StringAppendF(&result, "pixelformat=%s, ", pixelFormat.c_str());
+ StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
+ static_cast<double>(color.r), static_cast<double>(color.g),
+ static_cast<double>(color.b), static_cast<double>(color.a), flags);
+ StringAppendF(&result, "tr=%s", transform.to_string().c_str());
+ result.append("\n");
+ StringAppendF(&result, " parent=%s\n",
+ parent == nullptr ? "none" : parent->name.c_str());
+ StringAppendF(&result, " zOrderRelativeOf=%s\n",
+ zOrderRelativeOf == nullptr ? "none" : zOrderRelativeOf->name.c_str());
+ StringAppendF(&result, " activeBuffer=%s,", activeBuffer.to_string().c_str());
+ StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d", queuedFrames,
+ refreshPending);
+
+ return result;
+ }
+ };
+
+ static std::vector<const Layer*> generateLayerTree(const LayersProto& layersProto);
+ static std::string layersToString(const std::vector<const LayerProtoParser::Layer*> layers);
+
+private:
+ static std::unordered_map<int32_t, Layer*> generateMap(const LayersProto& layersProto);
+ static LayerProtoParser::Layer* generateLayer(const LayerProto& layerProto);
+ static LayerProtoParser::Region generateRegion(const RegionProto& regionProto);
+ static LayerProtoParser::Rect generateRect(const RectProto& rectProto);
+ static LayerProtoParser::Transform generateTransform(const TransformProto& transformProto);
+ static LayerProtoParser::ActiveBuffer generateActiveBuffer(
+ const ActiveBufferProto& activeBufferProto);
+ static void updateChildrenAndRelative(const LayerProto& layerProto,
+ std::unordered_map<int32_t, Layer*>& layerMap);
+
+ static std::string layerToString(const LayerProtoParser::Layer* layer);
+};
+
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
new file mode 100644
index 0000000..d27dc9b
--- /dev/null
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -0,0 +1,110 @@
+// Definitions for SurfaceFlinger layers.
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package android.surfaceflinger;
+
+// Contains a list of all layers.
+message LayersProto {
+ repeated LayerProto layers = 1;
+}
+
+// Information about each layer.
+message LayerProto {
+ // unique id per layer.
+ optional int32 id = 1;
+ // unique name per layer.
+ optional string name = 2;
+ // list of children this layer may have. May be empty.
+ repeated int32 children = 3;
+ // list of layers that are z order relative to this layer.
+ repeated int32 relatives = 4;
+ // The type of layer, ex Color, Layer
+ optional string type = 5;
+ optional RegionProto transparent_region = 6;
+ optional RegionProto visible_region = 7;
+ optional RegionProto damage_region = 8;
+ optional uint32 layer_stack = 9;
+ // The layer's z order. Can be z order in layer stack, relative to parent,
+ // or relative to another layer specified in zOrderRelative.
+ optional int32 z = 10;
+ // The layer's position on the display.
+ optional PositionProto position = 11;
+ // The layer's requested position.
+ optional PositionProto requested_position = 12;
+ // The layer's size.
+ optional SizeProto size = 13;
+ // The layer's crop in it's own bounds.
+ optional RectProto crop = 14;
+ // The layer's crop in it's parent's bounds.
+ optional RectProto final_crop = 15;
+ optional bool is_opaque = 16;
+ optional bool invalidate = 17;
+ optional string dataspace = 18;
+ optional string pixel_format = 19;
+ // The layer's actual color.
+ optional ColorProto color = 20;
+ // The layer's requested color.
+ optional ColorProto requested_color = 21;
+ // Can be any combination of
+ // hidden = 0x01
+ // opaque = 0x02,
+ // secure = 0x80,
+ optional uint32 flags = 22;
+ // The layer's actual transform
+ optional TransformProto transform = 23;
+ // The layer's requested transform.
+ optional TransformProto requested_transform = 24;
+ // The parent layer. This value can be null if there is no parent.
+ optional int32 parent = 25 [default = -1];
+ // The layer that this layer has a z order relative to. This value can be null.
+ optional int32 z_order_relative_of = 26 [default = -1];
+ // This value can be null if there's nothing to draw.
+ optional ActiveBufferProto active_buffer = 27;
+ // The number of frames available.
+ optional int32 queued_frames = 28;
+ optional bool refresh_pending = 29;
+}
+
+message PositionProto {
+ optional float x = 1;
+ optional float y = 2;
+}
+
+message SizeProto {
+ optional int32 w = 1;
+ optional int32 h = 2;
+}
+
+message TransformProto {
+ optional float dsdx = 1;
+ optional float dtdx = 2;
+ optional float dsdy = 3;
+ optional float dtdy = 4;
+}
+
+message RegionProto {
+ optional uint64 id = 1;
+ repeated RectProto rect = 2;
+}
+
+message RectProto {
+ optional int32 left = 1;
+ optional int32 top = 2;
+ optional int32 right = 3;
+ optional int32 bottom = 4;
+}
+
+message ActiveBufferProto {
+ optional uint32 width = 1;
+ optional uint32 height = 2;
+ optional uint32 stride = 3;
+ optional int32 format = 4;
+}
+
+message ColorProto {
+ optional float r = 1;
+ optional float g = 2;
+ optional float b = 3;
+ optional float a = 4;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 94f3f25..212b9e7 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -22,7 +22,8 @@
"libsync",
"libfmq",
"libbase",
- "libhidltransport"
+ "libhidltransport",
+ "liblayers_proto"
],
static_libs: [
"libhwcomposer-client",