video_core: Implement IR based geometry shaders
This commit is contained in:
parent
a1b845b651
commit
e1fea1e0c5
@ -80,16 +80,11 @@ ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
|
|||||||
// Version is intentionally skipped in shader generation, it's added by the lazy compilation.
|
// Version is intentionally skipped in shader generation, it's added by the lazy compilation.
|
||||||
const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
|
const std::string id = fmt::format("{:016x}", setup.program.unique_identifier);
|
||||||
|
|
||||||
std::string out = out += "// Shader Unique Id: GS" + id + '\n';
|
std::string out = "// Shader Unique Id: GS" + id + '\n';
|
||||||
out += "#extension GL_ARB_separate_shader_objects : enable\n";
|
out += "#extension GL_ARB_separate_shader_objects : enable\n";
|
||||||
out += GetCommonDeclarations();
|
out += GetCommonDeclarations();
|
||||||
|
|
||||||
ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
|
out += R"(out gl_PerVertex {
|
||||||
ProgramResult program =
|
|
||||||
Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry");
|
|
||||||
|
|
||||||
out += R"(
|
|
||||||
out gl_PerVertex {
|
|
||||||
vec4 gl_Position;
|
vec4 gl_Position;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -103,9 +98,12 @@ layout (std140) uniform gs_config {
|
|||||||
};
|
};
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
|
||||||
|
ProgramResult program =
|
||||||
|
Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry");
|
||||||
out += program.first;
|
out += program.first;
|
||||||
|
|
||||||
out = R"(
|
out += R"(
|
||||||
void main() {
|
void main() {
|
||||||
execute_geometry();
|
execute_geometry();
|
||||||
};)";
|
};)";
|
||||||
|
@ -12,6 +12,7 @@ namespace VideoCommon::Shader {
|
|||||||
using Tegra::Shader::ConditionCode;
|
using Tegra::Shader::ConditionCode;
|
||||||
using Tegra::Shader::Instruction;
|
using Tegra::Shader::Instruction;
|
||||||
using Tegra::Shader::OpCode;
|
using Tegra::Shader::OpCode;
|
||||||
|
using Tegra::Shader::Register;
|
||||||
|
|
||||||
u32 ShaderIR::DecodeOther(BasicBlock& bb, u32 pc) {
|
u32 ShaderIR::DecodeOther(BasicBlock& bb, u32 pc) {
|
||||||
const Instruction instr = {program_code[pc]};
|
const Instruction instr = {program_code[pc]};
|
||||||
@ -140,6 +141,30 @@ u32 ShaderIR::DecodeOther(BasicBlock& bb, u32 pc) {
|
|||||||
SetRegister(bb, instr.gpr0, value);
|
SetRegister(bb, instr.gpr0, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OpCode::Id::OUT_R: {
|
||||||
|
UNIMPLEMENTED_IF_MSG(instr.gpr20.Value() != Register::ZeroIndex,
|
||||||
|
"Stream buffer is not supported");
|
||||||
|
|
||||||
|
if (instr.out.emit) {
|
||||||
|
// gpr0 is used to store the next address and gpr8 contains the address to emit.
|
||||||
|
// Hardware uses pointers here but we just ignore it
|
||||||
|
bb.push_back(Operation(OperationCode::EmitVertex));
|
||||||
|
SetRegister(bb, instr.gpr0, Immediate(0));
|
||||||
|
}
|
||||||
|
if (instr.out.cut) {
|
||||||
|
bb.push_back(Operation(OperationCode::EndPrimitive));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OpCode::Id::ISBERD: {
|
||||||
|
UNIMPLEMENTED_IF(instr.isberd.o != 0);
|
||||||
|
UNIMPLEMENTED_IF(instr.isberd.skew != 0);
|
||||||
|
UNIMPLEMENTED_IF(instr.isberd.shift != Tegra::Shader::IsberdShift::None);
|
||||||
|
UNIMPLEMENTED_IF(instr.isberd.mode != Tegra::Shader::IsberdMode::None);
|
||||||
|
LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete");
|
||||||
|
SetRegister(bb, instr.gpr0, GetRegister(instr.gpr8));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case OpCode::Id::DEPBAR: {
|
case OpCode::Id::DEPBAR: {
|
||||||
LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed");
|
LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed");
|
||||||
break;
|
break;
|
||||||
|
@ -89,6 +89,22 @@ static std::string GetSwizzle(u32 elem) {
|
|||||||
return swizzle;
|
return swizzle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Translate topology
|
||||||
|
static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
|
||||||
|
switch (topology) {
|
||||||
|
case Tegra::Shader::OutputTopology::PointList:
|
||||||
|
return "points";
|
||||||
|
case Tegra::Shader::OutputTopology::LineStrip:
|
||||||
|
return "line_strip";
|
||||||
|
case Tegra::Shader::OutputTopology::TriangleStrip:
|
||||||
|
return "triangle_strip";
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unknown output topology: {}", static_cast<u32>(topology));
|
||||||
|
return "points";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if an object has to be treated as precise
|
||||||
static bool IsPrecise(Operation operand) {
|
static bool IsPrecise(Operation operand) {
|
||||||
const auto& meta = operand.GetMeta();
|
const auto& meta = operand.GetMeta();
|
||||||
|
|
||||||
@ -115,6 +131,7 @@ public:
|
|||||||
|
|
||||||
void Decompile() {
|
void Decompile() {
|
||||||
DeclareVertex();
|
DeclareVertex();
|
||||||
|
DeclareGeometry();
|
||||||
DeclareRegisters();
|
DeclareRegisters();
|
||||||
DeclarePredicates();
|
DeclarePredicates();
|
||||||
DeclareLocalMemory();
|
DeclareLocalMemory();
|
||||||
@ -212,6 +229,16 @@ private:
|
|||||||
code.AddNewLine();
|
code.AddNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeclareGeometry() {
|
||||||
|
if (stage != ShaderStage::Geometry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto topology = GetTopologyName(header.common3.output_topology);
|
||||||
|
const auto max_vertices = std::to_string(header.common4.max_output_vertices);
|
||||||
|
code.AddLine("layout (" + topology + ", max_vertices = " + max_vertices + ") out;");
|
||||||
|
code.AddNewLine();
|
||||||
|
}
|
||||||
|
|
||||||
void DeclareRegisters() {
|
void DeclareRegisters() {
|
||||||
const auto& registers = ir.GetRegisters();
|
const auto& registers = ir.GetRegisters();
|
||||||
for (const u32 gpr : registers) {
|
for (const u32 gpr : registers) {
|
||||||
@ -419,9 +446,24 @@ private:
|
|||||||
const auto attribute = abuf->GetIndex();
|
const auto attribute = abuf->GetIndex();
|
||||||
const auto element = abuf->GetElement();
|
const auto element = abuf->GetElement();
|
||||||
|
|
||||||
|
const auto GeometryPass = [&](const std::string& name) {
|
||||||
|
if (stage == ShaderStage::Geometry && abuf->GetBuffer()) {
|
||||||
|
// TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
|
||||||
|
// set an 0x80000000 index for those and the shader fails to build. Find out why
|
||||||
|
// this happens and what's its intent.
|
||||||
|
return "gs_" + name + "[ftou(" + Visit(abuf->GetBuffer()) +
|
||||||
|
") % MAX_VERTEX_INPUT]";
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
};
|
||||||
|
|
||||||
switch (attribute) {
|
switch (attribute) {
|
||||||
case Attribute::Index::Position:
|
case Attribute::Index::Position:
|
||||||
return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element);
|
if (stage != ShaderStage::Fragment) {
|
||||||
|
return GeometryPass("position") + GetSwizzle(element);
|
||||||
|
} else {
|
||||||
|
return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element);
|
||||||
|
}
|
||||||
case Attribute::Index::PointCoord:
|
case Attribute::Index::PointCoord:
|
||||||
switch (element) {
|
switch (element) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -460,7 +502,7 @@ private:
|
|||||||
default:
|
default:
|
||||||
if (attribute >= Attribute::Index::Attribute_0 &&
|
if (attribute >= Attribute::Index::Attribute_0 &&
|
||||||
attribute <= Attribute::Index::Attribute_31) {
|
attribute <= Attribute::Index::Attribute_31) {
|
||||||
return GetInputAttribute(attribute) + GetSwizzle(abuf->GetElement());
|
return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1226,6 +1268,27 @@ private:
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string EmitVertex(Operation operation) {
|
||||||
|
ASSERT_MSG(stage == ShaderStage::Geometry,
|
||||||
|
"EmitVertex is expected to be used in a geometry shader.");
|
||||||
|
|
||||||
|
// If a geometry shader is attached, it will always flip (it's the last stage before
|
||||||
|
// fragment). For more info about flipping, refer to gl_shader_gen.cpp.
|
||||||
|
code.AddLine("position.xy *= viewport_flip.xy;");
|
||||||
|
code.AddLine("gl_Position = position;");
|
||||||
|
code.AddLine("position.w = 1.0;");
|
||||||
|
code.AddLine("EmitVertex();");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string EndPrimitive(Operation operation) {
|
||||||
|
ASSERT_MSG(stage == ShaderStage::Geometry,
|
||||||
|
"EndPrimitive is expected to be used in a geometry shader.");
|
||||||
|
|
||||||
|
code.AddLine("EndPrimitive();");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::string YNegate(Operation operation) {
|
std::string YNegate(Operation operation) {
|
||||||
// Config pack's third value is Y_NEGATE's state.
|
// Config pack's third value is Y_NEGATE's state.
|
||||||
return "uintBitsToFloat(config_pack[2])";
|
return "uintBitsToFloat(config_pack[2])";
|
||||||
@ -1361,6 +1424,9 @@ private:
|
|||||||
&Exit,
|
&Exit,
|
||||||
&Kil,
|
&Kil,
|
||||||
|
|
||||||
|
&EmitVertex,
|
||||||
|
&EndPrimitive,
|
||||||
|
|
||||||
&YNegate,
|
&YNegate,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -172,6 +172,9 @@ enum class OperationCode {
|
|||||||
Exit, /// () -> void
|
Exit, /// () -> void
|
||||||
Kil, /// () -> void
|
Kil, /// () -> void
|
||||||
|
|
||||||
|
EmitVertex, /// () -> void
|
||||||
|
EndPrimitive, /// () -> void
|
||||||
|
|
||||||
YNegate, /// () -> float
|
YNegate, /// () -> float
|
||||||
|
|
||||||
Amount,
|
Amount,
|
||||||
|
Loading…
Reference in New Issue
Block a user