[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:
committed by
Radosław Gliński
parent
2b071d9b09
commit
3ff230d23b
@@ -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,
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user