summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2021-11-11 18:53:00 -0800
committerGravatar bunnei2022-03-24 18:13:32 -0700
commit6e7f687df421e3d30b5b08e8e1747e6084e89342 (patch)
treea8bc3e010462f63d15c3943cf4f15b06f1516540
parenthle: nvflinger: Add implementation for QueueBufferInput and QueueBufferOutput... (diff)
downloadyuzu-6e7f687df421e3d30b5b08e8e1747e6084e89342.tar.gz
yuzu-6e7f687df421e3d30b5b08e8e1747e6084e89342.tar.xz
yuzu-6e7f687df421e3d30b5b08e8e1747e6084e89342.zip
hle: nvflinger: Add implementation for BufferQueueConsumer class.
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp225
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.h36
3 files changed, 263 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 045a08648..c7ce6c1a6 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -541,6 +541,8 @@ add_library(core STATIC
541 hle/service/nvflinger/buffer_item.h 541 hle/service/nvflinger/buffer_item.h
542 hle/service/nvflinger/buffer_item_consumer.cpp 542 hle/service/nvflinger/buffer_item_consumer.cpp
543 hle/service/nvflinger/buffer_item_consumer.h 543 hle/service/nvflinger/buffer_item_consumer.h
544 hle/service/nvflinger/buffer_queue_consumer.cpp
545 hle/service/nvflinger/buffer_queue_consumer.h
544 hle/service/nvflinger/buffer_queue_defs.h 546 hle/service/nvflinger/buffer_queue_defs.h
545 hle/service/nvflinger/buffer_slot.h 547 hle/service/nvflinger/buffer_slot.h
546 hle/service/nvflinger/buffer_transform_flags.h 548 hle/service/nvflinger/buffer_transform_flags.h
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
new file mode 100644
index 000000000..f0875eca3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -0,0 +1,225 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6
7#include "common/logging/log.h"
8#include "core/hle/service/nvflinger/buffer_item.h"
9#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
10#include "core/hle/service/nvflinger/buffer_queue_core.h"
11#include "core/hle/service/nvflinger/producer_listener.h"
12
13namespace android {
14
15BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
16 : core{std::move(core_)}, slots{core->slots} {}
17
18BufferQueueConsumer::~BufferQueueConsumer() = default;
19
20Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, s64 expected_presenst_ns,
21 u64 max_frame_number) {
22 s32 num_dropped_buffers{};
23
24 std::shared_ptr<IProducerListener> listener;
25 {
26 std::unique_lock lock(core->mutex);
27
28 // Check that the consumer doesn't currently have the maximum number of buffers acquired.
29 s32 num_acquired_buffers{};
30 for (const auto& slot : slots) {
31 if (slot.buffer_state == BufferState::Acquired) {
32 ++num_acquired_buffers;
33 }
34 }
35
36 if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
37 LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
38 num_acquired_buffers, core->max_acquired_buffer_count);
39 return Status::InvalidOperation;
40 }
41
42 // Check if the queue is empty.
43 if (core->queue.empty()) {
44 return Status::NoBufferAvailable;
45 }
46
47 auto front(core->queue.begin());
48
49 // If expected_presenst_ns is specified, we may not want to return a buffer yet.
50 if (expected_presenst_ns != 0) {
51 constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
52
53 // The expected_presenst_ns argument indicates when the buffer is expected to be
54 // presented on-screen.
55 while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
56 const auto& buffer_item{core->queue[1]};
57
58 // If dropping entry[0] would leave us with a buffer that the consumer is not yet
59 // ready for, don't drop it.
60 if (max_frame_number && buffer_item.frame_number > max_frame_number) {
61 break;
62 }
63
64 // If entry[1] is timely, drop entry[0] (and repeat).
65 const auto desired_present = buffer_item.timestamp;
66 if (desired_present < expected_presenst_ns - MAX_REASONABLE_NSEC ||
67 desired_present > expected_presenst_ns) {
68 // This buffer is set to display in the near future, or desired_present is
69 // garbage.
70 LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
71 expected_presenst_ns);
72 break;
73 }
74
75 LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
76 expected_presenst_ns, core->queue.size());
77
78 if (core->StillTracking(&*front)) {
79 // Front buffer is still in mSlots, so mark the slot as free
80 slots[front->slot].buffer_state = BufferState::Free;
81 core->free_buffers.push_back(front->slot);
82 listener = core->connected_producer_listener;
83 ++num_dropped_buffers;
84 }
85
86 core->queue.erase(front);
87 front = core->queue.begin();
88 }
89
90 // See if the front buffer is ready to be acquired.
91 const auto desired_present = front->timestamp;
92 const auto buffer_is_due = desired_present <= expected_presenst_ns ||
93 desired_present > expected_presenst_ns + MAX_REASONABLE_NSEC;
94 const auto consumer_is_ready =
95 max_frame_number > 0 ? front->frame_number <= max_frame_number : true;
96
97 if (!buffer_is_due || !consumer_is_ready) {
98 LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
99 expected_presenst_ns);
100 return Status::PresentLater;
101 }
102
103 LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
104 expected_presenst_ns);
105 }
106
107 const auto slot = front->slot;
108 *out_buffer = *front;
109
110 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
111
112 // If the front buffer is still being tracked, update its slot state
113 if (core->StillTracking(&*front)) {
114 slots[slot].acquire_called = true;
115 slots[slot].needs_cleanup_on_release = false;
116 slots[slot].buffer_state = BufferState::Acquired;
117 slots[slot].fence = Fence::NoFence();
118 }
119
120 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr
121 // to avoid unnecessarily remapping this buffer on the consumer side.
122 if (out_buffer->acquire_called) {
123 out_buffer->graphic_buffer = nullptr;
124 }
125
126 core->queue.erase(front);
127
128 // We might have freed a slot while dropping old buffers, or the producer may be blocked
129 // waiting for the number of buffers in the queue to decrease.
130 core->SignalDequeueCondition();
131 }
132
133 if (listener != nullptr) {
134 for (s32 i = 0; i < num_dropped_buffers; ++i) {
135 listener->OnBufferReleased();
136 }
137 }
138
139 return Status::NoError;
140}
141
142Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) {
143 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
144 LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot);
145 return Status::BadValue;
146 }
147
148 std::shared_ptr<IProducerListener> listener;
149 {
150 std::unique_lock lock(core->mutex);
151
152 // If the frame number has changed because the buffer has been reallocated, we can ignore
153 // this ReleaseBuffer for the old buffer.
154 if (frame_number != slots[slot].frame_number) {
155 return Status::StaleBufferSlot;
156 }
157
158 // Make sure this buffer hasn't been queued while acquired by the consumer.
159 auto current(core->queue.begin());
160 while (current != core->queue.end()) {
161 if (current->slot == slot) {
162 LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued",
163 slot);
164 return Status::BadValue;
165 }
166 ++current;
167 }
168
169 if (slots[slot].buffer_state == BufferState::Acquired) {
170 slots[slot].fence = release_fence;
171 slots[slot].buffer_state = BufferState::Free;
172
173 core->free_buffers.push_back(slot);
174
175 listener = core->connected_producer_listener;
176
177 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
178 } else if (slots[slot].needs_cleanup_on_release) {
179 LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
180 slots[slot].buffer_state);
181
182 slots[slot].needs_cleanup_on_release = false;
183
184 return Status::StaleBufferSlot;
185 } else {
186 LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
187 slot, slots[slot].buffer_state);
188
189 return Status::BadValue;
190 }
191
192 core->dequeue_condition.notify_all();
193 }
194
195 // Call back without lock held
196 if (listener != nullptr) {
197 listener->OnBufferReleased();
198 }
199
200 return Status::NoError;
201}
202
203Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener,
204 bool controlled_by_app) {
205 if (consumer_listener == nullptr) {
206 LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr");
207 return Status::BadValue;
208 }
209
210 LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
211
212 BufferQueueCore::AutoLock lock(core);
213
214 if (core->is_abandoned) {
215 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
216 return Status::NoInit;
217 }
218
219 core->consumer_listener = consumer_listener;
220 core->consumer_controlled_by_app = controlled_by_app;
221
222 return Status::NoError;
223}
224
225} // namespace android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
new file mode 100644
index 000000000..fbeb8b8d7
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
@@ -0,0 +1,36 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h
6
7#pragma once
8
9#include <memory>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvflinger/buffer_queue_defs.h"
13#include "core/hle/service/nvflinger/status.h"
14
15namespace android {
16
17class BufferItem;
18class BufferQueueCore;
19class IConsumerListener;
20
21class BufferQueueConsumer final {
22public:
23 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
24 ~BufferQueueConsumer();
25
26 Status AcquireBuffer(BufferItem* out_buffer, s64 expected_presenst_ns,
27 u64 max_frame_number = 0);
28 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
29 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
30
31private:
32 std::shared_ptr<BufferQueueCore> core;
33 BufferQueueDefs::SlotsType& slots;
34};
35
36} // namespace android