fix gx worker exit, wrap and frame end

This commit is contained in:
patchzyy
2026-10-21 13:07:23 +02:00
parent 5f053184cf
commit 62ee8eb3e9
6 changed files with 63 additions and 10 deletions
+3
View File
@@ -134,6 +134,7 @@ inline void ApplyPendingMkwDynamicAspectSurface() {
void GxWorkerBeginFrame();
void GxWorkerEndFrame(AuroraImguiFrame* imgui);
void SetAuroraFrameGxBegun(bool begun);
inline bool BeginAuroraFrame() {
if (GxWorker::ShouldDefer()) {
@@ -149,6 +150,7 @@ inline bool BeginAuroraFrame() {
if (!aurora_begin_frame()) {
return false;
}
SetAuroraFrameGxBegun(true);
ApplyPendingMkwDynamicAspectSurface();
return true;
}
@@ -163,6 +165,7 @@ inline void EndAuroraFrame() {
return;
}
aurora_end_frame();
SetAuroraFrameGxBegun(false);
}
// Poll Aurora events and update cached window/framebuffer dimensions.
+2
View File
@@ -28,6 +28,8 @@ void FlushFifo();
// Starts the worker unless [video] gx_worker disables it. Call once Aurora is up.
void Start();
void Stop();
// Stops without running queued work; bounded, for fatal exits.
void Abandon();
inline bool Active() noexcept { return detail::g_active.load(std::memory_order_relaxed); }
inline bool OnWorker() noexcept { return detail::t_onWorker; }
+9 -6
View File
@@ -112,26 +112,29 @@ void EnsureAuroraFrameActive() {
}
namespace {
// GX worker only.
bool g_workerFrameBegun = false;
// Whether the GX half of the current frame has begun, on either path. Inline frame calls only run
// after a sync, so the ring orders every access.
bool g_frameGxBegun = false;
}
void SetAuroraFrameGxBegun(bool begun) { g_frameGxBegun = begun; }
void GxWorkerBeginFrame() {
// gfx::begin_frame can briefly fail to map a staging buffer; retry like the inline path does.
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
while (!(g_workerFrameBegun = aurora_begin_frame_gx()) && std::chrono::steady_clock::now() < deadline) {
while (!(g_frameGxBegun = aurora_begin_frame_gx()) && std::chrono::steady_clock::now() < deadline) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
if (!g_workerFrameBegun) {
if (!g_frameGxBegun) {
RT_LOGF(RT_TAG_GX, "GX worker could not begin an aurora frame\n");
}
}
void GxWorkerEndFrame(AuroraImguiFrame* imgui) {
if (g_workerFrameBegun) {
if (g_frameGxBegun) {
aurora_end_frame_gx(imgui);
} else {
aurora_release_imgui_frame(imgui);
}
g_workerFrameBegun = false;
g_frameGxBegun = false;
}
+46 -3
View File
@@ -60,6 +60,8 @@ std::mutex g_tailMutex;
std::condition_variable g_tailCv;
void (*g_waitCallback)() = nullptr;
std::atomic<bool> g_stop{false};
std::atomic<bool> g_abandon{false};
std::atomic<bool> g_exited{false};
std::thread* g_thread = nullptr;
std::vector<uint8_t> g_fifoBytes;
@@ -139,6 +141,13 @@ void ReplayFifo(void* payload) {
void WorkerMain() {
detail::t_onWorker = true;
struct SignalExit {
~SignalExit() {
std::lock_guard lock{g_tailMutex};
g_exited.store(true, std::memory_order_release);
g_tailCv.notify_all();
}
} signalExit;
AuroraSetGxProducerThread();
#if defined(_WIN32)
SetThreadDescription(GetCurrentThread(), L"GX worker");
@@ -173,6 +182,9 @@ void WorkerMain() {
continue;
}
while (tail != head) {
if (g_abandon.load(std::memory_order_acquire)) {
return;
}
uint8_t* record = g_ring + tail % kCapacity;
const Header* header = reinterpret_cast<const Header*>(record);
if (header->run != nullptr) {
@@ -205,15 +217,16 @@ void* Reserve(size_t payloadSize, void (*run)(void*)) {
size_t offset = static_cast<size_t>(g_write % kCapacity);
const size_t remaining = kCapacity - offset;
if (total > remaining) {
WaitForSpace(remaining + total);
WaitForSpace(remaining);
auto* wrap = reinterpret_cast<Header*>(g_ring + offset);
wrap->run = nullptr;
wrap->size = static_cast<uint32_t>(remaining);
g_write += remaining;
// Publish the skip so the worker can drain past it while we wait for the front.
Commit();
offset = 0;
} else {
WaitForSpace(total);
}
WaitForSpace(total);
auto* header = reinterpret_cast<Header*>(g_ring + offset);
header->run = run;
header->size = static_cast<uint32_t>(total);
@@ -303,6 +316,8 @@ void Start() {
}
g_fifoBytes.reserve(kFifoFlushBytes + 4096);
g_stop.store(false, std::memory_order_relaxed);
g_abandon.store(false, std::memory_order_relaxed);
g_exited.store(false, std::memory_order_relaxed);
g_thread = new std::thread(WorkerMain);
detail::g_active.store(true, std::memory_order_release);
RT_LOGF(RT_TAG_GX, "GX worker started\n");
@@ -325,4 +340,32 @@ void Stop() {
}
}
void Abandon() {
if (!Active() || detail::t_onWorker) {
return;
}
detail::g_active.store(false, std::memory_order_release);
g_abandon.store(true, std::memory_order_release);
g_stop.store(true, std::memory_order_release);
{
std::lock_guard lock{g_headMutex};
g_headCv.notify_one();
}
bool exited = false;
{
std::unique_lock lock{g_tailMutex};
exited = g_tailCv.wait_for(lock, std::chrono::seconds{2},
[] { return g_exited.load(std::memory_order_acquire); });
}
if (g_thread != nullptr) {
if (exited) {
g_thread->join();
delete g_thread;
} else {
g_thread->detach();
}
g_thread = nullptr;
}
}
} // namespace GxWorker
+2
View File
@@ -765,6 +765,8 @@ extern "C" void DumpHostStackTraceForRuntimeHelper() {
void MarkFatalErrorReported() {
g_fatalErrorReported.store(true, std::memory_order_release);
// Every caller exits next; keep the worker out of static teardown.
GxWorker::Abandon();
}
void ShowRuntimeFatalPopup(std::string_view category, std::string_view details) noexcept {