[Fix] (Magma): a hang-watch marker slot is reused once its submission's fence completed, not once the host read its event.

This commit is contained in:
2026-10-07 17:53:39 -04:00
parent 67f4f311d3
commit ec973fba84
3 changed files with 22 additions and 10 deletions
@@ -96,7 +96,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true;
}
GpuProgressMarkers::Bracket GpuProgressMarkers::Acquire() {
GpuProgressMarkers::Bracket GpuProgressMarkers::Acquire(Uint64 completedSubmitIndex) {
Bracket bracket;
if (m_device == VK_NULL_HANDLE) return bracket;
const std::lock_guard<std::mutex> lock(m_mutex);
@@ -106,7 +106,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (slot.Claimed) continue;
// Retire what finished since the watch last looked: frames come faster than its polls.
if (slot.InFlight && vkGetEventStatus(m_device, slot.Finished) == VK_EVENT_SET) slot.InFlight = false;
if (!slot.InFlight && free == nullptr) free = &slot;
// Free once its submission is complete by fence, not by the event read (see the header).
if (!slot.InFlight && slot.SubmitIndex <= completedSubmitIndex && free == nullptr) free = &slot;
}
if (free == nullptr) {
if (!AddSlot()) return bracket;
@@ -125,7 +126,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return bracket;
}
void GpuProgressMarkers::Submitted(const Bracket& bracket) {
void GpuProgressMarkers::Submitted(const Bracket& bracket, Uint64 submitIndex) {
if (!bracket.Valid()) return;
const std::lock_guard<std::mutex> lock(m_mutex);
if (bracket.Slot >= m_slots.size()) return;
@@ -133,6 +134,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
slot.Claimed = false;
slot.InFlight = true;
slot.Dirty = true;
slot.SubmitIndex = submitIndex;
slot.Id = m_nextId++;
}
@@ -50,9 +50,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// The two buffers to put first and last in one submission (an invalid bracket when off or
// when every slot is in flight: the submission then goes unwatched).
Bracket Acquire();
// The submission carrying `bracket` was accepted by vkQueueSubmit / was not submitted.
void Submitted(const Bracket& bracket);
//
// A slot is reused only once the submission it last bracketed is COMPLETE by its fence
// (`completedSubmitIndex`, the renderer's contiguous completed-submit floor), not merely once
// the host has read its `finished` event as set. A host read of an event gives no device-side
// ordering: nothing orders the bracket's `started` set (TOP_OF_PIPE, first buffer) against
// its `finished` set, so a host vkResetEvent plus a re-set in a later submission could
// overlap a set the earlier submission has not executed yet. The synchronization validator
// reports that as SYNC-vkCmdSetEvent-missingbarrier-set. Event reads still drive the watch.
Bracket Acquire(Uint64 completedSubmitIndex);
// The submission carrying `bracket` (renderer submit index `submitIndex`) was accepted by
// vkQueueSubmit / was not submitted.
void Submitted(const Bracket& bracket, Uint64 submitIndex);
void Abandon(const Bracket& bracket);
// The watch named one of this device's submissions as hanging the GPU.
@@ -74,6 +83,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool InFlight = false; // submitted and not yet seen finished
Bool Claimed = false; // handed out by Acquire, not yet submitted or abandoned
Bool Dirty = false; // its events were set by a past submission: reset before reuse
Uint64 SubmitIndex = 0; // the renderer submit index of the submission it last bracketed
};
static constexpr Uint32 kMaxSlots = 16;
@@ -6117,9 +6117,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (LatchIfGpuHung("flush-submit")) return false;
SharedImageSubmitSync sharedImageSync;
AttachSharedImageSync(submitInfo, sharedImageSync);
const GpuProgressMarkers::Bracket bracket = m_progressMarkers.Acquire();
const GpuProgressMarkers::Bracket bracket = m_progressMarkers.Acquire(m_completedSubmitCounter);
const VkResult result = GpuProgressMarkers::SubmitBracketed(m_graphicsQueue, submitInfo, fence, bracket);
if (result == VK_SUCCESS) m_progressMarkers.Submitted(bracket);
if (result == VK_SUCCESS) m_progressMarkers.Submitted(bracket, m_submitCounter + 1); // RegisterSubmit below
else m_progressMarkers.Abandon(bracket);
#else
const VkResult result = vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, fence);
@@ -6586,10 +6586,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (LatchIfGpuHung("present-submit")) return;
SharedImageSubmitSync sharedImageSync;
AttachSharedImageSync(submitPacket.submitInfo, sharedImageSync);
const GpuProgressMarkers::Bracket bracket = m_progressMarkers.Acquire();
const GpuProgressMarkers::Bracket bracket = m_progressMarkers.Acquire(m_completedSubmitCounter);
const VkResult presentSubmit = GpuProgressMarkers::SubmitBracketed(m_graphicsQueue, submitPacket.submitInfo,
frame.imageInFlightFence, bracket);
if (presentSubmit == VK_SUCCESS) m_progressMarkers.Submitted(bracket);
if (presentSubmit == VK_SUCCESS) m_progressMarkers.Submitted(bracket, m_submitCounter + 1); // RegisterSubmit below
else m_progressMarkers.Abandon(bracket);
#else
const VkResult presentSubmit = vkQueueSubmit(m_graphicsQueue, 1, &submitPacket.submitInfo, frame.imageInFlightFence);