summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/service/nvflinger/buffer_queue_consumer.cpp')
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp182
1 files changed, 80 insertions, 102 deletions
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
index 677bec932..41fbba219 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -20,122 +20,102 @@ BufferQueueConsumer::~BufferQueueConsumer() = default;
Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
std::chrono::nanoseconds expected_present,
u64 max_frame_number) {
- s32 num_dropped_buffers{};
+ std::scoped_lock lock(core->mutex);
+
+ // Check that the consumer doesn't currently have the maximum number of buffers acquired.
+ const s32 num_acquired_buffers{
+ static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
+ return slot.buffer_state == BufferState::Acquired;
+ }))};
+
+ if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
+ LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
+ num_acquired_buffers, core->max_acquired_buffer_count);
+ return Status::InvalidOperation;
+ }
- std::shared_ptr<IProducerListener> listener;
- {
- std::unique_lock lock(core->mutex);
-
- // Check that the consumer doesn't currently have the maximum number of buffers acquired.
- const s32 num_acquired_buffers{
- static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
- return slot.buffer_state == BufferState::Acquired;
- }))};
-
- if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
- LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
- num_acquired_buffers, core->max_acquired_buffer_count);
- return Status::InvalidOperation;
- }
+ // Check if the queue is empty.
+ if (core->queue.empty()) {
+ return Status::NoBufferAvailable;
+ }
- // Check if the queue is empty.
- if (core->queue.empty()) {
- return Status::NoBufferAvailable;
- }
+ auto front(core->queue.begin());
- auto front(core->queue.begin());
-
- // If expected_present is specified, we may not want to return a buffer yet.
- if (expected_present.count() != 0) {
- constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
-
- // The expected_present argument indicates when the buffer is expected to be
- // presented on-screen.
- while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
- const auto& buffer_item{core->queue[1]};
-
- // If dropping entry[0] would leave us with a buffer that the consumer is not yet
- // ready for, don't drop it.
- if (max_frame_number && buffer_item.frame_number > max_frame_number) {
- break;
- }
-
- // If entry[1] is timely, drop entry[0] (and repeat).
- const auto desired_present = buffer_item.timestamp;
- if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
- desired_present > expected_present.count()) {
- // This buffer is set to display in the near future, or desired_present is
- // garbage.
- LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
- expected_present.count());
- break;
- }
-
- LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
- expected_present.count(), core->queue.size());
-
- if (core->StillTracking(*front)) {
- // Front buffer is still in mSlots, so mark the slot as free
- slots[front->slot].buffer_state = BufferState::Free;
- core->free_buffers.push_back(front->slot);
- listener = core->connected_producer_listener;
- ++num_dropped_buffers;
- }
-
- core->queue.erase(front);
- front = core->queue.begin();
- }
+ // If expected_present is specified, we may not want to return a buffer yet.
+ if (expected_present.count() != 0) {
+ constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
- // See if the front buffer is ready to be acquired.
- const auto desired_present = front->timestamp;
- const auto buffer_is_due =
- desired_present <= expected_present.count() ||
- desired_present > expected_present.count() + MAX_REASONABLE_NSEC;
- const auto consumer_is_ready =
- max_frame_number > 0 ? front->frame_number <= max_frame_number : true;
+ // The expected_present argument indicates when the buffer is expected to be presented
+ // on-screen.
+ while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
+ const auto& buffer_item{core->queue[1]};
- if (!buffer_is_due || !consumer_is_ready) {
- LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
- expected_present.count());
- return Status::PresentLater;
+ // If dropping entry[0] would leave us with a buffer that the consumer is not yet ready
+ // for, don't drop it.
+ if (max_frame_number && buffer_item.frame_number > max_frame_number) {
+ break;
}
- LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
- expected_present.count());
- }
+ // If entry[1] is timely, drop entry[0] (and repeat).
+ const auto desired_present = buffer_item.timestamp;
+ if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
+ desired_present > expected_present.count()) {
+ // This buffer is set to display in the near future, or desired_present is garbage.
+ LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
+ expected_present.count());
+ break;
+ }
- const auto slot = front->slot;
- *out_buffer = *front;
+ LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
+ expected_present.count(), core->queue.size());
- LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
+ if (core->StillTracking(*front)) {
+ // Front buffer is still in mSlots, so mark the slot as free
+ slots[front->slot].buffer_state = BufferState::Free;
+ }
- // If the front buffer is still being tracked, update its slot state
- if (core->StillTracking(*front)) {
- slots[slot].acquire_called = true;
- slots[slot].needs_cleanup_on_release = false;
- slots[slot].buffer_state = BufferState::Acquired;
- slots[slot].fence = Fence::NoFence();
+ core->queue.erase(front);
+ front = core->queue.begin();
}
- // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr
- // to avoid unnecessarily remapping this buffer on the consumer side.
- if (out_buffer->acquire_called) {
- out_buffer->graphic_buffer = nullptr;
+ // See if the front buffer is ready to be acquired.
+ const auto desired_present = front->timestamp;
+ if (desired_present > expected_present.count() &&
+ desired_present < expected_present.count() + MAX_REASONABLE_NSEC) {
+ LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
+ expected_present.count());
+ return Status::PresentLater;
}
- core->queue.erase(front);
+ LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
+ expected_present.count());
+ }
+
+ const auto slot = front->slot;
+ *out_buffer = *front;
- // We might have freed a slot while dropping old buffers, or the producer may be blocked
- // waiting for the number of buffers in the queue to decrease.
- core->SignalDequeueCondition();
+ LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
+
+ // If the front buffer is still being tracked, update its slot state
+ if (core->StillTracking(*front)) {
+ slots[slot].acquire_called = true;
+ slots[slot].needs_cleanup_on_release = false;
+ slots[slot].buffer_state = BufferState::Acquired;
+ slots[slot].fence = Fence::NoFence();
}
- if (listener != nullptr) {
- for (s32 i = 0; i < num_dropped_buffers; ++i) {
- listener->OnBufferReleased();
- }
+ // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
+ // avoid unnecessarily remapping this buffer on the consumer side.
+ if (out_buffer->acquire_called) {
+ out_buffer->graphic_buffer = nullptr;
}
+ core->queue.erase(front);
+
+ // We might have freed a slot while dropping old buffers, or the producer may be blocked
+ // waiting for the number of buffers in the queue to decrease.
+ core->SignalDequeueCondition();
+
return Status::NoError;
}
@@ -147,7 +127,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
std::shared_ptr<IProducerListener> listener;
{
- std::unique_lock lock(core->mutex);
+ std::scoped_lock lock(core->mutex);
// If the frame number has changed because the buffer has been reallocated, we can ignore
// this ReleaseBuffer for the old buffer.
@@ -170,8 +150,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
slots[slot].fence = release_fence;
slots[slot].buffer_state = BufferState::Free;
- core->free_buffers.push_back(slot);
-
listener = core->connected_producer_listener;
LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
@@ -189,7 +167,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
return Status::BadValue;
}
- core->dequeue_condition.notify_all();
+ core->SignalDequeueCondition();
}
// Call back without lock held
@@ -209,7 +187,7 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
- BufferQueueCore::AutoLock lock(core);
+ std::scoped_lock lock(core->mutex);
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");