diff options
27 files changed, 1735 insertions, 38 deletions
diff --git a/include/layerproto b/include/layerproto new file mode 120000 index 0000000000..ef21a4eb8e --- /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 a610a516ac..102964ffad 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -118,4 +118,7 @@ cc_library_headers { vendor_available: true, } -subdirs = ["tests"] +subdirs = [ + "tests", + "tools", +] diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp index f32720038a..e3cb494527 100644 --- a/libs/vr/libbufferhub/Android.bp +++ b/libs/vr/libbufferhub/Android.bp @@ -61,10 +61,10 @@ cc_library { 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 index c4b9a8c88d..3c99f9927b 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 kHeight = 480; 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 @@ TEST_F(LibBufferHubTest, TestStateMask) { 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 @@ TEST_F(LibBufferHubTest, TestStateMask) { 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 @@ TEST_F(LibBufferHubTest, TestStateTransitions) { 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 1186f9348d..a356959a5d 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 @@ class BufferHubBuffer : public pdx::Client { 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 8bd1ef1414..c4ffb41385 100644 --- a/libs/vr/libbufferhubqueue/tests/Android.bp +++ b/libs/vr/libbufferhubqueue/tests/Android.bp @@ -12,6 +12,7 @@ shared_libraries = [ "libhardware", "libui", "libutils", + "libnativewindow", ] static_libraries = [ @@ -20,6 +21,7 @@ static_libraries = [ "libchrome", "libdvrcommon", "libpdx_default_transport", + "libperformance", ] cc_test { @@ -51,3 +53,17 @@ cc_test { 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 0000000000..5b580df284 --- /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 64a2a509ab..bd7f0ea364 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -5,4 +5,4 @@ cc_library_static { 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 390263ff2e..d8152e08cc 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -39,6 +39,7 @@ LOCAL_SRC_FILES := \ RenderEngine/RenderEngine.cpp \ RenderEngine/Texture.cpp \ RenderEngine/GLES20RenderEngine.cpp \ + LayerProtoHelper.cpp \ LOCAL_MODULE := libsurfaceflinger LOCAL_C_INCLUDES := \ @@ -98,7 +99,8 @@ LOCAL_SHARED_LIBRARIES := \ 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 @@ LOCAL_SHARED_LIBRARIES := \ 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 7d6d9886f6..c707e3cc65 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -223,6 +223,10 @@ void Composer::resetCommands() { mWriter.reset(); } +Error Composer::executeCommands() { + return execute(); +} + uint32_t Composer::getMaxVirtualDisplayCount() { auto ret = mClient->getMaxVirtualDisplayCount(); @@ -750,6 +754,11 @@ Error Composer::execute() } } + 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 31a3c1d785..104ca608e6 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -152,6 +152,9 @@ public: // 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 78c0c8567a..ab4a4b23bc 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -221,6 +221,11 @@ void Device::loadCapabilities() } } +Error Device::flushCommands() +{ + return static_cast<Error>(mComposer->executeCommands()); +} + // Display methods Display::Display(android::Hwc2::Composer& composer, @@ -632,11 +637,6 @@ Error Display::presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests 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 fbe4c7ebed..a15c6d940c 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -106,6 +106,11 @@ public: 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 @@ public: 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 b096a3ae57..5328a228bc 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -605,8 +605,11 @@ status_t HWComposer::presentAndGetReleaseFences(int32_t displayId) { 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 956f7f6c02..27739ce074 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 @@ void Layer::commitChildList() { 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 47924ae877..06c4863ce2 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 @@ public: */ 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 0000000000..6a33148d27 --- /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 0000000000..45a0b5d173 --- /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 5982422658..54c040dae9 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 @@ void SurfaceFlinger::readPersistentProperties() { 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 @@ void SurfaceFlinger::createDefaultDisplayDevice() { 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 @@ mat4 SurfaceFlinger::computeSaturationMatrix() const { // 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 @@ bool SurfaceFlinger::doComposeSurfaces( 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 @@ status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args) { 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::dumpBufferingStats(String8& result) const { 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 @@ void SurfaceFlinger::dumpWideColorInfo(String8& result) const { 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 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, 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 @@ status_t SurfaceFlinger::onTransact( 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 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, 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 @@ void SurfaceFlinger::renderScreenImplLocked( } #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 2cba500500..bd98c8f0e0 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 @@ private: 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 @@ private: #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 b718ec806c..ed7641fe99 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 @@ status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args) 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 @@ void SurfaceFlinger::dumpBufferingStats(String8& result) const { 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 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, 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 0000000000..4c52bdfaa7 --- /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 0000000000..65541674f1 --- /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 0000000000..054d4f2cbc --- /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 0000000000..7b94cefbc8 --- /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 0000000000..d27dc9b2ea --- /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 94f3f2561a..212b9e7ebb 100644 --- a/services/surfaceflinger/tests/fakehwc/Android.bp +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -22,7 +22,8 @@ cc_test { "libsync", "libfmq", "libbase", - "libhidltransport" + "libhidltransport", + "liblayers_proto" ], static_libs: [ "libhwcomposer-client", |