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