summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2018-12-20 21:45:49 -0300
committerGravatar ReinUsesLisp2019-01-15 17:54:50 -0300
commit0c6fb456e0abae4f6552960543e2aabbb3985f7f (patch)
tree22171752fe9afe2c35984e7e7376ff89bafaa908 /src
parentshader_ir: Add condition code helper (diff)
downloadyuzu-0c6fb456e0abae4f6552960543e2aabbb3985f7f.tar.gz
yuzu-0c6fb456e0abae4f6552960543e2aabbb3985f7f.tar.xz
yuzu-0c6fb456e0abae4f6552960543e2aabbb3985f7f.zip
glsl_decompiler: Implementation
Diffstat (limited to 'src')
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/shader/glsl_decompiler.cpp1393
-rw-r--r--src/video_core/shader/glsl_decompiler.h88
3 files changed, 1483 insertions, 0 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index e9e324386..86b06487d 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -84,6 +84,8 @@ add_library(video_core STATIC
84 shader/decode/xmad.cpp 84 shader/decode/xmad.cpp
85 shader/decode/other.cpp 85 shader/decode/other.cpp
86 shader/decode.cpp 86 shader/decode.cpp
87 shader/glsl_decompiler.cpp
88 shader/glsl_decompiler.h
87 shader/shader_ir.cpp 89 shader/shader_ir.cpp
88 shader/shader_ir.h 90 shader/shader_ir.h
89 surface.cpp 91 surface.cpp
diff --git a/src/video_core/shader/glsl_decompiler.cpp b/src/video_core/shader/glsl_decompiler.cpp
new file mode 100644
index 000000000..46a48652d
--- /dev/null
+++ b/src/video_core/shader/glsl_decompiler.cpp
@@ -0,0 +1,1393 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6#include <string_view>
7#include <variant>
8
9#include <fmt/format.h>
10
11#include "common/alignment.h"
12#include "common/assert.h"
13#include "common/common_types.h"
14#include "video_core/engines/maxwell_3d.h"
15#include "video_core/shader/glsl_decompiler.h"
16#include "video_core/shader/shader_ir.h"
17
18namespace OpenGL::GLShader {
19
20using Tegra::Shader::Attribute;
21using Tegra::Shader::Header;
22using Tegra::Shader::IpaInterpMode;
23using Tegra::Shader::IpaMode;
24using Tegra::Shader::IpaSampleMode;
25using namespace VideoCommon::Shader;
26
27using Maxwell = Tegra::Engines::Maxwell3D::Regs;
28using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage;
29using Operation = const OperationNode&;
30
31enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
32constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 65536 / 16; // TODO(Rodrigo): Use rasterizer's value
33
34enum class Type { Bool, Float, Int, Uint, HalfFloat };
35
36class ShaderWriter {
37public:
38 void AddExpression(std::string_view text) {
39 DEBUG_ASSERT(scope >= 0);
40 if (!text.empty()) {
41 AppendIndentation();
42 }
43 shader_source += text;
44 }
45
46 void AddLine(std::string_view text) {
47 AddExpression(text);
48 AddNewLine();
49 }
50
51 void AddLine(char character) {
52 DEBUG_ASSERT(scope >= 0);
53 AppendIndentation();
54 shader_source += character;
55 AddNewLine();
56 }
57
58 void AddNewLine() {
59 DEBUG_ASSERT(scope >= 0);
60 shader_source += '\n';
61 }
62
63 std::string GenerateTemporal() {
64 std::string temporal = "tmp";
65 temporal += std::to_string(temporal_index++);
66 return temporal;
67 }
68
69 std::string GetResult() {
70 return std::move(shader_source);
71 }
72
73 s32 scope = 0;
74
75private:
76 void AppendIndentation() {
77 shader_source.append(static_cast<std::size_t>(scope) * 4, ' ');
78 }
79
80 std::string shader_source;
81 u32 temporal_index = 1;
82};
83
84/// Generates code to use for a swizzle operation.
85static std::string GetSwizzle(u32 elem) {
86 ASSERT(elem <= 3);
87 std::string swizzle = ".";
88 swizzle += "xyzw"[elem];
89 return swizzle;
90}
91
92static bool IsPrecise(Operation operand) {
93 const auto& meta = operand.GetMeta();
94
95 if (std::holds_alternative<MetaArithmetic>(meta)) {
96 return std::get<MetaArithmetic>(meta).precise;
97 }
98 if (std::holds_alternative<MetaHalfArithmetic>(meta)) {
99 return std::get<MetaHalfArithmetic>(meta).precise;
100 }
101 return false;
102}
103
104static bool IsPrecise(Node node) {
105 if (!std::holds_alternative<OperationNode>(*node)) {
106 return false;
107 }
108 return IsPrecise(std::get<OperationNode>(*node));
109}
110
111class GLSLDecompiler final {
112public:
113 explicit GLSLDecompiler(const ShaderIR& ir, ShaderStage stage, std::string suffix)
114 : ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {}
115
116 void Decompile() {
117 DeclareVertex();
118 DeclareRegisters();
119 DeclarePredicates();
120 DeclareLocalMemory();
121 DeclareInternalFlags();
122 DeclareInputAttributes();
123 DeclareOutputAttributes();
124 DeclareConstantBuffers();
125 DeclareSamplers();
126
127 code.AddLine("void execute_" + suffix + "() {");
128 ++code.scope;
129
130 // VM's program counter
131 const auto first_address = ir.GetBasicBlocks().begin()->first;
132 code.AddLine("uint jmp_to = " + std::to_string(first_address) + "u;");
133
134 // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
135 // unlikely that shaders will use 20 nested SSYs and PBKs.
136 constexpr u32 FLOW_STACK_SIZE = 20;
137 code.AddLine(fmt::format("uint flow_stack[{}];", FLOW_STACK_SIZE));
138 code.AddLine("uint flow_stack_top = 0u;");
139
140 code.AddLine("while (true) {");
141 ++code.scope;
142
143 code.AddLine("switch (jmp_to) {");
144
145 for (const auto& pair : ir.GetBasicBlocks()) {
146 const auto [address, bb] = pair;
147 code.AddLine(fmt::format("case 0x{:x}u: {{", address));
148 ++code.scope;
149
150 VisitBasicBlock(bb);
151
152 --code.scope;
153 code.AddLine('}');
154 }
155
156 code.AddLine("default: return;");
157 code.AddLine('}');
158
159 for (std::size_t i = 0; i < 2; ++i) {
160 --code.scope;
161 code.AddLine('}');
162 }
163 }
164
165 std::string GetResult() {
166 return code.GetResult();
167 }
168
169 ShaderEntries GetShaderEntries() const {
170 ShaderEntries entries;
171 for (const auto& cbuf : ir.GetConstantBuffers()) {
172 ConstBufferEntry desc(cbuf.second, stage, GetConstBufferBlock(cbuf.first), cbuf.first);
173 entries.const_buffers.push_back(desc);
174 }
175 for (const auto& sampler : ir.GetSamplers()) {
176 SamplerEntry desc(sampler, stage, GetSampler(sampler));
177 entries.samplers.push_back(desc);
178 }
179 entries.clip_distances = ir.GetClipDistances();
180 entries.shader_length = ir.GetLength();
181 return entries;
182 }
183
184private:
185 using OperationDecompilerFn = std::string (GLSLDecompiler::*)(Operation);
186 using OperationDecompilersArray =
187 std::array<OperationDecompilerFn, static_cast<std::size_t>(OperationCode::Amount)>;
188
189 void DeclareVertex() {
190 if (stage != ShaderStage::Vertex)
191 return;
192
193 bool clip_distances_declared = false;
194
195 code.AddLine("out gl_PerVertex {");
196 ++code.scope;
197
198 code.AddLine("vec4 gl_Position;");
199
200 for (const auto o : ir.GetOutputAttributes()) {
201 if (o == Attribute::Index::PointSize)
202 code.AddLine("float gl_PointSize;");
203 if (!clip_distances_declared && (o == Attribute::Index::ClipDistances0123 ||
204 o == Attribute::Index::ClipDistances4567)) {
205 code.AddLine("float gl_ClipDistance[];");
206 clip_distances_declared = true;
207 }
208 }
209
210 --code.scope;
211 code.AddLine("};");
212 code.AddNewLine();
213 }
214
215 void DeclareRegisters() {
216 const auto& registers = ir.GetRegisters();
217 for (const u32 gpr : registers) {
218 code.AddLine("float " + GetRegister(gpr) + " = 0;");
219 }
220 if (!registers.empty())
221 code.AddNewLine();
222 }
223
224 void DeclarePredicates() {
225 const auto& predicates = ir.GetPredicates();
226 for (const auto pred : predicates) {
227 code.AddLine("bool " + GetPredicate(pred) + " = false;");
228 }
229 if (!predicates.empty())
230 code.AddNewLine();
231 }
232
233 void DeclareLocalMemory() {
234 if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) {
235 const auto element_count = Common::AlignUp(local_memory_size, 4) / 4;
236 code.AddLine("float " + GetLocalMemory() + '[' + std::to_string(element_count) + "];");
237 code.AddNewLine();
238 }
239 }
240
241 void DeclareInternalFlags() {
242 for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
243 const InternalFlag flag_code = static_cast<InternalFlag>(flag);
244 code.AddLine("bool " + GetInternalFlag(flag_code) + " = false;");
245 }
246 code.AddNewLine();
247 }
248
249 std::string GetInputFlags(const IpaMode& input_mode) {
250 const IpaSampleMode sample_mode = input_mode.sampling_mode;
251 const IpaInterpMode interp_mode = input_mode.interpolation_mode;
252 std::string out;
253
254 switch (interp_mode) {
255 case IpaInterpMode::Flat:
256 out += "flat ";
257 break;
258 case IpaInterpMode::Linear:
259 out += "noperspective ";
260 break;
261 case IpaInterpMode::Perspective:
262 // Default, Smooth
263 break;
264 default:
265 UNIMPLEMENTED_MSG("Unhandled IPA interp mode: {}", static_cast<u32>(interp_mode));
266 }
267 switch (sample_mode) {
268 case IpaSampleMode::Centroid:
269 // It can be implemented with the "centroid " keyword in GLSL
270 UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode centroid");
271 break;
272 case IpaSampleMode::Default:
273 // Default, n/a
274 break;
275 default:
276 UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode: {}", static_cast<u32>(sample_mode));
277 }
278 return out;
279 }
280
281 void DeclareInputAttributes() {
282 const auto& attributes = ir.GetInputAttributes();
283 for (const auto element : attributes) {
284 const Attribute::Index index = element.first;
285 const IpaMode& input_mode = *element.second.begin();
286 if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) {
287 // Skip when it's not a generic attribute
288 continue;
289 }
290
291 ASSERT(element.second.size() > 0);
292 // UNIMPLEMENTED_IF_MSG(element.second.size() > 1,
293 // "Multiple input flag modes are not supported in GLSL");
294
295 // TODO(bunnei): Use proper number of elements for these
296 u32 idx = static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
297 if (stage != ShaderStage::Vertex) {
298 // If inputs are varyings, add an offset
299 idx += GENERIC_VARYING_START_LOCATION;
300 }
301
302 std::string attr = GetInputAttribute(index);
303 if (stage == ShaderStage::Geometry) {
304 attr = "gs_" + attr + "[]";
305 }
306 code.AddLine("layout (location = " + std::to_string(idx) + ") " +
307 GetInputFlags(input_mode) + "in vec4 " + attr + ';');
308 }
309 if (!attributes.empty())
310 code.AddNewLine();
311 }
312
313 void DeclareOutputAttributes() {
314 const auto& attributes = ir.GetOutputAttributes();
315 for (const auto index : attributes) {
316 if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) {
317 // Skip when it's not a generic attribute
318 continue;
319 }
320 // TODO(bunnei): Use proper number of elements for these
321 const auto idx = static_cast<u32>(index) -
322 static_cast<u32>(Attribute::Index::Attribute_0) +
323 GENERIC_VARYING_START_LOCATION;
324 code.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " +
325 GetOutputAttribute(index) + ';');
326 }
327 if (!attributes.empty())
328 code.AddNewLine();
329 }
330
331 void DeclareConstantBuffers() {
332 for (const auto& entry : ir.GetConstantBuffers()) {
333 const auto [index, size] = entry;
334 code.AddLine("layout (std140) uniform " + GetConstBufferBlock(index) + " {");
335 code.AddLine(" vec4 " + GetConstBuffer(index) + "[MAX_CONSTBUFFER_ELEMENTS];");
336 code.AddLine("};");
337 code.AddNewLine();
338 }
339 }
340
341 void DeclareSamplers() {
342 const auto& samplers = ir.GetSamplers();
343 for (const auto& sampler : samplers) {
344 std::string sampler_type = [&]() {
345 switch (sampler.GetType()) {
346 case Tegra::Shader::TextureType::Texture1D:
347 return "sampler1D";
348 case Tegra::Shader::TextureType::Texture2D:
349 return "sampler2D";
350 case Tegra::Shader::TextureType::Texture3D:
351 return "sampler3D";
352 case Tegra::Shader::TextureType::TextureCube:
353 return "samplerCube";
354 default:
355 UNREACHABLE();
356 }
357 }();
358 if (sampler.IsArray())
359 sampler_type += "Array";
360 if (sampler.IsShadow())
361 sampler_type += "Shadow";
362
363 code.AddLine("uniform " + sampler_type + ' ' + GetSampler(sampler) + ';');
364 }
365 if (!samplers.empty())
366 code.AddNewLine();
367 }
368
369 void VisitBasicBlock(const BasicBlock& bb) {
370 for (const Node node : bb) {
371 if (const std::string expr = Visit(node); !expr.empty()) {
372 code.AddLine(expr);
373 }
374 }
375 }
376
377 std::string Visit(Node node) {
378 if (const auto operation = std::get_if<OperationNode>(node)) {
379 const auto operation_index = static_cast<std::size_t>(operation->GetCode());
380 const auto decompiler = operation_decompilers[operation_index];
381 if (decompiler == nullptr) {
382 UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index);
383 }
384 return (this->*decompiler)(*operation);
385
386 } else if (const auto gpr = std::get_if<GprNode>(node)) {
387 const u32 index = gpr->GetIndex();
388 if (index == RZ) {
389 return "0";
390 }
391 return GetRegister(index);
392
393 } else if (const auto immediate = std::get_if<ImmediateNode>(node)) {
394 const u32 value = immediate->GetValue();
395 if (value < 10) {
396 // For eyecandy avoid using hex numbers on single digits
397 return fmt::format("utof({}u)", immediate->GetValue());
398 }
399 return fmt::format("utof(0x{:x}u)", immediate->GetValue());
400
401 } else if (const auto predicate = std::get_if<PredicateNode>(node)) {
402 const auto value = [&]() -> std::string {
403 switch (const auto index = predicate->GetIndex(); index) {
404 case Tegra::Shader::Pred::UnusedIndex:
405 return "true";
406 case Tegra::Shader::Pred::NeverExecute:
407 return "false";
408 default:
409 return GetPredicate(index);
410 }
411 }();
412 if (predicate->IsNegated()) {
413 return "!(" + value + ')';
414 }
415 return value;
416
417 } else if (const auto abuf = std::get_if<AbufNode>(node)) {
418 const auto attribute = abuf->GetIndex();
419 const auto element = abuf->GetElement();
420
421 switch (attribute) {
422 case Attribute::Index::Position:
423 return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element);
424 case Attribute::Index::PointCoord:
425 switch (element) {
426 case 0:
427 return "gl_PointCoord.x";
428 case 1:
429 return "gl_PointCoord.y";
430 case 2:
431 case 3:
432 return "0";
433 }
434 UNREACHABLE();
435 return "0";
436 case Attribute::Index::TessCoordInstanceIDVertexID:
437 // TODO(Subv): Find out what the values are for the first two elements when inside a
438 // vertex shader, and what's the value of the fourth element when inside a Tess Eval
439 // shader.
440 ASSERT(stage == ShaderStage::Vertex);
441 switch (element) {
442 case 2:
443 // Config pack's first value is instance_id.
444 return "uintBitsToFloat(config_pack[0])";
445 case 3:
446 return "uintBitsToFloat(gl_VertexID)";
447 }
448 UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
449 return "0";
450 case Attribute::Index::FrontFacing:
451 // TODO(Subv): Find out what the values are for the other elements.
452 ASSERT(stage == ShaderStage::Fragment);
453 switch (element) {
454 case 3:
455 return "itof(gl_FrontFacing ? -1 : 0)";
456 }
457 UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
458 return "0";
459 default:
460 if (attribute >= Attribute::Index::Attribute_0 &&
461 attribute <= Attribute::Index::Attribute_31) {
462 return GetInputAttribute(attribute) + GetSwizzle(abuf->GetElement());
463 }
464 break;
465 }
466 UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
467
468 } else if (const auto cbuf = std::get_if<CbufNode>(node)) {
469 const Node offset = cbuf->GetOffset();
470 if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
471 // Direct access
472 const u32 offset_imm = immediate->GetValue();
473 return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), offset_imm / 4,
474 offset_imm % 4);
475
476 } else if (std::holds_alternative<OperationNode>(*offset)) {
477 // Indirect access
478 const std::string final_offset = code.GenerateTemporal();
479 code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4) & " +
480 std::to_string(MAX_CONSTBUFFER_ELEMENTS - 1) + ';');
481 return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
482 final_offset, final_offset);
483
484 } else {
485 UNREACHABLE_MSG("Unmanaged offset node type");
486 }
487
488 } else if (const auto lmem = std::get_if<LmemNode>(node)) {
489 return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
490
491 } else if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) {
492 return GetInternalFlag(internal_flag->GetFlag());
493
494 } else if (const auto conditional = std::get_if<ConditionalNode>(node)) {
495 // It's invalid to call conditional on nested nodes, use an operation instead
496 code.AddLine("if (" + Visit(conditional->GetCondition()) + ") {");
497 ++code.scope;
498
499 VisitBasicBlock(conditional->GetCode());
500
501 --code.scope;
502 code.AddLine('}');
503 return {};
504
505 } else if (const auto comment = std::get_if<CommentNode>(node)) {
506 return "// " + comment->GetText();
507 }
508 UNREACHABLE();
509 }
510
511 std::string ApplyPrecise(Operation operation, const std::string& value) {
512 if (!IsPrecise(operation)) {
513 return value;
514 }
515 // There's a bug in NVidia's proprietary drivers that makes precise fail on fragment shaders
516 const std::string precise = stage != ShaderStage::Fragment ? "precise " : "";
517
518 const std::string temporal = code.GenerateTemporal();
519 code.AddLine(precise + "float " + temporal + " = " + value + ';');
520 return temporal;
521 }
522
523 std::string VisitOperand(Operation operation, std::size_t operand_index) {
524 const auto& operand = operation[operand_index];
525 const bool parent_precise = IsPrecise(operation);
526 const bool child_precise = IsPrecise(operand);
527 const bool child_trivial = !std::holds_alternative<OperationNode>(*operand);
528 if (!parent_precise || child_precise || child_trivial) {
529 return Visit(operand);
530 }
531
532 const std::string temporal = code.GenerateTemporal();
533 code.AddLine("float " + temporal + " = " + Visit(operand) + ';');
534 return temporal;
535 }
536
537 std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) {
538 std::string value = VisitOperand(operation, operand_index);
539
540 switch (type) {
541 case Type::Bool:
542 case Type::Float:
543 return value;
544 case Type::Int:
545 return "ftoi(" + value + ')';
546 case Type::Uint:
547 return "ftou(" + value + ')';
548 case Type::HalfFloat:
549 if (!std::holds_alternative<MetaHalfArithmetic>(operation.GetMeta())) {
550 value = "toHalf2(" + value + ')';
551 }
552
553 const auto& half_meta = std::get<MetaHalfArithmetic>(operation.GetMeta());
554 switch (half_meta.types.at(operand_index)) {
555 case Tegra::Shader::HalfType::H0_H1:
556 return "toHalf2(" + value + ')';
557 case Tegra::Shader::HalfType::F32:
558 return "vec2(" + value + ')';
559 case Tegra::Shader::HalfType::H0_H0:
560 return "vec2(toHalf2(" + value + ")[0])";
561 case Tegra::Shader::HalfType::H1_H1:
562 return "vec2(toHalf2(" + value + ")[1])";
563 }
564 }
565 UNREACHABLE();
566 }
567
568 std::string BitwiseCastResult(std::string value, Type type, bool needs_parenthesis = false) {
569 switch (type) {
570 case Type::Bool:
571 case Type::Float:
572 if (needs_parenthesis) {
573 return '(' + value + ')';
574 }
575 return value;
576 case Type::Int:
577 return "itof(" + value + ')';
578 case Type::Uint:
579 return "utof(" + value + ')';
580 case Type::HalfFloat:
581 return "fromHalf2(" + value + ')';
582 }
583 UNREACHABLE();
584 }
585
586 std::string GenerateUnary(Operation operation, const std::string& func, Type result_type,
587 Type type_a, bool needs_parenthesis = true) {
588 return ApplyPrecise(operation,
589 BitwiseCastResult(func + '(' + VisitOperand(operation, 0, type_a) + ')',
590 result_type, needs_parenthesis));
591 }
592
593 std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type,
594 Type type_a, Type type_b) {
595 const std::string op_a = VisitOperand(operation, 0, type_a);
596 const std::string op_b = VisitOperand(operation, 1, type_b);
597
598 return ApplyPrecise(
599 operation, BitwiseCastResult('(' + op_a + ' ' + func + ' ' + op_b + ')', result_type));
600 }
601
602 std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type,
603 Type type_a, Type type_b) {
604 const std::string op_a = VisitOperand(operation, 0, type_a);
605 const std::string op_b = VisitOperand(operation, 1, type_b);
606
607 return ApplyPrecise(operation,
608 BitwiseCastResult(func + '(' + op_a + ", " + op_b + ')', result_type));
609 }
610
611 std::string GenerateTernary(Operation operation, const std::string& func, Type result_type,
612 Type type_a, Type type_b, Type type_c) {
613 const std::string op_a = VisitOperand(operation, 0, type_a);
614 const std::string op_b = VisitOperand(operation, 1, type_b);
615 const std::string op_c = VisitOperand(operation, 2, type_c);
616
617 return ApplyPrecise(
618 operation,
619 BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + op_c + ')', result_type));
620 }
621
622 std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type,
623 Type type_a, Type type_b, Type type_c, Type type_d) {
624 const std::string op_a = VisitOperand(operation, 0, type_a);
625 const std::string op_b = VisitOperand(operation, 1, type_b);
626 const std::string op_c = VisitOperand(operation, 2, type_c);
627 const std::string op_d = VisitOperand(operation, 3, type_d);
628
629 return ApplyPrecise(operation, BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " +
630 op_c + ", " + op_d + ')',
631 result_type));
632 }
633
634 std::string GenerateTexture(Operation operation, const std::string& func,
635 const std::string& extra_cast = "") {
636 constexpr std::array<const char*, 4> coord_constructors = {"float", "vec2", "vec3", "vec4"};
637
638 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
639 const auto count = static_cast<u32>(operation.GetOperandsCount());
640
641 std::string expr = func;
642 expr += '(';
643 expr += GetSampler(meta.sampler);
644 expr += ", ";
645
646 expr += coord_constructors[meta.coords_count - 1];
647 expr += '(';
648 for (u32 i = 0; i < count; ++i) {
649 const bool is_extra = i >= meta.coords_count;
650 const bool do_cast = is_extra && !extra_cast.empty();
651 if (do_cast) {
652 expr += extra_cast;
653 expr += '(';
654 }
655 expr += Visit(operation[i]);
656 if (do_cast) {
657 expr += ')';
658 }
659 if (i + 1 == meta.coords_count) {
660 expr += ')';
661 }
662 if (i + 1 < count) {
663 expr += ", ";
664 }
665 }
666 expr += ')';
667 return expr;
668 }
669
670 std::string Assign(Operation operation) {
671 const Node dest = operation[0];
672 const Node src = operation[1];
673
674 std::string target;
675 if (const auto gpr = std::get_if<GprNode>(dest)) {
676 if (gpr->GetIndex() == RZ) {
677 // Writing to RZ is a no op
678 return {};
679 }
680 target = GetRegister(gpr->GetIndex());
681
682 } else if (const auto abuf = std::get_if<AbufNode>(dest)) {
683 target = [&]() -> std::string {
684 switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) {
685 case Attribute::Index::Position:
686 return "position" + GetSwizzle(abuf->GetElement());
687 case Attribute::Index::PointSize:
688 return "gl_PointSize";
689 case Attribute::Index::ClipDistances0123:
690 return "gl_ClipDistance[" + std::to_string(abuf->GetElement()) + ']';
691 case Attribute::Index::ClipDistances4567:
692 return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']';
693 default:
694 if (attribute >= Attribute::Index::Attribute_0 &&
695 attribute <= Attribute::Index::Attribute_31) {
696 return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement());
697 }
698 UNIMPLEMENTED_MSG("Unhandled output attribute: {}",
699 static_cast<u32>(attribute));
700 }
701 }();
702
703 } else if (const auto lmem = std::get_if<LmemNode>(dest)) {
704 target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]";
705
706 } else {
707 UNREACHABLE_MSG("Assign called without a proper target");
708 }
709
710 code.AddLine(target + " = " + Visit(src) + ';');
711 return {};
712 }
713
714 std::string AssignComposite(Operation operation) {
715 const auto& meta = std::get<MetaComponents>(operation.GetMeta());
716
717 const std::string composite = code.GenerateTemporal();
718 code.AddLine("vec4 " + composite + " = " + Visit(operation[0]) + ';');
719
720 constexpr u32 composite_size = 4;
721 for (u32 i = 0; i < composite_size; ++i) {
722 const auto gpr = std::get<GprNode>(*operation[i + 1]).GetIndex();
723 if (gpr == RZ) {
724 continue;
725 }
726 code.AddLine(GetRegister(gpr) + " = " + composite +
727 GetSwizzle(meta.GetSourceComponent(i)) + ';');
728 }
729 return {};
730 }
731
732 std::string Composite(Operation operation) {
733 std::string value = "vec4(";
734 for (std::size_t i = 0; i < 4; ++i) {
735 value += Visit(operation[i]);
736 if (i < 3)
737 value += ", ";
738 }
739 value += ')';
740 return value;
741 }
742
743 template <Type type>
744 std::string Add(Operation operation) {
745 return GenerateBinaryInfix(operation, "+", type, type, type);
746 }
747
748 template <Type type>
749 std::string Mul(Operation operation) {
750 return GenerateBinaryInfix(operation, "*", type, type, type);
751 }
752
753 template <Type type>
754 std::string Div(Operation operation) {
755 return GenerateBinaryInfix(operation, "/", type, type, type);
756 }
757
758 std::string FFma(Operation operation) {
759 return GenerateTernary(operation, "fma", Type::Float, Type::Float, Type::Float,
760 Type::Float);
761 }
762
763 template <Type type>
764 std::string Negate(Operation operation) {
765 return GenerateUnary(operation, "-", type, type, true);
766 }
767
768 template <Type type>
769 std::string Absolute(Operation operation) {
770 return GenerateUnary(operation, "abs", type, type, false);
771 }
772
773 std::string FClamp(Operation operation) {
774 return GenerateTernary(operation, "clamp", Type::Float, Type::Float, Type::Float,
775 Type::Float);
776 }
777
778 template <Type type>
779 std::string Min(Operation operation) {
780 return GenerateBinaryCall(operation, "min", type, type, type);
781 }
782
783 template <Type type>
784 std::string Max(Operation operation) {
785 return GenerateBinaryCall(operation, "max", type, type, type);
786 }
787
788 std::string Select(Operation operation) {
789 const std::string condition = Visit(operation[0]);
790 const std::string true_case = Visit(operation[1]);
791 const std::string false_case = Visit(operation[2]);
792 return ApplyPrecise(operation,
793 '(' + condition + " ? " + true_case + " : " + false_case + ')');
794 }
795
796 std::string FCos(Operation operation) {
797 return GenerateUnary(operation, "cos", Type::Float, Type::Float, false);
798 }
799
800 std::string FSin(Operation operation) {
801 return GenerateUnary(operation, "sin", Type::Float, Type::Float, false);
802 }
803
804 std::string FExp2(Operation operation) {
805 return GenerateUnary(operation, "exp2", Type::Float, Type::Float, false);
806 }
807
808 std::string FLog2(Operation operation) {
809 return GenerateUnary(operation, "log2", Type::Float, Type::Float, false);
810 }
811
812 std::string FInverseSqrt(Operation operation) {
813 return GenerateUnary(operation, "inversesqrt", Type::Float, Type::Float, false);
814 }
815
816 std::string FSqrt(Operation operation) {
817 return GenerateUnary(operation, "sqrt", Type::Float, Type::Float, false);
818 }
819
820 std::string FRoundEven(Operation operation) {
821 return GenerateUnary(operation, "roundEven", Type::Float, Type::Float, false);
822 }
823
824 std::string FFloor(Operation operation) {
825 return GenerateUnary(operation, "floor", Type::Float, Type::Float, false);
826 }
827
828 std::string FCeil(Operation operation) {
829 return GenerateUnary(operation, "ceil", Type::Float, Type::Float, false);
830 }
831
832 std::string FTrunc(Operation operation) {
833 return GenerateUnary(operation, "trunc", Type::Float, Type::Float, false);
834 }
835
836 template <Type type>
837 std::string FCastInteger(Operation operation) {
838 return GenerateUnary(operation, "float", Type::Float, type, false);
839 }
840
841 std::string ICastFloat(Operation operation) {
842 return GenerateUnary(operation, "int", Type::Int, Type::Float, false);
843 }
844
845 std::string ICastUnsigned(Operation operation) {
846 return GenerateUnary(operation, "int", Type::Int, Type::Uint, false);
847 }
848
849 template <Type type>
850 std::string LogicalShiftLeft(Operation operation) {
851 return GenerateBinaryInfix(operation, "<<", type, type, Type::Uint);
852 }
853
854 std::string ILogicalShiftRight(Operation operation) {
855 const std::string op_a = VisitOperand(operation, 0, Type::Uint);
856 const std::string op_b = VisitOperand(operation, 1, Type::Uint);
857
858 return ApplyPrecise(operation,
859 BitwiseCastResult("int(" + op_a + " >> " + op_b + ')', Type::Int));
860 }
861
862 std::string IArithmeticShiftRight(Operation operation) {
863 return GenerateBinaryInfix(operation, ">>", Type::Int, Type::Int, Type::Uint);
864 }
865
866 template <Type type>
867 std::string BitwiseAnd(Operation operation) {
868 return GenerateBinaryInfix(operation, "&", type, type, type);
869 }
870
871 template <Type type>
872 std::string BitwiseOr(Operation operation) {
873 return GenerateBinaryInfix(operation, "|", type, type, type);
874 }
875
876 template <Type type>
877 std::string BitwiseXor(Operation operation) {
878 return GenerateBinaryInfix(operation, "^", type, type, type);
879 }
880
881 template <Type type>
882 std::string BitwiseNot(Operation operation) {
883 return GenerateUnary(operation, "~", type, type, false);
884 }
885
886 std::string UCastFloat(Operation operation) {
887 return GenerateUnary(operation, "uint", Type::Uint, Type::Float, false);
888 }
889
890 std::string UCastSigned(Operation operation) {
891 return GenerateUnary(operation, "uint", Type::Uint, Type::Int, false);
892 }
893
894 std::string UShiftRight(Operation operation) {
895 return GenerateBinaryInfix(operation, ">>", Type::Uint, Type::Uint, Type::Uint);
896 }
897
898 template <Type type>
899 std::string BitfieldInsert(Operation operation) {
900 return GenerateQuaternary(operation, "bitfieldInsert", type, type, type, Type::Int,
901 Type::Int);
902 }
903
904 std::string HNegate(Operation operation) {
905 const auto GetNegate = [&](std::size_t index) -> std::string {
906 if (const auto pred = std::get_if<PredicateNode>(operation[index])) {
907 if (!pred->IsNegated()) {
908 switch (pred->GetIndex()) {
909 case Tegra::Shader::Pred::UnusedIndex:
910 return "-1";
911 case Tegra::Shader::Pred::NeverExecute:
912 return "1";
913 }
914 }
915 }
916 return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1";
917 };
918 const std::string value = '(' + VisitOperand(operation, 0, Type::HalfFloat) + " * vec2(" +
919 GetNegate(1) + ", " + GetNegate(2) + "))";
920 return BitwiseCastResult(value, Type::HalfFloat);
921 }
922
923 std::string HMergeF32(Operation operation) {
924 return "float(toHalf2(" + Visit(operation[0]) + ")[0])";
925 }
926
927 std::string HMergeH0(Operation operation) {
928 return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[1], toHalf2(" +
929 Visit(operation[1]) + ")[0]))";
930 }
931
932 std::string HMergeH1(Operation operation) {
933 return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[0], toHalf2(" +
934 Visit(operation[1]) + ")[1]))";
935 }
936
937 template <Type type>
938 std::string LogicalLessThan(Operation operation) {
939 return GenerateBinaryInfix(operation, "<", Type::Bool, type, type);
940 }
941
942 template <Type type>
943 std::string LogicalEqual(Operation operation) {
944 return GenerateBinaryInfix(operation, "==", Type::Bool, type, type);
945 }
946
947 template <Type type>
948 std::string LogicalLessEqual(Operation operation) {
949 return GenerateBinaryInfix(operation, "<=", Type::Bool, type, type);
950 }
951
952 template <Type type>
953 std::string LogicalGreaterThan(Operation operation) {
954 return GenerateBinaryInfix(operation, ">", Type::Bool, type, type);
955 }
956
957 template <Type type>
958 std::string LogicalNotEqual(Operation operation) {
959 return GenerateBinaryInfix(operation, "!=", Type::Bool, type, type);
960 }
961
962 template <Type type>
963 std::string LogicalGreaterEqual(Operation operation) {
964 return GenerateBinaryInfix(operation, ">=", Type::Bool, type, type);
965 }
966
967 std::string LogicalFIsNan(Operation operation) {
968 return GenerateUnary(operation, "isnan", Type::Bool, Type::Float, false);
969 }
970
971 std::string LogicalAssign(Operation operation) {
972 const Node dest = operation[0];
973 const Node src = operation[1];
974
975 std::string target;
976
977 if (const auto pred = std::get_if<PredicateNode>(dest)) {
978 ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment");
979
980 const auto index = pred->GetIndex();
981 switch (index) {
982 case Tegra::Shader::Pred::NeverExecute:
983 case Tegra::Shader::Pred::UnusedIndex:
984 // Writing to these predicates is a no-op
985 return {};
986 }
987 target = GetPredicate(index);
988 } else if (const auto flag = std::get_if<InternalFlagNode>(dest)) {
989 target = GetInternalFlag(flag->GetFlag());
990 }
991
992 code.AddLine(target + " = " + Visit(src) + ';');
993 return {};
994 }
995
996 std::string LogicalAnd(Operation operation) {
997 return GenerateBinaryInfix(operation, "&&", Type::Bool, Type::Bool, Type::Bool);
998 }
999
1000 std::string LogicalOr(Operation operation) {
1001 return GenerateBinaryInfix(operation, "||", Type::Bool, Type::Bool, Type::Bool);
1002 }
1003
1004 std::string LogicalXor(Operation operation) {
1005 return GenerateBinaryInfix(operation, "^^", Type::Bool, Type::Bool, Type::Bool);
1006 }
1007
1008 std::string LogicalNegate(Operation operation) {
1009 return GenerateUnary(operation, "!", Type::Bool, Type::Bool, false);
1010 }
1011
1012 std::string LogicalHComparison(Operation operation, const std::string& func) {
1013 const auto& meta = std::get<MetaHalfArithmetic>(operation.GetMeta());
1014 const std::string op_a = VisitOperand(operation, 0, Type::HalfFloat);
1015 const std::string op_b = VisitOperand(operation, 1, Type::HalfFloat);
1016
1017 std::string value = meta.and_comparison ? "all" : "any";
1018 value += '(' + func + '(' + op_a + ", " + op_b + "))";
1019 return value;
1020 }
1021
1022 std::string LogicalHLessThan(Operation operation) {
1023 return LogicalHComparison(operation, "lessThan");
1024 }
1025
1026 std::string LogicalHEqual(Operation operation) {
1027 return LogicalHComparison(operation, "equal");
1028 }
1029
1030 std::string LogicalHLessEqual(Operation operation) {
1031 return LogicalHComparison(operation, "lessThanEqual");
1032 }
1033
1034 std::string LogicalHGreaterThan(Operation operation) {
1035 return LogicalHComparison(operation, "greaterThan");
1036 }
1037
1038 std::string LogicalHNotEqual(Operation operation) {
1039 return LogicalHComparison(operation, "notEqual");
1040 }
1041
1042 std::string LogicalHGreaterEqual(Operation operation) {
1043 return LogicalHComparison(operation, "greaterThanEqual");
1044 }
1045
1046 std::string F4Texture(Operation operation) {
1047 std::string expr = GenerateTexture(operation, "texture");
1048 if (std::get<MetaTexture>(operation.GetMeta()).sampler.IsShadow()) {
1049 expr = "vec4(" + expr + ')';
1050 }
1051 return expr;
1052 }
1053
1054 std::string F4TextureLod(Operation operation) {
1055 std::string expr = GenerateTexture(operation, "textureLod");
1056 if (std::get<MetaTexture>(operation.GetMeta()).sampler.IsShadow()) {
1057 expr = "vec4(" + expr + ')';
1058 }
1059 return expr;
1060 }
1061
1062 std::string F4TextureGather(Operation operation) {
1063 return GenerateTexture(operation, "textureGather", "int");
1064 }
1065
1066 std::string F4TextureQueryDimensions(Operation operation) {
1067 const auto& meta = std::get<MetaTexture>(operation.GetMeta());
1068 const std::string sampler = GetSampler(meta.sampler);
1069 const std::string lod = VisitOperand(operation, 0, Type::Int);
1070
1071 const std::string sizes = code.GenerateTemporal();
1072 code.AddLine("ivec2 " + sizes + " = textureSize(" + sampler + ", " + lod + ");");
1073
1074 const std::string mip_level = "textureQueryLevels(" + sampler + ')';
1075
1076 return "itof(ivec4(" + sizes + ", 0, " + mip_level + "))";
1077 }
1078
1079 std::string F4TextureQueryLod(Operation operation) {
1080 const std::string tmp = code.GenerateTemporal();
1081 code.AddLine("vec2 " + tmp + " = " + GenerateTexture(operation, "textureQueryLod") +
1082 " * vec2(256);");
1083
1084 return "vec4(itof(int(" + tmp + ".y)), utof(uint(" + tmp + ".x)), 0, 0)";
1085 }
1086
1087 std::string Ipa(Operation operation) {
1088 const auto& attribute = operation[0];
1089 // TODO(Rodrigo): Special IPA attribute interactions
1090 return Visit(attribute);
1091 }
1092
1093 std::string Bra(Operation operation) {
1094 const auto target = std::get<ImmediateNode>(*operation[0]);
1095 code.AddLine(fmt::format("jmp_to = 0x{:x}u;", target.GetValue()));
1096 code.AddLine("break;");
1097 return {};
1098 }
1099
1100 std::string PushFlowStack(Operation operation) {
1101 const auto target = std::get<ImmediateNode>(*operation[0]);
1102 code.AddLine(fmt::format("flow_stack[flow_stack_top] = 0x{:x}u;", target.GetValue()));
1103 code.AddLine("flow_stack_top++;");
1104 return {};
1105 }
1106
1107 std::string PopFlowStack(Operation operation) {
1108 code.AddLine("flow_stack_top--;");
1109 code.AddLine("jmp_to = flow_stack[flow_stack_top];");
1110 code.AddLine("break;");
1111 return {};
1112 }
1113
1114 std::string Exit(Operation operation) {
1115 if (stage != ShaderStage::Fragment) {
1116 code.AddLine("return;");
1117 return {};
1118 }
1119 const auto& used_registers = ir.GetRegisters();
1120 const auto SafeGetRegister = [&](u32 reg) -> std::string {
1121 // TODO(Rodrigo): Replace with contains once C++20 releases
1122 if (used_registers.find(reg) != used_registers.end()) {
1123 return GetRegister(reg);
1124 }
1125 return "0.0f";
1126 };
1127
1128 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented");
1129
1130 code.AddLine("if (alpha_test[0] != 0) {");
1131 ++code.scope;
1132 // We start on the register containing the alpha value in the first RT.
1133 u32 current_reg = 3;
1134 for (u32 render_target = 0; render_target < Maxwell::NumRenderTargets; ++render_target) {
1135 // TODO(Blinkhawk): verify the behavior of alpha testing on hardware when
1136 // multiple render targets are used.
1137 if (header.ps.IsColorComponentOutputEnabled(render_target, 0) ||
1138 header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
1139 header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
1140 header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
1141 code.AddLine(
1142 fmt::format("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg)));
1143 current_reg += 4;
1144 }
1145 }
1146 --code.scope;
1147 code.AddLine('}');
1148
1149 // Write the color outputs using the data in the shader registers, disabled
1150 // rendertargets/components are skipped in the register assignment.
1151 current_reg = 0;
1152 for (u32 render_target = 0; render_target < Maxwell::NumRenderTargets; ++render_target) {
1153 // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
1154 for (u32 component = 0; component < 4; ++component) {
1155 if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
1156 code.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component,
1157 SafeGetRegister(current_reg)));
1158 ++current_reg;
1159 }
1160 }
1161 }
1162
1163 if (header.ps.omap.depth) {
1164 // The depth output is always 2 registers after the last color output, and current_reg
1165 // already contains one past the last color register.
1166 code.AddLine("gl_FragDepth = " + SafeGetRegister(current_reg + 1) + ';');
1167 }
1168
1169 code.AddLine("return;");
1170 return {};
1171 }
1172
1173 std::string Kil(Operation operation) {
1174 // Enclose "discard" in a conditional, so that GLSL compilation does not complain
1175 // about unexecuted instructions that may follow this.
1176 code.AddLine("if (true) {");
1177 ++code.scope;
1178 code.AddLine("discard;");
1179 --code.scope;
1180 code.AddLine("}");
1181 return {};
1182 }
1183
1184 std::string YNegate(Operation operation) {
1185 // Config pack's third value is Y_NEGATE's state.
1186 return "uintBitsToFloat(config_pack[2])";
1187 }
1188
1189 static constexpr OperationDecompilersArray operation_decompilers = {
1190 &Assign,
1191 &AssignComposite,
1192
1193 &Composite,
1194 &Select,
1195
1196 &Add<Type::Float>,
1197 &Mul<Type::Float>,
1198 &Div<Type::Float>,
1199 &FFma,
1200 &Negate<Type::Float>,
1201 &Absolute<Type::Float>,
1202 &FClamp,
1203 &Min<Type::Float>,
1204 &Max<Type::Float>,
1205 &FCos,
1206 &FSin,
1207 &FExp2,
1208 &FLog2,
1209 &FInverseSqrt,
1210 &FSqrt,
1211 &FRoundEven,
1212 &FFloor,
1213 &FCeil,
1214 &FTrunc,
1215 &FCastInteger<Type::Int>,
1216 &FCastInteger<Type::Uint>,
1217
1218 &Add<Type::Int>,
1219 &Mul<Type::Int>,
1220 &Div<Type::Int>,
1221 &Negate<Type::Int>,
1222 &Absolute<Type::Int>,
1223 &Min<Type::Int>,
1224 &Max<Type::Int>,
1225
1226 &ICastFloat,
1227 &ICastUnsigned,
1228 &LogicalShiftLeft<Type::Int>,
1229 &ILogicalShiftRight,
1230 &IArithmeticShiftRight,
1231 &BitwiseAnd<Type::Int>,
1232 &BitwiseOr<Type::Int>,
1233 &BitwiseXor<Type::Int>,
1234 &BitwiseNot<Type::Int>,
1235 &BitfieldInsert<Type::Int>,
1236
1237 &Add<Type::Uint>,
1238 &Mul<Type::Uint>,
1239 &Div<Type::Uint>,
1240 &Min<Type::Uint>,
1241 &Max<Type::Uint>,
1242 &UCastFloat,
1243 &UCastSigned,
1244 &LogicalShiftLeft<Type::Uint>,
1245 &UShiftRight,
1246 &UShiftRight,
1247 &BitwiseAnd<Type::Uint>,
1248 &BitwiseOr<Type::Uint>,
1249 &BitwiseXor<Type::Uint>,
1250 &BitwiseNot<Type::Uint>,
1251 &BitfieldInsert<Type::Uint>,
1252
1253 &Add<Type::HalfFloat>,
1254 &Mul<Type::HalfFloat>,
1255 &Absolute<Type::HalfFloat>,
1256 &HNegate,
1257 &HMergeF32,
1258 &HMergeH0,
1259 &HMergeH1,
1260
1261 &LogicalAssign,
1262 &LogicalAnd,
1263 &LogicalOr,
1264 &LogicalXor,
1265 &LogicalNegate,
1266
1267 &LogicalLessThan<Type::Float>,
1268 &LogicalEqual<Type::Float>,
1269 &LogicalLessEqual<Type::Float>,
1270 &LogicalGreaterThan<Type::Float>,
1271 &LogicalNotEqual<Type::Float>,
1272 &LogicalGreaterEqual<Type::Float>,
1273 &LogicalFIsNan,
1274
1275 &LogicalLessThan<Type::Int>,
1276 &LogicalEqual<Type::Int>,
1277 &LogicalLessEqual<Type::Int>,
1278 &LogicalGreaterThan<Type::Int>,
1279 &LogicalNotEqual<Type::Int>,
1280 &LogicalGreaterEqual<Type::Int>,
1281
1282 &LogicalLessThan<Type::Uint>,
1283 &LogicalEqual<Type::Uint>,
1284 &LogicalLessEqual<Type::Uint>,
1285 &LogicalGreaterThan<Type::Uint>,
1286 &LogicalNotEqual<Type::Uint>,
1287 &LogicalGreaterEqual<Type::Uint>,
1288
1289 &LogicalHLessThan,
1290 &LogicalHEqual,
1291 &LogicalHLessEqual,
1292 &LogicalHGreaterThan,
1293 &LogicalHNotEqual,
1294 &LogicalHGreaterEqual,
1295
1296 &F4Texture,
1297 &F4TextureLod,
1298 &F4TextureGather,
1299 &F4TextureQueryDimensions,
1300 &F4TextureQueryLod,
1301
1302 &Ipa,
1303
1304 &Bra,
1305 &PushFlowStack, // Ssy
1306 &PushFlowStack, // Brk
1307 &PopFlowStack, // Sync
1308 &PopFlowStack, // Brk
1309 &Exit,
1310 &Kil,
1311
1312 &YNegate,
1313 };
1314
1315 std::string GetRegister(u32 index) const {
1316 return GetDeclarationWithSuffix(index, "gpr");
1317 }
1318
1319 std::string GetPredicate(Tegra::Shader::Pred pred) const {
1320 return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred");
1321 }
1322
1323 std::string GetInputAttribute(Attribute::Index attribute) const {
1324 const auto index{static_cast<u32>(attribute) -
1325 static_cast<u32>(Attribute::Index::Attribute_0)};
1326 return GetDeclarationWithSuffix(index, "input_attr");
1327 }
1328
1329 std::string GetOutputAttribute(Attribute::Index attribute) const {
1330 const auto index{static_cast<u32>(attribute) -
1331 static_cast<u32>(Attribute::Index::Attribute_0)};
1332 return GetDeclarationWithSuffix(index, "output_attr");
1333 }
1334
1335 std::string GetConstBuffer(u32 index) const {
1336 return GetDeclarationWithSuffix(index, "cbuf");
1337 }
1338
1339 std::string GetConstBufferBlock(u32 index) const {
1340 return GetDeclarationWithSuffix(index, "cbuf_block");
1341 }
1342
1343 std::string GetLocalMemory() const {
1344 return "lmem_" + suffix;
1345 }
1346
1347 std::string GetInternalFlag(InternalFlag flag) const {
1348 constexpr std::array<const char*, 4> InternalFlagNames = {"zero_flag", "sign_flag",
1349 "carry_flag", "overflow_flag"};
1350 const auto index = static_cast<u32>(flag);
1351 ASSERT(index < static_cast<u32>(InternalFlag::Amount));
1352
1353 return std::string(InternalFlagNames[index]) + '_' + suffix;
1354 }
1355
1356 std::string GetSampler(const Sampler& sampler) const {
1357 return GetDeclarationWithSuffix(sampler.GetIndex(), "sampler");
1358 }
1359
1360 std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const {
1361 return name + '_' + std::to_string(index) + '_' + suffix;
1362 }
1363
1364 const ShaderIR& ir;
1365 const ShaderStage stage;
1366 const std::string suffix;
1367 const Header header;
1368
1369 ShaderWriter code;
1370};
1371
1372std::string GetCommonDeclarations() {
1373 return "#define MAX_CONSTBUFFER_ELEMENTS " + std::to_string(MAX_CONSTBUFFER_ELEMENTS) +
1374 "\n"
1375 "#define ftoi floatBitsToInt\n"
1376 "#define ftou floatBitsToUint\n"
1377 "#define itof intBitsToFloat\n"
1378 "#define utof uintBitsToFloat\n\n"
1379 "float fromHalf2(vec2 pair) {\n"
1380 " return utof(packHalf2x16(pair));\n"
1381 "}\n\n"
1382 "vec2 toHalf2(float value) {\n"
1383 " return unpackHalf2x16(ftou(value));\n"
1384 "}\n\n";
1385}
1386
1387ProgramResult Decompile(const ShaderIR& ir, Maxwell::ShaderStage stage, const std::string& suffix) {
1388 GLSLDecompiler decompiler(ir, stage, suffix);
1389 decompiler.Decompile();
1390 return {decompiler.GetResult(), decompiler.GetShaderEntries()};
1391}
1392
1393} // namespace OpenGL::GLShader \ No newline at end of file
diff --git a/src/video_core/shader/glsl_decompiler.h b/src/video_core/shader/glsl_decompiler.h
new file mode 100644
index 000000000..7be461f1b
--- /dev/null
+++ b/src/video_core/shader/glsl_decompiler.h
@@ -0,0 +1,88 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <string>
9#include <utility>
10#include <vector>
11#include "common/common_types.h"
12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/shader/shader_ir.h"
14
15namespace VideoCommon::Shader {
16class ShaderIR;
17}
18
19namespace OpenGL::GLShader {
20
21using Maxwell = Tegra::Engines::Maxwell3D::Regs;
22
23class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer {
24public:
25 explicit ConstBufferEntry(const VideoCommon::Shader::ConstBuffer& entry,
26 Maxwell::ShaderStage stage, const std::string& name, u32 index)
27 : VideoCommon::Shader::ConstBuffer{entry}, stage{stage}, name{name}, index{index} {}
28
29 const std::string& GetName() const {
30 return name;
31 }
32
33 Maxwell::ShaderStage GetStage() const {
34 return stage;
35 }
36
37 u32 GetIndex() const {
38 return index;
39 }
40
41 u32 GetHash() const {
42 return (static_cast<u32>(stage) << 16) | index;
43 }
44
45private:
46 std::string name;
47 Maxwell::ShaderStage stage{};
48 u32 index{};
49};
50
51class SamplerEntry : public VideoCommon::Shader::Sampler {
52public:
53 explicit SamplerEntry(const VideoCommon::Shader::Sampler& entry, Maxwell::ShaderStage stage,
54 const std::string& name)
55 : VideoCommon::Shader::Sampler{entry}, stage{stage}, name{name} {}
56
57 const std::string& GetName() const {
58 return name;
59 }
60
61 Maxwell::ShaderStage GetStage() const {
62 return stage;
63 }
64
65 u32 GetHash() const {
66 return (static_cast<u32>(stage) << 16) | GetIndex();
67 }
68
69private:
70 std::string name;
71 Maxwell::ShaderStage stage{};
72};
73
74struct ShaderEntries {
75 std::vector<ConstBufferEntry> const_buffers;
76 std::vector<SamplerEntry> samplers;
77 std::array<bool, Maxwell::NumClipDistances> clip_distances{};
78 std::size_t shader_length{};
79};
80
81using ProgramResult = std::pair<std::string, ShaderEntries>;
82
83std::string GetCommonDeclarations();
84
85ProgramResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage,
86 const std::string& suffix);
87
88} // namespace OpenGL::GLShader \ No newline at end of file