summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2021-11-11 19:12:36 -0800
committerGravatar bunnei2022-03-24 18:13:33 -0700
commit56284bff6c312da130eb0f2d0cda80cd29c82046 (patch)
tree555305467b7a0ae49ab23b2d33b016284a143daa /src
parenthle: nvflinger: Add implementation for BufferQueueCore class. (diff)
downloadyuzu-56284bff6c312da130eb0f2d0cda80cd29c82046.tar.gz
yuzu-56284bff6c312da130eb0f2d0cda80cd29c82046.tar.xz
yuzu-56284bff6c312da130eb0f2d0cda80cd29c82046.zip
hle: nvflinger: Add implementation for BufferQueueProducer class.
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp936
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h83
3 files changed, 1021 insertions, 2 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5ba9b6c09..669de3091 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -535,8 +535,6 @@ add_library(core STATIC
535 hle/service/nvdrv/nvmemp.h 535 hle/service/nvdrv/nvmemp.h
536 hle/service/nvdrv/syncpoint_manager.cpp 536 hle/service/nvdrv/syncpoint_manager.cpp
537 hle/service/nvdrv/syncpoint_manager.h 537 hle/service/nvdrv/syncpoint_manager.h
538 hle/service/nvflinger/buffer_queue.cpp
539 hle/service/nvflinger/buffer_queue.h
540 hle/service/nvflinger/binder.h 538 hle/service/nvflinger/binder.h
541 hle/service/nvflinger/buffer_item.h 539 hle/service/nvflinger/buffer_item.h
542 hle/service/nvflinger/buffer_item_consumer.cpp 540 hle/service/nvflinger/buffer_item_consumer.cpp
@@ -546,6 +544,8 @@ add_library(core STATIC
546 hle/service/nvflinger/buffer_queue_core.cpp 544 hle/service/nvflinger/buffer_queue_core.cpp
547 hle/service/nvflinger/buffer_queue_core.h 545 hle/service/nvflinger/buffer_queue_core.h
548 hle/service/nvflinger/buffer_queue_defs.h 546 hle/service/nvflinger/buffer_queue_defs.h
547 hle/service/nvflinger/buffer_queue_producer.cpp
548 hle/service/nvflinger/buffer_queue_producer.h
549 hle/service/nvflinger/buffer_slot.h 549 hle/service/nvflinger/buffer_slot.h
550 hle/service/nvflinger/buffer_transform_flags.h 550 hle/service/nvflinger/buffer_transform_flags.h
551 hle/service/nvflinger/consumer_base.cpp 551 hle/service/nvflinger/consumer_base.cpp
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
new file mode 100644
index 000000000..d48e96b14
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -0,0 +1,936 @@
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/BufferQueueProducer.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "core/core.h"
11#include "core/hle/kernel/hle_ipc.h"
12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/service/kernel_helpers.h"
17#include "core/hle/service/nvdrv/nvdrv.h"
18#include "core/hle/service/nvflinger/buffer_queue_core.h"
19#include "core/hle/service/nvflinger/buffer_queue_producer.h"
20#include "core/hle/service/nvflinger/consumer_listener.h"
21#include "core/hle/service/nvflinger/parcel.h"
22#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
23#include "core/hle/service/nvflinger/window.h"
24#include "core/hle/service/vi/vi.h"
25
26namespace android {
27
28BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
29 std::shared_ptr<BufferQueueCore> buffer_queue_core_)
30 : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) {
31 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
32}
33
34BufferQueueProducer::~BufferQueueProducer() {
35 service_context.CloseEvent(buffer_wait_event);
36}
37
38Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
39 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
40
41 BufferQueueCore::AutoLock lock(core);
42
43 if (core->is_abandoned) {
44 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
45 return Status::NoInit;
46 }
47 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
48 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
49 BufferQueueDefs::NUM_BUFFER_SLOTS);
50 return Status::BadValue;
51 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
52 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
53 slots[slot].buffer_state);
54 return Status::BadValue;
55 }
56
57 slots[slot].request_buffer_called = true;
58 *buf = slots[slot].graphic_buffer;
59
60 return Status::NoError;
61}
62
63Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
64 LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count);
65 std::shared_ptr<IConsumerListener> listener;
66
67 {
68 BufferQueueCore::AutoLock lock(core);
69 core->WaitWhileAllocatingLocked();
70 if (core->is_abandoned) {
71 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
72 return Status::NoInit;
73 }
74
75 if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) {
76 LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count,
77 BufferQueueDefs::NUM_BUFFER_SLOTS);
78 return Status::BadValue;
79 }
80
81 // There must be no dequeued buffers when changing the buffer count.
82 for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
83 if (slots[s].buffer_state == BufferState::Dequeued) {
84 LOG_ERROR(Service_NVFlinger, "buffer owned by producer");
85 return Status::BadValue;
86 }
87 }
88
89 if (buffer_count == 0) {
90 core->override_max_buffer_count = 0;
91 core->SignalDequeueCondition();
92 return Status::NoError;
93 }
94
95 const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false);
96 if (buffer_count < min_buffer_slots) {
97 LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}",
98 buffer_count, min_buffer_slots);
99 return Status::BadValue;
100 }
101
102 // Here we are guaranteed that the producer doesn't have any dequeued buffers and will
103 // release all of its buffer references.
104 if (core->GetPreallocatedBufferCountLocked() <= 0) {
105 core->FreeAllBuffersLocked();
106 }
107
108 core->override_max_buffer_count = buffer_count;
109 core->SignalDequeueCondition();
110 buffer_wait_event->GetWritableEvent().Signal();
111 listener = core->consumer_listener;
112 }
113
114 // Call back without lock held
115 if (listener != nullptr) {
116 listener->OnBuffersReleased();
117 }
118
119 return Status::NoError;
120}
121
122Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
123 Status* returnFlags) const {
124 bool try_again = true;
125
126 while (try_again) {
127 if (core->is_abandoned) {
128 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
129 return Status::NoInit;
130 }
131
132 const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
133 if (async && core->override_max_buffer_count) {
134 if (core->override_max_buffer_count < max_buffer_count) {
135 LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override");
136 return Status::BadValue;
137 }
138 }
139
140 // Free up any buffers that are in slots beyond the max buffer count
141 for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
142 ASSERT(slots[s].buffer_state == BufferState::Free);
143 if (slots[s].graphic_buffer != nullptr) {
144 core->FreeBufferLocked(s);
145 *returnFlags |= Status::ReleaseAllBuffers;
146 }
147 }
148
149 s32 dequeued_count{};
150 s32 acquired_count{};
151 for (s32 s{}; s < max_buffer_count; ++s) {
152 switch (slots[s].buffer_state) {
153 case BufferState::Dequeued:
154 ++dequeued_count;
155 break;
156 case BufferState::Acquired:
157 ++acquired_count;
158 break;
159 default:
160 break;
161 }
162 }
163
164 // Producers are not allowed to dequeue more than one buffer if they did not set a buffer
165 // count
166 if (!core->override_max_buffer_count && dequeued_count) {
167 LOG_ERROR(Service_NVFlinger,
168 "can't dequeue multiple buffers without setting the buffer count");
169 return Status::InvalidOperation;
170 }
171
172 // See whether a buffer has been queued since the last SetBufferCount so we know whether to
173 // perform the min undequeued buffers check below
174 if (core->buffer_has_been_queued) {
175 // Make sure the producer is not trying to dequeue more buffers than allowed
176 const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1);
177 const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async);
178 if (new_undequeued_count < min_undequeued_count) {
179 LOG_ERROR(Service_NVFlinger,
180 "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})",
181 min_undequeued_count, dequeued_count, new_undequeued_count);
182 return Status::InvalidOperation;
183 }
184 }
185
186 *found = BufferQueueCore::INVALID_BUFFER_SLOT;
187
188 // If we disconnect and reconnect quickly, we can be in a state where our slots are empty
189 // but we have many buffers in the queue. This can cause us to run out of memory if we
190 // outrun the consumer. Wait here if it looks like we have too many buffers queued up.
191 const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count);
192 if (too_many_buffers) {
193 LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size());
194 } else {
195 if (!core->free_buffers.empty()) {
196 auto slot = core->free_buffers.begin();
197 *found = *slot;
198 core->free_buffers.erase(slot);
199 } else if (core->allow_allocation && !core->free_slots.empty()) {
200 auto slot = core->free_slots.begin();
201 // Only return free slots up to the max buffer count
202 if (*slot < max_buffer_count) {
203 *found = *slot;
204 core->free_slots.erase(slot);
205 }
206 }
207 }
208
209 // If no buffer is found, or if the queue has too many buffers outstanding, wait for a
210 // buffer to be acquired or released, or for the max buffer count to change.
211 try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers;
212 if (try_again) {
213 // Return an error if we're in non-blocking mode (producer and consumer are controlled
214 // by the application).
215 if (core->dequeue_buffer_cannot_block &&
216 (acquired_count <= core->max_acquired_buffer_count)) {
217 return Status::WouldBlock;
218 }
219
220 if (!core->WaitForDequeueCondition()) {
221 // We are no longer running
222 return Status::NoError;
223 }
224 }
225 }
226
227 return Status::NoError;
228}
229
230Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width,
231 u32 height, PixelFormat format, u32 usage) {
232 { BufferQueueCore::AutoLock lock(core); }
233
234 LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false",
235 width, height, format, usage);
236
237 if ((width && !height) || (!width && height)) {
238 LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height);
239 return Status::BadValue;
240 }
241
242 Status return_flags = Status::NoError;
243 bool attached_by_consumer = false;
244 {
245 BufferQueueCore::AutoLock lock(core);
246 core->WaitWhileAllocatingLocked();
247 if (format == PixelFormat::NoFormat) {
248 format = core->default_buffer_format;
249 }
250
251 // Enable the usage bits the consumer requested
252 usage |= core->consumer_usage_bit;
253 const bool use_default_size = !width && !height;
254 if (use_default_size) {
255 width = core->default_width;
256 height = core->default_height;
257 }
258
259 s32 found = BufferItem::INVALID_BUFFER_SLOT;
260 while (found == BufferItem::INVALID_BUFFER_SLOT) {
261 Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
262 if (status != Status::NoError) {
263 return status;
264 }
265
266 // This should not happen
267 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
268 LOG_DEBUG(Service_NVFlinger, "no available buffer slots");
269 return Status::Busy;
270 }
271
272 const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
273
274 // If we are not allowed to allocate new buffers, WaitForFreeSlotThenRelock must have
275 // returned a slot containing a buffer. If this buffer would require reallocation to
276 // meet the requested attributes, we free it and attempt to get another one.
277 if (!core->allow_allocation) {
278 if (buffer->NeedsReallocation(width, height, format, usage)) {
279 core->FreeBufferLocked(found);
280 found = BufferItem::INVALID_BUFFER_SLOT;
281 continue;
282 }
283 }
284 }
285
286 *out_slot = found;
287 attached_by_consumer = slots[found].attached_by_consumer;
288 slots[found].buffer_state = BufferState::Dequeued;
289
290 const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
291
292 if ((buffer == nullptr) || buffer->NeedsReallocation(width, height, format, usage)) {
293 slots[found].acquire_called = false;
294 slots[found].graphic_buffer = nullptr;
295 slots[found].request_buffer_called = false;
296 slots[found].fence = Fence::NoFence();
297 core->buffer_age = 0;
298 return_flags |= Status::BufferNeedsReallocation;
299 } else {
300 // We add 1 because that will be the frame number when this buffer
301 // is queued
302 core->buffer_age = core->frame_counter + 1 - slots[found].frame_number;
303 }
304
305 LOG_DEBUG(Service_NVFlinger, "setting buffer age to {}", core->buffer_age);
306
307 *out_fence = slots[found].fence;
308
309 slots[found].fence = Fence::NoFence();
310 }
311
312 if ((return_flags & Status::BufferNeedsReallocation) != Status::None) {
313 LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot);
314
315 auto graphic_buffer = std::make_shared<GraphicBuffer>(width, height, format, usage);
316 if (graphic_buffer == nullptr) {
317 LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed");
318 return Status::NoMemory;
319 }
320
321 {
322 BufferQueueCore::AutoLock lock(core);
323 if (core->is_abandoned) {
324 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
325 return Status::NoInit;
326 }
327
328 slots[*out_slot].graphic_buffer = graphic_buffer;
329 }
330 }
331
332 if (attached_by_consumer) {
333 return_flags |= Status::BufferNeedsReallocation;
334 }
335
336 LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot,
337 slots[*out_slot].frame_number, return_flags);
338 return return_flags;
339}
340
341Status BufferQueueProducer::DetachBuffer(s32 slot) {
342 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
343
344 BufferQueueCore::AutoLock lock(core);
345 if (core->is_abandoned) {
346 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
347 return Status::NoInit;
348 }
349
350 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
351 LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot,
352 BufferQueueDefs::NUM_BUFFER_SLOTS);
353 return Status::BadValue;
354 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
355 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
356 slots[slot].buffer_state);
357 return Status::BadValue;
358 } else if (!slots[slot].request_buffer_called) {
359 LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot);
360 return Status::BadValue;
361 }
362
363 core->FreeBufferLocked(slot);
364 core->SignalDequeueCondition();
365
366 return Status::NoError;
367}
368
369Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer,
370 Fence* out_fence) {
371 if (out_buffer == nullptr) {
372 LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr");
373 return Status::BadValue;
374 } else if (out_fence == nullptr) {
375 LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr");
376 return Status::BadValue;
377 }
378
379 BufferQueueCore::AutoLock lock(core);
380
381 core->WaitWhileAllocatingLocked();
382
383 if (core->is_abandoned) {
384 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
385 return Status::NoInit;
386 }
387 if (core->free_buffers.empty()) {
388 return Status::NoMemory;
389 }
390
391 const s32 found = core->free_buffers.front();
392 core->free_buffers.remove(found);
393
394 LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found);
395
396 *out_buffer = slots[found].graphic_buffer;
397 *out_fence = slots[found].fence;
398
399 core->FreeBufferLocked(found);
400
401 return Status::NoError;
402}
403
404Status BufferQueueProducer::AttachBuffer(s32* out_slot,
405 const std::shared_ptr<GraphicBuffer>& buffer) {
406 if (out_slot == nullptr) {
407 LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr");
408 return Status::BadValue;
409 } else if (buffer == nullptr) {
410 LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer");
411 return Status::BadValue;
412 }
413
414 BufferQueueCore::AutoLock lock(core);
415 core->WaitWhileAllocatingLocked();
416
417 Status return_flags = Status::NoError;
418 s32 found{};
419
420 const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags);
421 if (status != Status::NoError) {
422 return status;
423 }
424
425 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
426 LOG_ERROR(Service_NVFlinger, "No available buffer slots");
427 return Status::Busy;
428 }
429
430 *out_slot = found;
431
432 LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags);
433
434 slots[*out_slot].graphic_buffer = buffer;
435 slots[*out_slot].buffer_state = BufferState::Dequeued;
436 slots[*out_slot].fence = Fence::NoFence();
437 slots[*out_slot].request_buffer_called = true;
438
439 return return_flags;
440}
441
442Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
443 QueueBufferOutput* output) {
444 s64 timestamp{};
445 bool is_auto_timestamp{};
446 Rect crop;
447 NativeWindowScalingMode scaling_mode{};
448 NativeWindowTransform transform;
449 u32 sticky_transform_{};
450 bool async{};
451 s32 swap_interval{};
452 Fence fence{};
453
454 input.Deflate(&timestamp, &is_auto_timestamp, &crop, &scaling_mode, &transform,
455 &sticky_transform_, &async, &swap_interval, &fence);
456
457 switch (scaling_mode) {
458 case NativeWindowScalingMode::Freeze:
459 case NativeWindowScalingMode::ScaleToWindow:
460 case NativeWindowScalingMode::ScaleCrop:
461 case NativeWindowScalingMode::NoScaleCrop:
462 break;
463 default:
464 LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode);
465 return Status::BadValue;
466 }
467
468 std::shared_ptr<IConsumerListener> frameAvailableListener;
469 std::shared_ptr<IConsumerListener> frameReplacedListener;
470 s32 callback_ticket{};
471 BufferItem item;
472
473 {
474 BufferQueueCore::AutoLock lock(core);
475
476 if (core->is_abandoned) {
477 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
478 return Status::NoInit;
479 }
480
481 const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
482 if (async && core->override_max_buffer_count) {
483 if (core->override_max_buffer_count < max_buffer_count) {
484 LOG_ERROR(Service_NVFlinger, "async mode is invalid with "
485 "buffer count override");
486 return Status::BadValue;
487 }
488 }
489
490 if (slot < 0 || slot >= max_buffer_count) {
491 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
492 max_buffer_count);
493 return Status::BadValue;
494 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
495 LOG_ERROR(Service_NVFlinger,
496 "slot {} is not owned by the producer "
497 "(state = {})",
498 slot, slots[slot].buffer_state);
499 return Status::BadValue;
500 } else if (!slots[slot].request_buffer_called) {
501 LOG_ERROR(Service_NVFlinger,
502 "slot {} was queued without requesting "
503 "a buffer",
504 slot);
505 return Status::BadValue;
506 }
507
508 LOG_DEBUG(Service_NVFlinger,
509 "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot,
510 core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(),
511 crop.Bottom(), transform, scaling_mode);
512
513 const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer);
514 Rect buffer_rect(graphic_buffer->Width(), graphic_buffer->Height());
515 Rect cropped_rect;
516 crop.Intersect(buffer_rect, &cropped_rect);
517
518 if (cropped_rect != crop) {
519 LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}",
520 slot);
521 return Status::BadValue;
522 }
523
524 slots[slot].fence = fence;
525 slots[slot].buffer_state = BufferState::Queued;
526 ++core->frame_counter;
527 slots[slot].frame_number = core->frame_counter;
528
529 item.acquire_called = slots[slot].acquire_called;
530 item.graphic_buffer = slots[slot].graphic_buffer;
531 item.crop = crop;
532 item.transform = transform & ~NativeWindowTransform::InverseDisplay;
533 item.transform_to_display_inverse =
534 (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None;
535 item.scaling_mode = static_cast<u32>(scaling_mode);
536 item.timestamp = timestamp;
537 item.is_auto_timestamp = is_auto_timestamp;
538 item.frame_number = core->frame_counter;
539 item.slot = slot;
540 item.fence = fence;
541 item.is_droppable = core->dequeue_buffer_cannot_block || async;
542 item.swap_interval = swap_interval;
543 sticky_transform = sticky_transform_;
544
545 if (core->queue.empty()) {
546 // When the queue is empty, we can simply queue this buffer
547 core->queue.push_back(item);
548 frameAvailableListener = core->consumer_listener;
549 } else {
550 // When the queue is not empty, we need to look at the front buffer
551 // state to see if we need to replace it
552 auto front(core->queue.begin());
553
554 if (front->is_droppable) {
555 // If the front queued buffer is still being tracked, we first
556 // mark it as freed
557 if (core->StillTracking(&*front)) {
558 slots[front->slot].buffer_state = BufferState::Free;
559 core->free_buffers.push_front(front->slot);
560 }
561 // Overwrite the droppable buffer with the incoming one
562 *front = item;
563 frameReplacedListener = core->consumer_listener;
564 } else {
565 core->queue.push_back(item);
566 frameAvailableListener = core->consumer_listener;
567 }
568 }
569
570 core->buffer_has_been_queued = true;
571 core->SignalDequeueCondition();
572 output->Inflate(core->default_width, core->default_height, core->transform_hint,
573 static_cast<u32>(core->queue.size()));
574
575 // Take a ticket for the callback functions
576 callback_ticket = next_callback_ticket++;
577 }
578
579 // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the
580 // consumer shouldn't need it
581 item.graphic_buffer.reset();
582 item.slot = BufferItem::INVALID_BUFFER_SLOT;
583
584 // Call back without the main BufferQueue lock held, but with the callback lock held so we can
585 // ensure that callbacks occur in order
586 {
587 std::unique_lock lock(callback_mutex);
588 while (callback_ticket != current_callback_ticket) {
589 std::unique_lock<std::mutex> lk(callback_mutex);
590 callback_condition.wait(lk);
591 }
592
593 if (frameAvailableListener != nullptr) {
594 frameAvailableListener->OnFrameAvailable(item);
595 } else if (frameReplacedListener != nullptr) {
596 frameReplacedListener->OnFrameReplaced(item);
597 }
598
599 ++current_callback_ticket;
600 callback_condition.notify_all();
601 }
602
603 return Status::NoError;
604}
605
606void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
607 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
608
609 BufferQueueCore::AutoLock lock(core);
610
611 if (core->is_abandoned) {
612 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
613 return;
614 }
615
616 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
617 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
618 BufferQueueDefs::NUM_BUFFER_SLOTS);
619 return;
620 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
621 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
622 slots[slot].buffer_state);
623 return;
624 }
625
626 core->free_buffers.push_front(slot);
627 slots[slot].buffer_state = BufferState::Free;
628 slots[slot].fence = fence;
629
630 core->SignalDequeueCondition();
631 buffer_wait_event->GetWritableEvent().Signal();
632}
633
634Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
635 BufferQueueCore::AutoLock lock(core);
636
637 if (out_value == nullptr) {
638 LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
639 return Status::BadValue;
640 }
641
642 if (core->is_abandoned) {
643 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
644 return Status::NoInit;
645 }
646
647 s32 value{};
648 switch (what) {
649 case NativeWindow::Width:
650 value = static_cast<s32>(core->default_width);
651 break;
652 case NativeWindow::Height:
653 value = static_cast<s32>(core->default_height);
654 break;
655 case NativeWindow::Format:
656 value = static_cast<s32>(core->default_buffer_format);
657 break;
658 case NativeWindow::MinUndequeedBuffers:
659 value = core->GetMinUndequeuedBufferCountLocked(false);
660 break;
661 case NativeWindow::StickyTransform:
662 value = static_cast<s32>(sticky_transform);
663 break;
664 case NativeWindow::ConsumerRunningBehind:
665 value = (core->queue.size() > 1);
666 break;
667 case NativeWindow::ConsumerUsageBits:
668 value = static_cast<s32>(core->consumer_usage_bit);
669 break;
670 case NativeWindow::BufferAge:
671 if (core->buffer_age > INT32_MAX) {
672 value = 0;
673 } else {
674 value = static_cast<s32>(core->buffer_age);
675 }
676 break;
677 default:
678 UNREACHABLE();
679 return Status::BadValue;
680 }
681
682 LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value);
683
684 *out_value = value;
685
686 return Status::NoError;
687}
688
689Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
690 NativeWindowApi api, bool producer_controlled_by_app,
691 QueueBufferOutput* output) {
692 BufferQueueCore::AutoLock lock(core);
693
694 LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
695 producer_controlled_by_app);
696
697 if (core->is_abandoned) {
698 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
699 return Status::NoInit;
700 }
701
702 if (core->consumer_listener == nullptr) {
703 LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer");
704 return Status::NoInit;
705 }
706
707 if (output == nullptr) {
708 LOG_ERROR(Service_NVFlinger, "output was nullptr");
709 return Status::BadValue;
710 }
711
712 if (core->connected_api != NativeWindowApi::NoConnectedApi) {
713 LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api,
714 api);
715 return Status::BadValue;
716 }
717
718 Status status = Status::NoError;
719 switch (api) {
720 case NativeWindowApi::Egl:
721 case NativeWindowApi::Cpu:
722 case NativeWindowApi::Media:
723 case NativeWindowApi::Camera:
724 core->connected_api = api;
725 output->Inflate(core->default_width, core->default_height, core->transform_hint,
726 static_cast<u32>(core->queue.size()));
727 core->connected_producer_listener = listener;
728 break;
729 default:
730 LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
731 status = Status::BadValue;
732 break;
733 }
734
735 core->buffer_has_been_queued = false;
736 core->dequeue_buffer_cannot_block =
737 core->consumer_controlled_by_app && producer_controlled_by_app;
738 core->allow_allocation = true;
739
740 return status;
741}
742
743Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
744 LOG_DEBUG(Service_NVFlinger, "api = {}", api);
745
746 Status status = Status::NoError;
747 std::shared_ptr<IConsumerListener> listener;
748
749 {
750 BufferQueueCore::AutoLock lock(core);
751
752 core->WaitWhileAllocatingLocked();
753
754 if (core->is_abandoned) {
755 // Disconnecting after the surface has been abandoned is a no-op.
756 return Status::NoError;
757 }
758
759 switch (api) {
760 case NativeWindowApi::Egl:
761 case NativeWindowApi::Cpu:
762 case NativeWindowApi::Media:
763 case NativeWindowApi::Camera:
764 if (core->connected_api == api) {
765 core->FreeAllBuffersLocked();
766 core->connected_producer_listener = nullptr;
767 core->connected_api = NativeWindowApi::NoConnectedApi;
768 core->SignalDequeueCondition();
769 buffer_wait_event->GetWritableEvent().Signal();
770 listener = core->consumer_listener;
771 } else if (core->connected_api != NativeWindowApi::NoConnectedApi) {
772 LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
773 core->connected_api, api);
774 status = Status::BadValue;
775 }
776 break;
777 default:
778 LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
779 status = Status::BadValue;
780 break;
781 }
782 }
783
784 // Call back without lock held
785 if (listener != nullptr) {
786 listener->OnBuffersReleased();
787 }
788
789 return status;
790}
791
792Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
793 const std::shared_ptr<GraphicBuffer>& buffer) {
794 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
795
796 UNIMPLEMENTED_IF_MSG(!buffer, "buffer must be valid!");
797
798 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
799 return Status::BadValue;
800 }
801
802 BufferQueueCore::AutoLock lock(core);
803
804 slots[slot] = {};
805 slots[slot].is_preallocated = true;
806 slots[slot].graphic_buffer = buffer;
807
808 core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked();
809 core->default_width = buffer->Width();
810 core->default_height = buffer->Height();
811 core->default_buffer_format = buffer->Format();
812
813 core->SignalDequeueCondition();
814 buffer_wait_event->GetWritableEvent().Signal();
815
816 return Status::NoError;
817}
818
819void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) {
820 Status status{Status::NoError};
821 Parcel parcel_in{ctx.ReadBuffer()};
822 Parcel parcel_out{};
823
824 switch (code) {
825 case TransactionId::Connect: {
826 const auto enable_listener = parcel_in.Read<bool>();
827 const auto api = parcel_in.Read<NativeWindowApi>();
828 const auto producer_controlled_by_app = parcel_in.Read<bool>();
829
830 UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!");
831
832 std::shared_ptr<IProducerListener> listener;
833 QueueBufferOutput output{};
834
835 status = Connect(listener, api, producer_controlled_by_app, &output);
836
837 parcel_out.Write(output);
838 break;
839 }
840 case TransactionId::SetPreallocatedBuffer: {
841 const auto slot = parcel_in.Read<s32>();
842 const auto buffer = parcel_in.ReadObject<GraphicBuffer>();
843
844 status = SetPreallocatedBuffer(slot, buffer);
845 break;
846 }
847 case TransactionId::DequeueBuffer: {
848 const auto is_async = parcel_in.Read<bool>();
849 const auto width = parcel_in.Read<u32>();
850 const auto height = parcel_in.Read<u32>();
851 const auto pixel_format = parcel_in.Read<PixelFormat>();
852 const auto usage = parcel_in.Read<u32>();
853
854 s32 slot{};
855 Fence fence{};
856
857 status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage);
858
859 parcel_out.Write(slot);
860 parcel_out.WriteObject(&fence);
861 break;
862 }
863 case TransactionId::RequestBuffer: {
864 const auto slot = parcel_in.Read<s32>();
865
866 std::shared_ptr<GraphicBuffer> buf;
867
868 status = RequestBuffer(slot, &buf);
869
870 parcel_out.WriteObject(buf);
871 break;
872 }
873 case TransactionId::QueueBuffer: {
874 const auto slot = parcel_in.Read<s32>();
875
876 QueueBufferInput input{parcel_in};
877 QueueBufferOutput output;
878
879 status = QueueBuffer(slot, input, &output);
880
881 parcel_out.Write(output);
882 break;
883 }
884 case TransactionId::Query: {
885 const auto what = parcel_in.Read<NativeWindow>();
886
887 s32 value{};
888
889 status = Query(what, &value);
890
891 parcel_out.Write(value);
892 break;
893 }
894 case TransactionId::CancelBuffer: {
895 const auto slot = parcel_in.Read<s32>();
896 const auto fence = parcel_in.ReadFlattened<Fence>();
897
898 CancelBuffer(slot, fence);
899 break;
900 }
901 case TransactionId::Disconnect: {
902 const auto api = parcel_in.Read<NativeWindowApi>();
903
904 status = Disconnect(api);
905 break;
906 }
907 case TransactionId::DetachBuffer: {
908 const auto slot = parcel_in.Read<s32>();
909
910 status = DetachBuffer(slot);
911 break;
912 }
913 case TransactionId::SetBufferCount: {
914 const auto buffer_count = parcel_in.Read<s32>();
915
916 status = SetBufferCount(buffer_count);
917 break;
918 }
919 case TransactionId::GetBufferHistory: {
920 LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory");
921 break;
922 }
923 default:
924 ASSERT_MSG(false, "Unimplemented");
925 }
926
927 parcel_out.Write(status);
928
929 ctx.WriteBuffer(parcel_out.Serialize());
930}
931
932Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() {
933 return buffer_wait_event->GetReadableEvent();
934}
935
936} // namespace android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h
new file mode 100644
index 000000000..fcb71a794
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -0,0 +1,83 @@
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/BufferQueueProducer.h
6
7#pragma once
8
9#include <condition_variable>
10#include <memory>
11#include <mutex>
12
13#include "common/common_funcs.h"
14#include "core/hle/service/nvdrv/nvdata.h"
15#include "core/hle/service/nvflinger/binder.h"
16#include "core/hle/service/nvflinger/buffer_queue_defs.h"
17#include "core/hle/service/nvflinger/buffer_slot.h"
18#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
19#include "core/hle/service/nvflinger/pixel_format.h"
20#include "core/hle/service/nvflinger/status.h"
21#include "core/hle/service/nvflinger/window.h"
22
23namespace Kernel {
24class KernelCore;
25class KEvent;
26class KReadableEvent;
27class KWritableEvent;
28} // namespace Kernel
29
30namespace Service::KernelHelpers {
31class ServiceContext;
32} // namespace Service::KernelHelpers
33
34namespace android {
35
36class BufferQueueCore;
37class IProducerListener;
38
39class BufferQueueProducer final : public IBinder {
40public:
41 explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
42 std::shared_ptr<BufferQueueCore> buffer_queue_core_);
43 ~BufferQueueProducer();
44
45 void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
46
47 Kernel::KReadableEvent& GetNativeHandle() override;
48
49public:
50 Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
51 Status SetBufferCount(s32 buffer_count);
52 Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width,
53 u32 height, PixelFormat format, u32 usage);
54 Status DetachBuffer(s32 slot);
55 Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence);
56 Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer);
57 Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output);
58 void CancelBuffer(s32 slot, const Fence& fence);
59 Status Query(NativeWindow what, s32* out_value);
60 Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api,
61 bool producer_controlled_by_app, QueueBufferOutput* output);
62
63 Status Disconnect(NativeWindowApi api);
64 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
65
66private:
67 BufferQueueProducer(const BufferQueueProducer&) = delete;
68
69 Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* returnFlags) const;
70
71 Kernel::KEvent* buffer_wait_event{};
72 Service::KernelHelpers::ServiceContext& service_context;
73
74 std::shared_ptr<BufferQueueCore> core;
75 BufferQueueDefs::SlotsType& slots;
76 u32 sticky_transform{};
77 std::mutex callback_mutex;
78 s32 next_callback_ticket{};
79 s32 current_callback_ticket{};
80 std::condition_variable callback_condition;
81};
82
83} // namespace android