graphics: simplify render target format and color mapping resolution

This commit is contained in:
nmzik
2026-08-11 10:13:19 +02:00
parent ca7c8f0074
commit 11b95bed20
6 changed files with 355 additions and 160 deletions
+30 -8
View File
@@ -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<uint8_t>(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,
+87
View File
@@ -3,6 +3,7 @@
#include "graphics/guest_gpu/gpu_defs.h"
#include <array>
#include <bit>
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<uint8_t>(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<const FormatInfo*, kMaxFormat + 1> 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<ChannelType>(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<uint32_t>(layout_info->linear_type_mask));
format = static_cast<BufferFormat>(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;
+35 -7
View File
@@ -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
@@ -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<Prospero::ChannelLayout>(raw_layout);
const auto type = static_cast<Prospero::ChannelType>(raw_type);
const auto order = static_cast<Prospero::ChannelOrder>(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<Prospero::ChannelOrder>(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,
+22 -2
View File
@@ -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
+131 -5
View File
@@ -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<Prospero::ColorComponentMapping, 4> 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<ShaderRecompiler::IR::Program>();
std::array<Prospero::ColorComponentMapping, 8> mappings {};
mappings[0] = format.export_mapping;
mappings[0] = gr32.export_mapping;
ShaderPixelInputInfo compiled_info {};
std::span<const uint32_t> 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();