shader: honor front-face input format

This commit is contained in:
shadowbeat070
2026-10-16 12:49:52 +02:00
parent 3ff6606fcb
commit 57b161019d
4 changed files with 108 additions and 6 deletions
@@ -52,9 +52,12 @@ uint32_t EmitBuiltinU32(EmitterState& state, IR::StageInputKind kind, uint32_t c
const auto value = state.builder.AllocateId();
const auto bits = state.builder.AllocateId();
state.builder.AddFunction(spv::OpLoad, TypeBool(state), value, variable);
// PS5 initializes v_front_face with float +1.0/-1.0 bits.
state.builder.AddFunction(spv::OpSelect, TypeU32(state), bits, value,
ConstantU32(state, 0x3f800000u), ConstantU32(state, 0xbf800000u));
// SPI_BARYC_CNTL.FRONT_FACE_ALL_BITS selects uint masks or float +/-1.
const bool all_bits =
state.input_info.pixel != nullptr && state.input_info.pixel->ps_front_face_all_bits;
const auto front = ConstantU32(state, all_bits ? 0xffffffffu : 0x3f800000u);
const auto back = ConstantU32(state, all_bits ? 0u : 0xbf800000u);
state.builder.AddFunction(spv::OpSelect, TypeU32(state), bits, value, front, back);
return bits;
}
if (kind == IR::StageInputKind::VertexIndex || kind == IR::StageInputKind::InstanceIndex ||
+6 -3
View File
@@ -344,6 +344,8 @@ static void ShaderGetStaticInputInfoPS(
ps_info.ps_pos_z = (active_inputs & 0x00000400u) != 0;
ps_info.ps_pos_w = (active_inputs & 0x00000800u) != 0;
ps_info.ps_front_face = (active_inputs & 0x00001000u) != 0;
// SPI_BARYC_CNTL.FRONT_FACE_ALL_BITS selects integer masks instead of float +/-1.
ps_info.ps_front_face_all_bits = (sh.baryc_cntl & 0x01000000u) != 0;
ps_info.ps_ancillary = (active_inputs & 0x00002000u) != 0;
ps_info.ps_sample_shading = (active_inputs & 0x00000011u) != 0;
ps_info.ps_no_perspective = (sh.ps_input_ena & sh.ps_input_addr & 0x00000020u) != 0;
@@ -464,6 +466,7 @@ void BuildStageStaticKey(const ShaderPixelInputInfo& info, std::vector<uint32_t>
key.push_back(static_cast<uint32_t>(info.ps_pos_z));
key.push_back(static_cast<uint32_t>(info.ps_pos_w));
key.push_back(static_cast<uint32_t>(info.ps_front_face));
key.push_back(static_cast<uint32_t>(info.ps_front_face_all_bits));
key.push_back(static_cast<uint32_t>(info.ps_ancillary));
key.push_back(static_cast<uint32_t>(info.ps_no_perspective));
key.push_back(static_cast<uint32_t>(info.parameter_mode));
@@ -737,6 +740,7 @@ void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo& info) {
"\t ps_pos_z = %s\n"
"\t ps_pos_w = %s\n"
"\t ps_front_face = %s\n"
"\t ps_front_face_all_bits = %s\n"
"\t ps_ancillary = %s\n"
"\t ps_sample_shading = %s\n"
"\t ps_no_perspective = %s\n"
@@ -745,11 +749,10 @@ void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo& info) {
"\t ps_execute_on_noop = %s\n",
info.input_num, info.ps_system_input_base, info.custom_interpolation_mask,
info.ps_perspective_center_vgpr, info.ps_perspective_sample_vgpr,
info.ps_perspective_centroid_vgpr,
info.ps_pos_x ? "true" : "false",
info.ps_perspective_centroid_vgpr, info.ps_pos_x ? "true" : "false",
info.ps_pos_y ? "true" : "false", info.ps_pos_z ? "true" : "false",
info.ps_pos_w ? "true" : "false", info.ps_front_face ? "true" : "false",
info.ps_ancillary ? "true" : "false",
info.ps_front_face_all_bits ? "true" : "false", info.ps_ancillary ? "true" : "false",
info.ps_sample_shading ? "true" : "false", info.ps_no_perspective ? "true" : "false",
info.ps_pixel_kill_enable ? "true" : "false", info.ps_early_z ? "true" : "false",
info.ps_execute_on_noop ? "true" : "false");
+1
View File
@@ -196,6 +196,7 @@ struct ShaderPixelInputInfo {
bool ps_pos_z = false;
bool ps_pos_w = false;
bool ps_front_face = false;
bool ps_front_face_all_bits = false;
bool ps_ancillary = false;
bool ps_no_perspective = false;
ShaderPixelParameterMode parameter_mode = ShaderPixelParameterMode::FirstVertex;
+95
View File
@@ -1383,6 +1383,7 @@ struct GraphicsCase {
std::vector<u32> vertices;
bool pixel_ancillary = false;
bool pixel_front_face = false;
bool pixel_front_face_all_bits = false;
u32 layers = 1;
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1;
bool pixel_position_w = false;
@@ -1834,6 +1835,7 @@ CompiledShader CompileFragmentCase(const GraphicsCase &test) {
: ShaderPixelParameterMode::FirstVertex;
pixel_info.ps_ancillary = test.pixel_ancillary;
pixel_info.ps_front_face = test.pixel_front_face;
pixel_info.ps_front_face_all_bits = test.pixel_front_face_all_bits;
pixel_info.ps_pos_w = test.pixel_position_w;
pixel_info.ps_depth_export_enable = test.pixel_depth_export;
pixel_info.ps_system_input_base = 2;
@@ -41299,6 +41301,83 @@ GraphicsCase GraphicsAncillarySampleId() {
return test;
}
// SPI_BARYC_CNTL[24] selects all-bits mode for uint/bool S_FRONT_FACE;
// float S_FRONT_FACE and __getSpecialVgprFloat(__v_front_face) leave it clear.
GraphicsCase GraphicsFrontFaceBits(bool reversed, bool all_bits) {
GraphicsCase test;
test.name = all_bits ? (reversed ? "GraphicsFrontFaceUintFront" : "GraphicsFrontFaceUintBack")
: (reversed ? "GraphicsFrontFaceFloatFront" : "GraphicsFrontFaceFloatBack");
test.pixel_front_face = true;
test.pixel_front_face_all_bits = all_bits;
// Preserve every raw bit as two finite, exactly representable integer-to-float
// results. Directly exporting the integer front value would export a NaN.
AppendVop3(&test.fragment_code, 0x148, 4, Vgpr(2), InlineU32(0), InlineU32(16));
AppendVop3(&test.fragment_code, 0x148, 5, Vgpr(2), InlineU32(16), InlineU32(16));
test.fragment_code.push_back(EncodeVop1(0x06, 0, Vgpr(4)));
test.fragment_code.push_back(EncodeVop1(0x06, 1, Vgpr(5)));
// Integer inputs test raw zero; float inputs test the sign.
test.fragment_code.push_back(EncodeVopc(all_bits ? 0xc2 : 0x04, InlineU32(0), 2));
AppendVMovLiteral(&test.fragment_code, 3, 0x3f800000u);
test.fragment_code.push_back(EncodeVop2(0x01, 2, InlineU32(0), 3));
test.fragment_code.push_back(EncodeExp0(0x00, 0xf));
test.fragment_code.push_back(EncodeExp1(0, 1, 2, 3));
AppendEnd(&test.fragment_code);
test.opcodes = {ShaderOpcode::V_BFE_U32, ShaderOpcode::V_CVT_F32_U32,
all_bits ? ShaderOpcode::V_CMP_EQ_U32 : ShaderOpcode::V_CMP_GT_F32,
ShaderOpcode::V_CNDMASK_B32, ShaderOpcode::V_MOV_B32,
ShaderOpcode::EXP, ShaderOpcode::S_ENDPGM};
test.vertices = {
0xbf800000u, 0xbf800000u, 0x3e800000u, 0x3f000000u, 0x3f400000u, 0x3f800000u,
0x40400000u, 0xbf800000u, 0x3e800000u, 0x3f000000u, 0x3f400000u, 0x3f800000u,
0xbf800000u, 0x40400000u, 0x3e800000u, 0x3f000000u, 0x3f400000u, 0x3f800000u};
if (reversed) {
std::swap_ranges(test.vertices.begin() + 6, test.vertices.begin() + 12,
test.vertices.begin() + 12);
}
// With the harness's positive-height viewport, the default triangle faces back.
const u32 bits = all_bits ? (reversed ? UINT32_MAX : 0u)
: (reversed ? 0x3f800000u : 0xbf800000u);
test.expected_pixel = {
std::bit_cast<u32>(static_cast<float>(bits & 0xffffu)),
std::bit_cast<u32>(static_cast<float>(bits >> 16u)),
reversed ? 0u : 0x3f800000u, 0x3f800000u};
return test;
}
void CheckPixelFrontFaceModes() {
constexpr const char *name = "PixelFrontFaceModes";
static const std::array<u32, 1> code = {EncodeSopp(0x01)};
HW::PixelShaderInfo regs{};
regs.ps_regs.data_addr = reinterpret_cast<uint64_t>(code.data());
ShaderMapUserData(regs.ps_regs.data_addr,
{.type = Prospero::ShaderBinaryType::kPs,
.code_size_bytes = static_cast<u32>(sizeof(code))});
HW::ShaderRegisters sh{};
sh.ps_input_ena = sh.ps_input_addr = 0x1002u;
const std::array<Prospero::ColorComponentMapping, 8> export_mapping{};
std::vector<u32> float_key;
for (bool all_bits : {false, true}) {
for (u32 other_bits : {0u, 0x120u}) {
sh.baryc_cntl = (all_bits ? 0x01000000u : 0u) | other_bits;
ShaderPixelInputInfo pixel{};
(void)PrepareProgram(regs, sh, export_mapping, pixel);
Require(name, "register format decoding",
pixel.ps_front_face && pixel.ps_front_face_all_bits == all_bits &&
pixel.ps_system_input_base == 2u,
"SPI_BARYC_CNTL[24] did not select the front-face format");
std::vector<u32> key;
BuildStageStaticKey(pixel, key);
if (!all_bits && other_bits == 0u) {
float_key = key;
}
Require(name, "format specialization",
(key != float_key) == all_bits,
"front-face formats must have distinct shader keys and ignore unrelated bits");
}
}
std::printf("[host] %-32s ok\n", name);
}
GraphicsCase GraphicsFlatInterpolatorExport() {
using O = ShaderOpcode;
@@ -42179,6 +42258,10 @@ std::vector<GraphicsCase> MakeGraphicsCases() {
GraphicsAncillaryLayer(false),
GraphicsAncillaryLayer(true),
GraphicsAncillarySampleId(),
GraphicsFrontFaceBits(false, false),
GraphicsFrontFaceBits(true, false),
GraphicsFrontFaceBits(false, true),
GraphicsFrontFaceBits(true, true),
GraphicsFlatInterpolatorExport(),
GraphicsDsAddtidScratchExport(),
GraphicsDirectSgprPushConstantExport(),
@@ -47959,6 +48042,17 @@ int main(int argc, char **argv) {
RunCase(&vulkan, SiblingSharedExitKeepsCapturedConditions());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--front-face-only") == 0) {
CheckPixelFrontFaceModes();
VulkanHarness vulkan;
for (bool all_bits : {false, true}) {
for (bool reversed : {false, true}) {
RunGraphicsCase(&vulkan, GraphicsFrontFaceBits(reversed, all_bits));
}
}
RunGraphicsCase(&vulkan, GraphicsAncillaryLayer(true));
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--position-w-only") == 0) {
VulkanHarness vulkan;
RunGraphicsCase(&vulkan, GraphicsPositionWExport());
@@ -48574,6 +48668,7 @@ int main(int argc, char **argv) {
CheckEmbeddedFetchLaneSpill();
CheckTessellationPrograms();
CheckPixelParameterAliases();
CheckPixelFrontFaceModes();
CheckRectListShaders();
CheckSigned16ImageStore(vulkan);
CheckBoundedFormattedBufferCopy(vulkan);