Address some corrections towards MSAA discard/enable removal

This commit is contained in:
CamilleLaVey
2026-09-30 13:03:09 -04:00
parent dd1c024efc
commit bad10039ae
7 changed files with 334 additions and 196 deletions
@@ -690,9 +690,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
};
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
const auto attachments =
std::span(dst_framebuffer->Images()).first(dst_framebuffer->NumImages());
if (std::ranges::find(attachments, src_image) != attachments.end()) {
if (scheduler.IsRenderPassImage(src_image)) {
scheduler.RequestOutsideRenderPassOperationContext();
}
scheduler.RequestRenderpass(dst_framebuffer);
@@ -958,9 +956,14 @@ void BlitImageHelper::CopyMSAAImpl(const RenderingFormats& formats, VkPipeline p
.extent = dst_extent,
};
const VkImageView dst_view_handle = *dst_view;
const RenderingAttachments attachments =
RenderingAttachments attachments =
MakeRenderingAttachments(formats, std::span(&dst_view_handle, formats.num_colors),
dst_view_handle, render_area, 1);
attachments.colors[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments.depth.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
if (copy_stencil) {
attachments.stencil.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
}
const MSAACopyPushConstants push_constants{
.dst_offset = {dst_offset.x, dst_offset.y},
.src_offset = {copy.src_offset.x, copy.src_offset.y},
@@ -575,8 +575,16 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
prepare_stage(4);
}
texture_cache.UpdateRenderTargets(false);
texture_cache.CheckFeedbackLoop(std::span<const VideoCommon::ImageViewInOut>{views.data(),
views.size()});
const std::span<const VideoCommon::ImageViewInOut> view_span{views.data(), views.size()};
texture_cache.CheckFeedbackLoop(view_span);
const auto samples_render_pass_image = [&](const VideoCommon::ImageViewInOut& view) {
return view.id &&
scheduler.IsRenderPassImage(texture_cache.GetImageView(view.id).ImageHandle());
};
if (scheduler.CanAliasRenderPass(texture_cache.GetFramebuffer()) &&
std::ranges::any_of(view_span, samples_render_pass_image)) {
scheduler.RequestOutsideRenderPassOperationContext();
}
if (IsBuilt() && !pipeline) {
return false;
}
@@ -37,6 +37,12 @@ using Tegra::Engines::Maxwell3D;
using VideoCommon::QueryType;
namespace {
bool LacksQueryWriteBack(const Device& device) {
const VkDriverId driver_id = device.GetDriverID();
return driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP;
}
class SamplesQueryBank : public VideoCommon::BankBase {
public:
static constexpr size_t BANK_SIZE = 256;
@@ -235,9 +241,7 @@ public:
return;
}
PauseCounter();
const auto driver_id = device.GetDriverID();
if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
if (LacksQueryWriteBack(device)) {
pending_sync.clear();
sync_values_stash.clear();
return;
@@ -1467,6 +1471,10 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValue(VideoCommon::Lookup
if (!impl->device.IsExtConditionalRendering()) {
return false;
}
if (LacksQueryWriteBack(impl->device)) {
EndHostConditionalRendering();
return true;
}
HostConditionalRenderingCompareBCImpl(object_1.address, true, true);
return true;
}
@@ -1516,7 +1524,8 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
auto driver_id = impl->device.GetDriverID();
const bool is_gpu_high = Settings::IsGPULevelHigh();
if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) ||
LacksQueryWriteBack(impl->device)) {
EndHostConditionalRendering();
return true;
}
+157 -76
View File
@@ -30,6 +30,8 @@
namespace Vulkan {
namespace {
constexpr size_t DEPTH_VIEW = 8;
struct BeginRenderingCommand {
void operator()(vk::CommandBuffer cmdbuf, vk::CommandBuffer) const {
BeginRendering(cmdbuf, attachments);
@@ -37,6 +39,24 @@ struct BeginRenderingCommand {
RenderingAttachments attachments;
};
VkImageView DepthStencilView(const RenderingAttachments& attachments) {
if (attachments.depth.imageView != VK_NULL_HANDLE) {
return attachments.depth.imageView;
}
return attachments.stencil.imageView;
}
VkAccessFlags2 AttachmentWriteAccess(VkImageAspectFlags aspect_mask) {
if ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
return VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
}
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
return VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
}
return VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
}
} // Anonymous namespace
void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf,
@@ -129,6 +149,12 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer) {
state.framebuffer_id = framebuffer->Id();
++renderpass_serial;
renderpass_pristine = true;
alias_framebuffer_id = 0;
std::ranges::transform(attachments.colors, renderpass_views.begin(),
&VkRenderingAttachmentInfo::imageView);
renderpass_views[DEPTH_VIEW] = DepthStencilView(attachments);
renderpass_extent = attachments.render_area.extent;
renderpass_layers = attachments.layers;
attachments_touched = 0;
attachments_written = 0;
@@ -153,11 +179,49 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer) {
framebuffer->MarkResolveShadowsUpToDate();
}
void Scheduler::RequestRenderpass(const Framebuffer* framebuffer, u32 touched, u32 written) {
if (framebuffer->Id() != state.framebuffer_id) {
EndRenderPass();
BeginRenderPassImpl(framebuffer);
void Scheduler::BindFramebuffer(const Framebuffer* framebuffer) {
if (framebuffer->Id() == state.framebuffer_id) {
return;
}
if (CanAliasRenderPass(framebuffer)) {
alias_framebuffer_id = framebuffer->Id();
return;
}
EndRenderPass();
BeginRenderPassImpl(framebuffer);
}
bool Scheduler::CanAliasRenderPass(const Framebuffer* framebuffer) const {
const u64 id = framebuffer->Id();
if (id == alias_framebuffer_id) {
return true;
}
if (id == state.framebuffer_id || state.framebuffer_id == 0 ||
!device.IsExtDynamicRenderingUnusedAttachmentsSupported()) {
return false;
}
const RenderingAttachments& attachments = framebuffer->Attachments();
if (attachments.layers != renderpass_layers ||
attachments.render_area.extent.width != renderpass_extent.width ||
attachments.render_area.extent.height != renderpass_extent.height) {
return false;
}
for (u32 slot = 0; slot < attachments.num_colors; ++slot) {
if (attachments.colors[slot].imageView != renderpass_views[slot]) {
return false;
}
}
const VkImageView depth_stencil = DepthStencilView(attachments);
return depth_stencil == VK_NULL_HANDLE || depth_stencil == renderpass_views[DEPTH_VIEW];
}
bool Scheduler::IsRenderPassImage(VkImage image) const {
const auto images = std::span(renderpass_images).first(num_renderpass_images);
return std::ranges::find(images, image) != images.end();
}
void Scheduler::RequestRenderpass(const Framebuffer* framebuffer, u32 touched, u32 written) {
BindFramebuffer(framebuffer);
renderpass_pristine = false;
attachments_touched |= touched;
attachments_written |= written;
@@ -165,10 +229,7 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer, u32 touched, u
bool Scheduler::OverrideLoadOps(const Framebuffer* framebuffer, u32 attachments,
VkAttachmentLoadOp load_op, const VkClearValue& value) {
if (framebuffer->Id() != state.framebuffer_id) {
EndRenderPass();
BeginRenderPassImpl(framebuffer);
}
BindFramebuffer(framebuffer);
if (!renderpass_pristine || recorded_attachments == nullptr) {
return false;
}
@@ -252,6 +313,36 @@ void Scheduler::RelaxAttachmentOps(RenderingAttachments& attachments) const {
}
}
u32 Scheduler::RefineImageAccesses(const RenderingAttachments& attachments,
std::array<VkAccessFlags2, 9>& accesses) const {
const auto stores = [](const VkRenderingAttachmentInfo& attachment) {
return attachment.imageView != VK_NULL_HANDLE &&
attachment.storeOp != VK_ATTACHMENT_STORE_OP_NONE;
};
u32 unused_images = 0;
u32 image = 0;
const auto refine = [&](bool used, bool stored) {
if (!used) {
unused_images |= 1u << image;
}
if (!stored) {
accesses[image] = 0;
}
++image;
};
for (u32 slot = 0; slot < attachments.num_colors; ++slot) {
if (renderpass_views[slot] != VK_NULL_HANDLE) {
const VkRenderingAttachmentInfo& color = attachments.colors[slot];
refine(color.imageView != VK_NULL_HANDLE, stores(color));
}
}
if (renderpass_views[DEPTH_VIEW] != VK_NULL_HANDLE) {
refine(DepthStencilView(attachments) != VK_NULL_HANDLE,
stores(attachments.depth) || stores(attachments.stencil));
}
return unused_images;
}
void Scheduler::PublishComputeWrites() {
if (!std::exchange(compute_writes, false)) {
return;
@@ -428,78 +519,68 @@ void Scheduler::EndPendingOperations() {
EndRenderPass();
}
void Scheduler::EndRenderPass()
{
if (state.framebuffer_id == 0) {
return;
}
if (recorded_attachments != nullptr) {
RelaxAttachmentOps(*recorded_attachments);
}
ended_attachments = std::exchange(recorded_attachments, nullptr);
void Scheduler::EndRenderPass() {
if (state.framebuffer_id == 0) {
return;
}
std::array<VkAccessFlags2, 9> accesses{};
for (u32 index = 0; index < num_renderpass_images; ++index) {
accesses[index] = AttachmentWriteAccess(renderpass_image_ranges[index].aspectMask);
}
u32 unused_images = 0;
if (recorded_attachments != nullptr) {
RelaxAttachmentOps(*recorded_attachments);
unused_images = RefineImageAccesses(*recorded_attachments, accesses);
}
ended_attachments = std::exchange(recorded_attachments, nullptr);
query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount);
query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount);
// Log render pass end
if (GPU::Logging::IsActive() &&
Settings::values.gpu_log_vulkan_calls.GetValue()) {
GPU::Logging::GPULogger::GetInstance().LogRenderPassEnd();
}
query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false);
query_cache->NotifySegment(false);
Record([num_images = num_renderpass_images,
images = renderpass_images,
ranges = renderpass_image_ranges,
write_barrier = &renderpass_write_barrier,
num_memory_barriers =
static_cast<size_t>(std::exchange(renderpass_writes, false))](
vk::CommandBuffer cmdbuf) {
std::array<VkImageMemoryBarrier2, 9> barriers;
for (size_t i = 0; i < num_images; ++i) {
const VkImageSubresourceRange& range = ranges[i];
const bool is_color = (range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
const bool is_depth_stencil = (range.aspectMask
& (VK_IMAGE_ASPECT_DEPTH_BIT
| VK_IMAGE_ASPECT_STENCIL_BIT)) !=0;
VkAccessFlags2 src_access = 0;
if (is_color)
src_access |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
else if (is_depth_stencil)
src_access |= VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
else
src_access |= VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT
| VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
barriers[i] = VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT
| VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT
| VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = src_access,
.dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
.dstAccessMask = vk::ACCESS_IMAGE_USERS,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = images[i],
.subresourceRange = range,
};
}
cmdbuf.EndRendering();
cmdbuf.PipelineBarrier(0, vk::Span(write_barrier, num_memory_barriers), {},
vk::Span(barriers.data(), num_images));
});
state.framebuffer_id = 0;
num_renderpass_images = 0;
// Log render pass end
if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) {
GPU::Logging::GPULogger::GetInstance().LogRenderPassEnd();
}
query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false);
query_cache->NotifySegment(false);
Record([num_images = num_renderpass_images, images = renderpass_images,
ranges = renderpass_image_ranges, accesses, unused_images,
write_barrier = &renderpass_write_barrier,
num_memory_barriers = static_cast<size_t>(std::exchange(renderpass_writes, false))](
vk::CommandBuffer cmdbuf) {
std::array<VkImageMemoryBarrier2, 9> barriers;
size_t num_barriers = 0;
for (size_t i = 0; i < num_images; ++i) {
if (((unused_images >> i) & 1) != 0) {
continue;
}
barriers[num_barriers++] = VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = vk::PIPELINE_STAGE_ATTACHMENTS,
.srcAccessMask = accesses[i],
.dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
.dstAccessMask = vk::ACCESS_IMAGE_USERS,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = images[i],
.subresourceRange = ranges[i],
};
}
cmdbuf.EndRendering();
if (num_barriers + num_memory_barriers != 0) {
cmdbuf.PipelineBarrier(0, vk::Span(write_barrier, num_memory_barriers), {},
vk::Span(barriers.data(), num_barriers));
}
});
state.framebuffer_id = 0;
alias_framebuffer_id = 0;
num_renderpass_images = 0;
}
void Scheduler::AcquireNewChunk() {
std::scoped_lock rl{reserve_mutex};
@@ -71,6 +71,10 @@ public:
void DiscardResolvedAttachments(VkImageView resolve_view);
bool CanAliasRenderPass(const Framebuffer* framebuffer) const;
bool IsRenderPassImage(VkImage image) const;
/// Requests the current execution context to be able to execute operations only allowed outside
/// of a renderpass.
void RequestOutsideRenderPassOperationContext();
@@ -300,6 +304,8 @@ private:
void BeginRenderPassImpl(const Framebuffer* framebuffer);
void BindFramebuffer(const Framebuffer* framebuffer);
void WorkerThread(std::stop_token stop_token);
void AllocateWorkerCommandBuffer();
@@ -314,6 +320,9 @@ private:
void RelaxAttachmentOps(RenderingAttachments& attachments) const;
u32 RefineImageAccesses(const RenderingAttachments& attachments,
std::array<VkAccessFlags2, 9>& accesses) const;
void AcquireNewChunk();
const Device& device;
@@ -333,6 +342,7 @@ private:
State state;
u64 renderpass_serial = 0;
u64 alias_framebuffer_id = 0;
u64 wfi_serial = 0;
bool renderpass_writes = false;
bool renderpass_pristine = false;
@@ -347,6 +357,9 @@ private:
u32 num_renderpass_images = 0;
std::array<VkImage, 9> renderpass_images{};
std::array<VkImageSubresourceRange, 9> renderpass_image_ranges{};
std::array<VkImageView, 9> renderpass_views{};
VkExtent2D renderpass_extent{};
u32 renderpass_layers = 0;
std::queue<std::unique_ptr<CommandChunk>> work_queue;
std::vector<std::unique_ptr<CommandChunk>> chunk_reserve;
@@ -1373,7 +1373,24 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
return;
}
ASSERT(src.format == dst.format);
if (is_src_msaa && !is_dst_msaa &&
const bool is_resolve = is_src_msaa && !is_dst_msaa;
if (ENABLE_MSAA_RESOLVE_CONSUME && is_resolve && HaveSameExtent(dst_region, src_region)) {
const VkImageResolve2 resolve = MakeImageResolve(
dst_region, src_region, MakeSubresourceLayers(&dst), MakeSubresourceLayers(&src));
const VkImageCopy2 region{
.sType = VK_STRUCTURE_TYPE_IMAGE_COPY_2,
.pNext = nullptr,
.srcSubresource = resolve.srcSubresource,
.srcOffset = resolve.srcOffset,
.dstSubresource = resolve.dstSubresource,
.dstOffset = resolve.dstOffset,
.extent = resolve.extent,
};
if (CopyResolveShadow(src.ImageHandle(), dst.ImageHandle(), region)) {
return;
}
}
if (is_resolve &&
(aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) == 0) {
UNIMPLEMENTED_MSG("Stencil-only MSAA resolve is not supported");
@@ -1416,7 +1433,6 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
return;
}
const bool is_resolve = is_src_msaa && !is_dst_msaa;
if (is_resolve && !HaveSameExtent(dst_region, src_region)) {
blit_image_helper.BlitColorMSAA(dst_framebuffer, src, dst_region, src_region);
return;
@@ -1729,120 +1745,126 @@ void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
});
}
bool TextureCacheRuntime::CopyResolveShadow(VkImage msaa_image, VkImage dst_image,
const VkImageCopy2& region) {
const ResolveShadow* const shadow = GetValidResolveShadow(msaa_image);
if (shadow == nullptr || shadow->aspect_mask != region.srcSubresource.aspectMask ||
region.srcSubresource.mipLevel != 0 || region.srcOffset.x < 0 || region.srcOffset.y < 0 ||
static_cast<u32>(region.srcOffset.x) + region.extent.width > shadow->extent.width ||
static_cast<u32>(region.srcOffset.y) + region.extent.height > shadow->extent.height ||
region.srcSubresource.baseArrayLayer + region.srcSubresource.layerCount >
shadow->layers) {
return false;
}
const VkImageAspectFlags aspect_mask = shadow->aspect_mask;
const VkImage shadow_image = *shadow->image;
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([shadow_image, dst_image, region, aspect_mask](vk::CommandBuffer cmdbuf) {
const std::array pre_barriers{
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
.srcAccessMask = vk::ACCESS_IMAGE_WRITES,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = shadow_image,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
.srcAccessMask = vk::ACCESS_IMAGE_WRITES,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
};
const std::array post_barriers{
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = 0,
.dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
.dstAccessMask = vk::ACCESS_IMAGE_USERS,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = shadow_image,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
.dstAccessMask = vk::ACCESS_IMAGE_USERS,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
};
cmdbuf.PipelineBarrier(0, {}, {}, pre_barriers);
cmdbuf.CopyImage(shadow_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region);
cmdbuf.PipelineBarrier(0, {}, {}, post_barriers);
});
const bool is_color = aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT;
if ((is_color && ENABLE_MSAA_COLOR_DISCARD) ||
(!is_color && ENABLE_MSAA_DEPTH_STENCIL_DISCARD)) {
scheduler.DiscardResolvedAttachments(*shadow->view);
}
return true;
}
void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
std::span<const VideoCommon::ImageCopy> copies) {
const bool msaa_to_non_msaa = src.info.num_samples > 1 && dst.info.num_samples == 1;
const u32 num_samples = msaa_to_non_msaa ? src.info.num_samples : dst.info.num_samples;
if (ENABLE_MSAA_RESOLVE_CONSUME && msaa_to_non_msaa && copies.size() == 1 &&
src.info.format == dst.info.format) {
src.info.format == dst.info.format && copies.front().src_offset.x == 0 &&
copies.front().src_offset.y == 0) {
const VideoCommon::ImageCopy& copy = copies.front();
const ResolveShadow* const shadow = GetValidResolveShadow(src.Handle());
if (shadow != nullptr && shadow->aspect_mask == dst.AspectMask() &&
copy.src_offset.x == 0 && copy.src_offset.y == 0 &&
copy.src_subresource.base_level == 0 &&
static_cast<u32>(copy.extent.width) <= shadow->extent.width &&
static_cast<u32>(copy.extent.height) <= shadow->extent.height &&
static_cast<u32>(copy.src_subresource.base_layer + copy.src_subresource.num_layers) <=
shadow->layers) {
const VkImageAspectFlags aspect_mask = shadow->aspect_mask;
const VkImage shadow_image = *shadow->image;
const VkImage dst_image = dst.Handle();
const VkImageCopy2 region{
.sType = VK_STRUCTURE_TYPE_IMAGE_COPY_2,
.pNext = nullptr,
.srcSubresource{
.aspectMask = aspect_mask,
.mipLevel = 0,
.baseArrayLayer = static_cast<u32>(copy.src_subresource.base_layer),
.layerCount = static_cast<u32>(copy.src_subresource.num_layers),
},
.srcOffset = {0, 0, 0},
.dstSubresource{
.aspectMask = aspect_mask,
.mipLevel = static_cast<u32>(copy.dst_subresource.base_level),
.baseArrayLayer = static_cast<u32>(copy.dst_subresource.base_layer),
.layerCount = static_cast<u32>(copy.dst_subresource.num_layers),
},
.dstOffset = {copy.dst_offset.x, copy.dst_offset.y, copy.dst_offset.z},
.extent = {copy.extent.width, copy.extent.height, 1},
};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([shadow_image, dst_image, region,
aspect_mask](vk::CommandBuffer cmdbuf) {
const std::array pre_barriers{
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
.srcAccessMask = vk::ACCESS_IMAGE_WRITES,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = shadow_image,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
.srcAccessMask = vk::ACCESS_IMAGE_WRITES,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
};
const std::array post_barriers{
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = 0,
.dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
.dstAccessMask = vk::ACCESS_IMAGE_USERS,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = shadow_image,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
VkImageMemoryBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = vk::PIPELINE_STAGE_IMAGE_USERS,
.dstAccessMask = vk::ACCESS_IMAGE_USERS,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = dst_image,
.subresourceRange{aspect_mask, 0, VK_REMAINING_MIP_LEVELS, 0,
VK_REMAINING_ARRAY_LAYERS},
},
};
cmdbuf.PipelineBarrier(0, {}, {}, pre_barriers);
cmdbuf.CopyImage(shadow_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region);
cmdbuf.PipelineBarrier(0, {}, {}, post_barriers);
});
const bool is_color = aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT;
if ((is_color && ENABLE_MSAA_COLOR_DISCARD) ||
(!is_color && ENABLE_MSAA_DEPTH_STENCIL_DISCARD)) {
scheduler.DiscardResolvedAttachments(*shadow->view);
}
const VkImageCopy2 region{
.sType = VK_STRUCTURE_TYPE_IMAGE_COPY_2,
.pNext = nullptr,
.srcSubresource{
.aspectMask = dst.AspectMask(),
.mipLevel = static_cast<u32>(copy.src_subresource.base_level),
.baseArrayLayer = static_cast<u32>(copy.src_subresource.base_layer),
.layerCount = static_cast<u32>(copy.src_subresource.num_layers),
},
.srcOffset = {0, 0, 0},
.dstSubresource{
.aspectMask = dst.AspectMask(),
.mipLevel = static_cast<u32>(copy.dst_subresource.base_level),
.baseArrayLayer = static_cast<u32>(copy.dst_subresource.base_layer),
.layerCount = static_cast<u32>(copy.dst_subresource.num_layers),
},
.dstOffset = {copy.dst_offset.x, copy.dst_offset.y, copy.dst_offset.z},
.extent = {copy.extent.width, copy.extent.height, 1},
};
if (CopyResolveShadow(src.Handle(), dst.Handle(), region)) {
return;
}
}
@@ -130,6 +130,8 @@ public:
void EraseResolveShadow(VkImage msaa_image);
bool CopyResolveShadow(VkImage msaa_image, VkImage dst_image, const VkImageCopy2& region);
std::span<const VkFormat> ViewFormats(PixelFormat format) {
return view_formats[static_cast<std::size_t>(format)];
}