Vulkan: Fix incorrect handling of buffer wrap-around in StreamBuffer

This was happening when a fence wait happened mid-frame. The data written
between the fence being queued and the allocation occuring was incorrectly
assumed to be consumed by the GPU.
This commit is contained in:
Stenzek 2016-11-30 22:13:55 +10:00
parent 3039794ffd
commit 37550501cc
1 changed files with 18 additions and 18 deletions

View File

@ -286,8 +286,11 @@ bool StreamBuffer::WaitForClearSpace(size_t num_bytes)
for (; iter != m_tracked_fences.end(); iter++) for (; iter != m_tracked_fences.end(); iter++)
{ {
// Would this fence bring us in line with the GPU? // Would this fence bring us in line with the GPU?
// This is the "last resort" case, where a command buffer execution has been forced
// after no additional data has been written to it, so we can assume that after the
// fence has been signaled the entire buffer is now consumed.
size_t gpu_position = iter->second; size_t gpu_position = iter->second;
if (gpu_position == m_current_offset) if (m_current_offset == gpu_position)
{ {
// Start at the start of the buffer again. // Start at the start of the buffer again.
new_offset = 0; new_offset = 0;
@ -295,12 +298,12 @@ bool StreamBuffer::WaitForClearSpace(size_t num_bytes)
break; break;
} }
// We can wrap around to the start, behind the GPU, if there is enough space. // Assuming that we wait for this fence, are we allocating in front of the GPU?
// We use > here because otherwise we'd end up lining up with the GPU, and then the if (m_current_offset > gpu_position)
// allocator would assume that the GPU has consumed what we just wrote.
if (m_current_offset >= m_current_gpu_position)
{ {
// Wrap around to the start (behind the GPU) if there is sufficient space. // We can wrap around to the start, behind the GPU, if there is enough space.
// We use > here because otherwise we'd end up lining up with the GPU, and then the
// allocator would assume that the GPU has consumed what we just wrote.
if (gpu_position > num_bytes) if (gpu_position > num_bytes)
{ {
new_offset = 0; new_offset = 0;
@ -310,19 +313,16 @@ bool StreamBuffer::WaitForClearSpace(size_t num_bytes)
} }
else else
{ {
// We're currently allocating behind the GPU. Therefore, if this fence is behind us, // We're currently allocating behind the GPU. This would give us between the current
// and it's the last fence in the list (no data has been written after it), we can // offset and the GPU position worth of space to work with. Again, > because we can't
// move back to allocating in front of the GPU. // align the GPU position with the buffer offset.
if (gpu_position < m_current_offset) size_t available_space_inbetween = m_current_offset - gpu_position;
if (available_space_inbetween > num_bytes)
{ {
if (std::none_of(iter, m_tracked_fences.end(), // Leave the offset as-is, but update the GPU position.
[gpu_position](const auto& it) { return it.second > gpu_position; })) new_offset = m_current_offset;
{ new_gpu_position = gpu_position;
// Wait for this fence to complete, then allocate directly after it. break;
new_offset = gpu_position;
new_gpu_position = gpu_position;
break;
}
} }
} }
} }