[GPU] Emulate extended-range float16 in render target pack/unpack

Exp 31 is a large finite value up to 131,008 on the guest, not Inf or NaN.
Packing used to clamp colors at 65504 and unpacking returned Inf for extended encodings.

Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
This commit is contained in:
goldislead
2026-08-02 10:46:00 -07:00
committed by Radosław Gliński
parent 2b071d9b09
commit 3ff230d23b
7 changed files with 236 additions and 181 deletions

View File

@@ -816,6 +816,15 @@ class DxbcShaderTranslator : public ShaderTranslator {
// Adds the surviving coverage MSAA counts from ROV params to the active ZPD
// counter slot after the final PS depth/stencil decision.
void ROV_AddPassedMSAASamplesToZPD();
// Converts the float32 components of the register to extended-range float16
// in their low 16 bits. Exponent 31 holds finite values up to 131008 of
// either sign on the Xbox 360 instead of Inf or NaN, and NaN maps to 0.
// Pushes and pops its own temporary registers.
void Float32ToF16ExtendedRange(uint32_t reg, uint32_t components);
// Converts extended-range float16 in the low 16 bits of the components of
// the register, with zeros above, back to float32. Pushes and pops its own
// temporary registers.
void Float16ExtendedRangeTo32(uint32_t reg, uint32_t components);
// Unpacks a 32bpp or a 64bpp color in packed_temp.packed_temp_components to
// color_temp, using 2 temporary VGPRs.
void ROV_UnpackColor(uint32_t rt_index, uint32_t packed_temp,

View File

@@ -487,64 +487,16 @@ void DxbcShaderTranslator::ExportToMemory(uint8_t export_eM) {
}
a_.OpBreak();
// Xbox 360 float16 uses extended range: exponent 31 is NOT Inf/NaN
// but a valid large value (up to ±131008). Standard DXBC f32tof16
// clamps to ±65504 and produces Inf for larger values. This helper
// detects where standard conversion overflowed to Inf/NaN and
// re-encodes those values using the extended range representation:
// halve the value, convert to standard f16 (giving exponent <= 30),
// then increment the exponent by 1 (placing it in the exponent 31
// slot that Xbox 360 treats as a normal value, not Inf/NaN).
// Convert to extended-range float16 with the helper shared with the ROV
// render target packing.
auto f32_to_f16_extended_range = [&](uint32_t components) {
uint32_t ext_original_temp = PushSystemTemp();
uint32_t ext_mask_temp = PushSystemTemp();
uint8_t eM_remaining_ext = export_eM;
uint32_t eM_index_ext;
while (xe::bit_scan_forward(eM_remaining_ext, &eM_index_ext)) {
eM_remaining_ext &= ~(uint8_t(1) << eM_index_ext);
uint32_t eM = system_temps_memexport_data_[eM_index_ext];
// Save original float32 values before conversion.
a_.OpMov(dxbc::Dest::R(ext_original_temp, components),
dxbc::Src::R(eM));
// Standard f32 to f16 conversion (handles ±0..65504 correctly).
a_.OpF32ToF16(dxbc::Dest::R(eM, components), dxbc::Src::R(eM));
// Detect where standard conversion produced Inf or NaN
// (exponent field = 31, i.e. bits [14:10] all set = 0x7C00).
a_.OpAnd(dxbc::Dest::R(ext_mask_temp, components), dxbc::Src::R(eM),
dxbc::Src::LU(0x7C00));
a_.OpIEq(dxbc::Dest::R(ext_mask_temp, components),
dxbc::Src::R(ext_mask_temp), dxbc::Src::LU(0x7C00));
// For values that overflowed, compute extended range encoding
// from the saved original float32 values:
// 1. Clamp to ±131008.0 (max extended float16 can represent).
a_.OpMin(dxbc::Dest::R(ext_original_temp, components),
dxbc::Src::R(ext_original_temp), dxbc::Src::LF(131008.0f));
a_.OpMax(dxbc::Dest::R(ext_original_temp, components),
dxbc::Src::R(ext_original_temp), dxbc::Src::LF(-131008.0f));
// 2. Halve to bring into standard float16 range (max 65504).
a_.OpMul(dxbc::Dest::R(ext_original_temp, components),
dxbc::Src::R(ext_original_temp), dxbc::Src::LF(0.5f));
// 3. Convert the halved value (will have exponent <= 30).
a_.OpF32ToF16(dxbc::Dest::R(ext_original_temp, components),
dxbc::Src::R(ext_original_temp));
// 4. Increment exponent by 1 (add 1 << 10 = 0x0400) to
// compensate for the halving → places value at exponent 31.
a_.OpIAdd(dxbc::Dest::R(ext_original_temp, components),
dxbc::Src::R(ext_original_temp), dxbc::Src::LU(0x0400));
// Select: use extended result where standard gave Inf/NaN,
// keep standard result otherwise.
a_.OpMovC(dxbc::Dest::R(eM, components), dxbc::Src::R(ext_mask_temp),
dxbc::Src::R(ext_original_temp), dxbc::Src::R(eM));
Float32ToF16ExtendedRange(system_temps_memexport_data_[eM_index_ext],
components);
}
// Release ext_mask_temp and ext_original_temp.
PopSystemTemp(2);
};
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::ColorFormat::k_16_FLOAT)));

View File

@@ -1294,8 +1294,9 @@ void DxbcShaderTranslator::ROV_UnpackColor(
dxbc::Src::LU(0, 16, 0, 16),
dxbc::Src::R(packed_temp,
0b01010000 + packed_temp_components * 0b01010101));
// Convert from 16-bit float.
a_.OpF16ToF32(color_components_dest, dxbc::Src::R(color_temp));
// Convert from extended-range float16, exponent 31 holds finite values on
// the guest.
Float16ExtendedRangeTo32(color_temp, i ? 0b1111 : 0b0011);
a_.OpBreak();
}
@@ -1465,18 +1466,20 @@ void DxbcShaderTranslator::ROV_PackPreClampedColor(
a_.OpCase(dxbc::Src::LU(RenderTargetCache::AddPSIColorFormatFlags(
i ? xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT
: xenos::ColorRenderTargetFormat::k_16_16_FLOAT)));
// Convert to extended-range float16 instead of the plain IEEE conversion,
// exponent 31 holds finite values on the guest.
Float32ToF16ExtendedRange(color_temp, i ? 0b1111 : 0b0011);
for (uint32_t j = 0; j < (uint32_t(2) << i); ++j) {
dxbc::Dest packed_dest_half(
dxbc::Dest::R(packed_temp, 1 << (packed_temp_components + (j >> 1))));
// Convert to 16-bit float.
a_.OpF32ToF16((j & 1) ? temp1_dest : packed_dest_half,
dxbc::Src::R(color_temp).Select(j));
// Pack green or alpha.
// Pack green or alpha into the high half.
if (j & 1) {
a_.OpBFI(packed_dest_half, dxbc::Src::LU(16), dxbc::Src::LU(16),
temp1_src,
dxbc::Src::R(color_temp).Select(j),
dxbc::Src::R(packed_temp)
.Select(packed_temp_components + (j >> 1)));
} else {
a_.OpMov(packed_dest_half, dxbc::Src::R(color_temp).Select(j));
}
}
a_.OpBreak();
@@ -3458,6 +3461,73 @@ void DxbcShaderTranslator::CompletePixelShader() {
}
}
void DxbcShaderTranslator::Float32ToF16ExtendedRange(uint32_t reg,
uint32_t components) {
uint32_t original_temp = PushSystemTemp();
uint32_t mask_temp = PushSystemTemp();
// The Xbox 360 float16 has no NaN, map it to 0 before converting. Otherwise
// the overflow check below reads its exponent as an overflow, and since min
// and max drop the NaN operand in DXBC, the clamp would turn it into
// +131008.
a_.OpNE(dxbc::Dest::R(mask_temp, components), dxbc::Src::R(reg),
dxbc::Src::R(reg));
a_.OpMovC(dxbc::Dest::R(reg, components), dxbc::Src::R(mask_temp),
dxbc::Src::LF(0.0f), dxbc::Src::R(reg));
// Keep the original float32 values for the overflow path.
a_.OpMov(dxbc::Dest::R(original_temp, components), dxbc::Src::R(reg));
// The standard conversion covers magnitudes up to 65504, anything larger
// becomes Inf with all five exponent bits set.
a_.OpF32ToF16(dxbc::Dest::R(reg, components), dxbc::Src::R(reg));
// Find the lanes that overflowed.
a_.OpAnd(dxbc::Dest::R(mask_temp, components), dxbc::Src::R(reg),
dxbc::Src::LU(0x7C00));
a_.OpIEq(dxbc::Dest::R(mask_temp, components), dxbc::Src::R(mask_temp),
dxbc::Src::LU(0x7C00));
// Encode the overflowed lanes into exponent 31, which the Xbox 360 treats
// as finite. Clamp to the 131008 limit, halve so the exponent lands at 30
// or below, convert, then add one to the exponent to undo the halving.
a_.OpMin(dxbc::Dest::R(original_temp, components),
dxbc::Src::R(original_temp), dxbc::Src::LF(131008.0f));
a_.OpMax(dxbc::Dest::R(original_temp, components),
dxbc::Src::R(original_temp), dxbc::Src::LF(-131008.0f));
a_.OpMul(dxbc::Dest::R(original_temp, components),
dxbc::Src::R(original_temp), dxbc::Src::LF(0.5f));
a_.OpF32ToF16(dxbc::Dest::R(original_temp, components),
dxbc::Src::R(original_temp));
a_.OpIAdd(dxbc::Dest::R(original_temp, components),
dxbc::Src::R(original_temp), dxbc::Src::LU(0x0400));
// Choose the encoded value for the overflowed lanes.
a_.OpMovC(dxbc::Dest::R(reg, components), dxbc::Src::R(mask_temp),
dxbc::Src::R(original_temp), dxbc::Src::R(reg));
// Release original_temp and mask_temp.
PopSystemTemp(2);
}
void DxbcShaderTranslator::Float16ExtendedRangeTo32(uint32_t reg,
uint32_t components) {
uint32_t reduced_temp = PushSystemTemp();
uint32_t mask_temp = PushSystemTemp();
// Find the exponent 31 lanes, which hold finite values on the Xbox 360
// rather than Inf or NaN.
a_.OpAnd(dxbc::Dest::R(mask_temp, components), dxbc::Src::R(reg),
dxbc::Src::LU(0x7C00));
a_.OpIEq(dxbc::Dest::R(mask_temp, components), dxbc::Src::R(mask_temp),
dxbc::Src::LU(0x7C00));
// Lower their exponent by one so the conversion sees a normal float16, then
// double the result to undo it.
a_.OpIAdd(dxbc::Dest::R(reduced_temp, components), dxbc::Src::R(reg),
dxbc::Src::LI(-0x0400));
a_.OpMovC(dxbc::Dest::R(reg, components), dxbc::Src::R(mask_temp),
dxbc::Src::R(reduced_temp), dxbc::Src::R(reg));
a_.OpF16ToF32(dxbc::Dest::R(reg, components), dxbc::Src::R(reg));
a_.OpMul(dxbc::Dest::R(reduced_temp, components), dxbc::Src::R(reg),
dxbc::Src::LF(2.0f));
a_.OpMovC(dxbc::Dest::R(reg, components), dxbc::Src::R(mask_temp),
dxbc::Src::R(reduced_temp), dxbc::Src::R(reg));
// Release reduced_temp and mask_temp.
PopSystemTemp(2);
}
void DxbcShaderTranslator::PreClampedFloat32To7e3(
dxbc::Assembler& a, uint32_t f10_temp, uint32_t f10_temp_component,
uint32_t f32_temp, uint32_t f32_temp_component, uint32_t temp_temp,

View File

@@ -282,11 +282,11 @@ void RenderTargetCache::GetPSIColorFormatInfo(
break;
case xenos::ColorRenderTargetFormat::k_16_16_FLOAT:
case xenos::ColorRenderTargetFormat::k_16_16_16_16_FLOAT:
// No NaNs on the Xbox 360 GPU, though can't use the extended range with
// Direct3D and Vulkan conversions.
// TODO(Triang3l): Use the extended-range encoding in all implementations.
clamp_rgb_low = clamp_alpha_low = -65504.0f;
clamp_rgb_high = clamp_alpha_high = 65504.0f;
// No NaNs on the Xbox 360 GPU. The interlock paths of both backends
// emulate the extended-range encoding in their pack and unpack, so the
// whole guest range survives the clamp.
clamp_rgb_low = clamp_alpha_low = -131008.0f;
clamp_rgb_high = clamp_alpha_high = 131008.0f;
if (!(write_mask & 0b0001)) {
keep_mask_low |= 0xFFFFu;
}

View File

@@ -729,6 +729,10 @@ class SpirvShaderTranslator : public ShaderTranslator {
// must be called with absolute values of operands - use GetAbsoluteOperand!
spv::Id ZeroIfAnyOperandIsZero(spv::Id value, spv::Id operand_0_abs,
spv::Id operand_1_abs);
// Pack/unpack two floats as Xbox 360 extended-range float16, where exponent
// 31 is a large finite value (up to +-131008), not Inf/NaN.
spv::Id PackFloat16x2ExtendedRange(spv::Id float2_value);
spv::Id UnpackFloat16x2ExtendedRange(spv::Id packed_uint);
// Conditionally discard the current fragment. Changes the build point.
void KillPixel(spv::Id condition,
uint8_t memexport_eM_potentially_written_before);

View File

@@ -650,95 +650,8 @@ void SpirvShaderTranslator::ExportToMemory(uint8_t export_eM) {
add_format_case(fixed16_packed, 3);
}
// Xbox 360 float16 uses extended range: exponent 31 is NOT Inf/NaN
// but a valid large value (up to +/-131008). Standard PackHalf2x16
// clamps to +/-65504 and produces Inf for larger values. This helper
// detects where standard conversion overflowed to Inf/NaN and
// re-encodes those values using the extended range representation:
// halve the value, convert to standard f16 (giving exponent <= 30),
// then increment the exponent by 1 (placing it in the exponent 31
// slot that Xbox 360 treats as a normal value, not Inf/NaN).
// Operates on a float2 packed via PackHalf2x16 into a uint32, with
// per-lane overflow detection and selection.
auto pack_half_2x16_extended_range = [&](spv::Id float2_value) -> spv::Id {
// Standard f32 to f16 conversion (handles +/-0..65504 correctly).
spv::Id standard =
builder_->createUnaryBuiltinCall(type_uint_, ext_inst_glsl_std_450_,
GLSLstd450PackHalf2x16, float2_value);
// Detect where standard conversion produced Inf or NaN
// (exponent field = 31, i.e. bits [14:10] all set = 0x7C00)
// in each 16-bit lane of the packed result.
spv::Id const_0x7C00 = builder_->makeUintConstant(0x7C00);
spv::Id lower_exp = builder_->createBinOp(spv::OpBitwiseAnd, type_uint_,
standard, const_0x7C00);
spv::Id lower_overflow = builder_->createBinOp(spv::OpIEqual, type_bool_,
lower_exp, const_0x7C00);
spv::Id upper_bits =
builder_->createBinOp(spv::OpShiftRightLogical, type_uint_, standard,
builder_->makeUintConstant(16));
spv::Id upper_exp = builder_->createBinOp(spv::OpBitwiseAnd, type_uint_,
upper_bits, const_0x7C00);
spv::Id upper_overflow = builder_->createBinOp(spv::OpIEqual, type_bool_,
upper_exp, const_0x7C00);
// For values that overflowed, compute extended range encoding:
// 1. Clamp to +/-131008.0 (max extended float16 can represent).
spv::Id const_131008 = builder_->makeFloatConstant(131008.0f);
spv::Id const_neg_131008 = builder_->makeFloatConstant(-131008.0f);
id_vector_temp_.clear();
id_vector_temp_.push_back(const_neg_131008);
id_vector_temp_.push_back(const_neg_131008);
spv::Id const_neg_131008_vec2 =
builder_->makeCompositeConstant(type_float2_, id_vector_temp_);
id_vector_temp_.clear();
id_vector_temp_.push_back(const_131008);
id_vector_temp_.push_back(const_131008);
spv::Id const_131008_vec2 =
builder_->makeCompositeConstant(type_float2_, id_vector_temp_);
spv::Id clamped = builder_->createTriBuiltinCall(
type_float2_, ext_inst_glsl_std_450_, GLSLstd450FClamp, float2_value,
const_neg_131008_vec2, const_131008_vec2);
// 2. Halve to bring into standard float16 range (max 65504).
spv::Id const_half = builder_->makeFloatConstant(0.5f);
id_vector_temp_.clear();
id_vector_temp_.push_back(const_half);
id_vector_temp_.push_back(const_half);
spv::Id const_half_vec2 =
builder_->makeCompositeConstant(type_float2_, id_vector_temp_);
spv::Id halved = builder_->createBinOp(spv::OpFMul, type_float2_, clamped,
const_half_vec2);
// 3. Convert the halved value (will have exponent <= 30).
spv::Id halved_packed = builder_->createUnaryBuiltinCall(
type_uint_, ext_inst_glsl_std_450_, GLSLstd450PackHalf2x16, halved);
// 4. Increment exponent by 1 in both lanes (add 0x0400 to each
// 16-bit half = 0x04000400) to compensate for the halving.
spv::Id extended =
builder_->createBinOp(spv::OpIAdd, type_uint_, halved_packed,
builder_->makeUintConstant(0x04000400));
// Select: use extended result where standard gave Inf/NaN,
// keep standard result otherwise. Per-lane selection via masking.
spv::Id const_0xFFFF = builder_->makeUintConstant(0xFFFF);
spv::Id const_0xFFFF0000 = builder_->makeUintConstant(0xFFFF0000);
spv::Id result_lower = builder_->createTriOp(
spv::OpSelect, type_uint_, lower_overflow,
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, extended,
const_0xFFFF),
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, standard,
const_0xFFFF));
spv::Id result_upper = builder_->createTriOp(
spv::OpSelect, type_uint_, upper_overflow,
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, extended,
const_0xFFFF0000),
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_, standard,
const_0xFFFF0000));
return builder_->createBinOp(spv::OpBitwiseOr, type_uint_, result_lower,
result_upper);
};
// Xbox 360 float16 uses extended range: exponent 31 is a large finite value,
// not Inf/NaN. See PackFloat16x2ExtendedRange.
// k_16_FLOAT
format_switch.makeBeginCase(
@@ -750,7 +663,7 @@ void SpirvShaderTranslator::ExportToMemory(uint8_t export_eM) {
id_vector_temp_.push_back(builder_->createCompositeExtract(
eM_swapped[eM_index], type_float_, 0));
id_vector_temp_.push_back(const_float_0_);
spv::Id format_packed_16_float_x = pack_half_2x16_extended_range(
spv::Id format_packed_16_float_x = PackFloat16x2ExtendedRange(
builder_->createCompositeConstruct(type_float2_, id_vector_temp_));
id_vector_temp_.clear();
id_vector_temp_.resize(4, const_uint_0_);
@@ -771,7 +684,7 @@ void SpirvShaderTranslator::ExportToMemory(uint8_t export_eM) {
uint_vector_temp_.push_back(0);
uint_vector_temp_.push_back(1);
spv::Id format_packed_16_16_float_xy =
pack_half_2x16_extended_range(builder_->createRvalueSwizzle(
PackFloat16x2ExtendedRange(builder_->createRvalueSwizzle(
spv::NoPrecision, type_float2_, eM_swapped[eM_index],
uint_vector_temp_));
id_vector_temp_.clear();
@@ -796,7 +709,7 @@ void SpirvShaderTranslator::ExportToMemory(uint8_t export_eM) {
uint_vector_temp_.push_back(2 * component_index);
uint_vector_temp_.push_back(2 * component_index + 1);
format_packed_16_16_16_16_float_xy_zw[component_index] =
pack_half_2x16_extended_range(builder_->createRvalueSwizzle(
PackFloat16x2ExtendedRange(builder_->createRvalueSwizzle(
spv::NoPrecision, type_float2_, eM_swapped[eM_index],
uint_vector_temp_));
}

View File

@@ -2680,6 +2680,132 @@ void SpirvShaderTranslator::FSI_DepthStencilTest(
}
}
spv::Id SpirvShaderTranslator::PackFloat16x2ExtendedRange(
spv::Id float2_value) {
// The Xbox 360 float16 has no NaN, map it to 0. Also keeps the overflow
// detection below from misreading a NaN's exponent 31 as a finite extended
// value, and FClamp's NaN result is undefined.
float2_value = builder_->createTriOp(
spv::OpSelect, type_float2_,
builder_->createUnaryOp(spv::OpIsNan, type_bool2_, float2_value),
const_float2_0_, float2_value);
// The standard conversion covers magnitudes up to 65504. Anything larger
// overflows to Inf (exponent field 0x7C00). Re-encode the overflowed lanes
// using the extended range: halve into the standard range, convert
// (exponent <= 30), then bump the exponent by 1 into the exponent 31 slot
// the Xbox 360 treats as finite.
spv::Id standard = builder_->createUnaryBuiltinCall(
type_uint_, ext_inst_glsl_std_450_, GLSLstd450PackHalf2x16, float2_value);
spv::Id const_0x7C00 = builder_->makeUintConstant(0x7C00);
spv::Id lower_overflow =
builder_->createBinOp(spv::OpIEqual, type_bool_,
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_,
standard, const_0x7C00),
const_0x7C00);
spv::Id upper_overflow = builder_->createBinOp(
spv::OpIEqual, type_bool_,
builder_->createBinOp(
spv::OpBitwiseAnd, type_uint_,
builder_->createBinOp(spv::OpShiftRightLogical, type_uint_, standard,
builder_->makeUintConstant(16)),
const_0x7C00),
const_0x7C00);
id_vector_temp_.clear();
id_vector_temp_.resize(2, builder_->makeFloatConstant(-131008.0f));
spv::Id const_neg_131008 =
builder_->makeCompositeConstant(type_float2_, id_vector_temp_);
id_vector_temp_.clear();
id_vector_temp_.resize(2, builder_->makeFloatConstant(131008.0f));
spv::Id const_131008 =
builder_->makeCompositeConstant(type_float2_, id_vector_temp_);
spv::Id clamped = builder_->createTriBuiltinCall(
type_float2_, ext_inst_glsl_std_450_, GLSLstd450FClamp, float2_value,
const_neg_131008, const_131008);
id_vector_temp_.clear();
id_vector_temp_.resize(2, builder_->makeFloatConstant(0.5f));
spv::Id halved = builder_->createBinOp(
spv::OpFMul, type_float2_, clamped,
builder_->makeCompositeConstant(type_float2_, id_vector_temp_));
spv::Id halved_packed = builder_->createUnaryBuiltinCall(
type_uint_, ext_inst_glsl_std_450_, GLSLstd450PackHalf2x16, halved);
// halved_packed has exponent <= 30 in both lanes, so adding 0x0400 per lane
// bumps the exponent without ever carrying across lanes.
spv::Id extended =
builder_->createBinOp(spv::OpIAdd, type_uint_, halved_packed,
builder_->makeUintConstant(0x04000400));
spv::Id const_0xFFFF = builder_->makeUintConstant(0xFFFF);
spv::Id const_0xFFFF0000 = builder_->makeUintConstant(0xFFFF0000);
spv::Id result_lower =
builder_->createTriOp(spv::OpSelect, type_uint_, lower_overflow,
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_,
extended, const_0xFFFF),
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_,
standard, const_0xFFFF));
spv::Id result_upper =
builder_->createTriOp(spv::OpSelect, type_uint_, upper_overflow,
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_,
extended, const_0xFFFF0000),
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_,
standard, const_0xFFFF0000));
return builder_->createBinOp(spv::OpBitwiseOr, type_uint_, result_lower,
result_upper);
}
spv::Id SpirvShaderTranslator::UnpackFloat16x2ExtendedRange(
spv::Id packed_uint) {
// Inverse of PackFloat16x2ExtendedRange. Exponent 31 lanes are large finite
// values rather than Inf or NaN. Decrement their exponent by 1 into the
// standard range, unpack, then double to compensate.
spv::Id const_0x7C00 = builder_->makeUintConstant(0x7C00);
spv::Id lower_overflow =
builder_->createBinOp(spv::OpIEqual, type_bool_,
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_,
packed_uint, const_0x7C00),
const_0x7C00);
spv::Id upper_overflow = builder_->createBinOp(
spv::OpIEqual, type_bool_,
builder_->createBinOp(
spv::OpBitwiseAnd, type_uint_,
builder_->createBinOp(spv::OpShiftRightLogical, type_uint_,
packed_uint, builder_->makeUintConstant(16)),
const_0x7C00),
const_0x7C00);
spv::Id standard =
builder_->createUnaryBuiltinCall(type_float2_, ext_inst_glsl_std_450_,
GLSLstd450UnpackHalf2x16, packed_uint);
// Decrement the exponent only in overflowed lanes (0x0400 in the low lane,
// 0x04000000 in the high one) so the subtraction never borrows across lanes.
spv::Id sub_lower =
builder_->createTriOp(spv::OpSelect, type_uint_, lower_overflow,
builder_->makeUintConstant(0x0400), const_uint_0_);
spv::Id sub_upper = builder_->createTriOp(
spv::OpSelect, type_uint_, upper_overflow,
builder_->makeUintConstant(0x04000000), const_uint_0_);
spv::Id reduced =
builder_->createBinOp(spv::OpISub, type_uint_, packed_uint,
builder_->createBinOp(spv::OpBitwiseOr, type_uint_,
sub_lower, sub_upper));
spv::Id reduced_unpacked = builder_->createUnaryBuiltinCall(
type_float2_, ext_inst_glsl_std_450_, GLSLstd450UnpackHalf2x16, reduced);
id_vector_temp_.clear();
id_vector_temp_.resize(2, builder_->makeFloatConstant(2.0f));
spv::Id extended = builder_->createBinOp(
spv::OpFMul, type_float2_, reduced_unpacked,
builder_->makeCompositeConstant(type_float2_, id_vector_temp_));
spv::Id result_x = builder_->createTriOp(
spv::OpSelect, type_float_, lower_overflow,
builder_->createCompositeExtract(extended, type_float_, 0),
builder_->createCompositeExtract(standard, type_float_, 0));
spv::Id result_y = builder_->createTriOp(
spv::OpSelect, type_float_, upper_overflow,
builder_->createCompositeExtract(extended, type_float_, 1),
builder_->createCompositeExtract(standard, type_float_, 1));
id_vector_temp_.clear();
id_vector_temp_.push_back(result_x);
id_vector_temp_.push_back(result_y);
return builder_->createCompositeConstruct(type_float2_, id_vector_temp_);
}
std::array<spv::Id, 2> SpirvShaderTranslator::FSI_ClampAndPackColor(
spv::Id color_float4, spv::Id format_with_flags) {
spv::Block& block_format_head = *builder_->getBuildPoint();
@@ -2977,31 +3103,14 @@ std::array<spv::Id, 2> SpirvShaderTranslator::FSI_ClampAndPackColor(
std::array<spv::Id, 2> packed_16_float;
{
builder_->setBuildPoint(&block_format_16_float);
// TODO(Triang3l): Xenos extended-range float16.
id_vector_temp_.clear();
id_vector_temp_.resize(4, builder_->makeFloatConstant(-65504.0f));
spv::Id const_float4_minus_float16_max =
builder_->makeCompositeConstant(type_float4_, id_vector_temp_);
id_vector_temp_.clear();
id_vector_temp_.resize(4, builder_->makeFloatConstant(65504.0f));
spv::Id const_float4_float16_max =
builder_->makeCompositeConstant(type_float4_, id_vector_temp_);
// NaN to 0, not to -max.
spv::Id color_clamped = builder_->createTriBuiltinCall(
type_float4_, ext_inst_glsl_std_450_, GLSLstd450FClamp,
builder_->createTriOp(
spv::OpSelect, type_float4_,
builder_->createUnaryOp(spv::OpIsNan, type_bool4_, color_float4),
const_float4_0_, color_float4),
const_float4_minus_float16_max, const_float4_float16_max);
// NaN is flushed to 0 inside PackFloat16x2ExtendedRange.
for (uint32_t i = 0; i < 2; ++i) {
uint_vector_temp_.clear();
uint_vector_temp_.push_back(2 * i);
uint_vector_temp_.push_back(2 * i + 1);
packed_16_float[i] = builder_->createUnaryBuiltinCall(
type_uint_, ext_inst_glsl_std_450_, GLSLstd450PackHalf2x16,
builder_->createRvalueSwizzle(spv::NoPrecision, type_float2_,
color_clamped, uint_vector_temp_));
packed_16_float[i] =
PackFloat16x2ExtendedRange(builder_->createRvalueSwizzle(
spv::NoPrecision, type_float2_, color_float4, uint_vector_temp_));
}
builder_->createBranch(&block_format_merge);
}
@@ -3290,11 +3399,9 @@ std::array<spv::Id, 4> SpirvShaderTranslator::FSI_UnpackColor(
for (uint32_t i = 0; i < 2; ++i) {
builder_->setBuildPoint(i ? &block_format_16_16_16_16_float
: &block_format_16_16_float);
// TODO(Triang3l): Xenos extended-range float16.
for (uint32_t j = 0; j <= i; ++j) {
spv::Id components_float2 = builder_->createUnaryBuiltinCall(
type_float2_, ext_inst_glsl_std_450_, GLSLstd450UnpackHalf2x16,
color_packed[j]);
spv::Id components_float2 =
UnpackFloat16x2ExtendedRange(color_packed[j]);
for (uint32_t k = 0; k < 2; ++k) {
unpacked_16_float[i][2 * j + k] = builder_->createCompositeExtract(
components_float2, type_float_, k);