renderer_vulkan: Fix Multisample ConvertDS24S8ToRGBA8

Migrate `ResolveTexture` to `BlitHelper`.
If the conversion was multi-sample, do a final resolve at the end of the conversion to ensure the non-multisample textures are updated as well.

Fixes Pokemon!
This commit is contained in:
Wunkolo 2026-04-29 00:25:25 -07:00
parent a0a0cd648e
commit 0cb1680a20
4 changed files with 102 additions and 92 deletions

View file

@ -523,6 +523,11 @@ bool BlitHelper::ConvertDS24S8ToRGBA8(Surface& source, Surface& dest,
vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer,
vk::DependencyFlagBits::eByRegion, {}, {}, post_barriers); vk::DependencyFlagBits::eByRegion, {}, {}, post_barriers);
}); });
if (multisample) {
// Resolve the destination image if needed
ResolveTexture(dest);
}
return true; return true;
} }
@ -601,6 +606,99 @@ bool BlitHelper::DepthToBuffer(Surface& source, vk::Buffer buffer,
return true; return true;
} }
void BlitHelper::ResolveTexture(Surface& surface) {
scheduler.Record([width = surface.GetScaledWidth(), height = surface.GetScaledHeight(),
aspect = surface.Aspect(), access_flags = surface.AccessFlags(),
pipeline_state_flags = surface.PipelineStageFlags(),
msaa_image = surface.Image(Type::MultiSampled),
dest_image = surface.Image()](vk::CommandBuffer cmdbuf) {
const vk::ImageResolve resolve_area = {
.srcSubresource{
.aspectMask = aspect,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcOffset = {},
.dstSubresource{
.aspectMask = aspect,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.dstOffset = {},
.extent{
.width = width,
.height = height,
.depth = 1,
},
};
const vk::ImageSubresourceRange subresource_range = vk::ImageSubresourceRange{
.aspectMask = aspect,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
};
const std::array read_barriers = {
vk::ImageMemoryBarrier{
.srcAccessMask = access_flags,
.dstAccessMask = vk::AccessFlagBits::eTransferRead,
.oldLayout = vk::ImageLayout::eGeneral,
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = msaa_image,
.subresourceRange = subresource_range,
},
vk::ImageMemoryBarrier{
.srcAccessMask = access_flags,
.dstAccessMask = vk::AccessFlagBits::eTransferWrite,
.oldLayout = vk::ImageLayout::eGeneral,
.newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dest_image,
.subresourceRange = subresource_range,
},
};
const std::array write_barriers = {
vk::ImageMemoryBarrier{
.srcAccessMask = vk::AccessFlagBits::eTransferRead,
.dstAccessMask = access_flags,
.oldLayout = vk::ImageLayout::eTransferSrcOptimal,
.newLayout = vk::ImageLayout::eGeneral,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = msaa_image,
.subresourceRange = subresource_range,
},
vk::ImageMemoryBarrier{
.srcAccessMask = vk::AccessFlagBits::eTransferWrite,
.dstAccessMask = access_flags,
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eGeneral,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dest_image,
.subresourceRange = subresource_range,
},
};
cmdbuf.pipelineBarrier(pipeline_state_flags, vk::PipelineStageFlagBits::eTransfer,
vk::DependencyFlagBits::eByRegion, {}, {}, read_barriers);
cmdbuf.resolveImage(msaa_image, vk::ImageLayout::eTransferSrcOptimal, dest_image,
vk::ImageLayout::eTransferDstOptimal, resolve_area);
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, pipeline_state_flags,
vk::DependencyFlagBits::eByRegion, {}, {}, write_barriers);
});
}
vk::Pipeline BlitHelper::MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout) { vk::Pipeline BlitHelper::MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout) {
const vk::ComputePipelineCreateInfo compute_info = { const vk::ComputePipelineCreateInfo compute_info = {
.stage = MakeStages(shader), .stage = MakeStages(shader),

View file

@ -39,6 +39,8 @@ public:
bool DepthToBuffer(Surface& source, vk::Buffer buffer, bool DepthToBuffer(Surface& source, vk::Buffer buffer,
const VideoCore::BufferTextureCopy& copy); const VideoCore::BufferTextureCopy& copy);
void ResolveTexture(Surface& surface);
private: private:
vk::Pipeline MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout); vk::Pipeline MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout);
vk::Pipeline MakeDepthStencilBlitPipeline(); vk::Pipeline MakeDepthStencilBlitPipeline();

View file

@ -495,93 +495,6 @@ void TextureRuntime::ClearTextureWithRenderpass(Surface& surface,
}); });
} }
void TextureRuntime::ResolveTexture(Surface& surface) {
scheduler.Record([width = surface.GetScaledWidth(), height = surface.GetScaledHeight(),
aspect = surface.Aspect(), access_flags = surface.AccessFlags(),
pipeline_state_flags = surface.PipelineStageFlags(),
msaa_image = surface.Image(Type::MultiSampled),
dest_image = surface.Image()](vk::CommandBuffer cmdbuf) {
const vk::ImageResolve resolve_area = {
.srcSubresource{
.aspectMask = aspect,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcOffset = {},
.dstSubresource{
.aspectMask = aspect,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.dstOffset = {},
.extent{
.width = width,
.height = height,
.depth = 1,
},
};
const vk::ImageSubresourceRange subresource_range = MakeSubresourceRange(aspect, 0);
const std::array read_barriers = {
vk::ImageMemoryBarrier{
.srcAccessMask = access_flags,
.dstAccessMask = vk::AccessFlagBits::eTransferRead,
.oldLayout = vk::ImageLayout::eGeneral,
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = msaa_image,
.subresourceRange = subresource_range,
},
vk::ImageMemoryBarrier{
.srcAccessMask = access_flags,
.dstAccessMask = vk::AccessFlagBits::eTransferWrite,
.oldLayout = vk::ImageLayout::eGeneral,
.newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dest_image,
.subresourceRange = subresource_range,
},
};
const std::array write_barriers = {
vk::ImageMemoryBarrier{
.srcAccessMask = vk::AccessFlagBits::eTransferRead,
.dstAccessMask = access_flags,
.oldLayout = vk::ImageLayout::eTransferSrcOptimal,
.newLayout = vk::ImageLayout::eGeneral,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = msaa_image,
.subresourceRange = subresource_range,
},
vk::ImageMemoryBarrier{
.srcAccessMask = vk::AccessFlagBits::eTransferWrite,
.dstAccessMask = access_flags,
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eGeneral,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dest_image,
.subresourceRange = subresource_range,
},
};
cmdbuf.pipelineBarrier(pipeline_state_flags, vk::PipelineStageFlagBits::eTransfer,
vk::DependencyFlagBits::eByRegion, {}, {}, read_barriers);
cmdbuf.resolveImage(msaa_image, vk::ImageLayout::eTransferSrcOptimal, dest_image,
vk::ImageLayout::eTransferDstOptimal, resolve_area);
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, pipeline_state_flags,
vk::DependencyFlagBits::eByRegion, {}, {}, write_barriers);
});
}
bool TextureRuntime::CopyTextures(Surface& source, Surface& dest, bool TextureRuntime::CopyTextures(Surface& source, Surface& dest,
std::span<const VideoCore::TextureCopy> copies) { std::span<const VideoCore::TextureCopy> copies) {
renderpass_cache.EndRendering(); renderpass_cache.EndRendering();
@ -697,10 +610,10 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest,
// Must resolve images first // Must resolve images first
// Todo(wunk): Add a "dirty" flag for msaa resolves to avoid redundant image resolves // Todo(wunk): Add a "dirty" flag for msaa resolves to avoid redundant image resolves
if (source.sample_count > 1) { if (source.sample_count > 1) {
ResolveTexture(source); blit_helper.ResolveTexture(source);
} }
if (dest.sample_count > 1) { if (dest.sample_count > 1) {
ResolveTexture(dest); blit_helper.ResolveTexture(dest);
} }
const RecordParams params = { const RecordParams params = {

View file

@ -163,9 +163,6 @@ private:
/// Clears a partial texture rect using a clear rectangle /// Clears a partial texture rect using a clear rectangle
void ClearTextureWithRenderpass(Surface& surface, const VideoCore::TextureClear& clear); void ClearTextureWithRenderpass(Surface& surface, const VideoCore::TextureClear& clear);
/// Resolves the multi-sampled texture of a surface, if available, into the current texture
void ResolveTexture(Surface& surface);
private: private:
const Instance& instance; const Instance& instance;
Scheduler& scheduler; Scheduler& scheduler;