graphics: render packed 11-11-10 UNorm targets with exact rounding

Co-authored-by: fxpw <fxpw@outlook.com>
This commit is contained in:
nmzik
2026-10-15 04:35:56 +02:00
parent fb213e8ae5
commit b4406b0467
11 changed files with 291 additions and 10 deletions
@@ -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());