renderer_vulkan: Add TextureRuntime::ResolveTexture

Rather than use a big lambda, just rip this out into a proper function for other blit functions to utilize.
This commit is contained in:
Wunkolo 2026-04-26 14:48:29 -07:00
parent 7ce88bc63b
commit 5ded99d326
2 changed files with 99 additions and 91 deletions

View file

@ -492,6 +492,93 @@ 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,
std::span<const VideoCore::TextureCopy> copies) {
renderpass_cache.EndRendering();
@ -604,6 +691,15 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest,
renderpass_cache.EndRendering();
// Must resolve images first
// Todo(wunk): Add a "dirty" flag for msaa resolves to avoid redundant image resolves
if (source.sample_count > 1) {
ResolveTexture(source);
}
if (dest.sample_count > 1) {
ResolveTexture(dest);
}
const RecordParams params = {
.aspect = source.Aspect(),
.filter = MakeFilter(source.pixel_format),
@ -614,96 +710,6 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest,
.dst_image = dest.Image(),
};
// Todo(wunk): Add a "dirty" flag for msaa resolves to avoid redundant image resolves
const auto resolve_image = [&](const Surface& msaa_surface) {
scheduler.Record([width = msaa_surface.GetScaledWidth(),
height = msaa_surface.GetScaledHeight(), aspect = msaa_surface.Aspect(),
access_flags = msaa_surface.AccessFlags(),
pipeline_state_flags = msaa_surface.PipelineStageFlags(),
msaa_image = msaa_surface.Image(Type::MultiSampled),
dest_image = msaa_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, height, 1},
};
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 = MakeSubresourceRange(aspect, 0),
},
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 = MakeSubresourceRange(aspect, 0),
},
};
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 = MakeSubresourceRange(aspect, 0),
},
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 = MakeSubresourceRange(aspect, 0),
},
};
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);
});
};
// Must resolve images first
if (source.sample_count > 1) {
resolve_image(source);
}
if (dest.sample_count > 1) {
resolve_image(dest);
}
scheduler.Record([params, blit](vk::CommandBuffer cmdbuf) {
const std::array source_offsets = {
vk::Offset3D{static_cast<s32>(blit.src_rect.left),
@ -833,7 +839,6 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param
scheduler{runtime_.GetScheduler()}, traits{instance.GetTraits(pixel_format)},
handles{Handle(instance), Handle(instance), Handle(instance), Handle(instance),
Handle(instance)} {
if (pixel_format == VideoCore::PixelFormat::Invalid || !traits.transfer_support) {
return;
}

View file

@ -163,6 +163,9 @@ private:
/// Clears a partial texture rect using a clear rectangle
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:
const Instance& instance;
Scheduler& scheduler;