shader_recompiler: Add support for lowering geometry passthrough
Reuses most of the existing code for generating the gl_Layer passthrough. Fixes geometry in Nier: Automata on GPUs without HW passthrough support.
This commit is contained in:
parent
9e2997c4b6
commit
625a4af73a
|
@ -171,6 +171,64 @@ std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
|
||||||
}
|
}
|
||||||
return mapping;
|
return mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, const Shader::VaryingState &passthrough_mask, bool passthrough_position, std::optional<IR::Attribute> passthrough_layer_attr) {
|
||||||
|
for (u32 i = 0; i < program.output_vertices; i++) {
|
||||||
|
// Assign generics from input
|
||||||
|
for (u32 j = 0; j < 32; j++) {
|
||||||
|
if (!passthrough_mask.Generic(j)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4);
|
||||||
|
ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
|
||||||
|
ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
|
||||||
|
ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
|
||||||
|
ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passthrough_position) {
|
||||||
|
// Assign position from input
|
||||||
|
const IR::Attribute attr = IR::Attribute::PositionX;
|
||||||
|
ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
|
||||||
|
ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
|
||||||
|
ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
|
||||||
|
ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passthrough_layer_attr) {
|
||||||
|
// Assign layer
|
||||||
|
ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(*passthrough_layer_attr), ir.Imm32(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit vertex
|
||||||
|
ir.EmitVertex(ir.Imm32(0));
|
||||||
|
}
|
||||||
|
ir.EndPrimitive(ir.Imm32(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetOutputTopologyVertices(OutputTopology output_topology) {
|
||||||
|
switch (output_topology) {
|
||||||
|
case OutputTopology::PointList:
|
||||||
|
return 1;
|
||||||
|
case OutputTopology::LineStrip:
|
||||||
|
return 2;
|
||||||
|
default:
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) {
|
||||||
|
for (IR::Block *const block : program.blocks) {
|
||||||
|
for (IR::Inst &inst : block->Instructions()) {
|
||||||
|
if (inst.GetOpcode() == IR::Opcode::Epilogue) {
|
||||||
|
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||||
|
EmitGeometryPassthrough(ir, program, program.info.passthrough, program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||||
|
@ -198,6 +256,11 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||||
for (size_t i = 0; i < program.info.passthrough.mask.size(); ++i) {
|
for (size_t i = 0; i < program.info.passthrough.mask.size(); ++i) {
|
||||||
program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0;
|
program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!host_info.support_geometry_shader_passthrough) {
|
||||||
|
program.output_vertices = GetOutputTopologyVertices(program.output_topology);
|
||||||
|
LowerGeometryPassthrough(program, host_info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -342,17 +405,8 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
||||||
IR::Program program;
|
IR::Program program;
|
||||||
program.stage = Stage::Geometry;
|
program.stage = Stage::Geometry;
|
||||||
program.output_topology = output_topology;
|
program.output_topology = output_topology;
|
||||||
switch (output_topology) {
|
program.output_vertices = GetOutputTopologyVertices(output_topology);
|
||||||
case OutputTopology::PointList:
|
|
||||||
program.output_vertices = 1;
|
|
||||||
break;
|
|
||||||
case OutputTopology::LineStrip:
|
|
||||||
program.output_vertices = 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
program.output_vertices = 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
program.is_geometry_passthrough = false;
|
program.is_geometry_passthrough = false;
|
||||||
program.info.loads.mask = source_program.info.stores.mask;
|
program.info.loads.mask = source_program.info.stores.mask;
|
||||||
|
@ -366,35 +420,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
|
||||||
node.data.block = current_block;
|
node.data.block = current_block;
|
||||||
|
|
||||||
IR::IREmitter ir{*current_block};
|
IR::IREmitter ir{*current_block};
|
||||||
for (u32 i = 0; i < program.output_vertices; i++) {
|
EmitGeometryPassthrough(ir, program, program.info.stores, true, source_program.info.emulated_layer);
|
||||||
// Assign generics from input
|
|
||||||
for (u32 j = 0; j < 32; j++) {
|
|
||||||
if (!program.info.stores.Generic(j)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4);
|
|
||||||
ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
|
|
||||||
ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
|
|
||||||
ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
|
|
||||||
ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign position from input
|
|
||||||
const IR::Attribute attr = IR::Attribute::PositionX;
|
|
||||||
ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
|
|
||||||
ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
|
|
||||||
ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
|
|
||||||
ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
|
|
||||||
|
|
||||||
// Assign layer
|
|
||||||
ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(source_program.info.emulated_layer),
|
|
||||||
ir.Imm32(0));
|
|
||||||
|
|
||||||
// Emit vertex
|
|
||||||
ir.EmitVertex(ir.Imm32(0));
|
|
||||||
}
|
|
||||||
ir.EndPrimitive(ir.Imm32(0));
|
|
||||||
|
|
||||||
IR::Block* return_block{block_pool.Create(inst_pool)};
|
IR::Block* return_block{block_pool.Create(inst_pool)};
|
||||||
IR::IREmitter{*return_block}.Epilogue();
|
IR::IREmitter{*return_block}.Epilogue();
|
||||||
|
|
|
@ -16,6 +16,7 @@ struct HostTranslateInfo {
|
||||||
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
|
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
|
||||||
bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
|
bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
|
||||||
u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
|
u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
|
||||||
|
bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry passthrough shaders
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Shader
|
} // namespace Shader
|
||||||
|
|
Reference in New Issue