Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 656f406f authored by Jiwen 'Steve' Cai's avatar Jiwen 'Steve' Cai
Browse files

DvrWriteBufferQueue: support buffer resizing

Currently, the buffer resising is implemented at the C API's
implementation level, i.e. it's a pure client side logic out of the core
BufferHubQueue C++/PDX implementation. The logic is similar to
BufferHubQueueProducer's buffer resizing logic.

Other minor changes:
1/ Use ProducerQueueConfig in DisplaySurface as well, and plumb default
width, height, and format to bufferhubd when a BufferHubQueue is created
through a Display Surface.
2/ Added detailed dvr_buffer_queue comments.
3/ Since we now have more and more logic in dvr_buffer_queue, refactored
Dvr{Read,Write}BufferQueue to be C++-styled class.

Bug: 38324405
Test: buffer_hub_queue_producer-test, buffer_hub_queue-test, dvr_api-test
Change-Id: I7186fc04e84eafda46eca316a94739bb3b5c07ca
parent 9b8c084f
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -104,9 +104,16 @@ Status<void> Surface::SetAttributes(const SurfaceAttributes& attributes) {
  return {};
}

Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue() {
Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(uint32_t width,
                                                            uint32_t height,
                                                            uint32_t format) {
  ALOGD_IF(TRACE, "Surface::CreateQueue: Creating empty queue.");
  auto status = InvokeRemoteMethod<DisplayProtocol::CreateQueue>(0);
  auto status = InvokeRemoteMethod<DisplayProtocol::CreateQueue>(
      ProducerQueueConfigBuilder()
          .SetDefaultWidth(width)
          .SetDefaultHeight(height)
          .SetDefaultFormat(format)
          .Build());
  if (!status) {
    ALOGE("Surface::CreateQueue: Failed to create queue: %s",
          status.GetErrorMessage().c_str());
@@ -129,7 +136,7 @@ Status<std::unique_ptr<ProducerQueue>> Surface::CreateQueue(
           "Surface::CreateQueue: width=%u height=%u layer_count=%u format=%u "
           "usage=%" PRIx64 " capacity=%zu",
           width, height, layer_count, format, usage, capacity);
  auto status = CreateQueue();
  auto status = CreateQueue(width, height, format);
  if (!status)
    return status.error_status();

+3 −1
Original line number Diff line number Diff line
@@ -37,7 +37,9 @@ class Surface : public pdx::ClientBase<Surface> {
  pdx::Status<void> SetAttributes(const SurfaceAttributes& attributes);

  // Creates an empty queue.
  pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue();
  pdx::Status<std::unique_ptr<ProducerQueue>> CreateQueue(uint32_t width,
                                                          uint32_t height,
                                                          uint32_t format);

  // Creates a queue and populates it with |capacity| buffers of the specified
  // parameters.
+3 −2
Original line number Diff line number Diff line
@@ -213,8 +213,9 @@ struct DisplayProtocol {
  PDX_REMOTE_METHOD(CreateSurface, kOpCreateSurface,
                    SurfaceInfo(const SurfaceAttributes& attributes));
  PDX_REMOTE_METHOD(GetSurfaceInfo, kOpGetSurfaceInfo, SurfaceInfo(Void));
  PDX_REMOTE_METHOD(CreateQueue, kOpCreateQueue,
                    LocalChannelHandle(size_t meta_size_bytes));
  PDX_REMOTE_METHOD(
      CreateQueue, kOpCreateQueue,
      LocalChannelHandle(const ProducerQueueConfig& producer_config));
  PDX_REMOTE_METHOD(SetAttributes, kOpSetAttributes,
                    void(const SurfaceAttributes& attributes));
};
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ cc_library_headers {

cflags = [
    "-DLOG_TAG=\"libdvr\"",
    "-DTRACE=0",
]

srcs = [
+195 −108
Original line number Diff line number Diff line
@@ -2,60 +2,174 @@
#include "include/dvr/dvr_buffer_queue.h"

#include <android/native_window.h>
#include <gui/Surface.h>
#include <private/dvr/buffer_hub_queue_client.h>
#include <private/dvr/buffer_hub_queue_producer.h>

#include "dvr_internal.h"

#define CHECK_PARAM(param)                                               \
  LOG_ALWAYS_FATAL_IF(param == nullptr, "%s: " #param "cannot be NULL.", \
                      __FUNCTION__)
#include "dvr_buffer_queue_internal.h"

using namespace android;
using android::dvr::BufferHubQueueProducer;
using android::dvr::BufferProducer;
using android::dvr::ConsumerQueue;
using android::dvr::ProducerQueue;

extern "C" {

namespace android {
namespace dvr {
DvrWriteBufferQueue::DvrWriteBufferQueue(
    const std::shared_ptr<ProducerQueue>& producer_queue)
    : producer_queue_(producer_queue),
      width_(producer_queue->default_width()),
      height_(producer_queue->default_height()),
      format_(producer_queue->default_format()) {}

DvrWriteBufferQueue* CreateDvrWriteBufferQueueFromProducerQueue(
    const std::shared_ptr<dvr::ProducerQueue>& producer_queue) {
  return new DvrWriteBufferQueue{std::move(producer_queue)};
int DvrWriteBufferQueue::GetNativeWindow(ANativeWindow** out_window) {
  if (producer_queue_->metadata_size() != sizeof(DvrNativeBufferMetadata)) {
    ALOGE(
        "DvrWriteBufferQueue::GetNativeWindow: The size of buffer metadata "
        "(%zu) of the write queue does not match  of size of "
        "DvrNativeBufferMetadata (%zu).",
        producer_queue_->metadata_size(), sizeof(DvrNativeBufferMetadata));
    return -EINVAL;
  }

DvrReadBufferQueue* CreateDvrReadBufferQueueFromConsumerQueue(
    const std::shared_ptr<dvr::ConsumerQueue>& consumer_queue) {
  return new DvrReadBufferQueue{std::move(consumer_queue)};
  if (native_window_ == nullptr) {
    // Lazy creation of |native_window|, as not everyone is using
    // DvrWriteBufferQueue as an external surface.
    sp<IGraphicBufferProducer> gbp =
        BufferHubQueueProducer::Create(producer_queue_);
    native_window_ = new Surface(gbp, true);
  }

dvr::ProducerQueue* GetProducerQueueFromDvrWriteBufferQueue(
    DvrWriteBufferQueue* write_queue) {
  return write_queue->producer_queue.get();
  *out_window = static_cast<ANativeWindow*>(native_window_.get());
  return 0;
}

}  // namespace dvr
}  // namespace android
int DvrWriteBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) {
  std::unique_ptr<ConsumerQueue> consumer_queue =
      producer_queue_->CreateConsumerQueue();
  if (consumer_queue == nullptr) {
    ALOGE(
        "DvrWriteBufferQueue::CreateReadQueue: Failed to create consumer queue "
        "from producer queue: queue_id=%d.", producer_queue_->id());
    return -ENOMEM;
  }

extern "C" {
  *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue));
  return 0;
}

void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) {
  if (write_queue != nullptr && write_queue->native_window != nullptr)
    ANativeWindow_release(write_queue->native_window);
int DvrWriteBufferQueue::Dequeue(int timeout, DvrWriteBuffer* write_buffer,
                                 int* out_fence_fd) {
  size_t slot;
  pdx::LocalHandle fence;
  std::shared_ptr<BufferProducer> buffer_producer;

  // Need to retry N+1 times, where N is total number of buffers in the queue.
  // As in the worst case, we will dequeue all N buffers and reallocate them, on
  // the {N+1}th dequeue, we are guaranteed to get a buffer with new dimension.
  size_t max_retries = 1 + producer_queue_->capacity();
  size_t retry = 0;

  for (; retry < max_retries; retry++) {
    auto buffer_status = producer_queue_->Dequeue(timeout, &slot, &fence);
    if (!buffer_status) {
      ALOGE_IF(buffer_status.error() != ETIMEDOUT,
               "DvrWriteBufferQueue::Dequeue: Failed to dequeue buffer: %s",
               buffer_status.GetErrorMessage().c_str());
      return -buffer_status.error();
    }

    buffer_producer = buffer_status.take();
    if (!buffer_producer)
      return -ENOMEM;

    if (width_ == buffer_producer->width() &&
        height_ == buffer_producer->height() &&
        format_ == buffer_producer->format()) {
      // Producer queue returns a buffer matches the current request.
      break;
    }

    // Needs reallocation. Note that if there are already multiple available
    // buffers in the queue, the next one returned from |queue_->Dequeue| may
    // still have the old buffer dimension or format. Retry up to N+1 times or
    // until we dequeued a buffer with new configuration.
    ALOGD_IF(TRACE,
             "DvrWriteBufferQueue::Dequeue: requested buffer at slot: %zu "
             "(w=%u, h=%u, fmt=%u) is different from the buffer returned "
             "(w=%u, h=%u, fmt=%u). Need re-allocation.",
             slot, width_, height_, format_, buffer_producer->width(),
             buffer_producer->height(), buffer_producer->format());

    // Currently, we are not storing |layer_count| and |usage| in queue
    // configuration. Copy those setup from the last buffer dequeued before we
    // remove it.
    uint32_t old_layer_count = buffer_producer->layer_count();
    uint64_t old_usage = buffer_producer->usage();

    // Allocate a new producer buffer with new buffer configs. Note that if
    // there are already multiple available buffers in the queue, the next one
    // returned from |queue_->Dequeue| may still have the old buffer dimension
    // or format. Retry up to BufferHubQueue::kMaxQueueCapacity times or until
    // we dequeued a buffer with new configuration.
    auto detach_status = producer_queue_->DetachBuffer(slot);
    if (!detach_status) {
      ALOGE("DvrWriteBufferQueue::Dequeue: Failed to detach buffer: %s",
            detach_status.GetErrorMessage().c_str());
      return -detach_status.error();
    }

    auto allocate_status = producer_queue_->AllocateBuffer(
        width_, height_, old_layer_count, format_, old_usage, &slot);
    if (!allocate_status) {
      ALOGE("DvrWriteBufferQueue::Dequeue: Failed to allocate buffer: %s",
            allocate_status.GetErrorMessage().c_str());
      return -allocate_status.error();
    }
  }

  if (retry >= max_retries) {
    ALOGE(
        "DvrWriteBufferQueue::Dequeue: Failed to re-allocate buffer after "
        "resizing.");
    return -ENOMEM;
  }

  write_buffer->write_buffer = std::move(buffer_producer);
  *out_fence_fd = fence.Release();
  return 0;
}

int DvrWriteBufferQueue::ResizeBuffer(uint32_t width, uint32_t height) {
  if (width == 0 || height == 0) {
    ALOGE(
        "DvrWriteBufferQueue::ResizeBuffer: invalid buffer dimension: w=%u, "
        "h=%u.",
        width, height);
    return -EINVAL;
  }

  width_ = width;
  height_ = height;
  return 0;
}

void dvrWriteBufferQueueDestroy(DvrWriteBufferQueue* write_queue) {
  delete write_queue;
}

ssize_t dvrWriteBufferQueueGetCapacity(DvrWriteBufferQueue* write_queue) {
  if (!write_queue || !write_queue->producer_queue)
  if (!write_queue)
    return -EINVAL;

  return write_queue->producer_queue->capacity();
  return write_queue->capacity();
}

int dvrWriteBufferQueueGetId(DvrWriteBufferQueue* write_queue) {
  if (!write_queue)
    return -EINVAL;

  return write_queue->producer_queue->id();
  return write_queue->id();
}

int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue,
@@ -63,74 +177,81 @@ int dvrWriteBufferQueueGetExternalSurface(DvrWriteBufferQueue* write_queue,
  if (!write_queue || !out_window)
    return -EINVAL;

  if (write_queue->producer_queue->metadata_size() !=
      sizeof(DvrNativeBufferMetadata)) {
    ALOGE(
        "The size of buffer metadata (%zu) of the write queue does not match "
        "of size of DvrNativeBufferMetadata (%zu).",
        write_queue->producer_queue->metadata_size(),
        sizeof(DvrNativeBufferMetadata));
    return -EINVAL;
  return write_queue->GetNativeWindow(out_window);
}

  // Lazy creation of |native_window|.
  if (write_queue->native_window == nullptr) {
    sp<IGraphicBufferProducer> gbp =
        dvr::BufferHubQueueProducer::Create(write_queue->producer_queue);
    sp<Surface> surface = new Surface(gbp, true);
    write_queue->native_window = static_cast<ANativeWindow*>(surface.get());
    ANativeWindow_acquire(write_queue->native_window);
int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue,
                                       DvrReadBufferQueue** out_read_queue) {
  if (!write_queue || !out_read_queue)
    return -EINVAL;

  return write_queue->CreateReadQueue(out_read_queue);
}

  *out_window = write_queue->native_window;
  return 0;
int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
                               DvrWriteBuffer* write_buffer,
                               int* out_fence_fd) {
  if (!write_queue || !write_buffer || !out_fence_fd)
    return -EINVAL;

  return write_queue->Dequeue(timeout, write_buffer, out_fence_fd);
}

int dvrWriteBufferQueueCreateReadQueue(DvrWriteBufferQueue* write_queue,
                                       DvrReadBufferQueue** out_read_queue) {
  if (!write_queue || !write_queue->producer_queue || !out_read_queue)
int dvrWriteBufferQueueResizeBuffer(DvrWriteBufferQueue* write_queue,
                                    uint32_t width, uint32_t height) {
  if (!write_queue)
    return -EINVAL;

  auto read_queue = std::make_unique<DvrReadBufferQueue>();
  read_queue->consumer_queue =
      write_queue->producer_queue->CreateConsumerQueue();
  if (read_queue->consumer_queue == nullptr) {
  return write_queue->ResizeBuffer(width, height);
}

// ReadBufferQueue

DvrReadBufferQueue::DvrReadBufferQueue(
    const std::shared_ptr<ConsumerQueue>& consumer_queue)
    : consumer_queue_(consumer_queue) {}

int DvrReadBufferQueue::CreateReadQueue(DvrReadBufferQueue** out_read_queue) {
  std::unique_ptr<ConsumerQueue> consumer_queue =
      consumer_queue_->CreateConsumerQueue();
  if (consumer_queue == nullptr) {
    ALOGE(
        "dvrWriteBufferQueueCreateReadQueue: Failed to create consumer queue "
        "from DvrWriteBufferQueue[%p].",
        write_queue);
        "DvrReadBufferQueue::CreateReadQueue: Failed to create consumer queue "
        "from producer queue: queue_id=%d.", consumer_queue_->id());
    return -ENOMEM;
  }

  *out_read_queue = read_queue.release();
  *out_read_queue = new DvrReadBufferQueue(std::move(consumer_queue));
  return 0;
}

int dvrWriteBufferQueueDequeue(DvrWriteBufferQueue* write_queue, int timeout,
                               DvrWriteBuffer* write_buffer,
                               int* out_fence_fd) {
  if (!write_queue || !write_queue->producer_queue || !write_buffer ||
      !out_fence_fd) {
int DvrReadBufferQueue::Dequeue(int timeout, DvrReadBuffer* read_buffer,
                                int* out_fence_fd, void* out_meta,
                                size_t meta_size_bytes) {
  if (meta_size_bytes != consumer_queue_->metadata_size()) {
    ALOGE(
        "DvrReadBufferQueue::Dequeue: Invalid metadata size, expected (%zu), "
        "but actual (%zu).",
        consumer_queue_->metadata_size(), meta_size_bytes);
    return -EINVAL;
  }

  size_t slot;
  pdx::LocalHandle release_fence;
  auto buffer_status =
      write_queue->producer_queue->Dequeue(timeout, &slot, &release_fence);
  pdx::LocalHandle acquire_fence;
  auto buffer_status = consumer_queue_->Dequeue(
      timeout, &slot, out_meta, meta_size_bytes, &acquire_fence);
  if (!buffer_status) {
    ALOGE_IF(buffer_status.error() != ETIMEDOUT,
             "dvrWriteBufferQueueDequeue: Failed to dequeue buffer: %s",
             "dvrReadBufferQueueDequeue: Failed to dequeue buffer: %s",
             buffer_status.GetErrorMessage().c_str());
    return -buffer_status.error();
  }

  write_buffer->write_buffer = buffer_status.take();
  *out_fence_fd = release_fence.Release();
  read_buffer->read_buffer = buffer_status.take();
  *out_fence_fd = acquire_fence.Release();
  return 0;
}

// ReadBufferQueue
void dvrReadBufferQueueDestroy(DvrReadBufferQueue* read_queue) {
  delete read_queue;
}
@@ -139,66 +260,32 @@ ssize_t dvrReadBufferQueueGetCapacity(DvrReadBufferQueue* read_queue) {
  if (!read_queue)
    return -EINVAL;

  return read_queue->consumer_queue->capacity();
  return read_queue->capacity();
}

int dvrReadBufferQueueGetId(DvrReadBufferQueue* read_queue) {
  if (!read_queue)
    return -EINVAL;

  return read_queue->consumer_queue->id();
  return read_queue->id();
}

int dvrReadBufferQueueCreateReadQueue(DvrReadBufferQueue* read_queue,
                                      DvrReadBufferQueue** out_read_queue) {
  if (!read_queue || !read_queue->consumer_queue || !out_read_queue)
  if (!read_queue || !out_read_queue)
    return -EINVAL;

  auto new_read_queue = std::make_unique<DvrReadBufferQueue>();
  new_read_queue->consumer_queue =
      read_queue->consumer_queue->CreateConsumerQueue();
  if (new_read_queue->consumer_queue == nullptr) {
    ALOGE(
        "dvrReadBufferQueueCreateReadQueue: Failed to create consumer queue "
        "from DvrReadBufferQueue[%p].",
        read_queue);
    return -ENOMEM;
  }

  *out_read_queue = new_read_queue.release();
  return 0;
  return read_queue->CreateReadQueue(out_read_queue);
}

int dvrReadBufferQueueDequeue(DvrReadBufferQueue* read_queue, int timeout,
                              DvrReadBuffer* read_buffer, int* out_fence_fd,
                              void* out_meta, size_t meta_size_bytes) {
  if (!read_queue || !read_queue->consumer_queue || !read_buffer ||
      !out_fence_fd || !out_meta) {
  if (!read_queue || !read_buffer || !out_fence_fd || !out_meta)
    return -EINVAL;
  }

  if (meta_size_bytes != read_queue->consumer_queue->metadata_size()) {
    ALOGE(
        "dvrReadBufferQueueDequeue: Invalid metadata size, expected (%zu), "
        "but actual (%zu).",
        read_queue->consumer_queue->metadata_size(), meta_size_bytes);
    return -EINVAL;
  }

  size_t slot;
  pdx::LocalHandle acquire_fence;
  auto buffer_status = read_queue->consumer_queue->Dequeue(
      timeout, &slot, out_meta, meta_size_bytes, &acquire_fence);
  if (!buffer_status) {
    ALOGE_IF(buffer_status.error() != ETIMEDOUT,
             "dvrReadBufferQueueDequeue: Failed to dequeue buffer: %s",
             buffer_status.GetErrorMessage().c_str());
    return -buffer_status.error();
  }

  read_buffer->read_buffer = buffer_status.take();
  *out_fence_fd = acquire_fence.Release();
  return 0;
  return read_queue->Dequeue(timeout, read_buffer, out_fence_fd, out_meta,
                             meta_size_bytes);
}

}  // extern "C"
Loading