diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index f4532ec4e8..ebd8b65760 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1532,45 +1532,53 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, // Handle MSAA upload if necessary if (info.num_samples > 1 && runtime->CanUploadMSAA()) { - // Create a temporary non-MSAA image to upload the data first - ImageInfo temp_info = info; - temp_info.num_samples = 1; + // Only use MSAA copy pass for color formats + // Depth/stencil formats need special handling + if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) { + // Create a temporary non-MSAA image to upload the data first + ImageInfo temp_info = info; + temp_info.num_samples = 1; - vk::Image temp_image = MakeImage(runtime->device, runtime->memory_allocator, temp_info, - runtime->ViewFormats(info.format)); + // Create image with same usage flags as the target image to avoid validation errors + VkImageCreateInfo image_ci = MakeImageCreateInfo(runtime->device, temp_info); + image_ci.usage = original_image.UsageFlags(); + vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci); - // Upload to the temporary non-MSAA image - scheduler->RequestOutsideRenderPassOperationContext(); - auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); - const VkBuffer src_buffer = buffer; - const VkImage temp_vk_image = *temp_image; - const VkImageAspectFlags vk_aspect_mask = aspect_mask; - scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { - CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, vk_copies); - }); + // Upload to the temporary non-MSAA image + scheduler->RequestOutsideRenderPassOperationContext(); + auto vk_copies = TransformBufferImageCopies(copies, offset, aspect_mask); + const VkBuffer src_buffer = buffer; + const VkImage temp_vk_image = *temp_image; + const VkImageAspectFlags vk_aspect_mask = aspect_mask; + scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { + CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, vk_copies); + }); - // Now use MSAACopyPass to convert from non-MSAA to MSAA - std::vector image_copies; - for (const auto& copy : copies) { - VideoCommon::ImageCopy image_copy; - image_copy.src_offset = {0, 0, 0}; // Use zero offset for source - image_copy.dst_offset = copy.image_offset; - image_copy.src_subresource = copy.image_subresource; - image_copy.dst_subresource = copy.image_subresource; - image_copy.extent = copy.image_extent; - image_copies.push_back(image_copy); + // Now use MSAACopyPass to convert from non-MSAA to MSAA + std::vector image_copies; + for (const auto& copy : copies) { + VideoCommon::ImageCopy image_copy; + image_copy.src_offset = {0, 0, 0}; // Use zero offset for source + image_copy.dst_offset = copy.image_offset; + image_copy.src_subresource = copy.image_subresource; + image_copy.dst_subresource = copy.image_subresource; + image_copy.extent = copy.image_extent; + image_copies.push_back(image_copy); + } + + // Create a wrapper Image for the temporary image + Image temp_wrapper(*runtime, temp_info, 0, 0); + temp_wrapper.original_image = std::move(temp_image); + temp_wrapper.current_image = &Image::original_image; + temp_wrapper.aspect_mask = aspect_mask; + temp_wrapper.initialized = true; + + // Use MSAACopyPass to convert from non-MSAA to MSAA + runtime->msaa_copy_pass->CopyImage(*this, temp_wrapper, image_copies, false); + std::exchange(initialized, true); + return; } - - // Create a wrapper Image for the temporary image - Image temp_wrapper(*runtime, temp_info, 0, 0); - temp_wrapper.original_image = std::move(temp_image); - temp_wrapper.current_image = &Image::original_image; - temp_wrapper.aspect_mask = aspect_mask; - temp_wrapper.initialized = true; - - // Use MSAACopyPass to convert from non-MSAA to MSAA - runtime->msaa_copy_pass->CopyImage(*this, temp_wrapper, image_copies, false); - std::exchange(initialized, true); + // For depth/stencil formats, fall back to regular upload } else { // Regular non-MSAA upload scheduler->RequestOutsideRenderPassOperationContext(); @@ -1614,101 +1622,109 @@ void Image::DownloadMemory(std::span buffers_span, std::span o // Handle MSAA download if necessary if (info.num_samples > 1 && runtime->msaa_copy_pass) { - // Create a temporary non-MSAA image to download the data - ImageInfo temp_info = info; - temp_info.num_samples = 1; + // Only use MSAA copy pass for color formats + // Depth/stencil formats need special handling + if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) { + // Create a temporary non-MSAA image to download the data + ImageInfo temp_info = info; + temp_info.num_samples = 1; - vk::Image temp_image = MakeImage(runtime->device, runtime->memory_allocator, temp_info, - runtime->ViewFormats(info.format)); + // Create image with same usage flags as the target image to avoid validation errors + VkImageCreateInfo image_ci = MakeImageCreateInfo(runtime->device, temp_info); + image_ci.usage = original_image.UsageFlags(); + vk::Image temp_image = runtime->memory_allocator.CreateImage(image_ci); - // Create a wrapper Image for the temporary image - Image temp_wrapper(*runtime, temp_info, 0, 0); - temp_wrapper.original_image = std::move(temp_image); - temp_wrapper.current_image = &Image::original_image; - temp_wrapper.aspect_mask = aspect_mask; - temp_wrapper.initialized = true; + // Create a wrapper Image for the temporary image + Image temp_wrapper(*runtime, temp_info, 0, 0); + temp_wrapper.original_image = std::move(temp_image); + temp_wrapper.current_image = &Image::original_image; + temp_wrapper.aspect_mask = aspect_mask; + temp_wrapper.initialized = true; - // Convert from MSAA to non-MSAA using MSAACopyPass - std::vector image_copies; - for (const auto& copy : copies) { - VideoCommon::ImageCopy image_copy; - image_copy.src_offset = copy.image_offset; - image_copy.dst_offset = copy.image_offset; - image_copy.src_subresource = copy.image_subresource; - image_copy.dst_subresource = copy.image_subresource; - image_copy.extent = copy.image_extent; - image_copies.push_back(image_copy); - } - - // Use MSAACopyPass to convert from MSAA to non-MSAA - runtime->msaa_copy_pass->CopyImage(temp_wrapper, *this, image_copies, true); - - // Now download from the non-MSAA image - boost::container::small_vector buffers_vector{}; - boost::container::small_vector, 8> - vk_copies; - for (size_t index = 0; index < buffers_span.size(); index++) { - buffers_vector.emplace_back(buffers_span[index]); - vk_copies.emplace_back( - TransformBufferImageCopies(copies, offsets_span[index], aspect_mask)); - } - - scheduler->RequestOutsideRenderPassOperationContext(); - scheduler->Record([buffers = std::move(buffers_vector), image = *temp_wrapper.original_image, - aspect_mask_ = aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier read_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, - .dstAccessMask = VK_ACCESS_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 = image, - .subresourceRange{ - .aspectMask = aspect_mask_, - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, read_barrier); - - for (size_t index = 0; index < buffers.size(); index++) { - cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffers[index], - vk_copies[index]); + // Convert from MSAA to non-MSAA using MSAACopyPass + std::vector image_copies; + for (const auto& copy : copies) { + VideoCommon::ImageCopy image_copy; + image_copy.src_offset = copy.image_offset; + image_copy.dst_offset = copy.image_offset; + image_copy.src_subresource = copy.image_subresource; + image_copy.dst_subresource = copy.image_subresource; + image_copy.extent = copy.image_extent; + image_copies.push_back(image_copy); } - const VkMemoryBarrier memory_write_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, - .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, - }; - const VkImageMemoryBarrier image_write_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = image, - .subresourceRange{ - .aspectMask = aspect_mask_, - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - 0, memory_write_barrier, nullptr, image_write_barrier); - }); + // Use MSAACopyPass to convert from MSAA to non-MSAA + runtime->msaa_copy_pass->CopyImage(temp_wrapper, *this, image_copies, true); + + // Now download from the non-MSAA image + boost::container::small_vector buffers_vector{}; + boost::container::small_vector, 8> + vk_copies; + for (size_t index = 0; index < buffers_span.size(); index++) { + buffers_vector.emplace_back(buffers_span[index]); + vk_copies.emplace_back( + TransformBufferImageCopies(copies, offsets_span[index], aspect_mask)); + } + + scheduler->RequestOutsideRenderPassOperationContext(); + scheduler->Record([buffers = std::move(buffers_vector), image = *temp_wrapper.original_image, + aspect_mask_ = aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier read_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_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 = image, + .subresourceRange{ + .aspectMask = aspect_mask_, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, read_barrier); + + for (size_t index = 0; index < buffers.size(); index++) { + cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffers[index], + vk_copies[index]); + } + + const VkMemoryBarrier memory_write_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + }; + const VkImageMemoryBarrier image_write_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = aspect_mask_, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + 0, memory_write_barrier, nullptr, image_write_barrier); + }); + return; + } + // For depth/stencil formats, fall back to regular download } else { // Regular non-MSAA download boost::container::small_vector buffers_vector{};