vk: Fix CPU frame misalignment bug

- This one has been around for a really long time.
- The frame-based structure was due to translating the original vulkan tutorial to working code.
- While it is not feasible to throw the arch away, we don't need a rigid 2-frame set for acquire-submit semaphores.
This commit is contained in:
kd-11
2026-01-25 20:39:52 +03:00
committed by Ani
parent a907cc838b
commit 33d79ee2cd
4 changed files with 55 additions and 22 deletions

View File

@@ -505,9 +505,6 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
m_occlusion_query_manager->set_control_flags(VK_QUERY_CONTROL_PRECISE_BIT, 0);
}
VkSemaphoreCreateInfo semaphore_info = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
// VRAM allocation
// This first set is bound persistently, so grow notifications are enabled.
m_attrib_ring_info.create(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, VK_ATTRIB_RING_BUFFER_SIZE_M * 0x100000, vk::heap_pool_default, "attrib buffer", 0x400000, VK_TRUE);
@@ -570,10 +567,13 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
rsx_log.warning("Current driver may crash due to memory limitations (%uk)", m_texbuffer_view_size / 1024);
}
for (auto &ctx : frame_context_storage)
m_max_async_frames = m_swapchain->get_swap_image_count();
m_frame_context_storage.resize(m_max_async_frames);
m_current_frame = &m_frame_context_storage[0];
for (auto& ctx : m_frame_context_storage)
{
vkCreateSemaphore((*m_device), &semaphore_info, nullptr, &ctx.present_wait_semaphore);
vkCreateSemaphore((*m_device), &semaphore_info, nullptr, &ctx.acquire_signal_semaphore);
ctx.init(*m_device);
}
const auto& memory_map = m_device->get_memory_mapping();
@@ -612,8 +612,6 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
}
m_current_frame = &frame_context_storage[0];
m_texture_cache.initialize((*m_device), m_device->get_graphics_queue(),
m_texture_upload_buffer_ring_info);
@@ -830,16 +828,17 @@ VKGSRender::~VKGSRender()
if (m_current_frame == &m_aux_frame_context)
{
// Return resources back to the owner
m_current_frame = &frame_context_storage[m_current_queue_index];
m_current_frame = &m_frame_context_storage[m_current_queue_index];
m_current_frame->grab_resources(m_aux_frame_context);
}
// NOTE: aux_context uses descriptor pools borrowed from the main queues and any allocations will be automatically freed when pool is destroyed
for (auto &ctx : frame_context_storage)
// CPU frame contexts
for (auto &ctx : m_frame_context_storage)
{
vkDestroySemaphore((*m_device), ctx.present_wait_semaphore, nullptr);
vkDestroySemaphore((*m_device), ctx.acquire_signal_semaphore, nullptr);
ctx.destroy(*m_device);
}
m_current_frame = nullptr;
m_frame_context_storage.clear();
// Textures
m_rtts.destroy();

View File

@@ -159,8 +159,9 @@ private:
u64 m_texture_parameters_dynamic_offset = 0;
u64 m_stipple_array_dynamic_offset = 0;
std::array<vk::frame_context_t, VK_MAX_ASYNC_FRAMES> frame_context_storage;
//Temp frame context to use if the real frame queue is overburdened. Only used for storage
std::vector<vk::frame_context_t> m_frame_context_storage;
u32 m_max_async_frames = 0u;
// Temp frame context to use if the real frame queue is overburdened. Only used for storage
vk::frame_context_t m_aux_frame_context;
u32 m_current_queue_index = 0;

View File

@@ -23,7 +23,6 @@
#define VK_INDEX_RING_BUFFER_SIZE_M 16
#define VK_MAX_ASYNC_CB_COUNT 512
#define VK_MAX_ASYNC_FRAMES 2
#define FRAME_PRESENT_TIMEOUT 10000000ull // 10 seconds
#define GENERAL_WAIT_TIMEOUT 2000000ull // 2 seconds
@@ -186,6 +185,20 @@ namespace vk
data_heap_manager::managed_heap_snapshot_t heap_snapshot;
u64 last_frame_sync_time = 0;
void init(VkDevice dev)
{
VkSemaphoreCreateInfo semaphore_info = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
vkCreateSemaphore(dev, &semaphore_info, nullptr, &present_wait_semaphore);
vkCreateSemaphore(dev, &semaphore_info, nullptr, &acquire_signal_semaphore);
}
void destroy(VkDevice dev)
{
vkDestroySemaphore(dev, present_wait_semaphore, nullptr);
vkDestroySemaphore(dev, acquire_signal_semaphore, nullptr);
}
// Copy shareable information
void grab_resources(frame_context_t& other)
{

View File

@@ -52,7 +52,7 @@ void VKGSRender::reinitialize_swapchain()
m_current_command_buffer->reset();
m_current_command_buffer->begin();
for (auto &ctx : frame_context_storage)
for (auto &ctx : m_frame_context_storage)
{
if (ctx.present_image == umax)
continue;
@@ -67,6 +67,16 @@ void VKGSRender::reinitialize_swapchain()
// Drain all the queues
vkDeviceWaitIdle(*m_device);
// Reset frame context storage
for (auto& ctx : m_frame_context_storage)
{
ctx.destroy(*m_device);
}
m_current_frame = nullptr;
m_max_async_frames = 0;
m_current_queue_index = 0;
m_frame_context_storage.clear();
// Rebuild swapchain. Old swapchain destruction is handled by the init_swapchain call
if (!m_swapchain->init(m_swapchain_dims.width, m_swapchain_dims.height))
{
@@ -75,6 +85,16 @@ void VKGSRender::reinitialize_swapchain()
return;
}
// Re-initialize CPU frame contexts
m_max_async_frames = m_swapchain->get_swap_image_count();
m_frame_context_storage.resize(m_max_async_frames);
for (auto& ctx : m_frame_context_storage)
{
ctx.init(*m_device);
}
m_current_queue_index = 0;
m_current_frame = &m_frame_context_storage[0];
// Prepare new swapchain images for use
for (u32 i = 0; i < m_swapchain->get_swap_image_count(); ++i)
{
@@ -158,10 +178,10 @@ void VKGSRender::advance_queued_frames()
m_current_frame->tag_frame_end();
m_queued_frames.push_back(m_current_frame);
ensure(m_queued_frames.size() <= VK_MAX_ASYNC_FRAMES);
ensure(m_queued_frames.size() <= m_max_async_frames);
m_current_queue_index = (m_current_queue_index + 1) % VK_MAX_ASYNC_FRAMES;
m_current_frame = &frame_context_storage[m_current_queue_index];
m_current_queue_index = (m_current_queue_index + 1) % m_max_async_frames;
m_current_frame = &m_frame_context_storage[m_current_queue_index];
m_current_frame->flags |= frame_context_state::dirty;
vk::advance_frame_counter();
@@ -398,7 +418,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
if (m_current_frame == &m_aux_frame_context)
{
m_current_frame = &frame_context_storage[m_current_queue_index];
m_current_frame = &m_frame_context_storage[m_current_queue_index];
if (m_current_frame->swap_command_buffer)
{
// Its possible this flip request is triggered by overlays and the flip queue is in undefined state
@@ -520,7 +540,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
ensure(m_current_frame->present_image == umax);
ensure(m_current_frame->swap_command_buffer == nullptr);
u64 timeout = m_swapchain->get_swap_image_count() <= VK_MAX_ASYNC_FRAMES? 0ull: 100000000ull;
u64 timeout = m_swapchain->get_swap_image_count() <= 2? 0ull: 100000000ull;
while (VkResult status = m_swapchain->acquire_next_swapchain_image(m_current_frame->acquire_signal_semaphore, timeout, &m_current_frame->present_image))
{
switch (status)