diff --git a/rpcs3/Emu/RSX/VK/VKFrameGen.cpp b/rpcs3/Emu/RSX/VK/VKFrameGen.cpp index 1c8d135ba..31e4ec773 100644 --- a/rpcs3/Emu/RSX/VK/VKFrameGen.cpp +++ b/rpcs3/Emu/RSX/VK/VKFrameGen.cpp @@ -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. diff --git a/rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp b/rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp index f09eea7af..9b47ad7ad 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/swapchain.cpp @@ -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;