diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index c4e7ac3f6f..33d2bdbe16 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -206,6 +206,39 @@ foreach(VARIANT IN ITEMS ${SHADER_TYPE_VARIANTS}) set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE}) endforeach() +set(SHADER_DEFINE_VARIANTS + "block_linear_unswizzle_2d.comp|nonarrow|HAS_EXTENDED_TYPES=0" + "pitch_unswizzle.comp|nonarrow|HAS_EXTENDED_TYPES=0" +) + +foreach(VARIANT IN ITEMS ${SHADER_DEFINE_VARIANTS}) + string(REPLACE "|" ";" VARIANT_PARTS ${VARIANT}) + list(GET VARIANT_PARTS 0 VARIANT_FILENAME) + list(GET VARIANT_PARTS 1 VARIANT_SUFFIX) + list(GET VARIANT_PARTS 2 VARIANT_DEFINE) + + set(VARIANT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${VARIANT_FILENAME}) + get_filename_component(VARIANT_STEM ${VARIANT_FILENAME} NAME_WE) + get_filename_component(VARIANT_EXT ${VARIANT_FILENAME} EXT) + string(REPLACE "." "" VARIANT_EXT ${VARIANT_EXT}) + set(VARIANT_NAME ${VARIANT_STEM}_${VARIANT_SUFFIX}_${VARIANT_EXT}) + + string(TOUPPER ${VARIANT_NAME}_SPV VARIANT_VARIABLE_NAME) + set(VARIANT_HEADER_FILE ${SHADER_DIR}/${VARIANT_NAME}_spv.h) + add_custom_command( + OUTPUT + ${VARIANT_HEADER_FILE} + COMMAND + ${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS} + -D${VARIANT_DEFINE} + --variable-name ${VARIANT_VARIABLE_NAME} -o ${VARIANT_HEADER_FILE} ${VARIANT_SOURCE} + --target-env ${SPIR_V_VERSION} + MAIN_DEPENDENCY + ${VARIANT_SOURCE} + ) + set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE}) +endforeach() + foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES}) get_filename_component(FILENAME ${FILEPATH} NAME) string(REPLACE "." "_" HEADER_NAME ${FILENAME}) diff --git a/src/video_core/host_shaders/block_linear_unswizzle_2d.comp b/src/video_core/host_shaders/block_linear_unswizzle_2d.comp index 2a3446ca66..df4bf16136 100644 --- a/src/video_core/host_shaders/block_linear_unswizzle_2d.comp +++ b/src/video_core/host_shaders/block_linear_unswizzle_2d.comp @@ -5,9 +5,13 @@ #ifdef VULKAN +#ifndef HAS_EXTENDED_TYPES +#define HAS_EXTENDED_TYPES 1 +#endif +#if HAS_EXTENDED_TYPES #extension GL_EXT_shader_16bit_storage : require #extension GL_EXT_shader_8bit_storage : require -#define HAS_EXTENDED_TYPES 1 +#endif #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { #define END_PUSH_CONSTANTS }; #define UNIFORM(n) diff --git a/src/video_core/host_shaders/pitch_unswizzle.comp b/src/video_core/host_shaders/pitch_unswizzle.comp index 7d23f594c7..8f07704fa9 100644 --- a/src/video_core/host_shaders/pitch_unswizzle.comp +++ b/src/video_core/host_shaders/pitch_unswizzle.comp @@ -5,9 +5,13 @@ #ifdef VULKAN +#ifndef HAS_EXTENDED_TYPES +#define HAS_EXTENDED_TYPES 1 +#endif +#if HAS_EXTENDED_TYPES #extension GL_EXT_shader_16bit_storage : require #extension GL_EXT_shader_8bit_storage : require -#define HAS_EXTENDED_TYPES 1 +#endif #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { #define END_PUSH_CONSTANTS }; #define UNIFORM(n) diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 80c434dc8f..309dcfa186 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -22,7 +22,11 @@ #include "video_core/host_shaders/resolve_conditional_render_comp_spv.h" #include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h" #include "video_core/host_shaders/vulkan_uint8_comp_spv.h" +#include "video_core/host_shaders/block_linear_unswizzle_2d_comp_spv.h" +#include "video_core/host_shaders/block_linear_unswizzle_2d_nonarrow_comp_spv.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h" +#include "video_core/host_shaders/pitch_unswizzle_comp_spv.h" +#include "video_core/host_shaders/pitch_unswizzle_nonarrow_comp_spv.h" #include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/surface.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h" @@ -872,4 +876,239 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk( }); } +constexpr u32 UNSWIZZLE_BINDING_INPUT_BUFFER = 0; +constexpr u32 UNSWIZZLE_BINDING_OUTPUT_IMAGE = 1; +constexpr size_t UNSWIZZLE_NUM_BINDINGS = 2; + +constexpr std::array + UNSWIZZLE_DESCRIPTOR_SET_BINDINGS{{ + { + .binding = UNSWIZZLE_BINDING_INPUT_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = UNSWIZZLE_BINDING_OUTPUT_IMAGE, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .pImmutableSamplers = nullptr, + }, + }}; + +constexpr std::array + UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE{{ + { + .dstBinding = UNSWIZZLE_BINDING_INPUT_BUFFER, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .offset = UNSWIZZLE_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + { + .dstBinding = UNSWIZZLE_BINDING_OUTPUT_IMAGE, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .offset = UNSWIZZLE_BINDING_OUTPUT_IMAGE * sizeof(DescriptorUpdateEntry), + .stride = sizeof(DescriptorUpdateEntry), + }, + }}; + +constexpr DescriptorBankInfo UNSWIZZLE_BANK_INFO{ + .uniform_buffers = 0, + .storage_buffers = 1, + .texture_buffers = 0, + .image_buffers = 0, + .textures = 0, + .images = 1, + .score = 2, +}; + +[[nodiscard]] std::span UnswizzleSpv(const Device& device, + std::span extended, + std::span narrow) { + if (device.IsStorageBuffer8BitAccessSupported() && + device.IsStorageBuffer16BitAccessSupported()) { + return extended; + } + return narrow; +} + +struct PitchUnswizzlePushConstants { + alignas(8) std::array origin; + alignas(8) std::array destination; + u32 bytes_per_block; + u32 pitch; +}; + +void RecordUnswizzleEntryBarrier(Scheduler& scheduler, VkPipeline vk_pipeline, VkImage vk_image, + VkImageAspectFlags aspect_mask, bool is_initialized) { + scheduler.Record([vk_pipeline, vk_image, aspect_mask, + is_initialized](vk::CommandBuffer cmdbuf) { + VkAccessFlags src_access = VK_ACCESS_NONE; + VkImageLayout old_layout = VK_IMAGE_LAYOUT_UNDEFINED; + if (is_initialized) { + src_access = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + old_layout = VK_IMAGE_LAYOUT_GENERAL; + } + const VkImageMemoryBarrier image_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = src_access, + .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .oldLayout = old_layout, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = vk_image, + .subresourceRange{ + .aspectMask = aspect_mask, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + VkPipelineStageFlags src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + if (is_initialized) { + src_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER; + } + cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline); + }); +} + +void RecordUnswizzleExitBarrier(Scheduler& scheduler, VkImage vk_image, + VkImageAspectFlags aspect_mask) { + scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) { + const VkImageMemoryBarrier image_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = vk_image, + .subresourceRange{ + .aspectMask = aspect_mask, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, image_barrier); + }); +} + +BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass( + const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_) + : ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS, + UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO, + COMPUTE_PUSH_CONSTANT_RANGE, + UnswizzleSpv(device_, BLOCK_LINEAR_UNSWIZZLE_2D_COMP_SPV, + BLOCK_LINEAR_UNSWIZZLE_2D_NONARROW_COMP_SPV)), + scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} + +BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default; + +void BlockLinearUnswizzle2DPass::Unswizzle( + Image& image, const StagingBufferRef& map, + std::span swizzles) { + using namespace VideoCommon::Accelerated; + scheduler.RequestOutsideRenderPassOperationContext(); + const VkPipeline vk_pipeline = *pipeline; + const VkImageAspectFlags aspect_mask = image.AspectMask(); + const VkImage vk_image = image.Handle(); + const bool is_initialized = image.ExchangeInitialization(); + RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized); + + const u32 num_layers = static_cast(image.info.resources.layers); + for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { + const size_t input_offset = swizzle.buffer_offset + map.offset; + const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U); + const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U); + + compute_pass_descriptor_queue.Acquire(scheduler, 2); + compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset, + image.guest_size_bytes - swizzle.buffer_offset); + compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level)); + const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; + + const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info); + scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_layers, params, + descriptor_data](vk::CommandBuffer cmdbuf) { + const VkDescriptorSet set = descriptor_allocator.Commit(); + device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); + cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params); + cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_layers); + }); + } + RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask); +} + +PitchUnswizzlePass::PitchUnswizzlePass( + const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_) + : ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS, + UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO, + COMPUTE_PUSH_CONSTANT_RANGE, + UnswizzleSpv(device_, PITCH_UNSWIZZLE_COMP_SPV, + PITCH_UNSWIZZLE_NONARROW_COMP_SPV)), + scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} + +PitchUnswizzlePass::~PitchUnswizzlePass() = default; + +void PitchUnswizzlePass::Unswizzle(Image& image, const StagingBufferRef& map, + std::span swizzles) { + scheduler.RequestOutsideRenderPassOperationContext(); + const VkPipeline vk_pipeline = *pipeline; + const VkImageAspectFlags aspect_mask = image.AspectMask(); + const VkImage vk_image = image.Handle(); + const bool is_initialized = image.ExchangeInitialization(); + RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized); + + const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format); + const u32 pitch = image.info.pitch; + for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { + const size_t input_offset = swizzle.buffer_offset + map.offset; + const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U); + const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U); + + compute_pass_descriptor_queue.Acquire(scheduler, 2); + compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset, + image.guest_size_bytes - swizzle.buffer_offset); + compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level)); + const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; + + const PitchUnswizzlePushConstants params{ + .origin{0, 0}, + .destination{0, 0}, + .bytes_per_block = bytes_per_block, + .pitch = pitch, + }; + scheduler.Record([this, num_dispatches_x, num_dispatches_y, params, + descriptor_data](vk::CommandBuffer cmdbuf) { + const VkDescriptorSet set = descriptor_allocator.Commit(); + device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); + cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params); + cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, 1); + }); + } + RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h index 3d30aa6bdc..117f228bee 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.h +++ b/src/video_core/renderer_vulkan/vk_compute_pass.h @@ -164,4 +164,34 @@ private: ComputePassDescriptorQueue& compute_pass_descriptor_queue; }; +class BlockLinearUnswizzle2DPass final : public ComputePass { +public: + explicit BlockLinearUnswizzle2DPass( + const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_); + ~BlockLinearUnswizzle2DPass(); + + void Unswizzle(Image& image, const StagingBufferRef& map, + std::span swizzles); + +private: + Scheduler& scheduler; + ComputePassDescriptorQueue& compute_pass_descriptor_queue; +}; + +class PitchUnswizzlePass final : public ComputePass { +public: + explicit PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_, + DescriptorPool& descriptor_pool_, + ComputePassDescriptorQueue& compute_pass_descriptor_queue_); + ~PitchUnswizzlePass(); + + void Unswizzle(Image& image, const StagingBufferRef& map, + std::span swizzles); + +private: + Scheduler& scheduler; + ComputePassDescriptorQueue& compute_pass_descriptor_queue; +}; + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 92dcd20111..33887f2f84 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -449,7 +449,9 @@ void Scheduler::EndRenderPass() | 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, + | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT + | VK_ACCESS_TRANSFER_READ_BIT + | VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -460,7 +462,7 @@ void Scheduler::EndRenderPass() } cmdbuf.EndRenderPass(); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); if (has_transform_feedback) { static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{ diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index c3f56408b5..e92f6e5e18 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -160,6 +160,54 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { info.size.depth == 1; } +[[nodiscard]] PixelFormat UnswizzleViewFormat(u32 bytes_per_block) { + switch (bytes_per_block) { + case 1: + return PixelFormat::R8_UINT; + case 2: + return PixelFormat::R16_UINT; + case 4: + return PixelFormat::R32_UINT; + case 8: + return PixelFormat::R32G32_UINT; + case 16: + return PixelFormat::R32G32B32A32_UINT; + default: + return PixelFormat::Invalid; + } +} + +constexpr u32 UNSWIZZLE_WORKGROUP_INVOCATIONS = 32 * 32; + +[[nodiscard]] bool SupportsAcceleratedUnswizzleDevice(const Device& device) { + return device.IsKhrImageFormatListSupported() && + device.GetMaxComputeWorkGroupInvocations() >= UNSWIZZLE_WORKGROUP_INVOCATIONS; +} + +[[nodiscard]] bool SupportsAcceleratedUnswizzle(const Device& device, const ImageInfo& info) { + if (!SupportsAcceleratedUnswizzleDevice(device)) { + return false; + } + if (info.num_samples > 1) { + return false; + } + if (info.type != ImageType::e2D && info.type != ImageType::Linear) { + return false; + } + const PixelFormat view_format = + UnswizzleViewFormat(VideoCore::Surface::BytesPerBlock(info.format)); + if (view_format == PixelFormat::Invalid) { + return false; + } + if (!VideoCore::Surface::IsViewCompatible(info.format, view_format, false, true)) { + return false; + } + const auto host_format = + MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, view_format); + return device.IsFormatSupported(host_format.format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, + FormatType::Optimal); +} + [[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info, std::optional format_override = {}) { auto format_info = @@ -248,8 +296,18 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { return allocator.CreateImage(image_ci); } +[[nodiscard]] VkImageViewType StorageViewType(ImageType type) { + if (type == ImageType::e3D) { + return VK_IMAGE_VIEW_TYPE_3D; + } + if (type == ImageType::Linear) { + return VK_IMAGE_VIEW_TYPE_2D; + } + return VK_IMAGE_VIEW_TYPE_2D_ARRAY; +} + [[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image, - VkFormat format) { + VkFormat format, VkImageViewType view_type) { static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, .pNext = nullptr, @@ -260,7 +318,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { .pNext = &storage_image_view_usage_create_info, .flags = 0, .image = image, - .viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY, + .viewType = view_type, .format = format, .components{ .r = VK_COMPONENT_SWIZZLE_IDENTITY, @@ -968,6 +1026,12 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool, compute_pass_descriptor_queue); } + if (SupportsAcceleratedUnswizzleDevice(device)) { + bl_unswizzle_2d_pass.emplace(device, scheduler, descriptor_pool, + compute_pass_descriptor_queue); + pitch_unswizzle_pass.emplace(device, scheduler, descriptor_pool, + compute_pass_descriptor_queue); + } } void TextureCacheRuntime::Finish() { @@ -1902,16 +1966,12 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu if (runtime->device.HasDebuggingToolAttached()) { original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str()); } + if (False(flags & VideoCommon::ImageFlagBits::Converted) && + SupportsAcceleratedUnswizzle(runtime->device, info)) { + flags |= VideoCommon::ImageFlagBits::AcceleratedUpload; + } current_image = &Image::original_image; storage_image_views.resize(info.resources.levels); - if (WillUseAcceleratedAstcDecode(runtime->device, info)) { - const auto& device = runtime->device.GetLogical(); - const VkFormat storage_format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; - for (s32 level = 0; level < info.resources.levels; ++level) { - storage_image_views[level] = - MakeStorageView(device, level, *original_image, storage_format); - } - } } Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {} @@ -2321,16 +2381,39 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span& Image::StorageViewsFor(vk::Image Image::*image) { + if (image == &Image::scaled_image) { + if (scaled_storage_image_views.empty()) { + scaled_storage_image_views.resize(info.resources.levels); + } + return scaled_storage_image_views; + } + return storage_image_views; +} + VkImageView Image::StorageImageView(s32 level) noexcept { - auto& view = storage_image_views[level]; + const bool astc_decode = WillUseAcceleratedAstcDecode(runtime->device, info); + const bool unswizzle_upload = + !astc_decode && True(flags & ImageFlagBits::AcceleratedUpload); + vk::Image Image::*target = current_image; + if (astc_decode || unswizzle_upload) { + target = &Image::original_image; + } + auto& view = StorageViewsFor(target)[level]; if (!view) { auto format_info = MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format); - if (WillUseAcceleratedAstcDecode(runtime->device, info)) { + if (astc_decode) { format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32; } - view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image), - format_info.format); + if (unswizzle_upload) { + const PixelFormat view_format = + UnswizzleViewFormat(VideoCore::Surface::BytesPerBlock(info.format)); + format_info = MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, false, + view_format); + } + view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*target), + format_info.format, StorageViewType(info.type)); } return *view; } @@ -3141,6 +3224,14 @@ void TextureCacheRuntime::AccelerateImageUpload( return astc_decoder_pass->Assemble(image, map, swizzles); } + if (bl_unswizzle_2d_pass && image.info.type == ImageType::e2D) { + return bl_unswizzle_2d_pass->Unswizzle(image, map, swizzles); + } + + if (pitch_unswizzle_pass && image.info.type == ImageType::Linear) { + return pitch_unswizzle_pass->Unswizzle(image, map, swizzles); + } + if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) { if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) { ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture"); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 1b1e35aa45..be7f56cf3c 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -159,6 +159,8 @@ public: std::optional astc_decoder_pass; std::optional bl3d_unswizzle_pass; + std::optional bl_unswizzle_2d_pass; + std::optional pitch_unswizzle_pass; const Settings::ResolutionScalingInfo& resolution; std::array, VideoCore::Surface::MaxPixelFormat> view_formats; @@ -370,6 +372,8 @@ private: bool NeedsScaleHelper() const; + std::vector& StorageViewsFor(vk::Image Image::*image); + Scheduler* scheduler{}; TextureCacheRuntime* runtime{}; @@ -387,6 +391,7 @@ private: vk::Image Image::*current_image{}; std::vector storage_image_views; + std::vector scaled_storage_image_views; VkImageAspectFlags aspect_mask = 0; bool initialized = false; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index a33ff5cb26..3b16f41c81 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -354,6 +354,7 @@ public: #define FN_MAX_LIMIT_LIST \ FN_MAX_LIMIT_ELEM(ComputeSharedMemorySize) \ + FN_MAX_LIMIT_ELEM(ComputeWorkGroupInvocations) \ FN_MAX_LIMIT_ELEM(PerStageDescriptorSampledImages) \ FN_MAX_LIMIT_ELEM(PerStageResources) \ FN_MAX_LIMIT_ELEM(DescriptorSetSamplers) \