Files
wiicompiled/aurora-main/lib/gx/fifo.cpp
T
therenow9 196b48528e Fix display lists dropping draws when the staging batch fills (#288)
* test: cover display lists that overflow the staging batch

GXCallDisplayList and GXCallDisplayListLE decode a list with a single
fifo::process() call. When a draw does not fit the staging batch,
process() stops at that draw, and the rest of the list is never drawn.

The test stubs can now admit N draws, refuse the next one as a full
batch would, and count the split that follows. The new test refuses
the middle or the last of three draws, in big- and little-endian
lists, and expects one split and the last draw's vertices.

Also stub aurora::window::set_force_aspect_16_9, called since #256,
so gx_fifo_tests links again.

Fails without the next commit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix: submit staging and continue when a display list overflows it

drain() already loops process() -> split_staging_batch() -> retry.
Move that loop into fifo::process_all() and use it for drain() and
both GXCallDisplayList variants, so a display list that fills the
staging batch submits it and draws the rest instead of dropping it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 22:48:24 +02:00

111 lines
3.2 KiB
C++

#include "fifo.hpp"
#include "command_processor.hpp"
#include "../gfx/common.hpp"
#include "../internal.hpp"
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "tracy/Tracy.hpp"
namespace aurora::gx::fifo {
static Module Log("aurora::gx::fifo");
namespace detail {
uint8_t* sBufferData = nullptr;
uint32_t sBufferSize = 0;
uint32_t sBufferCapacity = 0;
bool sInDisplayList = false;
uint8_t* sDlBuffer = nullptr;
uint32_t sDlSize = 0;
uint32_t sDlWritePos = 0;
} // namespace detail
void init() {
constexpr uint32_t initialCapacity = 64 * 1024;
reset_cp_register_cache();
free(detail::sBufferData);
detail::sBufferData = static_cast<uint8_t*>(malloc(initialCapacity));
detail::sBufferSize = 0;
detail::sBufferCapacity = initialCapacity;
detail::sInDisplayList = false;
detail::sDlBuffer = nullptr;
detail::sDlSize = 0;
detail::sDlWritePos = 0;
}
void write_data_grow(const void* data, uint32_t length) {
uint32_t needed = detail::sBufferSize + length;
uint32_t newCap = std::max(detail::sBufferCapacity * 2, needed);
detail::sBufferData = static_cast<uint8_t*>(realloc(detail::sBufferData, newCap));
std::memcpy(detail::sBufferData + detail::sBufferSize, data, length);
detail::sBufferSize = needed;
detail::sBufferCapacity = newCap;
}
void begin_display_list(uint8_t* buf, uint32_t size) {
detail::sInDisplayList = true;
detail::sDlBuffer = buf;
detail::sDlSize = size;
detail::sDlWritePos = 0;
}
uint32_t end_display_list() {
detail::sInDisplayList = false;
uint32_t bytesWritten = detail::sDlWritePos;
uint32_t padded = (bytesWritten + 31) & ~31u;
while (detail::sDlWritePos < padded && detail::sDlWritePos < detail::sDlSize) {
detail::sDlBuffer[detail::sDlWritePos++] = 0;
}
detail::sDlBuffer = nullptr;
detail::sDlSize = 0;
detail::sDlWritePos = 0;
return padded;
}
bool in_display_list() { return detail::sInDisplayList; }
// How much of the producer's frame is spent blocked before it may decode the next batch of GX commands.
static void note_drain_wait(uint64_t nanos) noexcept {
ZoneScopedN("FIFO drain wait");
TracyPlot("aurora: fifoDrainWaitUs", static_cast<int64_t>(nanos / 1000));
}
void drain() {
// SEALED, not DONE.
const auto waited = aurora::wait_for_frame_worker_sealed();
if (waited.count() > 0) UNLIKELY {
note_drain_wait(static_cast<uint64_t>(waited.count()));
}
if (detail::sBufferSize == 0) {
return;
}
process_all(detail::sBufferData, detail::sBufferSize, true);
detail::sBufferSize = 0;
}
void process_all(const uint8_t* data, uint32_t size, bool bigEndian) {
uint32_t consumed = 0;
bool retried = false;
while (consumed < size) {
const auto count = process(data + consumed, size - consumed, bigEndian);
if (count == 0 && retried)
throw gfx::StagingCapacityError("FIFO draw does not fit after capacity submission");
consumed += count;
if (consumed == size) break;
// process returned with its renderer lock released. No recursive drain.
gfx::split_staging_batch();
retried = true;
}
}
const uint8_t* get_buffer_data() { return detail::sBufferData; }
uint32_t get_buffer_size() { return detail::sBufferSize; }
void clear_buffer() {
detail::sBufferSize = 0;
}
} // namespace aurora::gx::fifo