From 5940e623d5ad6477bad5e9978009f01665e589cd Mon Sep 17 00:00:00 2001 From: nmzik Date: Sun, 4 Oct 2026 21:18:19 +0400 Subject: [PATCH] gpu: fix 10-11-11 render-target storage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Çağatay Güçlü <33038942+defektu@users.noreply.github.com> --- CMakeLists.txt | 2 +- src/graphics/guest_gpu/gpu_format.cpp | 5 +- .../host_gpu/renderer/cache/textureCache.cpp | 32 ++-- .../host_gpu/renderer/colorRenderTarget.cpp | 2 +- .../host_gpu/renderer/image/image.cpp | 21 ++- src/graphics/host_gpu/renderer/image/image.h | 3 +- .../host_gpu/renderer/image/imageInfo.h | 13 +- .../host_gpu/renderer/image/textureCommon.cpp | 6 +- .../host_gpu/renderer/image/textureCommon.h | 1 + .../host_gpu/renderer/image/tiler.cpp | 78 ++++---- src/graphics/host_gpu/renderer/image/tiler.h | 24 +-- .../host_gpu/shaders/gpu_tiler_color.inc | 20 +++ ...16.comp => gpu_tiler_color_transform.comp} | 19 +- .../host_gpu/shaders/gpu_tiler_common.inc | 11 +- tests/ShaderRecompilerComputeTests.cpp | 170 ++++++++++++++++-- 15 files changed, 319 insertions(+), 88 deletions(-) create mode 100644 src/graphics/host_gpu/shaders/gpu_tiler_color.inc rename src/graphics/host_gpu/shaders/{gpu_tiler_swap_bgra16.comp => gpu_tiler_color_transform.comp} (51%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c9fe9ee..f940723c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -259,7 +259,7 @@ set(gpu_tiler_shader_names depth promote_d16 demote_d16 - swap_bgra16 + color_transform ) file(GLOB gpu_tiler_shader_includes CONFIGURE_DEPENDS "${gpu_tiler_shader_dir}/gpu_tiler_*.inc") foreach(shader_name IN LISTS gpu_tiler_shader_names) diff --git a/src/graphics/guest_gpu/gpu_format.cpp b/src/graphics/guest_gpu/gpu_format.cpp index 8c352797..44f11ffe 100644 --- a/src/graphics/guest_gpu/gpu_format.cpp +++ b/src/graphics/guest_gpu/gpu_format.cpp @@ -21,7 +21,7 @@ struct FormatInfo { constexpr FormatInfo kFormatInfo[] = { {BufferFormat::k8UNorm, 1, 0, 1, true, false}, {BufferFormat::k8UScaled, 1, 0, 0, true, false}, - {BufferFormat::k8SNorm, 0, 0, 1, false, false}, + {BufferFormat::k8SNorm, 1, 0, 1, false, false}, {BufferFormat::k8UInt, 1, 0, 1, true, true}, {BufferFormat::k16UNorm, 2, 0, 2, true, false}, {BufferFormat::k16SNorm, 2, 0, 2, true, false}, @@ -43,6 +43,7 @@ constexpr FormatInfo kFormatInfo[] = { {BufferFormat::k16_16Float, 4, 0, 4, true, false}, {BufferFormat::k11_11_10UInt, 4, 0, 4, true, true}, {BufferFormat::k11_11_10Float, 4, 0, 4, true, false}, + {BufferFormat::k10_11_11Float, 4, 0, 4, true, false}, {BufferFormat::k10_10_10_2UNorm, 4, 0, 4, true, false}, {BufferFormat::k10_10_10_2UInt, 4, 0, 4, true, true}, {BufferFormat::k8_8_8_8UNorm, 4, 0, 4, true, false}, @@ -69,7 +70,7 @@ constexpr FormatInfo kFormatInfo[] = { {BufferFormat::k9_9_9_5Float, 4, 0, 0, true, false}, {BufferFormat::k5_6_5UNorm, 2, 0, 2, true, false}, {BufferFormat::k5_5_5_1UNorm, 2, 0, 2, true, false}, - {BufferFormat::k1_5_5_5UNorm, 0, 0, 2, false, false}, + {BufferFormat::k1_5_5_5UNorm, 2, 0, 2, false, false}, {BufferFormat::k4_4_4_4UNorm, 2, 0, 2, true, false}, {BufferFormat::kFmask8_S2_F1, 1, 0, 1, true, false}, {BufferFormat::kFmask8_S4_F1, 1, 0, 1, true, false}, diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index 00bc7070..3e1bd690 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -195,6 +195,9 @@ bool TextureCache::SameBacking(const ImageInfo& cached, const ImageInfo& request if (cached.tile_mode != requested.tile_mode) { return false; } + if (cached.GetColorTransform() != requested.GetColorTransform()) { + return false; + } if (!ImageViewOps::FormatsCompatible(cached.pixel_format, requested.pixel_format) || cached.type != requested.type) { return false; @@ -634,6 +637,7 @@ void TextureCache::CopyImage(ImageId destination_id, ImageId source_id) { const bool source_depth = source.info.IsDepth(); const bool dest_depth = destination.info.IsDepth(); const bool direct_copy = + source.info.GetColorTransform() == destination.info.GetColorTransform() && (source.backing.image_type == destination.backing.image_type || (source.backing.image_type != vk::ImageType::e1D && destination.backing.image_type != vk::ImageType::e1D)) && @@ -647,7 +651,7 @@ void TextureCache::CopyImage(ImageId destination_id, ImageId source_id) { EXIT("TextureCache: cross-format multisample image copy is unsupported\n"); } auto& copy_buffer = m_buffer_cache.GetUtilityBuffer(MemoryUsage::DeviceLocal); - destination.CopyImageWithBuffer(source, copy_buffer); + destination.CopyImageWithBuffer(source, copy_buffer, m_tiler); } if (source.IsGpuModified()) { destination.MarkGpuModified(); @@ -801,6 +805,9 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques } return {merged_id}; } + if (requested.GetColorTransform() != cached.info.GetColorTransform()) { + return {ExpandImage(requested, cached_id)}; + } if (requested.IsBlock() && !cached.info.IsBlock()) { return {ExpandImage(requested, cached_id)}; } @@ -905,8 +912,8 @@ struct TextureCache::TextureTransfer { TextureUploadLayout layout; std::vector regions; std::vector tiles; - bool swap_bgra16 = false; - bool valid = false; + ColorTransform color_transform = ColorTransform::None; + bool valid = false; [[nodiscard]] uint64_t LinearSize() const { uint64_t size = 0; @@ -935,7 +942,7 @@ TextureCache::BuildTextureTransfer(const Image& image, BindingType binding, const char* owner = "TextureCache readback"; TextureTransfer transfer; - transfer.swap_bgra16 = info.bgra16; + transfer.color_transform = info.GetColorTransform(); if (upload) { if ((render_target || video_out) && (info.resources.layers == 0 || info.data.size % info.resources.layers != 0 || @@ -1023,10 +1030,9 @@ void TextureCache::UploadImage(Image& image, Buffer& source, uint64_t source_off TileManager::Result linear {source.Handle(), source_offset, info.data.size}; if (!transfer.tiles.empty()) { linear = m_tiler.Detile(source.Handle(), source_offset, info.data.size, - transfer.LinearSize(), transfer.tiles); - } - if (transfer.swap_bgra16) { - linear = m_tiler.SwapBgra16(linear); + transfer.LinearSize(), transfer.tiles, transfer.color_transform); + } else if (transfer.color_transform != ColorTransform::None) { + linear = m_tiler.TransformColor(linear, transfer.color_transform, true); } upload(transfer.regions, linear); return; @@ -1741,14 +1747,14 @@ void TextureCache::DownloadImage(Image& image, Buffer& destination, uint64_t des } auto& texture = transfer.texture; - const auto transform = texture.swap_bgra16 ? TileManager::ColorTransform::SwapBgra16 - : TileManager::ColorTransform::None; + const auto transform = texture.color_transform; if (texture.tiles.empty()) { - if (transform == TileManager::ColorTransform::SwapBgra16) { + if (transform != ColorTransform::None) { auto linear = m_tiler.GetScratchBuffer(destination_size); image.Download(texture.regions, linear.buffer, 0, linear.size); - m_tiler.SwapBgra16(linear, - {destination.Handle(), destination_offset, destination_size}); + m_tiler.TransformColor(linear, + {destination.Handle(), destination_offset, destination_size}, + transform, false); return; } for (auto& copy: texture.regions) { diff --git a/src/graphics/host_gpu/renderer/colorRenderTarget.cpp b/src/graphics/host_gpu/renderer/colorRenderTarget.cpp index 27354e69..8af8b5e1 100644 --- a/src/graphics/host_gpu/renderer/colorRenderTarget.cpp +++ b/src/graphics/host_gpu/renderer/colorRenderTarget.cpp @@ -264,7 +264,7 @@ void RenderExecutor::ResolveRenderColorTarget(CommandBuffer& buffer, RenderColor desc.type = TextureCache::BindingType::RenderTarget; desc.info.data = {rt.base.addr, backing_size}; desc.info.pixel_format = target_format.format; - desc.info.guest_format = transfer_format; + desc.info.guest_format = target_format.guest_format; desc.info.type = image_type; desc.info.extent = {width, height, depth}; desc.info.resources = {levels, volume ? 1u : view.image_layers}; diff --git a/src/graphics/host_gpu/renderer/image/image.cpp b/src/graphics/host_gpu/renderer/image/image.cpp index dfeeb11a..c94866ed 100644 --- a/src/graphics/host_gpu/renderer/image/image.cpp +++ b/src/graphics/host_gpu/renderer/image/image.cpp @@ -6,6 +6,7 @@ #include "graphics/host_gpu/renderer/cache/streamBuffer.h" #include "graphics/host_gpu/renderer/commandScheduler.h" #include "graphics/host_gpu/renderer/image/imageView.h" +#include "graphics/host_gpu/renderer/image/tiler.h" #include "graphics/host_gpu/renderer/renderTarget.h" #include "kernel/memory.h" @@ -456,7 +457,7 @@ uint32_t Image::CopyRows(uint64_t row_size, uint32_t rows, uint64_t capacity) no return static_cast(std::min(rows, capacity / row_size)); } -void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) { +void Image::CopyImageWithBuffer(Image& source, Buffer& buffer, TileManager& tiler) { EXIT_IF(buffer.Handle() == nullptr || source.backing.samples != 1 || backing.samples != 1); m_scheduler.EndRendering(); const uint32_t levels = std::min(source.backing.mip_levels, backing.mip_levels); @@ -474,6 +475,8 @@ void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) { const uint32_t destination_block = info.IsBlock() ? 4u : 1u; EXIT_IF(levels == 0 || source_bytes == 0 || source_bytes != destination_bytes || source_block != destination_block); + const auto source_transform = source.info.GetColorTransform(); + const auto target_transform = info.GetColorTransform(); vk::BufferMemoryBarrier2 barrier {}; barrier.srcStageMask = vk::PipelineStageFlagBits2::eTransfer; @@ -535,9 +538,19 @@ void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) { command.copyImageToBuffer(source.backing.image, vk::ImageLayout::eTransferSrcOptimal, buffer.Handle(), source_copy); - barrier.srcAccessMask = vk::AccessFlagBits2::eTransferWrite; - barrier.dstAccessMask = vk::AccessFlagBits2::eTransferRead; - command.pipelineBarrier2(dependency); + if (source_transform != target_transform) { + const TileManager::Result bytes {buffer.Handle(), 0, copy_size}; + if (source_transform != ColorTransform::None) { + tiler.TransformColor(bytes, bytes, source_transform, false); + } + if (target_transform != ColorTransform::None) { + tiler.TransformColor(bytes, bytes, target_transform, true); + } + } else { + barrier.srcAccessMask = vk::AccessFlagBits2::eTransferWrite; + barrier.dstAccessMask = vk::AccessFlagBits2::eTransferRead; + command.pipelineBarrier2(dependency); + } command.copyBufferToImage(buffer.Handle(), backing.image, vk::ImageLayout::eTransferDstOptimal, destination_copy); } diff --git a/src/graphics/host_gpu/renderer/image/image.h b/src/graphics/host_gpu/renderer/image/image.h index 5c216786..442bedcf 100644 --- a/src/graphics/host_gpu/renderer/image/image.h +++ b/src/graphics/host_gpu/renderer/image/image.h @@ -18,6 +18,7 @@ namespace Libs::Graphics { class Buffer; class CommandScheduler; +class TileManager; struct ImageTestAccess; using ImageId = Common::SlotId; @@ -66,7 +67,7 @@ public: void CopyImage(Image& source); void Resolve(Image& source, const ImageSubresourceRange& source_range, const ImageSubresourceRange& destination_range); - void CopyImageWithBuffer(Image& source, Buffer& buffer); + void CopyImageWithBuffer(Image& source, Buffer& buffer, TileManager& tiler); void CopyMip(Image& source, uint32_t mip, uint32_t layer); void InvalidateCpuWrite(uint64_t vaddr, uint64_t size) { diff --git a/src/graphics/host_gpu/renderer/image/imageInfo.h b/src/graphics/host_gpu/renderer/image/imageInfo.h index 313baa31..f13efc68 100644 --- a/src/graphics/host_gpu/renderer/image/imageInfo.h +++ b/src/graphics/host_gpu/renderer/image/imageInfo.h @@ -15,6 +15,8 @@ namespace Libs::Graphics { +enum class ColorTransform : uint32_t { None, SwapBgra16, Reverse10_11_11 }; + enum class VideoOutCompression : uint8_t { Uncompressed, Dcc256_256_0, Dcc256_64_64, Unsupported }; enum class ImageMetadataKind : uint8_t { None, Htile, Dcc, Cmask }; @@ -70,6 +72,14 @@ struct ImageInfo { bool bgra16 = false; std::array mip_layout {}; + [[nodiscard]] ColorTransform GetColorTransform() const noexcept { + if (bgra16) return ColorTransform::SwapBgra16; + switch (guest_format) { + case Prospero::BufferFormat::k10_11_11Float: + return ColorTransform::Reverse10_11_11; + default: return ColorTransform::None; + } + } [[nodiscard]] constexpr bool HasStencil() const noexcept { return !stencil.Empty(); } [[nodiscard]] constexpr bool HasMetadata() const noexcept { return metadata.kind != ImageMetadataKind::None; @@ -91,7 +101,8 @@ struct ImageInfo { } [[nodiscard]] bool IsCompatible(const ImageInfo& other) const noexcept { return pixel_format == other.pixel_format && samples == other.samples && - bytes_per_block == other.bytes_per_block; + bytes_per_block == other.bytes_per_block && + GetColorTransform() == other.GetColorTransform(); } [[nodiscard]] int32_t MipOf(const ImageInfo& container) const noexcept { if (container.resources.levels > container.mip_layout.size()) { diff --git a/src/graphics/host_gpu/renderer/image/textureCommon.cpp b/src/graphics/host_gpu/renderer/image/textureCommon.cpp index ea90c5c0..7551a64c 100644 --- a/src/graphics/host_gpu/renderer/image/textureCommon.cpp +++ b/src/graphics/host_gpu/renderer/image/textureCommon.cpp @@ -35,6 +35,9 @@ struct HostFormatInfo { HostFormatInfo ResolveHostFormat(Prospero::BufferFormat guest_format, Prospero::ChannelOrder order) { + if (guest_format == Prospero::BufferFormat::k10_11_11Float) { + return {vk::Format::eB10G11R11UfloatPack32, Prospero::ColorMappingBgra}; + } if (order == Prospero::ChannelOrder::kAlt) { switch (guest_format) { case Prospero::BufferFormat::k8_8_8_8UNorm: @@ -131,7 +134,8 @@ RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layo if (host_format.format != vk::Format::eUndefined && bytes != 0) { const auto order_mapping = kRenderTargetColorMappings[static_cast(order)][encoding.components - 1u]; - return {host_format.format, bytes, host_format.host_to_storage.Then(order_mapping)}; + return {host_format.format, bytes, host_format.host_to_storage.Then(order_mapping), + encoding.buffer_format}; } } EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n", diff --git a/src/graphics/host_gpu/renderer/image/textureCommon.h b/src/graphics/host_gpu/renderer/image/textureCommon.h index a88de4e3..50bb31fe 100644 --- a/src/graphics/host_gpu/renderer/image/textureCommon.h +++ b/src/graphics/host_gpu/renderer/image/textureCommon.h @@ -15,6 +15,7 @@ struct RenderTargetFormatInfo { vk::Format format = vk::Format::eUndefined; uint32_t bytes_per_element = 0; Prospero::ColorComponentMapping export_mapping; + Prospero::BufferFormat guest_format = Prospero::BufferFormat::kInvalid; }; struct SurfaceFormatInfo { diff --git a/src/graphics/host_gpu/renderer/image/tiler.cpp b/src/graphics/host_gpu/renderer/image/tiler.cpp index f0de271e..a0332e4c 100644 --- a/src/graphics/host_gpu/renderer/image/tiler.cpp +++ b/src/graphics/host_gpu/renderer/image/tiler.cpp @@ -13,7 +13,7 @@ #include "gpu_tiler_shaders/gpu_tiler_standard4_spv.h" #include "gpu_tiler_shaders/gpu_tiler_standard64_3d_spv.h" #include "gpu_tiler_shaders/gpu_tiler_standard64_spv.h" -#include "gpu_tiler_shaders/gpu_tiler_swap_bgra16_spv.h" +#include "gpu_tiler_shaders/gpu_tiler_color_transform_spv.h" #include "graphics/host_gpu/graphicContext.h" #include "graphics/host_gpu/renderer/cache/streamBuffer.h" #include "graphics/host_gpu/renderer/commandScheduler.h" @@ -31,7 +31,7 @@ TileManager::TileManager(GraphicContext& graphics, CommandScheduler& scheduler, StreamBuffer& stream_buffer) : m_graphics(graphics), m_scheduler(scheduler), m_stream_buffer(stream_buffer) { static_assert(FamilyCount == 9); - static_assert(sizeof(Push) == 52); + static_assert(sizeof(Push) == 60); std::array bindings {}; for (uint32_t index = 0; index < 2; index++) { bindings[index] = {index, vk::DescriptorType::eStorageBuffer, 1, @@ -77,8 +77,8 @@ TileManager::~TileManager() { if (m_d32_to_d16 != nullptr) { m_graphics.device.destroyPipeline(m_d32_to_d16, nullptr); } - if (m_swap_bgra16 != nullptr) { - m_graphics.device.destroyPipeline(m_swap_bgra16, nullptr); + if (m_color_transform != nullptr) { + m_graphics.device.destroyPipeline(m_color_transform, nullptr); } if (m_pipeline_layout != nullptr) { m_graphics.device.destroyPipelineLayout(m_pipeline_layout, nullptr); @@ -90,7 +90,8 @@ TileManager::~TileManager() { void TileManager::Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_capacity, std::span infos, uint64_t source_base, - uint64_t target_base, std::vector& dispatches) { + uint64_t target_base, std::vector& dispatches, + ColorTransform transform) { EXIT_IF(infos.empty() || tiled_capacity == 0 || linear_capacity == 0); const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits; EXIT_NOT_IMPLEMENTED(tiled_capacity > UINT32_MAX || linear_capacity > UINT32_MAX); @@ -108,6 +109,8 @@ void TileManager::Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_ca dispatches.clear(); dispatches.reserve(infos.size()); for (const auto& info: infos) { + EXIT_IF((transform == ColorTransform::SwapBgra16 && info.bytes_per_element != 8u) || + (transform == ColorTransform::Reverse10_11_11 && info.bytes_per_element != 4u)); TileBlockLayout block {}; const uint32_t tiled_width = info.tiled_width != 0 ? info.tiled_width : info.pitch; const uint32_t tiled_height = info.tiled_height != 0 ? info.tiled_height : info.height; @@ -193,6 +196,7 @@ void TileManager::Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_ca dispatch.push.tail_x = info.tail_x; dispatch.push.tail_y = info.tail_y; dispatch.push.tail = info.tail; + dispatch.push.color_transform = static_cast(transform); dispatches.push_back(dispatch); } @@ -338,13 +342,14 @@ void TileManager::Record(vk::Buffer source, uint64_t source_offset, TileManager::Result TileManager::Detile(vk::Buffer tiled, uint64_t tiled_offset, uint64_t tiled_capacity, uint64_t linear_capacity, - std::span infos) { + std::span infos, + ColorTransform transform) { const auto& limits = m_graphics.GetPhysicalDeviceProperties().limits; const uint64_t descriptor_alignment = std::max(limits.minStorageBufferOffsetAlignment, 4); const uint64_t source_base = tiled_offset & (descriptor_alignment - 1); std::vector dispatches; - Prepare(false, tiled_capacity, linear_capacity, infos, source_base, 0, dispatches); + Prepare(false, tiled_capacity, linear_capacity, infos, source_base, 0, dispatches, transform); auto scratch = GetScratchBuffer(linear_capacity, tiled); Record(tiled, tiled_offset, tiled_capacity, scratch.buffer, 0, scratch.size, dispatches, true); @@ -375,15 +380,11 @@ void TileManager::TileImage(Image& image, std::span r std::max(limits.minStorageBufferOffsetAlignment, 4); const uint64_t target_base = tiled_offset & (descriptor_alignment - 1); std::vector dispatches; - // Reserve stream parameters before recording the image download and conversion. - Prepare(true, tiled_capacity, linear_capacity, infos, 0, target_base, dispatches); + // Reserve stream parameters before recording the image download. + Prepare(true, tiled_capacity, linear_capacity, infos, 0, target_base, dispatches, transform); auto linear = GetScratchBuffer(linear_capacity, tiled); image.Download(regions, linear.buffer, 0, linear.size); - Result source {linear.buffer, 0, linear.size}; - if (transform == ColorTransform::SwapBgra16) { - source = SwapBgra16(source); - } - Record(source.buffer, source.offset, linear_capacity, tiled, tiled_offset, tiled_capacity, + Record(linear.buffer, 0, linear_capacity, tiled, tiled_offset, tiled_capacity, dispatches, false); } @@ -582,9 +583,20 @@ void TileManager::ConvertD16(Result source, Result target, D16Direction directio &barriers[1], 0, nullptr); } -void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) { - if (m_swap_bgra16 == nullptr) { - const auto module = CompileSPV(GPU_TILER_SWAP_BGRA16_SPV, m_graphics.device); +void TileManager::TransformColor(Result input, Result output, ColorTransform transform, + bool to_host) { + uint32_t element_bytes = 0; + switch (transform) { + case ColorTransform::SwapBgra16: element_bytes = 8; break; + case ColorTransform::Reverse10_11_11: element_bytes = 4; break; + default: EXIT("invalid color transform\n"); + } + EXIT_IF(input.size == 0 || input.size % element_bytes != 0 || + input.size / element_bytes > UINT32_MAX || + output.size < input.size); + const auto pixels = static_cast(input.size / element_bytes); + if (m_color_transform == nullptr) { + const auto module = CompileSPV(GPU_TILER_COLOR_TRANSFORM_SPV, m_graphics.device); vk::PipelineShaderStageCreateInfo stage {}; stage.stage = vk::ShaderStageFlagBits::eCompute; stage.module = module; @@ -593,14 +605,12 @@ void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) { create.stage = stage; create.layout = m_pipeline_layout; const auto result = - m_graphics.device.createComputePipelines(nullptr, 1, &create, nullptr, &m_swap_bgra16); + m_graphics.device.createComputePipelines(nullptr, 1, &create, nullptr, &m_color_transform); m_graphics.device.destroyShaderModule(module, nullptr); - RequireVulkanSuccess(result, "create BGRA16 swap pipeline"); + RequireVulkanSuccess(result, "create color transform pipeline"); } - const uint64_t bytes = static_cast(pixels) * 8u; - EXIT_IF(pixels == 0); - const auto input_binding = BindStorage(input, bytes); - const auto output_binding = BindStorage(output, bytes); + const auto input_binding = BindStorage(input, input.size); + const auto output_binding = BindStorage(output, input.size); const vk::DescriptorBufferInfo infos[] { input_binding.info, @@ -631,13 +641,15 @@ void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) { command.pipelineBarrier( vk::PipelineStageFlagBits::eAllCommands | vk::PipelineStageFlagBits::eHost, vk::PipelineStageFlagBits::eComputeShader, {}, 0, nullptr, 2, barriers, 0, nullptr); - command.bindPipeline(vk::PipelineBindPoint::eCompute, m_swap_bgra16); + command.bindPipeline(vk::PipelineBindPoint::eCompute, m_color_transform); command.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, m_pipeline_layout, 0, static_cast(writes.size()), writes.data()); Push push {}; - push.src_base = input_binding.base; - push.dst_base = output_binding.base; - push.width = pixels; + push.src_base = input_binding.base; + push.dst_base = output_binding.base; + push.width = pixels; + push.color_transform = static_cast(transform); + push.to_host = to_host; command.pushConstants(m_pipeline_layout, vk::ShaderStageFlagBits::eCompute, 0, sizeof(push), &push); command.dispatch((pixels + 63u) / 64u, 1, 1); @@ -648,17 +660,11 @@ void TileManager::SwapBgra16(Result input, Result output, uint32_t pixels) { 0, nullptr); } -TileManager::Result TileManager::SwapBgra16(Result input) { - EXIT_NOT_IMPLEMENTED(input.size == 0 || input.size % 8u != 0 || input.size / 8u > UINT32_MAX); +TileManager::Result TileManager::TransformColor(Result input, ColorTransform transform, + bool to_host) { auto result = GetScratchBuffer(input.size, input.buffer); - SwapBgra16(input, result, static_cast(input.size / 8u)); + TransformColor(input, result, transform, to_host); return result; } -void TileManager::SwapBgra16(Result input, Result output) { - EXIT_NOT_IMPLEMENTED(input.size == 0 || input.size % 8u != 0 || input.size / 8u > UINT32_MAX || - output.size < input.size); - SwapBgra16(input, output, static_cast(input.size / 8u)); -} - } // namespace Libs::Graphics diff --git a/src/graphics/host_gpu/renderer/image/tiler.h b/src/graphics/host_gpu/renderer/image/tiler.h index 9ba3499f..aa0e10aa 100644 --- a/src/graphics/host_gpu/renderer/image/tiler.h +++ b/src/graphics/host_gpu/renderer/image/tiler.h @@ -3,6 +3,7 @@ #include "common/common.h" #include "graphics/guest_gpu/tile.h" +#include "graphics/host_gpu/renderer/image/imageInfo.h" #include "graphics/host_gpu/vulkanCommon.h" #include @@ -42,7 +43,6 @@ struct GpuTileInfo { class TileManager final { public: enum class D16Direction { Promote, Demote }; - enum class ColorTransform { None, SwapBgra16 }; struct Result { vk::Buffer buffer = nullptr; @@ -65,7 +65,8 @@ public: // Consume scratch results before the next acquisition, or pass their buffer as input. [[nodiscard]] Result Detile(vk::Buffer tiled, uint64_t tiled_offset, uint64_t tiled_capacity, - uint64_t linear_capacity, std::span infos); + uint64_t linear_capacity, std::span infos, + ColorTransform transform = ColorTransform::None); void Tile(vk::Buffer linear, uint64_t linear_offset, uint64_t linear_capacity, vk::Buffer tiled, uint64_t tiled_offset, uint64_t tiled_capacity, std::span infos); void TileImage(Image& image, std::span regions, vk::Buffer tiled, @@ -75,8 +76,8 @@ public: [[nodiscard]] Result GetScratchBuffer(uint64_t size, vk::Buffer input = nullptr); void ConvertD16(Result source, Result target, D16Direction direction, bool d32, const D16Layout& layout); - [[nodiscard]] Result SwapBgra16(Result input); - void SwapBgra16(Result input, Result output); + [[nodiscard]] Result TransformColor(Result input, ColorTransform transform, bool to_host); + void TransformColor(Result input, Result output, ColorTransform transform, bool to_host); private: friend struct TileManagerTestAccess; @@ -100,6 +101,8 @@ private: uint32_t tail_x; uint32_t tail_y; uint32_t tail; + uint32_t color_transform; + uint32_t to_host; }; struct Dispatch { Push push {}; @@ -118,12 +121,11 @@ private: uint32_t max_groups) noexcept; void Prepare(bool tile, uint64_t tiled_capacity, uint64_t linear_capacity, std::span infos, uint64_t source_base, uint64_t target_base, - std::vector& dispatches); + std::vector& dispatches, ColorTransform transform = ColorTransform::None); void Record(vk::Buffer source, uint64_t source_offset, uint64_t source_capacity, vk::Buffer target, uint64_t target_offset, uint64_t target_capacity, std::span dispatches, bool clear_target); [[nodiscard]] vk::Pipeline GetPipeline(uint32_t slot); - void SwapBgra16(Result input, Result output, uint32_t pixels); GraphicContext& m_graphics; CommandScheduler& m_scheduler; @@ -132,11 +134,11 @@ private: vk::DescriptorSetLayout m_descriptor_layout = nullptr; vk::PipelineLayout m_pipeline_layout = nullptr; std::array m_pipelines {}; - vk::Pipeline m_d16_to_d24 = nullptr; - vk::Pipeline m_d16_to_d32 = nullptr; - vk::Pipeline m_d24_to_d16 = nullptr; - vk::Pipeline m_d32_to_d16 = nullptr; - vk::Pipeline m_swap_bgra16 = nullptr; + vk::Pipeline m_d16_to_d24 = nullptr; + vk::Pipeline m_d16_to_d32 = nullptr; + vk::Pipeline m_d24_to_d16 = nullptr; + vk::Pipeline m_d32_to_d16 = nullptr; + vk::Pipeline m_color_transform = nullptr; }; } // namespace Libs::Graphics diff --git a/src/graphics/host_gpu/shaders/gpu_tiler_color.inc b/src/graphics/host_gpu/shaders/gpu_tiler_color.inc new file mode 100644 index 00000000..4c388230 --- /dev/null +++ b/src/graphics/host_gpu/shaders/gpu_tiler_color.inc @@ -0,0 +1,20 @@ +// ColorTransform values match imageInfo.h. Preserve the floating-point bit fields exactly. +uvec2 transform_color(uvec2 value, uint transform, bool to_host) { + switch (transform) { + case 1u: + return uvec2((value.y & 0xffffu) | (value.x & 0xffff0000u), + (value.x & 0xffffu) | (value.y & 0xffff0000u)); + case 2u: { + uint packed = value.x; + if (to_host) { + value.x = (packed >> 21u) | (((packed >> 10u) & 0x7ffu) << 11u) | + ((packed & 0x3ffu) << 22u); + } else { + value.x = (packed >> 22u) | (((packed >> 11u) & 0x7ffu) << 10u) | + ((packed & 0x7ffu) << 21u); + } + break; + } + } + return value; +} diff --git a/src/graphics/host_gpu/shaders/gpu_tiler_swap_bgra16.comp b/src/graphics/host_gpu/shaders/gpu_tiler_color_transform.comp similarity index 51% rename from src/graphics/host_gpu/shaders/gpu_tiler_swap_bgra16.comp rename to src/graphics/host_gpu/shaders/gpu_tiler_color_transform.comp index fb5f833e..7892b4c3 100644 --- a/src/graphics/host_gpu/shaders/gpu_tiler_swap_bgra16.comp +++ b/src/graphics/host_gpu/shaders/gpu_tiler_color_transform.comp @@ -1,4 +1,5 @@ #version 450 +#extension GL_GOOGLE_include_directive : require layout(local_size_x = 64) in; @@ -19,17 +20,23 @@ layout(push_constant, std430) uniform Push { uint tail_x; uint tail_y; uint tail; + uint color_transform; + uint to_host; } params; +#include "gpu_tiler_color.inc" + void main() { uint pixel = gl_GlobalInvocationID.x; if (pixel >= params.width) { return; } - uint src = (params.src_base >> 2u) + pixel * 2u; - uint dst = (params.dst_base >> 2u) + pixel * 2u; - uint bg = input_buffer.data[src]; - uint ra = input_buffer.data[src + 1u]; - output_buffer.data[dst] = (ra & 0x0000ffffu) | (bg & 0xffff0000u); - output_buffer.data[dst + 1u] = (bg & 0x0000ffffu) | (ra & 0xffff0000u); + uint words = params.color_transform == 1u ? 2u : 1u; + uint src = (params.src_base >> 2u) + pixel * words; + uint dst = (params.dst_base >> 2u) + pixel * words; + uvec2 value = uvec2(input_buffer.data[src], 0u); + if (words == 2u) value.y = input_buffer.data[src + 1u]; + value = transform_color(value, params.color_transform, params.to_host != 0u); + output_buffer.data[dst] = value.x; + if (words == 2u) output_buffer.data[dst + 1u] = value.y; } diff --git a/src/graphics/host_gpu/shaders/gpu_tiler_common.inc b/src/graphics/host_gpu/shaders/gpu_tiler_common.inc index 1941223f..4182f9ea 100644 --- a/src/graphics/host_gpu/shaders/gpu_tiler_common.inc +++ b/src/graphics/host_gpu/shaders/gpu_tiler_common.inc @@ -17,10 +17,19 @@ layout(set = 0, binding = 2, std140) uniform Push { uint tail_x; uint tail_y; uint tail; + uint color_transform; } params; +#include "gpu_tiler_color.inc" + void copy_element(uint src, uint dst) { - if (ELEMENT_BYTES >= 4) { + if ((ELEMENT_BYTES == 4u || ELEMENT_BYTES == 8u) && params.color_transform != 0u) { + uvec2 value = uvec2(input_buffer.data[src >> 2], 0u); + if (ELEMENT_BYTES == 8u) value.y = input_buffer.data[(src >> 2) + 1u]; + value = transform_color(value, params.color_transform, TILE == 0u); + output_buffer.data[dst >> 2] = value.x; + if (ELEMENT_BYTES == 8u) output_buffer.data[(dst >> 2) + 1u] = value.y; + } else if (ELEMENT_BYTES >= 4) { for (uint i = 0; i < ELEMENT_BYTES; i += 4) { output_buffer.data[(dst + i) >> 2] = input_buffer.data[(src + i) >> 2]; } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 6b288fa5..e84f7159 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -8848,6 +8848,8 @@ public: total.size >= 8 && total.size <= allocation_size, "BGRA16 tiled layout exceeds its guest allocation"); std::memset(mapped, 0x5a, total.size); + constexpr std::array initial{0x3c00u, 0x4000u, 0x4200u, 0x4400u}; + std::memcpy(mapped, initial.data(), sizeof(initial)); ImageDesc desc{}; desc.type = BindingType::RenderTarget; @@ -8870,6 +8872,11 @@ public: auto &cache = resources.GetTextureCache(); const auto id = cache.FindImage(desc); + (void)cache.FindRenderTarget(id, desc); + Require(name, "tiled BGRA16 upload", + ReadCachedTexel(name, context, id) == + std::vector{0x40004200u, 0x44003c00u}, + "fused detiling lost the BGRA16 component transform"); auto &image = cache.GetImage(id); image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, @@ -10480,6 +10487,15 @@ public: target.format == vk::Format::eR5G5B5A1UnormPack16 && target.export_mapping == Prospero::ColorMappingAbgr, "shared format resolution changed the existing 1555 render target"); + for (const auto native : {target, TextureGetRenderTargetFormat( + Prospero::ChannelLayout::k8, Prospero::ChannelType::kSNorm, + Prospero::ChannelOrder::kStandard)}) { + const auto upload = TextureCalcUploadLayout(native.guest_format, 4, 1, 1, 1, + Prospero::TileMode::kLinear, 256, false, name); + Require(name, "render-target guest upload layout", + upload.pitch * native.bytes_per_element == 256 && upload.mips[0].size == 256, + "retaining the render-target format lost its physical byte width"); + } EnsureRuntimeContext(); int64_t direct_offset = -1; Require(name, "allocation", @@ -10568,6 +10584,134 @@ public: resources.UnmapMemory(base, allocation_size); scheduler.Finish(); } + for (const auto tile : {Prospero::TileMode::kLinear, Prospero::TileMode::kRenderTarget}) { + constexpr std::array words{0x883005e1u, 0x7c0e81c0u, + 0x800f0000u, 0x98120280u}; + constexpr std::array, 4> channels{{ + {1.03125f, 2.03125f, 4.0625f, 1.f}, {0.5f, 0.75f, 1.5f, 1.f}, + {0.f, 1.f, 2.f, 1.f}, {32.f, 8.f, 16.f, 1.f}}}; + const auto target = TextureGetRenderTargetFormat( + Prospero::ChannelLayout::k10_11_11, Prospero::ChannelType::kFloat, + Prospero::ChannelOrder::kReversed); + Require(name, "10_11_11 reversed render target", + target.format == vk::Format::eB10G11R11UfloatPack32 && + target.guest_format == Prospero::BufferFormat::k10_11_11Float && + target.export_mapping == Prospero::ColorMappingRgba, + "PPSA24156 reversed packed-float target lost its physical encoding"); + std::memset(mapped, 0, allocation_size); + std::array offsets{0, 4, 8, 12}; + if (tile == Prospero::TileMode::kRenderTarget) { + TileBlockLayout block{}; + u32 block_xor = 0; + Require(name, "10_11_11 tile layout", + TileGetBlockLayout(TileBlockFamily::RenderTarget64KB, 4, block) && + TileGetBlockXor(block, 0, 0, 0, block_xor), + "packed-float render-target layout is unavailable"); + for (u32 pixel = 0; pixel < words.size(); ++pixel) { + Require(name, "10_11_11 tile offset", + TileGetBlockOffset(block, pixel, 0, 0, offsets[pixel]), + "packed-float texel offset is unavailable"); + offsets[pixel] ^= block_xor; + } + } + for (u32 pixel = 0; pixel < words.size(); ++pixel) { + std::memcpy(static_cast(mapped) + offsets[pixel], &words[pixel], sizeof(u32)); + } + RenderContext context(m_runtime_context); + auto &scheduler = context.GetCommandScheduler(); + HW::Context registers{}; + HW::UserConfig user_config{}; + HW::Shader shaders{}; + scheduler.Begin(registers, user_config, shaders); + context.MapMemory(base, allocation_size); + auto &cache = context.GetTextureCache(); + auto &executor = context.GetRenderExecutor(); + constexpr u32 swizzle = DstSel(4, 5, 6, 1); + ShaderTextureResource descriptor{{static_cast(base >> 8u), + (static_cast(Prospero::BufferFormat::k10_11_11Float) << 20u) | + (3u << 30u), 0, + swizzle | (static_cast(tile) << 20u) | + (static_cast(Prospero::ImageType::kColor2D) << 28u), + 0, 0x00700000u, 0, 0}}; + TestCase test; + test.name = name; + test.has_user_data = true; + test.image_descriptor_swizzle = swizzle; + std::copy_n(descriptor.fields, 8, test.user_data.begin()); + test.user_data[50] = 16 * sizeof(u32); + for (u32 pixel = 0; pixel < words.size(); ++pixel) { + AppendVMovU32(&test.code, 20, pixel); + AppendVMovU32(&test.code, 21, 0); + test.code.push_back(EncodeMimg0(0x00, 0xf)); + test.code.push_back(EncodeMimg1(0, 20)); + for (u32 channel = 0; channel < 4; ++channel) { + AppendStoreVgpr(&test.code, channel, pixel * 4 + channel); + test.expected.push_back(std::bit_cast(channels[pixel][channel])); + } + } + AppendEnd(&test.code); + const auto compiled = CompileCase(test, SubgroupSize()); + ShaderRecompiler::IR::DescriptorValue value{}; + value.dword_count = 8; + std::copy_n(descriptor.fields, 8, value.dwords.begin()); + const auto binding = RenderExecutorTestAccess::ResolveTexture( + executor, compiled.program.info.images.at(0), value); + Image sampled; + sampled.view = cache.FindTexture(binding.image_id, binding.desc); + sampled.layout = cache.GetImage(binding.image_id).backing.state.layout; + scheduler.Finish(); + auto output = CreateStorageBuffer(name, {}, 16); + Dispatch(test, compiled, output, nullptr, &sampled); + Require(name, "10_11_11 uploaded components", + ReadBuffer(name, output, 16) == test.expected, + "packed-float upload or sample changed a component's width or order"); + DestroyBuffer(&output); + + const auto clear = [&](std::array channels) { + auto &native = cache.GetImage(binding.image_id); + native.Transit(vk::ImageLayout::eTransferDstOptimal, + vk::AccessFlagBits2::eTransferWrite, {}, + scheduler.Current().Handle()); + const vk::ImageSubresourceRange range{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}; + scheduler.Current().Handle().clearColorImage(native.backing.image, + vk::ImageLayout::eTransferDstOptimal, vk::ClearColorValue{channels}, range); + cache.MarkGpuWritten(binding.image_id); + }; + clear({8.f, 4.f, 2.f, 1.f}); + Require(name, "10_11_11 guest download", + TextureCacheTestAccess::TryDownload(cache, binding.image_id), + "packed-float native contents could not be downloaded"); + scheduler.Finish(); + scheduler.DrainPriorityOperations(); + std::array guest{}; + for (u32 pixel = 0; pixel < words.size(); ++pixel) { + Require(name, "10_11_11 guest texel", + Libs::LibKernel::Memory::TryReadBacking(base + offsets[pixel], + &guest[pixel], sizeof(u32)), + "packed-float guest texel is inaccessible"); + } + Require(name, "10_11_11 exact guest words", + guest == std::array{0x90110200u, 0x90110200u, 0x90110200u, 0x90110200u}, + "native packed-float write lost the guest 10/11/11 bit layout"); + + clear({16.f, 8.f, 4.f, 1.f}); + auto alias_desc = binding.desc; + alias_desc.info.guest_format = Prospero::BufferFormat::k11_11_10Float; + alias_desc.view_info.mapping = {}; + Require(name, "same-format transform compatibility", + !alias_desc.info.IsCompatible(binding.desc.info), + "equal Vulkan formats hid incompatible guest color transforms"); + const auto alias = cache.FindImage(alias_desc); + (void)cache.FindTexture(alias, alias_desc); + Require(name, "same-format transformed alias", + alias != binding.image_id && + ReadCachedTexel(name, context, alias, {}, {4, 1, 1}) == + std::vector(4, 0x98120220u), + "packed-float alias reused a transformed image or bypassed guest bytes"); + RenderExecutorTestAccess::ResetBindings(executor); + context.UnmapMemory(base, allocation_size); + scheduler.Finish(); + } Require(name, "release", Libs::LibKernel::Memory::KernelMunmap(base, allocation_size) == 0 && Libs::LibKernel::Memory::KernelReleaseDirectMemory( @@ -15980,13 +16124,16 @@ public: }; // Keep all four conversions in flight to check growth, overwrite ordering, // the requested span on reuse, and two simultaneously live workspaces. - const auto first = tile_manager.SwapBgra16({scratch_input.buffer, 0, 8}); + const auto first = tile_manager.TransformColor( + {scratch_input.buffer, 0, 8}, ColorTransform::SwapBgra16, true); copy_scratch(first, 0); - const auto grown = tile_manager.SwapBgra16({scratch_input.buffer, 0, 32}); + const auto grown = tile_manager.TransformColor( + {scratch_input.buffer, 0, 32}, ColorTransform::SwapBgra16, true); copy_scratch(grown, 8); - const auto reused = tile_manager.SwapBgra16({scratch_input.buffer, 8, 8}); + const auto reused = tile_manager.TransformColor( + {scratch_input.buffer, 8, 8}, ColorTransform::SwapBgra16, true); copy_scratch(reused, 40); - const auto paired = tile_manager.SwapBgra16(reused); + const auto paired = tile_manager.TransformColor(reused, ColorTransform::SwapBgra16, true); copy_scratch(paired, 48); Require(name, "scratch workspace reuse", first.buffer != grown.buffer && grown.buffer == reused.buffer && @@ -35223,7 +35370,9 @@ void CheckImageTransitionState(RenderContext &renderer) { buffered_upload_offset, buffered_expected.size()); Buffer copy_scratch(context, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags, 16); - buffered_destination.CopyImageWithBuffer(buffered_source, copy_scratch); + StreamBuffer copy_parameters(context, scheduler, MemoryUsage::Stream, 4096); + TileManager copy_tiler(context, scheduler, copy_parameters); + buffered_destination.CopyImageWithBuffer(buffered_source, copy_scratch, copy_tiler); const auto [buffered_download_data, buffered_download_offset] = download.Map(buffered_expected.size(), 4); Require(name, "buffered-copy download map", buffered_download_data != nullptr, @@ -38898,6 +39047,7 @@ int main(int argc, char **argv) { if (argc == 2 && std::strcmp(argv[1], "--packed-texture-only") == 0) { VulkanHarness vulkan; vulkan.CheckPackedTextureComponents(); + vulkan.CheckBgra16Readback(); RunCase(&vulkan, ImageLoadPackedUintUnpacksAndSwizzles()); RunCase(&vulkan, ImageSamplePackedUintConvertsSampleAndGather()); return 0; @@ -39374,6 +39524,11 @@ int main(int argc, char **argv) { vulkan.CheckRenderExecutorColor1DArrayDiscovery(); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--image-transition-only") == 0) { + VulkanHarness vulkan; + CheckImageTransitionState(vulkan.RuntimeRenderer()); + return 0; + } #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS if (argc == 2 && std::strcmp(argv[1], "--reverse-rt-death") == 0) { RunReverseRenderTargetDeathCase(); @@ -39396,11 +39551,6 @@ int main(int argc, char **argv) { CheckSampledColorViews(); return 0; } - if (argc == 2 && std::strcmp(argv[1], "--image-transition-only") == 0) { - VulkanHarness vulkan; - CheckImageTransitionState(vulkan.RuntimeRenderer()); - return 0; - } if (argc == 2 && std::strcmp(argv[1], "--storage-bgra-only") == 0) { CheckSampledColorViews(); CheckBasicStorageTextureDescriptor();