diff options
| author | 2018-12-24 02:24:38 -0300 | |
|---|---|---|
| committer | 2019-01-15 17:54:53 -0300 | |
| commit | e1fea1e0c594cc7c5a404e7006a4b4b2f29200ae (patch) | |
| tree | 269b0a3512d9ee97bbe7ef1e0df9ec8697056939 /src/video_core | |
| parent | shader_decode: Implement VMAD and VSETP (diff) | |
| download | yuzu-e1fea1e0c594cc7c5a404e7006a4b4b2f29200ae.tar.gz yuzu-e1fea1e0c594cc7c5a404e7006a4b4b2f29200ae.tar.xz yuzu-e1fea1e0c594cc7c5a404e7006a4b4b2f29200ae.zip | |
video_core: Implement IR based geometry shaders
Diffstat (limited to 'src/video_core')
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 14 | ||||
| -rw-r--r-- | src/video_core/shader/decode/other.cpp | 25 | ||||
| -rw-r--r-- | src/video_core/shader/glsl_decompiler.cpp | 70 | ||||
| -rw-r--r-- | src/video_core/shader/shader_ir.h | 3 |
4 files changed, 102 insertions, 10 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 59f45cde3..743a9c90e 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -80,16 +80,11 @@ ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { | |||
| 80 | // Version is intentionally skipped in shader generation, it's added by the lazy compilation. | 80 | // Version is intentionally skipped in shader generation, it's added by the lazy compilation. |
| 81 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | 81 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); |
| 82 | 82 | ||
| 83 | std::string out = out += "// Shader Unique Id: GS" + id + '\n'; | 83 | std::string out = "// Shader Unique Id: GS" + id + '\n'; |
| 84 | out += "#extension GL_ARB_separate_shader_objects : enable\n"; | 84 | out += "#extension GL_ARB_separate_shader_objects : enable\n"; |
| 85 | out += GetCommonDeclarations(); | 85 | out += GetCommonDeclarations(); |
| 86 | 86 | ||
| 87 | ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); | 87 | out += R"(out gl_PerVertex { |
| 88 | ProgramResult program = | ||
| 89 | Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry"); | ||
| 90 | |||
| 91 | out += R"( | ||
| 92 | out gl_PerVertex { | ||
| 93 | vec4 gl_Position; | 88 | vec4 gl_Position; |
| 94 | }; | 89 | }; |
| 95 | 90 | ||
| @@ -103,9 +98,12 @@ layout (std140) uniform gs_config { | |||
| 103 | }; | 98 | }; |
| 104 | )"; | 99 | )"; |
| 105 | 100 | ||
| 101 | ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); | ||
| 102 | ProgramResult program = | ||
| 103 | Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry"); | ||
| 106 | out += program.first; | 104 | out += program.first; |
| 107 | 105 | ||
| 108 | out = R"( | 106 | out += R"( |
| 109 | void main() { | 107 | void main() { |
| 110 | execute_geometry(); | 108 | execute_geometry(); |
| 111 | };)"; | 109 | };)"; |
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index 9630ef831..1918762b8 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp | |||
| @@ -12,6 +12,7 @@ namespace VideoCommon::Shader { | |||
| 12 | using Tegra::Shader::ConditionCode; | 12 | using Tegra::Shader::ConditionCode; |
| 13 | using Tegra::Shader::Instruction; | 13 | using Tegra::Shader::Instruction; |
| 14 | using Tegra::Shader::OpCode; | 14 | using Tegra::Shader::OpCode; |
| 15 | using Tegra::Shader::Register; | ||
| 15 | 16 | ||
| 16 | u32 ShaderIR::DecodeOther(BasicBlock& bb, u32 pc) { | 17 | u32 ShaderIR::DecodeOther(BasicBlock& bb, u32 pc) { |
| 17 | const Instruction instr = {program_code[pc]}; | 18 | const Instruction instr = {program_code[pc]}; |
| @@ -140,6 +141,30 @@ u32 ShaderIR::DecodeOther(BasicBlock& bb, u32 pc) { | |||
| 140 | SetRegister(bb, instr.gpr0, value); | 141 | SetRegister(bb, instr.gpr0, value); |
| 141 | break; | 142 | break; |
| 142 | } | 143 | } |
| 144 | case OpCode::Id::OUT_R: { | ||
| 145 | UNIMPLEMENTED_IF_MSG(instr.gpr20.Value() != Register::ZeroIndex, | ||
| 146 | "Stream buffer is not supported"); | ||
| 147 | |||
| 148 | if (instr.out.emit) { | ||
| 149 | // gpr0 is used to store the next address and gpr8 contains the address to emit. | ||
| 150 | // Hardware uses pointers here but we just ignore it | ||
| 151 | bb.push_back(Operation(OperationCode::EmitVertex)); | ||
| 152 | SetRegister(bb, instr.gpr0, Immediate(0)); | ||
| 153 | } | ||
| 154 | if (instr.out.cut) { | ||
| 155 | bb.push_back(Operation(OperationCode::EndPrimitive)); | ||
| 156 | } | ||
| 157 | break; | ||
| 158 | } | ||
| 159 | case OpCode::Id::ISBERD: { | ||
| 160 | UNIMPLEMENTED_IF(instr.isberd.o != 0); | ||
| 161 | UNIMPLEMENTED_IF(instr.isberd.skew != 0); | ||
| 162 | UNIMPLEMENTED_IF(instr.isberd.shift != Tegra::Shader::IsberdShift::None); | ||
| 163 | UNIMPLEMENTED_IF(instr.isberd.mode != Tegra::Shader::IsberdMode::None); | ||
| 164 | LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete"); | ||
| 165 | SetRegister(bb, instr.gpr0, GetRegister(instr.gpr8)); | ||
| 166 | break; | ||
| 167 | } | ||
| 143 | case OpCode::Id::DEPBAR: { | 168 | case OpCode::Id::DEPBAR: { |
| 144 | LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed"); | 169 | LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed"); |
| 145 | break; | 170 | break; |
diff --git a/src/video_core/shader/glsl_decompiler.cpp b/src/video_core/shader/glsl_decompiler.cpp index 2e9323df7..9b443e61a 100644 --- a/src/video_core/shader/glsl_decompiler.cpp +++ b/src/video_core/shader/glsl_decompiler.cpp | |||
| @@ -89,6 +89,22 @@ static std::string GetSwizzle(u32 elem) { | |||
| 89 | return swizzle; | 89 | return swizzle; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | /// Translate topology | ||
| 93 | static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { | ||
| 94 | switch (topology) { | ||
| 95 | case Tegra::Shader::OutputTopology::PointList: | ||
| 96 | return "points"; | ||
| 97 | case Tegra::Shader::OutputTopology::LineStrip: | ||
| 98 | return "line_strip"; | ||
| 99 | case Tegra::Shader::OutputTopology::TriangleStrip: | ||
| 100 | return "triangle_strip"; | ||
| 101 | default: | ||
| 102 | UNIMPLEMENTED_MSG("Unknown output topology: {}", static_cast<u32>(topology)); | ||
| 103 | return "points"; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Returns true if an object has to be treated as precise | ||
| 92 | static bool IsPrecise(Operation operand) { | 108 | static bool IsPrecise(Operation operand) { |
| 93 | const auto& meta = operand.GetMeta(); | 109 | const auto& meta = operand.GetMeta(); |
| 94 | 110 | ||
| @@ -115,6 +131,7 @@ public: | |||
| 115 | 131 | ||
| 116 | void Decompile() { | 132 | void Decompile() { |
| 117 | DeclareVertex(); | 133 | DeclareVertex(); |
| 134 | DeclareGeometry(); | ||
| 118 | DeclareRegisters(); | 135 | DeclareRegisters(); |
| 119 | DeclarePredicates(); | 136 | DeclarePredicates(); |
| 120 | DeclareLocalMemory(); | 137 | DeclareLocalMemory(); |
| @@ -212,6 +229,16 @@ private: | |||
| 212 | code.AddNewLine(); | 229 | code.AddNewLine(); |
| 213 | } | 230 | } |
| 214 | 231 | ||
| 232 | void DeclareGeometry() { | ||
| 233 | if (stage != ShaderStage::Geometry) | ||
| 234 | return; | ||
| 235 | |||
| 236 | const auto topology = GetTopologyName(header.common3.output_topology); | ||
| 237 | const auto max_vertices = std::to_string(header.common4.max_output_vertices); | ||
| 238 | code.AddLine("layout (" + topology + ", max_vertices = " + max_vertices + ") out;"); | ||
| 239 | code.AddNewLine(); | ||
| 240 | } | ||
| 241 | |||
| 215 | void DeclareRegisters() { | 242 | void DeclareRegisters() { |
| 216 | const auto& registers = ir.GetRegisters(); | 243 | const auto& registers = ir.GetRegisters(); |
| 217 | for (const u32 gpr : registers) { | 244 | for (const u32 gpr : registers) { |
| @@ -419,9 +446,24 @@ private: | |||
| 419 | const auto attribute = abuf->GetIndex(); | 446 | const auto attribute = abuf->GetIndex(); |
| 420 | const auto element = abuf->GetElement(); | 447 | const auto element = abuf->GetElement(); |
| 421 | 448 | ||
| 449 | const auto GeometryPass = [&](const std::string& name) { | ||
| 450 | if (stage == ShaderStage::Geometry && abuf->GetBuffer()) { | ||
| 451 | // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games | ||
| 452 | // set an 0x80000000 index for those and the shader fails to build. Find out why | ||
| 453 | // this happens and what's its intent. | ||
| 454 | return "gs_" + name + "[ftou(" + Visit(abuf->GetBuffer()) + | ||
| 455 | ") % MAX_VERTEX_INPUT]"; | ||
| 456 | } | ||
| 457 | return name; | ||
| 458 | }; | ||
| 459 | |||
| 422 | switch (attribute) { | 460 | switch (attribute) { |
| 423 | case Attribute::Index::Position: | 461 | case Attribute::Index::Position: |
| 424 | return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element); | 462 | if (stage != ShaderStage::Fragment) { |
| 463 | return GeometryPass("position") + GetSwizzle(element); | ||
| 464 | } else { | ||
| 465 | return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element); | ||
| 466 | } | ||
| 425 | case Attribute::Index::PointCoord: | 467 | case Attribute::Index::PointCoord: |
| 426 | switch (element) { | 468 | switch (element) { |
| 427 | case 0: | 469 | case 0: |
| @@ -460,7 +502,7 @@ private: | |||
| 460 | default: | 502 | default: |
| 461 | if (attribute >= Attribute::Index::Attribute_0 && | 503 | if (attribute >= Attribute::Index::Attribute_0 && |
| 462 | attribute <= Attribute::Index::Attribute_31) { | 504 | attribute <= Attribute::Index::Attribute_31) { |
| 463 | return GetInputAttribute(attribute) + GetSwizzle(abuf->GetElement()); | 505 | return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element); |
| 464 | } | 506 | } |
| 465 | break; | 507 | break; |
| 466 | } | 508 | } |
| @@ -1226,6 +1268,27 @@ private: | |||
| 1226 | return {}; | 1268 | return {}; |
| 1227 | } | 1269 | } |
| 1228 | 1270 | ||
| 1271 | std::string EmitVertex(Operation operation) { | ||
| 1272 | ASSERT_MSG(stage == ShaderStage::Geometry, | ||
| 1273 | "EmitVertex is expected to be used in a geometry shader."); | ||
| 1274 | |||
| 1275 | // If a geometry shader is attached, it will always flip (it's the last stage before | ||
| 1276 | // fragment). For more info about flipping, refer to gl_shader_gen.cpp. | ||
| 1277 | code.AddLine("position.xy *= viewport_flip.xy;"); | ||
| 1278 | code.AddLine("gl_Position = position;"); | ||
| 1279 | code.AddLine("position.w = 1.0;"); | ||
| 1280 | code.AddLine("EmitVertex();"); | ||
| 1281 | return {}; | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | std::string EndPrimitive(Operation operation) { | ||
| 1285 | ASSERT_MSG(stage == ShaderStage::Geometry, | ||
| 1286 | "EndPrimitive is expected to be used in a geometry shader."); | ||
| 1287 | |||
| 1288 | code.AddLine("EndPrimitive();"); | ||
| 1289 | return {}; | ||
| 1290 | } | ||
| 1291 | |||
| 1229 | std::string YNegate(Operation operation) { | 1292 | std::string YNegate(Operation operation) { |
| 1230 | // Config pack's third value is Y_NEGATE's state. | 1293 | // Config pack's third value is Y_NEGATE's state. |
| 1231 | return "uintBitsToFloat(config_pack[2])"; | 1294 | return "uintBitsToFloat(config_pack[2])"; |
| @@ -1361,6 +1424,9 @@ private: | |||
| 1361 | &Exit, | 1424 | &Exit, |
| 1362 | &Kil, | 1425 | &Kil, |
| 1363 | 1426 | ||
| 1427 | &EmitVertex, | ||
| 1428 | &EndPrimitive, | ||
| 1429 | |||
| 1364 | &YNegate, | 1430 | &YNegate, |
| 1365 | }; | 1431 | }; |
| 1366 | 1432 | ||
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 0ea2df6bd..5676d32a9 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -172,6 +172,9 @@ enum class OperationCode { | |||
| 172 | Exit, /// () -> void | 172 | Exit, /// () -> void |
| 173 | Kil, /// () -> void | 173 | Kil, /// () -> void |
| 174 | 174 | ||
| 175 | EmitVertex, /// () -> void | ||
| 176 | EndPrimitive, /// () -> void | ||
| 177 | |||
| 175 | YNegate, /// () -> float | 178 | YNegate, /// () -> float |
| 176 | 179 | ||
| 177 | Amount, | 180 | Amount, |