diff --git a/src/graphics/guest_gpu/gpu_defs.h b/src/graphics/guest_gpu/gpu_defs.h index ea4685f2b..1b7f1b164 100644 --- a/src/graphics/guest_gpu/gpu_defs.h +++ b/src/graphics/guest_gpu/gpu_defs.h @@ -115,32 +115,52 @@ enum class ChannelOrder : uint32_t { kAltReversed = 3, }; -// Maps a shader's logical RGBA export to the host color-attachment components. Two bits per -// component keep this cheap to carry in shader and pipeline keys. +// Selects the logical shader component written to each physical color-attachment component. +// Two bits per component keep this cheap to carry in shader and pipeline keys. struct ColorComponentMapping { - uint8_t packed = 0xe4u; // RGBA: 0, 1, 2, 3 + static constexpr uint8_t kIdentity = 0xe4u; + + uint8_t packed = kIdentity; [[nodiscard]] constexpr uint32_t Map(uint32_t component) const { return component < 4u ? (packed >> (component * 2u)) & 0x3u : component; } - [[nodiscard]] constexpr uint32_t ApplyMask(uint32_t mask) const { + [[nodiscard]] constexpr uint32_t ApplyMask(uint32_t logical_mask) const { uint32_t mapped = 0; - for (uint32_t component = 0; component < 4u; component++) { - mapped |= ((mask >> component) & 1u) << Map(component); + for (uint32_t physical_component = 0; physical_component < 4u; physical_component++) { + mapped |= ((logical_mask >> Map(physical_component)) & 1u) << physical_component; } return mapped; } - [[nodiscard]] constexpr bool IsIdentity() const { return packed == 0xe4u; } + // This mapping selects an intermediate component; next selects the final logical component. + [[nodiscard]] constexpr ColorComponentMapping Then(ColorComponentMapping next) const { + ColorComponentMapping result {0u}; + for (uint32_t physical_component = 0; physical_component < 4u; physical_component++) { + result.packed |= static_cast(next.Map(Map(physical_component)) + << (physical_component * 2u)); + } + return result; + } + + [[nodiscard]] constexpr bool IsIdentity() const { return packed == kIdentity; } bool operator==(const ColorComponentMapping&) const = default; }; static_assert(sizeof(ColorComponentMapping) == sizeof(uint8_t)); -inline constexpr ColorComponentMapping ColorMappingAbgr {0x1bu}; // 3, 2, 1, 0 +inline constexpr ColorComponentMapping ColorMappingRgba {ColorComponentMapping::kIdentity}; +inline constexpr ColorComponentMapping ColorMappingGr {0xe1u}; // 1, 0, 2, 3 +inline constexpr ColorComponentMapping ColorMappingRabg {0x6cu}; // 0, 3, 2, 1 +inline constexpr ColorComponentMapping ColorMappingRgab {0xb4u}; // 0, 1, 3, 2 inline constexpr ColorComponentMapping ColorMappingBgra {0xc6u}; // 2, 1, 0, 3 +inline constexpr ColorComponentMapping ColorMappingAbgr {0x1bu}; // 3, 2, 1, 0 +inline constexpr ColorComponentMapping ColorMappingAgba {0x27u}; // 3, 1, 2, 0 +inline constexpr ColorComponentMapping ColorMappingArbg {0x63u}; // 3, 0, 2, 1 +inline constexpr ColorComponentMapping ColorMappingAgbr {0x87u}; // 3, 1, 0, 2 +inline constexpr ColorComponentMapping ColorMappingArgb {0x93u}; // 3, 0, 1, 2 enum class DepthFormat : uint32_t { kInvalid = 0, @@ -388,9 +408,11 @@ enum class BufferFormat : uint32_t { k8Srgb = 128, k8_8Srgb = 129, k8_8_8_8Srgb = 130, + k10_10_10_2Float = 131, k9_9_9_5Float = 132, k5_6_5UNorm = 133, k5_5_5_1UNorm = 134, + k1_5_5_5UNorm = 135, k4_4_4_4UNorm = 136, kFmask8_S4_F4 = 161, kBc1UNorm = 169, diff --git a/src/graphics/guest_gpu/gpu_format.cpp b/src/graphics/guest_gpu/gpu_format.cpp index f7f824c92..507b06f2a 100644 --- a/src/graphics/guest_gpu/gpu_format.cpp +++ b/src/graphics/guest_gpu/gpu_format.cpp @@ -3,6 +3,7 @@ #include "graphics/guest_gpu/gpu_defs.h" #include +#include namespace Libs::Graphics::Prospero { namespace { @@ -64,6 +65,7 @@ constexpr FormatInfo kFormatInfo[] = { {GpuEnumValue(BufferFormat::k9_9_9_5Float), 4, 0, 0, true, false}, {GpuEnumValue(BufferFormat::k5_6_5UNorm), 2, 0, 2, true, false}, {GpuEnumValue(BufferFormat::k5_5_5_1UNorm), 2, 0, 2, true, false}, + {GpuEnumValue(BufferFormat::k1_5_5_5UNorm), 0, 0, 2, false, false}, {GpuEnumValue(BufferFormat::k4_4_4_4UNorm), 2, 0, 2, true, false}, {GpuEnumValue(BufferFormat::kFmask8_S4_F4), 1, 0, 1, true, false}, {GpuEnumValue(BufferFormat::kBc1UNorm), 0, 8, 0, true, false}, @@ -82,6 +84,68 @@ constexpr FormatInfo kFormatInfo[] = { {GpuEnumValue(BufferFormat::kBc7Srgb), 0, 16, 0, true, false}, }; +constexpr uint8_t RenderTargetTypeBit(ChannelType type) { + return static_cast(1u << GpuEnumValue(type)); +} + +constexpr auto kUNormType = RenderTargetTypeBit(ChannelType::kUNorm); +constexpr auto kIntegerTypes = + RenderTargetTypeBit(ChannelType::kUInt) | RenderTargetTypeBit(ChannelType::kSInt); +constexpr auto kNormalizedIntegerTypes = + kUNormType | RenderTargetTypeBit(ChannelType::kSNorm) | kIntegerTypes; + +struct RenderTargetLayoutInfo { + ChannelLayout layout; + BufferFormat first_linear_format; + uint8_t linear_type_mask; + uint8_t components; + BufferFormat extra_format = BufferFormat::kInvalid; + ChannelType extra_type = ChannelType::kUNorm; + ChannelOrderSupport order_support = ChannelOrderSupport::kAll; +}; + +constexpr RenderTargetLayoutInfo kRenderTargetLayouts[] = { + {ChannelLayout::k8, BufferFormat::k8UNorm, kNormalizedIntegerTypes, 1, BufferFormat::k8Srgb, + ChannelType::kSrgb}, + {ChannelLayout::k16, BufferFormat::k16UNorm, kNormalizedIntegerTypes, 1, BufferFormat::k16Float, + ChannelType::kFloat}, + {ChannelLayout::k8_8, BufferFormat::k8_8UNorm, kNormalizedIntegerTypes, 2, + BufferFormat::k8_8Srgb, ChannelType::kSrgb}, + {ChannelLayout::k32, BufferFormat::k32UInt, kIntegerTypes, 1, BufferFormat::k32Float, + ChannelType::kFloat}, + {ChannelLayout::k16_16, BufferFormat::k16_16UNorm, kNormalizedIntegerTypes, 2, + BufferFormat::k16_16Float, ChannelType::kFloat}, + {ChannelLayout::k11_11_10, BufferFormat::k11_11_10UNorm, kNormalizedIntegerTypes, 3, + BufferFormat::k11_11_10Float, ChannelType::kFloat}, + {ChannelLayout::k10_11_11, BufferFormat::k10_11_11UNorm, kNormalizedIntegerTypes, 3, + BufferFormat::k10_11_11Float, ChannelType::kFloat}, + {ChannelLayout::k2_10_10_10, BufferFormat::k2_10_10_10UNorm, kNormalizedIntegerTypes, 4}, + {ChannelLayout::k10_10_10_2, BufferFormat::k10_10_10_2UNorm, kNormalizedIntegerTypes, 4}, + {ChannelLayout::k8_8_8_8, BufferFormat::k8_8_8_8UNorm, kNormalizedIntegerTypes, 4, + BufferFormat::k8_8_8_8Srgb, ChannelType::kSrgb}, + {ChannelLayout::k32_32, BufferFormat::k32_32UInt, kIntegerTypes, 2, BufferFormat::k32_32Float, + ChannelType::kFloat}, + {ChannelLayout::k16_16_16_16, BufferFormat::k16_16_16_16UNorm, kNormalizedIntegerTypes, 4, + BufferFormat::k16_16_16_16Float, ChannelType::kFloat}, + {ChannelLayout::k32_32_32_32, BufferFormat::k32_32_32_32UInt, kIntegerTypes, 4, + BufferFormat::k32_32_32_32Float, ChannelType::kFloat}, + {ChannelLayout::k5_6_5, BufferFormat::k5_6_5UNorm, kUNormType, 3}, + {ChannelLayout::k5_5_5_1, BufferFormat::k5_5_5_1UNorm, kUNormType, 4}, + {ChannelLayout::k1_5_5_5, BufferFormat::k1_5_5_5UNorm, kUNormType, 4}, + {ChannelLayout::k4_4_4_4, BufferFormat::k4_4_4_4UNorm, kUNormType, 4}, + {ChannelLayout::k10_10_10_2Float, BufferFormat::kInvalid, 0, 4, BufferFormat::k10_10_10_2Float, + ChannelType::kFloat, ChannelOrderSupport::kStandardOnly}, +}; + +const RenderTargetLayoutInfo* FindRenderTargetLayout(uint32_t layout) { + for (const auto& info: kRenderTargetLayouts) { + if (GpuEnumValue(info.layout) == layout) { + return &info; + } + } + return nullptr; +} + constexpr auto MakeFormatInfoLookup() { constexpr uint32_t kMaxFormat = GpuEnumValue(BufferFormat::kBc7Srgb); std::array lookup {}; @@ -99,6 +163,29 @@ const FormatInfo* FindFormatInfo(uint32_t format) { } // namespace +RenderTargetFormatEncoding ResolveRenderTargetFormat(uint32_t layout, uint32_t type) { + const auto* layout_info = FindRenderTargetLayout(layout); + if (layout_info == nullptr || type >= 8u) { + return {}; + } + + const auto channel_type = static_cast(type); + auto format = BufferFormat::kInvalid; + if (layout_info->extra_format != BufferFormat::kInvalid && + channel_type == layout_info->extra_type) { + format = layout_info->extra_format; + } else if ((layout_info->linear_type_mask & (1u << type)) != 0) { + const auto first_type = + std::countr_zero(static_cast(layout_info->linear_type_mask)); + format = static_cast(GpuEnumValue(layout_info->first_linear_format) + type - + first_type); + } + if (format == BufferFormat::kInvalid) { + return {}; + } + return {format, layout_info->components, layout_info->order_support}; +} + uint32_t NumBytesPerElement(uint32_t format) { const auto* info = FindFormatInfo(format); return info != nullptr ? info->bytes_per_element : 0; diff --git a/src/graphics/guest_gpu/gpu_format.h b/src/graphics/guest_gpu/gpu_format.h index 208c19b3f..7533e0116 100644 --- a/src/graphics/guest_gpu/gpu_format.h +++ b/src/graphics/guest_gpu/gpu_format.h @@ -1,16 +1,44 @@ #ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GUEST_GPU_GPU_FORMAT_H_ #define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_GUEST_GPU_GPU_FORMAT_H_ -#include "common/common.h" +#include "graphics/guest_gpu/gpu_defs.h" namespace Libs::Graphics::Prospero { -uint32_t NumBytesPerElement(uint32_t format); -uint32_t BlockCompressedBytesPerBlock(uint32_t format); -uint32_t RenderTargetBytesPerElement(uint32_t format); -bool IsSupportedTextureFormat(uint32_t format); -bool IsUintTextureFormat(uint32_t format); -bool IsFmaskTextureFormat(uint32_t format); +enum class ChannelOrderSupport : uint8_t { + kNone, + kStandardOnly, + kAll, +}; + +struct RenderTargetFormatEncoding { + BufferFormat buffer_format = BufferFormat::kInvalid; + uint8_t components = 0; + ChannelOrderSupport order_support = ChannelOrderSupport::kNone; + + [[nodiscard]] constexpr bool IsValid() const { + return buffer_format != BufferFormat::kInvalid && components >= 1u && components <= 4u && + order_support != ChannelOrderSupport::kNone; + } + + [[nodiscard]] constexpr bool SupportsOrder(uint32_t raw_order) const { + switch (order_support) { + case ChannelOrderSupport::kStandardOnly: + return raw_order == GpuEnumValue(ChannelOrder::kStandard); + case ChannelOrderSupport::kAll: + return raw_order <= GpuEnumValue(ChannelOrder::kAltReversed); + default: return false; + } + } +}; + +RenderTargetFormatEncoding ResolveRenderTargetFormat(uint32_t layout, uint32_t type); +uint32_t NumBytesPerElement(uint32_t format); +uint32_t BlockCompressedBytesPerBlock(uint32_t format); +uint32_t RenderTargetBytesPerElement(uint32_t format); +bool IsSupportedTextureFormat(uint32_t format); +bool IsUintTextureFormat(uint32_t format); +bool IsFmaskTextureFormat(uint32_t format); } // namespace Libs::Graphics::Prospero diff --git a/src/graphics/host_gpu/renderer/image/textureCommon.cpp b/src/graphics/host_gpu/renderer/image/textureCommon.cpp index 3d318936c..61197ef9a 100644 --- a/src/graphics/host_gpu/renderer/image/textureCommon.cpp +++ b/src/graphics/host_gpu/renderer/image/textureCommon.cpp @@ -14,152 +14,64 @@ namespace Libs::Graphics { namespace { -struct RenderTargetFormatMapping { - Prospero::ChannelLayout layout; - Prospero::ChannelType type; - Prospero::ChannelOrder order; - RenderTargetFormatInfo info; +// Rows are channel order; columns are the number of physical components minus one. Unused +// selectors complete each entry to a permutation so logical write masks can be inverted. +constexpr Prospero::ColorComponentMapping kRenderTargetColorMappings[4][4] = { + {Prospero::ColorMappingRgba, Prospero::ColorMappingRgba, Prospero::ColorMappingRgba, + Prospero::ColorMappingRgba}, + {Prospero::ColorMappingGr, Prospero::ColorMappingRabg, Prospero::ColorMappingRgab, + Prospero::ColorMappingBgra}, + {Prospero::ColorMappingBgra, Prospero::ColorMappingGr, Prospero::ColorMappingBgra, + Prospero::ColorMappingAbgr}, + {Prospero::ColorMappingAgba, Prospero::ColorMappingArbg, Prospero::ColorMappingAgbr, + Prospero::ColorMappingArgb}, }; -constexpr RenderTargetFormatMapping kRenderTargetFormats[] = { - {Prospero::ChannelLayout::k8, - Prospero::ChannelType::kUInt, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR8Uint, 1}}, - {Prospero::ChannelLayout::k8_8, - Prospero::ChannelType::kUNorm, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR8G8Unorm, 2}}, - {Prospero::ChannelLayout::k8_8_8_8, - Prospero::ChannelType::kUNorm, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR8G8B8A8Unorm, 4}}, - {Prospero::ChannelLayout::k8_8_8_8, - Prospero::ChannelType::kSNorm, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR8G8B8A8Snorm, 4}}, - {Prospero::ChannelLayout::k8_8_8_8, - Prospero::ChannelType::kSrgb, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR8G8B8A8Srgb, 4}}, - {Prospero::ChannelLayout::k8_8_8_8, - Prospero::ChannelType::kUNorm, - Prospero::ChannelOrder::kAlt, - {vk::Format::eB8G8R8A8Unorm, 4}}, - {Prospero::ChannelLayout::k8_8_8_8, - Prospero::ChannelType::kSNorm, - Prospero::ChannelOrder::kAlt, - {vk::Format::eB8G8R8A8Snorm, 4}}, - {Prospero::ChannelLayout::k8_8_8_8, - Prospero::ChannelType::kSrgb, - Prospero::ChannelOrder::kAlt, - {vk::Format::eB8G8R8A8Srgb, 4}}, - {Prospero::ChannelLayout::k5_5_5_1, - Prospero::ChannelType::kUNorm, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR5G5B5A1UnormPack16, 2}}, - {Prospero::ChannelLayout::k4_4_4_4, - Prospero::ChannelType::kUNorm, - Prospero::ChannelOrder::kReversed, - {vk::Format::eB4G4R4A4UnormPack16, 2}}, - {Prospero::ChannelLayout::k10_10_10_2, - Prospero::ChannelType::kUNorm, - Prospero::ChannelOrder::kStandard, - {vk::Format::eA2B10G10R10UnormPack32, 4}}, - {Prospero::ChannelLayout::k10_10_10_2, - Prospero::ChannelType::kUNorm, - Prospero::ChannelOrder::kAlt, - {vk::Format::eA2R10G10B10UnormPack32, 4}}, - {Prospero::ChannelLayout::k11_11_10, - Prospero::ChannelType::kFloat, - Prospero::ChannelOrder::kStandard, - {vk::Format::eB10G11R11UfloatPack32, 4}}, - {Prospero::ChannelLayout::k5_6_5, - Prospero::ChannelType::kUNorm, - Prospero::ChannelOrder::kStandard, - {vk::Format::eB5G6R5UnormPack16, 2}}, - {Prospero::ChannelLayout::k16, - Prospero::ChannelType::kUNorm, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR16Unorm, 2}}, - {Prospero::ChannelLayout::k16, - Prospero::ChannelType::kUInt, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR16Uint, 2}}, - {Prospero::ChannelLayout::k16, - Prospero::ChannelType::kFloat, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR16Sfloat, 2}}, - {Prospero::ChannelLayout::k16_16, - Prospero::ChannelType::kUNorm, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR16G16Unorm, 4}}, - {Prospero::ChannelLayout::k16_16, - Prospero::ChannelType::kSNorm, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR16G16Snorm, 4}}, - {Prospero::ChannelLayout::k16_16, - Prospero::ChannelType::kUInt, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR16G16Uint, 4}}, - {Prospero::ChannelLayout::k16_16, - Prospero::ChannelType::kFloat, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR16G16Sfloat, 4}}, - {Prospero::ChannelLayout::k16_16_16_16, - Prospero::ChannelType::kUNorm, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR16G16B16A16Unorm, 8}}, - {Prospero::ChannelLayout::k16_16_16_16, - Prospero::ChannelType::kUInt, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR16G16B16A16Uint, 8}}, - {Prospero::ChannelLayout::k16_16_16_16, - Prospero::ChannelType::kFloat, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR16G16B16A16Sfloat, 8}}, - {Prospero::ChannelLayout::k16_16_16_16, - Prospero::ChannelType::kFloat, - Prospero::ChannelOrder::kAlt, - {vk::Format::eR16G16B16A16Sfloat, 8, Prospero::ColorMappingBgra}}, - {Prospero::ChannelLayout::k16_16_16_16, - Prospero::ChannelType::kFloat, - Prospero::ChannelOrder::kReversed, - {vk::Format::eR16G16B16A16Sfloat, 8, Prospero::ColorMappingAbgr}}, - {Prospero::ChannelLayout::k32, - Prospero::ChannelType::kFloat, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR32Sfloat, 4}}, - {Prospero::ChannelLayout::k32_32, - Prospero::ChannelType::kUInt, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR32G32Uint, 8}}, - {Prospero::ChannelLayout::k32_32, - Prospero::ChannelType::kFloat, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR32G32Sfloat, 8}}, - {Prospero::ChannelLayout::k32_32_32_32, - Prospero::ChannelType::kFloat, - Prospero::ChannelOrder::kStandard, - {vk::Format::eR32G32B32A32Sfloat, 16}}, +struct RenderTargetHostFormatInfo { + vk::Format format = vk::Format::eUndefined; + Prospero::ColorComponentMapping host_to_storage; }; +RenderTargetHostFormatInfo ResolveRenderTargetHostFormat(Prospero::BufferFormat guest_format, + Prospero::ChannelOrder order) { + if (order == Prospero::ChannelOrder::kAlt) { + switch (guest_format) { + case Prospero::BufferFormat::k8_8_8_8UNorm: + return {vk::Format::eB8G8R8A8Unorm, Prospero::ColorMappingBgra}; + case Prospero::BufferFormat::k8_8_8_8SNorm: + return {vk::Format::eB8G8R8A8Snorm, Prospero::ColorMappingBgra}; + case Prospero::BufferFormat::k8_8_8_8Srgb: + return {vk::Format::eB8G8R8A8Srgb, Prospero::ColorMappingBgra}; + case Prospero::BufferFormat::k10_10_10_2UNorm: + return {vk::Format::eA2R10G10B10UnormPack32, Prospero::ColorMappingBgra}; + default: break; + } + } + switch (guest_format) { + case Prospero::BufferFormat::k5_5_5_1UNorm: + return {vk::Format::eA1R5G5B5UnormPack16, Prospero::ColorMappingBgra}; + case Prospero::BufferFormat::k1_5_5_5UNorm: + return {vk::Format::eR5G5B5A1UnormPack16, Prospero::ColorMappingAbgr}; + case Prospero::BufferFormat::k4_4_4_4UNorm: + return {vk::Format::eR4G4B4A4UnormPack16, Prospero::ColorMappingAbgr}; + default: return {VulkanFormat(Prospero::GpuEnumValue(guest_format)), {}}; + } +} + } // namespace -// TODO: cleanup! RenderTargetFormatInfo TextureGetRenderTargetFormat(uint32_t raw_layout, uint32_t raw_type, uint32_t raw_order) { - const auto layout = static_cast(raw_layout); - const auto type = static_cast(raw_type); - const auto order = static_cast(raw_order); - - if (layout == Prospero::ChannelLayout::k8 && type == Prospero::ChannelType::kUNorm && - raw_order <= Prospero::GpuEnumValue(Prospero::ChannelOrder::kAltReversed)) { - return {vk::Format::eR8Unorm, 1}; - } - for (const auto& mapping: kRenderTargetFormats) { - if (mapping.layout == layout && mapping.type == type && mapping.order == order) { - return mapping.info; + const auto encoding = Prospero::ResolveRenderTargetFormat(raw_layout, raw_type); + if (encoding.IsValid() && encoding.SupportsOrder(raw_order)) { + const auto order = static_cast(raw_order); + const auto host = ResolveRenderTargetHostFormat(encoding.buffer_format, order); + const auto bytes = + Prospero::RenderTargetBytesPerElement(Prospero::GpuEnumValue(encoding.buffer_format)); + if (host.format != vk::Format::eUndefined && bytes != 0) { + const auto order_mapping = + kRenderTargetColorMappings[raw_order][encoding.components - 1u]; + return {host.format, bytes, host.host_to_storage.Then(order_mapping)}; } } EXIT("unsupported render-target format combination: layout=%u type=%u order=%u\n", raw_layout, diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index efad67616..6d8721323 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -15970,7 +15970,7 @@ void CheckEmbeddedFetchVertexOffset() { } [[noreturn]] void RunReverseRenderTargetDeathCase() { - (void)TextureGetRenderTargetFormat(12u, 7u, 3u); + (void)TextureGetRenderTargetFormat(12u, 6u, 0u); std::_Exit(0x7f); } @@ -16010,6 +16010,26 @@ void CheckRenderTargetFormatContract() { format.export_mapping.ApplyMask(0xfu) == 0xfu, "reverse RGBA16F component mask was not mapped exactly once"); + const auto rg32 = TextureGetRenderTargetFormat(11u, 7u, 2u); + Require("ReverseRenderTarget", "GR32 float", + rg32.format == vk::Format::eR32G32Sfloat && rg32.bytes_per_element == 8u && + rg32.export_mapping == Prospero::ColorMappingGr && + rg32.export_mapping.ApplyMask(0x1u) == 0x2u && + rg32.export_mapping.ApplyMask(0x2u) == 0x1u && + rg32.export_mapping.ApplyMask(0x3u) == 0x3u && + rg32.export_mapping.ApplyMask(0xfu) == 0xfu, + "reverse GR32F render-target export or write-mask mapping is incorrect"); + + const auto argb = TextureGetRenderTargetFormat(12u, 7u, 3u); + Require("ReverseRenderTarget", "ARGB float", + argb.format == vk::Format::eR16G16B16A16Sfloat && + argb.export_mapping == Prospero::ColorMappingArgb && + argb.export_mapping.ApplyMask(0x1u) == 0x2u && + argb.export_mapping.ApplyMask(0x2u) == 0x4u && + argb.export_mapping.ApplyMask(0x4u) == 0x8u && + argb.export_mapping.ApplyMask(0x8u) == 0x1u, + "alternate-reversed render-target mapping did not invert its ARGB cycle"); + char path[MAX_PATH] {}; Require("ReverseRenderTarget", "host", GetModuleFileNameA(nullptr, path, MAX_PATH) != 0, "GetModuleFileName failed"); @@ -16030,7 +16050,7 @@ void CheckRenderTargetFormatContract() { CloseHandle(process.hThread); CloseHandle(process.hProcess); Require("ReverseRenderTarget", "hard failure", exited && exit_code == 321, - "adjacent unproven render-target tuple did not retain the fatal guard"); + "invalid render-target tuple did not retain the fatal guard"); std::printf("[host] %-32s ok\n", "RenderTargetFormat"); } #endif diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 992d984e6..ff3978cbc 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -6484,7 +6484,7 @@ void TestNewShaderRecompilerExpPixelOutputs() { CheckSpirvBinaryValidates(uint16_result.spirv); } -void TestRenderTargetReverseFloat16ExportMapping() { +void TestRenderTargetReverseExportMapping() { const auto format = TextureGetRenderTargetFormat(Prospero::GpuEnumValue(Prospero::ChannelLayout::k16_16_16_16), Prospero::GpuEnumValue(Prospero::ChannelType::kFloat), @@ -6506,6 +6506,116 @@ void TestRenderTargetReverseFloat16ExportMapping() { Prospero::GpuEnumValue(Prospero::ChannelOrder::kAlt)); Check(legacy_alt.format == vk::Format::eB8G8R8A8Unorm && legacy_alt.export_mapping.IsIdentity(), "legacy BGRA render target acquired a duplicate shader export mapping"); + const auto gr32 = + TextureGetRenderTargetFormat(Prospero::GpuEnumValue(Prospero::ChannelLayout::k32_32), + Prospero::GpuEnumValue(Prospero::ChannelType::kFloat), + Prospero::GpuEnumValue(Prospero::ChannelOrder::kReversed)); + Check(gr32.format == vk::Format::eR32G32Sfloat && gr32.bytes_per_element == 8u && + gr32.export_mapping == Prospero::ColorMappingGr, + "reverse GR32F render target did not retain its native host format, " + "size, and mapping"); + + struct MappingCase { + Prospero::ChannelLayout layout; + Prospero::ChannelType type; + vk::Format format; + uint32_t bytes; + std::array mappings; + }; + const MappingCase mapping_cases[] = { + {Prospero::ChannelLayout::k8, + Prospero::ChannelType::kUInt, + vk::Format::eR8Uint, + 1, + {Prospero::ColorMappingRgba, Prospero::ColorMappingGr, Prospero::ColorMappingBgra, + Prospero::ColorMappingAgba}}, + {Prospero::ChannelLayout::k32_32, + Prospero::ChannelType::kFloat, + vk::Format::eR32G32Sfloat, + 8, + {Prospero::ColorMappingRgba, Prospero::ColorMappingRabg, Prospero::ColorMappingGr, + Prospero::ColorMappingArbg}}, + {Prospero::ChannelLayout::k5_6_5, + Prospero::ChannelType::kUNorm, + vk::Format::eB5G6R5UnormPack16, + 2, + {Prospero::ColorMappingRgba, Prospero::ColorMappingRgab, Prospero::ColorMappingBgra, + Prospero::ColorMappingAgbr}}, + {Prospero::ChannelLayout::k16_16_16_16, + Prospero::ChannelType::kFloat, + vk::Format::eR16G16B16A16Sfloat, + 8, + {Prospero::ColorMappingRgba, Prospero::ColorMappingBgra, Prospero::ColorMappingAbgr, + Prospero::ColorMappingArgb}}, + }; + for (const auto& mapping_case: mapping_cases) { + for (uint32_t order = 0; order < 4u; order++) { + const auto info = + TextureGetRenderTargetFormat(Prospero::GpuEnumValue(mapping_case.layout), + Prospero::GpuEnumValue(mapping_case.type), order); + Check(info.format == mapping_case.format && + info.bytes_per_element == mapping_case.bytes && + info.export_mapping == mapping_case.mappings[order], + "render-target channel order did not use the component-count " + "mapping"); + for (uint32_t physical = 0; physical < 4u; physical++) { + const auto logical = info.export_mapping.Map(physical); + Check(info.export_mapping.ApplyMask(1u << logical) == (1u << physical), + "logical color write mask was not mapped to the inverse physical " + "component"); + } + } + } + + const auto alt_1010102 = + TextureGetRenderTargetFormat(Prospero::GpuEnumValue(Prospero::ChannelLayout::k10_10_10_2), + Prospero::GpuEnumValue(Prospero::ChannelType::kUNorm), + Prospero::GpuEnumValue(Prospero::ChannelOrder::kAlt)); + Check(alt_1010102.format == vk::Format::eA2R10G10B10UnormPack32 && + alt_1010102.export_mapping.IsIdentity(), + "native alternate 10:10:10:2 render target acquired a shader mapping"); + const auto reversed_4444 = + TextureGetRenderTargetFormat(Prospero::GpuEnumValue(Prospero::ChannelLayout::k4_4_4_4), + Prospero::GpuEnumValue(Prospero::ChannelType::kUNorm), + Prospero::GpuEnumValue(Prospero::ChannelOrder::kReversed)); + Check(reversed_4444.format == vk::Format::eR4G4B4A4UnormPack16 && + reversed_4444.export_mapping.IsIdentity(), + "reversed 4:4:4:4 render target did not compose storage and channel " + "order"); + const auto standard_4444 = + TextureGetRenderTargetFormat(Prospero::GpuEnumValue(Prospero::ChannelLayout::k4_4_4_4), + Prospero::GpuEnumValue(Prospero::ChannelType::kUNorm), + Prospero::GpuEnumValue(Prospero::ChannelOrder::kStandard)); + Check(standard_4444.format == vk::Format::eR4G4B4A4UnormPack16 && + standard_4444.export_mapping == Prospero::ColorMappingAbgr, + "standard 4:4:4:4 render target did not compensate host packed-bit " + "order"); + const auto standard_5551 = + TextureGetRenderTargetFormat(Prospero::GpuEnumValue(Prospero::ChannelLayout::k5_5_5_1), + Prospero::GpuEnumValue(Prospero::ChannelType::kUNorm), + Prospero::GpuEnumValue(Prospero::ChannelOrder::kStandard)); + Check(standard_5551.format == vk::Format::eA1R5G5B5UnormPack16 && + standard_5551.export_mapping == Prospero::ColorMappingBgra, + "5:5:5:1 render target did not preserve its high one-bit component"); + const auto standard_1555 = + TextureGetRenderTargetFormat(Prospero::GpuEnumValue(Prospero::ChannelLayout::k1_5_5_5), + Prospero::GpuEnumValue(Prospero::ChannelType::kUNorm), + Prospero::GpuEnumValue(Prospero::ChannelOrder::kStandard)); + Check(standard_1555.format == vk::Format::eR5G5B5A1UnormPack16 && + standard_1555.export_mapping == Prospero::ColorMappingAbgr, + "1:5:5:5 render target did not preserve its low one-bit component"); + + const auto argb = + TextureGetRenderTargetFormat(Prospero::GpuEnumValue(Prospero::ChannelLayout::k16_16_16_16), + Prospero::GpuEnumValue(Prospero::ChannelType::kFloat), + Prospero::GpuEnumValue(Prospero::ChannelOrder::kAltReversed)); + Check(argb.export_mapping == Prospero::ColorMappingArgb && + argb.export_mapping.ApplyMask(0x1u) == 0x2u && + argb.export_mapping.ApplyMask(0x2u) == 0x4u && + argb.export_mapping.ApplyMask(0x4u) == 0x8u && + argb.export_mapping.ApplyMask(0x8u) == 0x1u, + "alternate-reversed render-target mapping did not invert its " + "non-involutive cycle"); const uint32_t shader[] = { EncodeExp0(0x00, 0xf), @@ -6532,6 +6642,21 @@ void TestRenderTargetReverseFloat16ExportMapping() { "reverse MRT export did not emit a WZYX component shuffle"); CheckSpirvBinaryValidates(reversed_result.spirv); + reversed_info.target_export_mapping[0] = gr32.export_mapping; + ShaderRecompiler::CompileResult gr32_result; + Check(ShaderRecompiler::TryRecompile(shader, options, gr32_result, &error), error.c_str()); + Check(SpirvContainsVectorShuffle(gr32_result.spirv, {1u, 0u, 2u, 3u}), + "reverse GR32F MRT export did not emit a YXZW component shuffle"); + CheckSpirvBinaryValidates(gr32_result.spirv); + + reversed_info.target_export_mapping[0] = argb.export_mapping; + ShaderRecompiler::CompileResult argb_result; + Check(ShaderRecompiler::TryRecompile(shader, options, argb_result, &error), error.c_str()); + Check(SpirvContainsVectorShuffle(argb_result.spirv, {3u, 0u, 1u, 2u}), + "alternate-reversed MRT export did not emit an ARGB component shuffle"); + CheckSpirvBinaryValidates(argb_result.spirv); + reversed_info.target_export_mapping[0] = gr32.export_mapping; + HW::PixelShaderInfo regs {}; Check(ShaderGetIdPS(regs, identity_info, false) != ShaderGetIdPS(regs, reversed_info, false), "pixel shader cache identity omitted the render-target export mapping"); @@ -6545,17 +6670,18 @@ void TestRenderTargetReverseFloat16ExportMapping() { ShaderVertexInputInfo vs_info {}; vs_info.stage.program = std::make_shared(); std::array mappings {}; - mappings[0] = format.export_mapping; + mappings[0] = gr32.export_mapping; ShaderPixelInputInfo compiled_info {}; std::span compiled_spirv; Check(ShaderCompileInfoPS(regs, sh, ShaderLaneMaskMode::NativeWave, vs_info, mappings, compiled_info, compiled_spirv) && compiled_info.target_export_mapping[0].IsIdentity(), - "inactive reverse MRT mapping was not normalized out of the shader cache key"); + "inactive reverse MRT mapping was not normalized out of the shader " + "cache key"); sh.target_output_mode[0] = 4; Check(ShaderCompileInfoPS(regs, sh, ShaderLaneMaskMode::NativeWave, vs_info, mappings, compiled_info, compiled_spirv) && - compiled_info.target_export_mapping[0] == format.export_mapping, + compiled_info.target_export_mapping[0] == gr32.export_mapping, "active reverse MRT mapping was lost before shader specialization"); } @@ -7625,7 +7751,7 @@ int main() { TestNewShaderRecompilerVertexExportUsesLaneExecMask(); TestNewShaderRecompilerPerInvocationMasks(); TestNewShaderRecompilerExpPixelOutputs(); - TestRenderTargetReverseFloat16ExportMapping(); + TestRenderTargetReverseExportMapping(); TestNewShaderRecompilerEarlyZDisabledWhenPixelKillEnabled(); TestScalarProvenanceRealWideMoveLowering(); TestScalarProvenanceRealCarryAndScalarLoads();