diff options
| author | 2021-11-11 19:12:36 -0800 | |
|---|---|---|
| committer | 2022-03-24 18:13:33 -0700 | |
| commit | 56284bff6c312da130eb0f2d0cda80cd29c82046 (patch) | |
| tree | 555305467b7a0ae49ab23b2d33b016284a143daa /src | |
| parent | hle: nvflinger: Add implementation for BufferQueueCore class. (diff) | |
| download | yuzu-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.txt | 4 | ||||
| -rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue_producer.cpp | 936 | ||||
| -rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue_producer.h | 83 |
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 | |||
| 26 | namespace android { | ||
| 27 | |||
| 28 | BufferQueueProducer::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 | |||
| 34 | BufferQueueProducer::~BufferQueueProducer() { | ||
| 35 | service_context.CloseEvent(buffer_wait_event); | ||
| 36 | } | ||
| 37 | |||
| 38 | Status 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 | |||
| 63 | Status 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 | |||
| 122 | Status 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 | |||
| 230 | Status 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 | |||
| 341 | Status 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 | |||
| 369 | Status 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 | |||
| 404 | Status 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 | |||
| 442 | Status 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(×tamp, &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 | |||
| 606 | void 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 | |||
| 634 | Status 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 | |||
| 689 | Status 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 | |||
| 743 | Status 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 | |||
| 792 | Status 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 | |||
| 819 | void 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 | |||
| 932 | Kernel::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 | |||
| 23 | namespace Kernel { | ||
| 24 | class KernelCore; | ||
| 25 | class KEvent; | ||
| 26 | class KReadableEvent; | ||
| 27 | class KWritableEvent; | ||
| 28 | } // namespace Kernel | ||
| 29 | |||
| 30 | namespace Service::KernelHelpers { | ||
| 31 | class ServiceContext; | ||
| 32 | } // namespace Service::KernelHelpers | ||
| 33 | |||
| 34 | namespace android { | ||
| 35 | |||
| 36 | class BufferQueueCore; | ||
| 37 | class IProducerListener; | ||
| 38 | |||
| 39 | class BufferQueueProducer final : public IBinder { | ||
| 40 | public: | ||
| 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 | |||
| 49 | public: | ||
| 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 | |||
| 66 | private: | ||
| 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 | ||