mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-04 21:05:06 +01:00
Wait on slot to be freed in GraphicBufferProducer::DequeueBuffer
We currently don't wait on a slot to be freed if none are free, this worked prior to async presentation as GBP's slots wouldn't change their state until other commands were called but now slots can be held by the presentation engine. As a result, we now have to wait on the presentation engine to free up slots. This commit also fixes the behavior of the `async` flag in `DequeueBuffer` as it was treated as a non-blocking flag but isn't supposed to do anything on HOS.
This commit is contained in:
parent
cdb2b85d6c
commit
597a6ff31d
@ -92,38 +92,36 @@ namespace skyline::service::hosbinder {
|
||||
constexpr i32 InvalidGraphicBufferSlot{-1}; //!< https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h;l=61
|
||||
slot = InvalidGraphicBufferSlot;
|
||||
|
||||
std::scoped_lock lock(mutex);
|
||||
// We don't need a loop here since the consumer is blocking and instantly frees all buffers
|
||||
// If a valid slot is not found on the first iteration then it would be stuck in an infinite loop
|
||||
// As a result of this, we simply warn and return InvalidOperation to the guest
|
||||
std::unique_lock lock{mutex};
|
||||
auto buffer{queue.end()};
|
||||
size_t dequeuedSlotCount{};
|
||||
for (auto it{queue.begin()}; it != std::min(queue.begin() + activeSlotCount, queue.end()); it++) {
|
||||
// We want to select the oldest slot that's free to use as we'd want all slots to be used
|
||||
// If we go linearly then we have a higher preference for selecting the former slots and being out of order
|
||||
if (it->state == BufferState::Free) {
|
||||
if (buffer == queue.end() || it->frameNumber < buffer->frameNumber)
|
||||
buffer = it;
|
||||
} else if (it->state == BufferState::Dequeued) {
|
||||
dequeuedSlotCount++;
|
||||
freeCondition.wait(lock, [&]() {
|
||||
size_t dequeuedSlotCount{};
|
||||
for (auto it{queue.begin()}; it != std::min(queue.begin() + activeSlotCount, queue.end()); it++) {
|
||||
// We want to select the oldest slot that's free to use as we'd want all slots to be used
|
||||
// If we go linearly then we have a higher preference for selecting the former slots and being out of order
|
||||
if (it->state == BufferState::Free) {
|
||||
if (buffer == queue.end() || it->frameNumber < buffer->frameNumber)
|
||||
buffer = it;
|
||||
} else if (it->state == BufferState::Dequeued) {
|
||||
dequeuedSlotCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer != queue.end()) {
|
||||
slot = static_cast<i32>(std::distance(queue.begin(), buffer));
|
||||
} else if (async) {
|
||||
return AndroidStatus::WouldBlock;
|
||||
} else if (dequeuedSlotCount == queue.size()) {
|
||||
Logger::Warn("Client attempting to dequeue more buffers when all buffers are dequeued by the client: {}", dequeuedSlotCount);
|
||||
if (buffer != queue.end()) {
|
||||
slot = static_cast<i32>(std::distance(queue.begin(), buffer));
|
||||
return true;
|
||||
} else if (dequeuedSlotCount == queue.size()) {
|
||||
Logger::Warn("Client attempting to dequeue more buffers when all buffers are dequeued by the client: {}", dequeuedSlotCount);
|
||||
slot = InvalidGraphicBufferSlot;
|
||||
return true;
|
||||
}
|
||||
|
||||
buffer = queue.end();
|
||||
return false;
|
||||
});
|
||||
|
||||
if (slot == InvalidGraphicBufferSlot) [[unlikely]]
|
||||
return AndroidStatus::InvalidOperation;
|
||||
} else {
|
||||
size_t index{};
|
||||
std::string bufferString;
|
||||
for (auto &bufferSlot : queue)
|
||||
bufferString += util::Format("\n#{} - State: {}, Has Graphic Buffer: {}, Frame Number: {}", ++index, ToString(bufferSlot.state), bufferSlot.graphicBuffer != nullptr, bufferSlot.frameNumber);
|
||||
Logger::Warn("Cannot find any free buffers to dequeue:{}", bufferString);
|
||||
return AndroidStatus::InvalidOperation;
|
||||
}
|
||||
|
||||
width = width ? width : defaultWidth;
|
||||
height = height ? height : defaultHeight;
|
||||
@ -386,10 +384,12 @@ namespace skyline::service::hosbinder {
|
||||
}
|
||||
|
||||
state.gpu->presentation.Present(buffer.texture, isAutoTimestamp ? 0 : timestamp, swapInterval, crop, scalingMode, transform, fence, [this, &buffer] {
|
||||
std::scoped_lock lock(mutex);
|
||||
std::unique_lock lock{mutex};
|
||||
|
||||
buffer.state = BufferState::Free;
|
||||
bufferEvent->Signal();
|
||||
|
||||
freeCondition.notify_all();
|
||||
});
|
||||
|
||||
buffer.state = BufferState::Queued;
|
||||
|
@ -61,6 +61,7 @@ namespace skyline::service::hosbinder {
|
||||
private:
|
||||
const DeviceState &state;
|
||||
std::mutex mutex; //!< Synchronizes access to the buffer queue
|
||||
std::condition_variable freeCondition; //!< Used to wait for a free buffer slot
|
||||
constexpr static u8 MaxSlotCount{16}; //!< The maximum amount of buffer slots that a buffer queue can hold, Android supports 64 but they go unused for applications like games so we've lowered this to 16 (https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h;l=29)
|
||||
std::array<BufferSlot, MaxSlotCount> queue;
|
||||
u8 activeSlotCount{}; //!< The amount of slots in the queue that can be dequeued
|
||||
|
Loading…
Reference in New Issue
Block a user