mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
graphics: render packed 11-11-10 UNorm targets with exact rounding
Co-authored-by: fxpw <fxpw@outlook.com>
This commit is contained in:
@@ -42,7 +42,7 @@ constexpr FormatInfo kFormatInfo[] = {
|
||||
{BufferFormat::k16_16UInt, 4, 0, 4, true, true},
|
||||
{BufferFormat::k16_16SInt, 4, 0, 4, true, false, true},
|
||||
{BufferFormat::k16_16Float, 4, 0, 4, true, false},
|
||||
{BufferFormat::k11_11_10UNorm, 4, 0, 0, true, false},
|
||||
{BufferFormat::k11_11_10UNorm, 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},
|
||||
|
||||
@@ -64,7 +64,10 @@ RenderTargetFormatInfo TextureGetRenderTargetFormat(Prospero::ChannelLayout layo
|
||||
Prospero::ChannelOrder order) {
|
||||
const auto encoding = Prospero::ResolveRenderTargetFormat(layout, type);
|
||||
if (encoding.IsValid() && encoding.SupportsOrder(order)) {
|
||||
const auto host_format = ResolveHostFormat(encoding.buffer_format, order);
|
||||
const auto backing_format = encoding.buffer_format == Prospero::BufferFormat::k11_11_10UNorm
|
||||
? Prospero::RemapTextureFormat(encoding.buffer_format)
|
||||
: encoding.buffer_format;
|
||||
const auto host_format = ResolveHostFormat(backing_format, order);
|
||||
const auto bytes = Prospero::RenderTargetBytesPerElement(encoding.buffer_format);
|
||||
if (host_format.format != vk::Format::eUndefined && bytes != 0) {
|
||||
const auto order_mapping =
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/profiler.h"
|
||||
#include "common/threads.h"
|
||||
#include "graphics/guest_gpu/gpu_format.h"
|
||||
#include "graphics/guest_gpu/hardwareContext.h"
|
||||
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
|
||||
#include "graphics/host_gpu/renderer/debug.h"
|
||||
@@ -681,6 +682,28 @@ PipelineCache::GraphicsPrograms PipelineCache::GetGraphicsPrograms(
|
||||
ShaderParams pixel_params;
|
||||
if (pixel_active) {
|
||||
pixel_params = PrepareProgram(pixel_regs, sh, target_export_mapping, pixel_info);
|
||||
for (uint32_t export_index = 0; export_index < 8u; export_index++) {
|
||||
const auto slot = ShaderPixelExportTarget(pixel_info.target_shader_mask, export_index);
|
||||
if (slot >= 8u || pixel_info.target_output_mode[export_index] == 0u) continue;
|
||||
const auto& rt = context.GetRenderTarget(slot);
|
||||
const auto target_mask = render_target_mask_slot(context.GetRenderTargetMask(), slot);
|
||||
if (rt.base.addr == 0 || target_mask == 0u) continue;
|
||||
const auto format = Prospero::ResolveRenderTargetFormat(rt.info.format, rt.info.channel_type);
|
||||
if (format.buffer_format != Prospero::BufferFormat::k11_11_10UNorm) continue;
|
||||
const auto mapping = pixel_info.target_export_mapping[export_index];
|
||||
const auto shader_mask = render_target_mask_slot(pixel_info.target_shader_mask, slot);
|
||||
const bool blend = context.GetBlendControl(slot).enable && !rt.info.blend_bypass;
|
||||
if (blend || (mapping.ApplyMask(target_mask) & 7u) != 7u ||
|
||||
(mapping.ApplyMask(shader_mask) & 7u) != 7u ||
|
||||
pixel_info.target_output_mode[export_index] != 9u) {
|
||||
EXIT("unsupported packed UNorm render-target export: slot=%u export=%u mode=%u "
|
||||
"target_mask=0x%x shader_mask=0x%x mapping=0x%02x blend=%u\n",
|
||||
slot, export_index, pixel_info.target_output_mode[export_index], target_mask,
|
||||
shader_mask, mapping.packed, static_cast<uint32_t>(blend));
|
||||
}
|
||||
pixel_info.target_packed_unorm_mask |= 1u << export_index;
|
||||
pixel_info.target_round_mode |= static_cast<uint8_t>(rt.info.round_mode) << export_index;
|
||||
}
|
||||
if (!mesh_active && !tess_active && Prospero::IsRectList(user_config.GetPrimType())) {
|
||||
pixel_info.parameter_mode = ShaderPixelParameterMode::Rectangle;
|
||||
} else if (context.GetModeControl().provoking_vtx_last) {
|
||||
@@ -803,6 +826,9 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline(
|
||||
EXIT_IF(!colors[i].image_id || colors[i].desc.view_info.format == vk::Format::eUndefined);
|
||||
static_params.color_mask[slot] = colors[i].export_mapping.ApplyMask(
|
||||
render_target_mask_slot(ctx.GetRenderTargetMask(), colors[i].target_slot));
|
||||
if (colors[i].desc.info.guest_format == Prospero::BufferFormat::k11_11_10UNorm) {
|
||||
static_params.color_mask[slot] = 1u;
|
||||
}
|
||||
rendering.color_formats[slot] = colors[i].desc.view_info.format;
|
||||
if (attachment_samples == 0) {
|
||||
attachment_samples = colors[i].desc.info.samples;
|
||||
|
||||
@@ -567,6 +567,28 @@ void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
mapping.Map(3));
|
||||
value = mapped;
|
||||
}
|
||||
if ((state.input_info.pixel->target_packed_unorm_mask & (1u << exp.index)) != 0u) {
|
||||
if (exp.compr || MrtOutputMode(state, exp) != 9u ||
|
||||
(mapping.ApplyMask(exp.en) & 7u) != 7u) {
|
||||
EXIT("unsupported packed UNorm EXP: export=%u mode=%u en=0x%x compr=%u\n",
|
||||
exp.index, MrtOutputMode(state, exp), exp.en, static_cast<uint32_t>(exp.compr));
|
||||
}
|
||||
const auto info = Format::GetFormatInfo(Prospero::BufferFormat::k11_11_10UNorm);
|
||||
const auto rounding = (state.input_info.pixel->target_round_mode & (1u << exp.index)) != 0
|
||||
? UnormRounding::Truncate : UnormRounding::HalfUp;
|
||||
const auto raw = Unary(state, spv::OpBitcast, TypeU32Vector(state, 4), value);
|
||||
auto packed = ConstantU32(state, 0);
|
||||
for (uint32_t component = 0; component < info.component_count; component++) {
|
||||
const auto encoded = EncodePackedColorComponent(
|
||||
state, info, component, ExportRawComponent(ctx, raw, component), 32u, rounding);
|
||||
packed = component == 0u ? encoded
|
||||
: Binary(state, spv::OpBitwiseOr, TypeU32(state), packed, encoded);
|
||||
}
|
||||
value = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(state, 4), value,
|
||||
packed, ConstantU32(state, 0), ConstantU32(state, 0),
|
||||
ConstantU32(state, 0));
|
||||
}
|
||||
}
|
||||
if (exp.kind == IR::ExportTargetKind::Position &&
|
||||
state.input_info.vertex->clip_space.enabled) {
|
||||
|
||||
@@ -456,11 +456,15 @@ uint32_t EmitUFloatToF32Bits(EmitterState& state, uint32_t raw, uint32_t bits);
|
||||
uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
||||
uint32_t component, uint32_t raw);
|
||||
|
||||
enum class UnormRounding { RoundEven, HalfUp, Truncate };
|
||||
|
||||
uint32_t EncodeFormattedStoreComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
||||
uint32_t component, uint32_t data, uint32_t data_bits);
|
||||
uint32_t component, uint32_t data, uint32_t data_bits,
|
||||
UnormRounding rounding = UnormRounding::RoundEven);
|
||||
|
||||
uint32_t EncodePackedColorComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
||||
uint32_t component, uint32_t data, uint32_t data_bits);
|
||||
uint32_t component, uint32_t data, uint32_t data_bits,
|
||||
UnormRounding rounding = UnormRounding::RoundEven);
|
||||
|
||||
spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode);
|
||||
|
||||
|
||||
@@ -429,7 +429,9 @@ uint32_t NormalizeFormatComponent(EmitterState& state, const Format::BufferForma
|
||||
}
|
||||
|
||||
uint32_t EncodeFormattedStoreComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
||||
uint32_t component, uint32_t data, uint32_t data_bits) {
|
||||
uint32_t component, uint32_t data, uint32_t data_bits,
|
||||
UnormRounding rounding) {
|
||||
EXIT_IF(rounding != UnormRounding::RoundEven && info.type != Format::ComponentType::Unorm);
|
||||
const auto bits = info.component_bits[component];
|
||||
if (data_bits == 16u) {
|
||||
if (info.type == Format::ComponentType::Uint) {
|
||||
@@ -464,8 +466,12 @@ uint32_t EncodeFormattedStoreComponent(EmitterState& state, const Format::Buffer
|
||||
value = EmitGlsl<GLSLstd450FClamp, IR::Type::F32>(
|
||||
state, value, ConstantF32Value(state, lower), ConstantF32Value(state, upper));
|
||||
if (normalized) {
|
||||
value = EmitFPRoundEven32(
|
||||
state, EmitFPMul32(state, value, ConstantF32Value(state, max_value)));
|
||||
value = EmitFPMul32(state, value, ConstantF32Value(state, max_value));
|
||||
if (rounding == UnormRounding::RoundEven) {
|
||||
value = EmitFPRoundEven32(state, value);
|
||||
} else if (rounding == UnormRounding::HalfUp) {
|
||||
value = EmitFPAdd32(state, value, ConstantF32Value(state, 0.5f));
|
||||
}
|
||||
}
|
||||
// Float-to-integer conversion truncates scaled values toward zero.
|
||||
value = Unary(state, is_signed ? spv::OpConvertFToS : spv::OpConvertFToU,
|
||||
@@ -481,8 +487,9 @@ uint32_t EncodeFormattedStoreComponent(EmitterState& state, const Format::Buffer
|
||||
}
|
||||
|
||||
uint32_t EncodePackedColorComponent(EmitterState& state, const Format::BufferFormatInfo& info,
|
||||
uint32_t component, uint32_t data, uint32_t data_bits) {
|
||||
auto value = EncodeFormattedStoreComponent(state, info, component, data, data_bits);
|
||||
uint32_t component, uint32_t data, uint32_t data_bits,
|
||||
UnormRounding rounding) {
|
||||
auto value = EncodeFormattedStoreComponent(state, info, component, data, data_bits, rounding);
|
||||
if (info.type == Format::ComponentType::Uint) {
|
||||
value = EmitUMin32(state, value,
|
||||
ConstantU32(state, (1u << info.component_bits[component]) - 1u));
|
||||
|
||||
@@ -663,7 +663,8 @@ void DefineOutputs(EmitterState& state) {
|
||||
binding.kind == IR::StageOutputKind::Mrt &&
|
||||
state.program.stage == ShaderType::Pixel &&
|
||||
binding.index < std::size(state.input_info.pixel->target_output_mode) &&
|
||||
state.input_info.pixel->target_output_mode[binding.index] == 7u;
|
||||
(state.input_info.pixel->target_output_mode[binding.index] == 7u ||
|
||||
(state.input_info.pixel->target_packed_unorm_mask & (1u << binding.index)) != 0u);
|
||||
const auto type = uint_output ? TypeU32Vector(state, 4) : TypeF32Vector(state, 4);
|
||||
binding.variable_id = DefineInterfaceVariable(state, type, spv::StorageClassOutput,
|
||||
binding.debug_name.c_str());
|
||||
|
||||
@@ -675,6 +675,7 @@ void BuildStageStaticKey(const ShaderPixelInputInfo& info, std::vector<uint32_t>
|
||||
key.push_back(static_cast<uint32_t>(info.dual_source_blending));
|
||||
key.push_back(static_cast<uint32_t>(info.alpha_blend_source));
|
||||
key.insert(key.end(), std::begin(info.target_output_mode), std::end(info.target_output_mode));
|
||||
key.push_back(info.target_packed_unorm_mask | (uint32_t {info.target_round_mode} << 8u));
|
||||
const auto mask = info.target_shader_mask;
|
||||
key.push_back((mask | (mask >> 1u) | (mask >> 2u) | (mask >> 3u)) & 0x11111111u);
|
||||
for (uint32_t base = 0; base < info.target_export_mapping.size(); base += 4u) {
|
||||
|
||||
@@ -175,6 +175,9 @@ struct ShaderPixelInputInfo {
|
||||
uint8_t target_output_mode[8] = {};
|
||||
uint32_t target_shader_mask = UINT32_MAX;
|
||||
std::array<Prospero::ColorComponentMapping, 8> target_export_mapping = {};
|
||||
uint8_t target_packed_unorm_mask = 0;
|
||||
// CB round mode per converted export: zero adds half an LSB, one truncates.
|
||||
uint8_t target_round_mode = 0;
|
||||
uint32_t scratch_size_dwords = 0;
|
||||
bool ps_pos_x = false;
|
||||
bool ps_pos_y = false;
|
||||
|
||||
@@ -18093,6 +18093,175 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
// Captured format30 uses uncompressed mode9 at MRT2. Keep a blended
|
||||
// floating-point MRT live beside it, including sparse export routing.
|
||||
constexpr uint64_t packed_address = depth_address + 0x28000;
|
||||
static const auto packed_pixels = [] {
|
||||
std::array<std::vector<u32>, 2> result;
|
||||
constexpr std::array<std::array<u32, 3>, 2> inputs{{
|
||||
{0x3aa01403u, 0x3ae01c04u, 0x3c282a0bu}, // just above 2.5/3.5/10.5 LSB
|
||||
{0xbf800000u, 0x40000000u, 0x3e800000u}}};
|
||||
for (u32 i = 0; i < result.size(); ++i) {
|
||||
auto &code = result[i];
|
||||
for (u32 component = 0; component < 3; ++component)
|
||||
AppendVMovLiteral(&code, component, inputs[i][component]);
|
||||
AppendVMovLiteral(&code, 4, 0x38004400u); // FP16 RG: 4, 0.5
|
||||
AppendVMovLiteral(&code, 5, 0x4000bc00u); // FP16 BA: -1, 2
|
||||
for (u32 ordinal = 0; ordinal < 7; ++ordinal) {
|
||||
code.push_back(EncodeExp0(ordinal, 0xf, ordinal == 6, ordinal != 2));
|
||||
code.push_back(ordinal == 2 ? EncodeExp1(0, 1, 2, 0) : EncodeExp1(4, 5, 0, 0));
|
||||
}
|
||||
AppendEnd(&code);
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
TestCase packed_read;
|
||||
packed_read.name = "PackedUnormRenderTargetRead";
|
||||
packed_read.has_user_data = true;
|
||||
packed_read.image_descriptor_swizzle = DstSel(4, 5, 6, 1);
|
||||
const ShaderTextureResource packed_descriptor{{static_cast<u32>(packed_address >> 8u),
|
||||
(static_cast<u32>(Prospero::BufferFormat::k11_11_10UNorm) << 20u) | (3u << 30u),
|
||||
((extent - 1u) >> 2u) | ((extent - 1u) << 14u),
|
||||
packed_read.image_descriptor_swizzle |
|
||||
(static_cast<u32>(Prospero::ImageType::kColor2D) << 28u),
|
||||
0, 0x00700000u, 0, 0}};
|
||||
std::copy_n(packed_descriptor.fields, 8, packed_read.user_data.begin());
|
||||
packed_read.user_data[8] = 0x92u;
|
||||
packed_read.user_data[9] = 0x00fff000u;
|
||||
packed_read.user_data[10] = 0x0a500000u; // Existing packed-UNorm bilinear clamp contract.
|
||||
packed_read.user_data[50] = 8 * sizeof(u32);
|
||||
AppendVMovU32(&packed_read.code, 20, extent / 2);
|
||||
AppendVMovU32(&packed_read.code, 21, extent / 2);
|
||||
packed_read.code.insert(packed_read.code.end(),
|
||||
{EncodeMimg0(0x00, 0xf), EncodeMimg1(0, 20)});
|
||||
AppendVMovLiteral(&packed_read.code, 20, 0x3f000000u);
|
||||
AppendVMovLiteral(&packed_read.code, 21, 0x3f000000u);
|
||||
packed_read.code.insert(packed_read.code.end(),
|
||||
{EncodeMimg0(0x27, 0xf), EncodeMimg1(4, 20, 0, 2)});
|
||||
for (u32 component = 0; component < 8; ++component)
|
||||
AppendStoreVgpr(&packed_read.code, component, component);
|
||||
AppendEnd(&packed_read.code);
|
||||
const auto packed_reader = CompileCase(packed_read, SubgroupSize());
|
||||
auto packed_output = CreateStorageBuffer(name, {}, 8);
|
||||
const auto packed_sampler = CreateSampler(name);
|
||||
uint64_t half_up_program = 0, truncate_program = 0;
|
||||
struct PackedDraw { u32 slot, wave; bool truncate, clamp; };
|
||||
for (const auto test : {PackedDraw{2, 64, false, false}, {2, 64, true, false},
|
||||
{2, 64, false, false}, {3, 32, false, false},
|
||||
{3, 32, false, true}}) {
|
||||
const u32 float_slot = test.slot == 2 ? 6 : 7;
|
||||
registers.Reset();
|
||||
registers.SetViewportTransformControl(0x300);
|
||||
registers.SetViewportScaleOffset(0, extent / 2, extent / 2,
|
||||
extent / 2, extent / 2, 1, 0);
|
||||
registers.SetViewportZMax(0, 1);
|
||||
registers.SetScreenScissor(0, 0, extent, extent);
|
||||
registers.SetWindowScissor(0, 0, extent, extent, false);
|
||||
registers.SetGenericScissor(0, 0, extent, extent, false);
|
||||
registers.SetViewportScissor(0, 0, 0, extent, extent, false);
|
||||
registers.SetRenderTargetMask((7u << (4u * test.slot)) | (0xfu << (4u * float_slot)));
|
||||
registers.SetShaderMask(test.slot == 2 ? 0x0fffffffu : 0xfffff0ffu);
|
||||
registers.SetPsInControl(test.wave == 32 ? 0x8000 : 0);
|
||||
constexpr std::array<u32, 7> modes{4, 4, 9, 5, 4, 4, 4};
|
||||
for (u32 ordinal = 0; ordinal < modes.size(); ++ordinal)
|
||||
registers.SetTargetOutputMode(ordinal, modes[ordinal]);
|
||||
for (const auto slot : {test.slot, float_slot}) {
|
||||
const bool packed = slot == test.slot;
|
||||
registers.SetColorBase(slot, {.addr = packed ? packed_address : depth_address + 0x10000});
|
||||
registers.SetColorInfo(slot,
|
||||
{.blend_clamp = packed, .round_mode = packed && test.truncate,
|
||||
.format = packed ? Prospero::ChannelLayout::k11_11_10
|
||||
: Prospero::ChannelLayout::k32_32_32_32,
|
||||
.channel_type = packed ? Prospero::ChannelType::kUNorm : Prospero::ChannelType::kFloat,
|
||||
.channel_order = Prospero::ChannelOrder::kStandard});
|
||||
registers.SetColorAttrib2(slot, {.height = extent - 1, .width = extent - 1});
|
||||
registers.SetColorAttrib3(slot, {.tile_mode = Prospero::TileMode::kLinear, .dimension = 1});
|
||||
}
|
||||
HW::BlendControl additive{};
|
||||
additive.enable = true;
|
||||
additive.color_srcblend = additive.color_destblend =
|
||||
additive.alpha_srcblend = additive.alpha_destblend =
|
||||
static_cast<uint8_t>(Prospero::BlendFactor::kOne);
|
||||
registers.SetBlendControl(float_slot, additive);
|
||||
const auto &code = packed_pixels[test.clamp];
|
||||
const auto ps_address = reinterpret_cast<uint64_t>(code.data());
|
||||
ShaderMapUserData(ps_address, {.type = Prospero::ShaderBinaryType::kPs,
|
||||
.code_size_bytes = static_cast<u32>(code.size() * sizeof(u32))});
|
||||
shaders.SetEsShaderBase(vertex_address);
|
||||
shaders.SetGsShaderResource2({});
|
||||
shaders.SetPsShaderBase(ps_address);
|
||||
user_config.SetPrimitiveType(Prospero::PrimitiveType::kTriList);
|
||||
native_vertex_regs.es_regs.data_addr = vertex_address;
|
||||
native_pixel_regs.ps_regs.data_addr = ps_address;
|
||||
const auto programs = context.GetPipelineCache().GetGraphicsPrograms(
|
||||
native_vertex_regs, native_pixel_regs, registers.GetShaderRegisters(),
|
||||
registers, user_config, export_mapping, true, native_vertex_info, pixel);
|
||||
Require(name, "packed format follows export ordinal",
|
||||
pixel.target_output_mode[2] == 9 && pixel.wave_size == test.wave &&
|
||||
pixel.target_packed_unorm_mask == 4u &&
|
||||
pixel.target_round_mode == (test.truncate ? 4u : 0u),
|
||||
"packed conversion or rounding contaminated another MRT or followed a physical slot");
|
||||
if (test.slot == 2) {
|
||||
auto &id = test.truncate ? truncate_program : half_up_program;
|
||||
Require(name, "packed rounding program reuse", id == 0 || id == programs.pixel.id,
|
||||
"restoring half-up rounding failed to reuse its program");
|
||||
id = programs.pixel.id;
|
||||
}
|
||||
RenderColorInfo packed_target{}, float_target{};
|
||||
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||
executor, scheduler.Current(), packed_target, test.slot);
|
||||
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||
executor, scheduler.Current(), float_target, float_slot);
|
||||
Require(name, "packed target storage", packed_target.desc.info.bytes_per_block == 4 &&
|
||||
packed_target.desc.info.pixel_format == vk::Format::eR32Uint,
|
||||
"packed target expanded its physical storage or used floating-point bit encoding");
|
||||
vk::ClearValue packed_clear{}, float_clear{};
|
||||
packed_clear.color.uint32[0] = 0xdeadbeefu;
|
||||
float_clear.color.float32 = std::array<float, 4>{-2.f, 0.25f, 3.f, 0.5f};
|
||||
const vk::ImageSubresourceRange range{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1};
|
||||
TextureCacheTestAccess::ClearImage(cache, scheduler.Current(), packed_target.image_id, range, packed_clear);
|
||||
TextureCacheTestAccess::ClearImage(cache, scheduler.Current(), float_target.image_id, range, float_clear);
|
||||
RenderExecutorTestAccess::DrawAuto(
|
||||
executor, scheduler.Current(), {.vertex_count = 3, .instance_count = 1});
|
||||
const std::array<u32, 3> channels = test.clamp ? std::array<u32, 3>{0, 2047, 256}
|
||||
: test.truncate ? std::array<u32, 3>{2, 3, 10} : std::array<u32, 3>{3, 4, 11};
|
||||
const u32 word = channels[0] | (channels[1] << 11u) | (channels[2] << 22u);
|
||||
Require(name, "packed MRT words",
|
||||
ReadCachedTexel(name, context, packed_target.image_id, {}, {extent, extent, 1}) ==
|
||||
std::vector<u32>(extent * extent, word),
|
||||
"mode9 export lost packed widths, half-up/truncate rounding, clamp or RGB write mask");
|
||||
const auto floats = ReadCachedTexel(name, context, float_target.image_id, {}, {extent, extent, 1});
|
||||
constexpr std::array<float, 4> blended{2.f, 0.75f, 2.f, 2.5f};
|
||||
for (size_t i = 0; i < floats.size(); ++i)
|
||||
Require(name, "unconverted MRT blending", floats[i] == std::bit_cast<u32>(blended[i % 4]),
|
||||
"packed MRT handling changed another target's floating-point export or blending");
|
||||
ShaderRecompiler::IR::DescriptorValue value{};
|
||||
value.dword_count = 8;
|
||||
std::copy_n(packed_descriptor.fields, 8, value.dwords.begin());
|
||||
const auto binding = RenderExecutorTestAccess::ResolveTexture(
|
||||
executor, packed_reader.program.info.images.at(0), value);
|
||||
Require(name, "packed target sampled alias", binding.image_id == packed_target.image_id,
|
||||
"sampling the packed target allocated a different image or lost the rendered data");
|
||||
Image sampled;
|
||||
sampled.view = cache.FindTexture(binding.image_id, binding.desc);
|
||||
sampled.layout = cache.GetImage(binding.image_id).backing.state.layout;
|
||||
scheduler.Finish();
|
||||
Dispatch(packed_read, packed_reader, packed_output, nullptr, &sampled,
|
||||
nullptr, nullptr, packed_sampler);
|
||||
const auto loaded = ReadBuffer(name, packed_output, 8);
|
||||
for (u32 i = 0; i < loaded.size(); ++i) {
|
||||
const auto component = i % 4;
|
||||
const float expected = component == 3 ? 1.f
|
||||
: channels[component] / (component == 2 ? 1023.f : 2047.f);
|
||||
Require(name, "rendered packed load and sample",
|
||||
std::abs(std::bit_cast<float>(loaded[i]) - expected) < 0.000001f,
|
||||
"packed rendered contents lost normalization, ordering or texture visibility");
|
||||
}
|
||||
}
|
||||
Require(name, "packed rounding program distinction", half_up_program != truncate_program,
|
||||
"half-up and truncate exports shared one shader specialization");
|
||||
DestroyBuffer(&packed_output);
|
||||
m_device.destroySampler(packed_sampler);
|
||||
DestroyBuffer(&stencil_readback);
|
||||
|
||||
vertex_shader = owned_vertex_shader;
|
||||
|
||||
@@ -13526,6 +13526,12 @@ void TestRenderTargetReverseExportMapping() {
|
||||
2,
|
||||
{Prospero::ColorMappingRgba, Prospero::ColorMappingRgab,
|
||||
Prospero::ColorMappingBgra, Prospero::ColorMappingAgbr}},
|
||||
{Prospero::ChannelLayout::k11_11_10,
|
||||
Prospero::ChannelType::kUNorm,
|
||||
vk::Format::eR32Uint,
|
||||
4,
|
||||
{Prospero::ColorMappingRgba, Prospero::ColorMappingRgab,
|
||||
Prospero::ColorMappingBgra, Prospero::ColorMappingAgbr}},
|
||||
{Prospero::ChannelLayout::k16_16_16_16,
|
||||
Prospero::ChannelType::kFloat,
|
||||
vk::Format::eR16G16B16A16Sfloat,
|
||||
@@ -13664,6 +13670,45 @@ void TestRenderTargetReverseExportMapping() {
|
||||
PrepareProgram(regs, sh, mappings, compiled_info);
|
||||
Check(sparse_slot_key == MakeStageStaticKey(compiled_info),
|
||||
"component-only shader mask changes introduced a duplicate shader variant");
|
||||
|
||||
ShaderPixelInputInfo packed_info{};
|
||||
packed_info.target_output_mode[0] = 9;
|
||||
const auto unconverted_key = MakeStageStaticKey(packed_info);
|
||||
packed_info.target_packed_unorm_mask = 1;
|
||||
const auto half_up_key = MakeStageStaticKey(packed_info);
|
||||
options.input_info.pixel = &packed_info;
|
||||
const auto packed = RecompileForTest(shader, options);
|
||||
const auto packed_source = DisassembleSpirvBinary(packed.spirv);
|
||||
Check(half_up_key != unconverted_key &&
|
||||
packed_source.find("OpVariable %_ptr_Output_v4uint Output") != std::string::npos &&
|
||||
packed_source.find("RoundEven") == std::string::npos &&
|
||||
packed_source.find("UnpackHalf2x16") == std::string::npos,
|
||||
"mode9 packed MRT export lost its cache identity, integer backing or FP32 interpretation");
|
||||
CheckSpirvBinaryValidates(packed.spirv);
|
||||
packed_info.target_round_mode = 1;
|
||||
const auto truncate = RecompileForTest(shader, options);
|
||||
Check(MakeStageStaticKey(packed_info) != half_up_key && truncate.spirv != packed.spirv,
|
||||
"packed MRT rounding was omitted from shader specialization");
|
||||
CheckSpirvBinaryValidates(truncate.spirv);
|
||||
packed_info.target_round_mode = 0;
|
||||
Check(MakeStageStaticKey(packed_info) == half_up_key,
|
||||
"restoring packed MRT rounding did not restore the static key");
|
||||
packed_info.target_export_mapping[0] = Prospero::ColorMappingBgra;
|
||||
const auto packed_reversed = RecompileForTest(shader, options);
|
||||
Check(MakeStageStaticKey(packed_info) != half_up_key &&
|
||||
SpirvContainsVectorShuffle(packed_reversed.spirv, {2u, 1u, 0u, 3u}),
|
||||
"packed MRT conversion bypassed logical component ordering");
|
||||
CheckSpirvBinaryValidates(packed_reversed.spirv);
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
const uint32_t partial_packed[] = {
|
||||
EncodeExp0(0, 0x3), EncodeExp1(0, 1, 2, 3), 0xbf810000u};
|
||||
ExpectFatal([&] { (void)RecompileForTest(partial_packed, options); },
|
||||
"partial packed MRT export was accepted without preserving untouched fields");
|
||||
const uint32_t compressed_packed[] = {
|
||||
EncodeExp0(0, 0xf, true, true), EncodeExp1(0, 1, 0, 0), 0xbf810000u};
|
||||
ExpectFatal([&] { (void)RecompileForTest(compressed_packed, options); },
|
||||
"packed MRT accepted an unsupported compressed export");
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestBlendMappingClassification() {
|
||||
|
||||
Reference in New Issue
Block a user