mirror of
https://github.com/ARMSX2/ARMSX3.git
synced 2026-08-24 16:58:52 -07:00
FrameGen: give the capture barrier a real source scope, and ask for TRANSFER_SRC
Two findings from the Vulkan audit, both of which let the interpolation read an image it had no valid dependency on. The barrier before Process used TOP_OF_PIPE with MEMORY_READ. TOP_OF_PIPE in srcStageMask specifies no stage of execution, so the first synchronization scope was empty and no availability operation happened for the colour-attachment and transfer writes the frame made into that image in the PREVIOUS submission. Submission order on a queue orders execution; it is not a memory dependency. So the interpolation could read a partially-flushed frame -- which presents as smearing during motion and does not respond to any interpolation setting, since the interpolation is not what is wrong. Source scope is now COLOR_ATTACHMENT_OUTPUT|TRANSFER with the matching write access, and the mirror barrier's empty BOTTOM_OF_PIPE second scope becomes ALL_COMMANDS. Separately, the swapchain was created COLOR_ATTACHMENT|TRANSFER_DST, but FrameGen::Process copies FROM the presented image as TRANSFER_SRC_OPTIMAL. Both that layout and vkCmdCopyImage require TRANSFER_SRC usage. Nothing else in the renderer reads a WSI image, so it had never been needed. Requested only where the surface reports it, with a warning otherwise, since it is not guaranteed on Android.
This commit is contained in:
@@ -1382,10 +1382,19 @@ namespace vk::frame_gen
|
||||
to_read.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
to_read.image = g_source_image;
|
||||
to_read.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
|
||||
to_read.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
// The frame's WRITES are what has to become visible here, not a read.
|
||||
//
|
||||
// This used TOP_OF_PIPE with MEMORY_READ, and TOP_OF_PIPE in srcStageMask specifies no
|
||||
// stage at all -- an empty first scope, so no availability operation happened for the
|
||||
// colour-attachment and transfer writes the frame made into this image in the previous
|
||||
// submission. Submission order on a queue is not a memory dependency. The interpolation
|
||||
// could therefore read a partially-flushed frame, which looks like smearing and does not
|
||||
// respond to any interpolation setting.
|
||||
to_read.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
to_read.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
|
||||
|
||||
vkCmdPipelineBarrier(g_native.cmd[slot], VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
vkCmdPipelineBarrier(g_native.cmd[slot],
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
0, 0, nullptr, 0, nullptr, 1, &to_read);
|
||||
|
||||
@@ -1398,12 +1407,14 @@ namespace vk::frame_gen
|
||||
VkImageMemoryBarrier from_read = to_read;
|
||||
from_read.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
from_read.newLayout = g_source_layout;
|
||||
// BOTTOM_OF_PIPE in dstStageMask is likewise an empty second scope. The presentation
|
||||
// engine is the consumer, so the release has to be visible to it.
|
||||
from_read.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
|
||||
from_read.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
|
||||
vkCmdPipelineBarrier(g_native.cmd[slot],
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &from_read);
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &from_read);
|
||||
|
||||
// Plan straight after Process, as the ARMSX2 driver does: Process is what updates the
|
||||
// pacer's view of the frame, and GeneratedFrameCount() only answers once it has warmed up.
|
||||
|
||||
@@ -410,6 +410,26 @@ namespace vk
|
||||
swap_info.imageColorSpace = m_color_space;
|
||||
|
||||
swap_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
|
||||
// Frame generation READS the presented image: FrameGen::Process copies it into the
|
||||
// interpolation chain as TRANSFER_SRC_OPTIMAL. Both that layout and vkCmdCopyImage
|
||||
// require the image to have been created with TRANSFER_SRC, and nothing else in the
|
||||
// renderer ever reads a WSI image, so this was never needed before and was never asked
|
||||
// for -- the copy was reading an image the driver had not prepared for reading.
|
||||
//
|
||||
// Requested only where the surface offers it, since TRANSFER_SRC is not guaranteed on
|
||||
// Android surfaces. Where it is missing, frame generation captures nothing valid, which
|
||||
// is a better failure than an invalid one.
|
||||
if (g_cfg.video.frame_generation != frame_generation_mode::off &&
|
||||
(surface_descriptors.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT))
|
||||
{
|
||||
swap_info.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
else if (g_cfg.video.frame_generation != frame_generation_mode::off)
|
||||
{
|
||||
rsx_log.warning("Swapchain: surface does not support TRANSFER_SRC; frame generation"
|
||||
" cannot read presented frames on this surface.");
|
||||
}
|
||||
swap_info.preTransform = pre_transform;
|
||||
swap_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
swap_info.imageArrayLayers = 1;
|
||||
|
||||
Reference in New Issue
Block a user