diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 070a68059d..8f052d65bd 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -128,6 +128,17 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { return usage; } +[[nodiscard]] VkImageUsageFlags ImageUsageFlags(const Device& device, + const MaxwellToVK::FormatInfo& info, + PixelFormat format) { + VkImageUsageFlags usage = ImageUsageFlags(info, format); + // ASTC recompression requires STORAGE_BIT for the GPU decoder pass + if (IsPixelFormatASTC(format) && !device.IsOptimalAstcSupported()) { + usage |= VK_IMAGE_USAGE_STORAGE_BIT; + } + return usage; +} + [[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info) { const auto format_info = MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, info.format); @@ -155,7 +166,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { .arrayLayers = static_cast(info.resources.layers), .samples = ConvertSampleCount(info.num_samples), .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = ImageUsageFlags(format_info, info.format), + .usage = ImageUsageFlags(device, format_info, info.format), .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -1670,14 +1681,13 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu } current_image = &Image::original_image; storage_image_views.resize(info.resources.levels); - if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported() && - Settings::values.astc_recompression.GetValue() == - Settings::AstcRecompression::Uncompressed) { - const auto& device = runtime->device.GetLogical(); - for (s32 level = 0; level < info.resources.levels; ++level) { - storage_image_views[level] = - MakeStorageView(device, level, *original_image, VK_FORMAT_A8B8G8R8_UNORM_PACK32); - } + + // Transition render targets to GENERAL layout + const auto format_info = + MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, false, info.format); + const VkImageUsageFlags usage = ImageUsageFlags(runtime->device, format_info, info.format); + if ((usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) != 0) { + runtime->TransitionImageLayout(*this); } } @@ -2054,8 +2064,15 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::spanTransitionImageLayout(*this); const auto format_info = MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format); view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image), @@ -2088,6 +2105,9 @@ bool Image::ScaleUp(bool ignore) { scaled_info.size.height = scaled_height; scaled_image = MakeImage(runtime->device, runtime->memory_allocator, scaled_info, runtime->ViewFormats(info.format)); + const VkImageAspectFlags init_aspect = + aspect_mask != 0 ? aspect_mask : ImageAspectMask(info.format); + runtime->TransitionImageLayout(*scaled_image, init_aspect); ignore = false; } current_image = &Image::scaled_image; @@ -2207,6 +2227,9 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI samples(ConvertSampleCount(image.info.num_samples)) { using Shader::TextureType; + // Ensure image is transitioned to GENERAL layout before creating views + runtime.TransitionImageLayout(image); + const VkImageAspectFlags aspect_mask = ImageViewAspectMask(info); std::array swizzle{ SwizzleSource::R, @@ -2318,6 +2341,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageV null_image = MakeImage(*device, runtime.memory_allocator, info, {}); image_handle = *null_image; + runtime.TransitionImageLayout(*null_image, VK_IMAGE_ASPECT_COLOR_BIT); for (u32 i = 0; i < Shader::NUM_TEXTURE_TYPES; i++) { image_views[i] = MakeView(VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_IMAGE_ASPECT_COLOR_BIT); } @@ -2596,41 +2620,36 @@ void TextureCacheRuntime::AccelerateImageUpload( } void TextureCacheRuntime::TransitionImageLayout(Image& image) { - if (!image.ExchangeInitialization()) { - VkImageMemoryBarrier barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_NONE, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = image.Handle(), - .subresourceRange{ - .aspectMask = image.AspectMask(), - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }; - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([barrier](vk::CommandBuffer cmdbuf) { - // After layout transition, image may be used in shaders or as attachment - const VkPipelineStageFlags dst_stages_layout = - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_TRANSFER_BIT; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - dst_stages_layout, 0, barrier); - }); + if (image.ExchangeInitialization()) { + return; } + TransitionImageLayout(image.Handle(), image.AspectMask()); +} + +void TextureCacheRuntime::TransitionImageLayout(VkImage image, VkImageAspectFlags aspect_mask) { + VkImageMemoryBarrier barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_NONE, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .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, + }, + }; + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([barrier](vk::CommandBuffer cmdbuf) { + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier); + }); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 4bb9687ab0..2e917264c5 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -97,6 +97,7 @@ public: void InsertUploadMemoryBarrier() {} void TransitionImageLayout(Image& image); + void TransitionImageLayout(VkImage image, VkImageAspectFlags aspect_mask); bool HasBrokenTextureViewFormats() const noexcept { // No known Vulkan driver has broken image views