summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2019-05-20 20:42:40 -0400
committerGravatar GitHub2019-05-20 20:42:40 -0400
commit9a17b20896166b599b22b21cb0057706ad79542a (patch)
treef8cf364953e9675e622c273426a5e562c6d7571d /src
parentMerge pull request #2499 from lioncash/translate (diff)
parentgl_shader_decompiler: Tidy up minor remaining cases of unnecessary std::strin... (diff)
downloadyuzu-9a17b20896166b599b22b21cb0057706ad79542a.tar.gz
yuzu-9a17b20896166b599b22b21cb0057706ad79542a.tar.xz
yuzu-9a17b20896166b599b22b21cb0057706ad79542a.zip
Merge pull request #2494 from lioncash/shader-text
gl_shader_decompiler: Add AddLine() overloads with single function that forwards to libfmt
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp376
1 files changed, 195 insertions, 181 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 4bff54a59..4c380677d 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -57,15 +57,14 @@ public:
57 shader_source += text; 57 shader_source += text;
58 } 58 }
59 59
60 void AddLine(std::string_view text) { 60 // Forwards all arguments directly to libfmt.
61 AddExpression(text); 61 // Note that all formatting requirements for fmt must be
62 AddNewLine(); 62 // obeyed when using this function. (e.g. {{ must be used
63 } 63 // printing the character '{' is desirable. Ditto for }} and '}',
64 64 // etc).
65 void AddLine(char character) { 65 template <typename... Args>
66 DEBUG_ASSERT(scope >= 0); 66 void AddLine(std::string_view text, Args&&... args) {
67 AppendIndentation(); 67 AddExpression(fmt::format(text, std::forward<Args>(args)...));
68 shader_source += character;
69 AddNewLine(); 68 AddNewLine();
70 } 69 }
71 70
@@ -75,9 +74,7 @@ public:
75 } 74 }
76 75
77 std::string GenerateTemporary() { 76 std::string GenerateTemporary() {
78 std::string temporary = "tmp"; 77 return fmt::format("tmp{}", temporary_index++);
79 temporary += std::to_string(temporary_index++);
80 return temporary;
81 } 78 }
82 79
83 std::string GetResult() { 80 std::string GetResult() {
@@ -167,41 +164,41 @@ public:
167 DeclareSamplers(); 164 DeclareSamplers();
168 DeclarePhysicalAttributeReader(); 165 DeclarePhysicalAttributeReader();
169 166
170 code.AddLine("void execute_" + suffix + "() {"); 167 code.AddLine("void execute_{}() {{", suffix);
171 ++code.scope; 168 ++code.scope;
172 169
173 // VM's program counter 170 // VM's program counter
174 const auto first_address = ir.GetBasicBlocks().begin()->first; 171 const auto first_address = ir.GetBasicBlocks().begin()->first;
175 code.AddLine("uint jmp_to = " + std::to_string(first_address) + "u;"); 172 code.AddLine("uint jmp_to = {}u;", first_address);
176 173
177 // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems 174 // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
178 // unlikely that shaders will use 20 nested SSYs and PBKs. 175 // unlikely that shaders will use 20 nested SSYs and PBKs.
179 constexpr u32 FLOW_STACK_SIZE = 20; 176 constexpr u32 FLOW_STACK_SIZE = 20;
180 code.AddLine(fmt::format("uint flow_stack[{}];", FLOW_STACK_SIZE)); 177 code.AddLine("uint flow_stack[{}];", FLOW_STACK_SIZE);
181 code.AddLine("uint flow_stack_top = 0u;"); 178 code.AddLine("uint flow_stack_top = 0u;");
182 179
183 code.AddLine("while (true) {"); 180 code.AddLine("while (true) {{");
184 ++code.scope; 181 ++code.scope;
185 182
186 code.AddLine("switch (jmp_to) {"); 183 code.AddLine("switch (jmp_to) {{");
187 184
188 for (const auto& pair : ir.GetBasicBlocks()) { 185 for (const auto& pair : ir.GetBasicBlocks()) {
189 const auto [address, bb] = pair; 186 const auto [address, bb] = pair;
190 code.AddLine(fmt::format("case 0x{:x}u: {{", address)); 187 code.AddLine("case 0x{:x}u: {{", address);
191 ++code.scope; 188 ++code.scope;
192 189
193 VisitBlock(bb); 190 VisitBlock(bb);
194 191
195 --code.scope; 192 --code.scope;
196 code.AddLine('}'); 193 code.AddLine("}}");
197 } 194 }
198 195
199 code.AddLine("default: return;"); 196 code.AddLine("default: return;");
200 code.AddLine('}'); 197 code.AddLine("}}");
201 198
202 for (std::size_t i = 0; i < 2; ++i) { 199 for (std::size_t i = 0; i < 2; ++i) {
203 --code.scope; 200 --code.scope;
204 code.AddLine('}'); 201 code.AddLine("}}");
205 } 202 }
206 } 203 }
207 204
@@ -241,12 +238,13 @@ private:
241 } 238 }
242 239
243 void DeclareGeometry() { 240 void DeclareGeometry() {
244 if (stage != ShaderStage::Geometry) 241 if (stage != ShaderStage::Geometry) {
245 return; 242 return;
243 }
246 244
247 const auto topology = GetTopologyName(header.common3.output_topology); 245 const auto topology = GetTopologyName(header.common3.output_topology);
248 const auto max_vertices = std::to_string(header.common4.max_output_vertices); 246 const auto max_vertices = header.common4.max_output_vertices.Value();
249 code.AddLine("layout (" + topology + ", max_vertices = " + max_vertices + ") out;"); 247 code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices);
250 code.AddNewLine(); 248 code.AddNewLine();
251 249
252 DeclareVertexRedeclarations(); 250 DeclareVertexRedeclarations();
@@ -255,7 +253,7 @@ private:
255 void DeclareVertexRedeclarations() { 253 void DeclareVertexRedeclarations() {
256 bool clip_distances_declared = false; 254 bool clip_distances_declared = false;
257 255
258 code.AddLine("out gl_PerVertex {"); 256 code.AddLine("out gl_PerVertex {{");
259 ++code.scope; 257 ++code.scope;
260 258
261 code.AddLine("vec4 gl_Position;"); 259 code.AddLine("vec4 gl_Position;");
@@ -271,40 +269,42 @@ private:
271 } 269 }
272 270
273 --code.scope; 271 --code.scope;
274 code.AddLine("};"); 272 code.AddLine("}};");
275 code.AddNewLine(); 273 code.AddNewLine();
276 } 274 }
277 275
278 void DeclareRegisters() { 276 void DeclareRegisters() {
279 const auto& registers = ir.GetRegisters(); 277 const auto& registers = ir.GetRegisters();
280 for (const u32 gpr : registers) { 278 for (const u32 gpr : registers) {
281 code.AddLine("float " + GetRegister(gpr) + " = 0;"); 279 code.AddLine("float {} = 0;", GetRegister(gpr));
282 } 280 }
283 if (!registers.empty()) 281 if (!registers.empty()) {
284 code.AddNewLine(); 282 code.AddNewLine();
283 }
285 } 284 }
286 285
287 void DeclarePredicates() { 286 void DeclarePredicates() {
288 const auto& predicates = ir.GetPredicates(); 287 const auto& predicates = ir.GetPredicates();
289 for (const auto pred : predicates) { 288 for (const auto pred : predicates) {
290 code.AddLine("bool " + GetPredicate(pred) + " = false;"); 289 code.AddLine("bool {} = false;", GetPredicate(pred));
291 } 290 }
292 if (!predicates.empty()) 291 if (!predicates.empty()) {
293 code.AddNewLine(); 292 code.AddNewLine();
293 }
294 } 294 }
295 295
296 void DeclareLocalMemory() { 296 void DeclareLocalMemory() {
297 if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { 297 if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) {
298 const auto element_count = Common::AlignUp(local_memory_size, 4) / 4; 298 const auto element_count = Common::AlignUp(local_memory_size, 4) / 4;
299 code.AddLine("float " + GetLocalMemory() + '[' + std::to_string(element_count) + "];"); 299 code.AddLine("float {}[{}];", GetLocalMemory(), element_count);
300 code.AddNewLine(); 300 code.AddNewLine();
301 } 301 }
302 } 302 }
303 303
304 void DeclareInternalFlags() { 304 void DeclareInternalFlags() {
305 for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) { 305 for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
306 const InternalFlag flag_code = static_cast<InternalFlag>(flag); 306 const auto flag_code = static_cast<InternalFlag>(flag);
307 code.AddLine("bool " + GetInternalFlag(flag_code) + " = false;"); 307 code.AddLine("bool {} = false;", GetInternalFlag(flag_code));
308 } 308 }
309 code.AddNewLine(); 309 code.AddNewLine();
310 } 310 }
@@ -343,8 +343,9 @@ private:
343 DeclareInputAttribute(index, false); 343 DeclareInputAttribute(index, false);
344 } 344 }
345 } 345 }
346 if (!attributes.empty()) 346 if (!attributes.empty()) {
347 code.AddNewLine(); 347 code.AddNewLine();
348 }
348 } 349 }
349 350
350 void DeclareInputAttribute(Attribute::Index index, bool skip_unused) { 351 void DeclareInputAttribute(Attribute::Index index, bool skip_unused) {
@@ -370,8 +371,7 @@ private:
370 location += GENERIC_VARYING_START_LOCATION; 371 location += GENERIC_VARYING_START_LOCATION;
371 } 372 }
372 373
373 code.AddLine("layout (location = " + std::to_string(location) + ") " + suffix + "in vec4 " + 374 code.AddLine("layout (location = {}) {} in vec4 {};", name, location, suffix, name);
374 name + ';');
375 } 375 }
376 376
377 void DeclareOutputAttributes() { 377 void DeclareOutputAttributes() {
@@ -389,23 +389,23 @@ private:
389 DeclareOutputAttribute(index); 389 DeclareOutputAttribute(index);
390 } 390 }
391 } 391 }
392 if (!attributes.empty()) 392 if (!attributes.empty()) {
393 code.AddNewLine(); 393 code.AddNewLine();
394 }
394 } 395 }
395 396
396 void DeclareOutputAttribute(Attribute::Index index) { 397 void DeclareOutputAttribute(Attribute::Index index) {
397 const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION}; 398 const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION};
398 code.AddLine("layout (location = " + std::to_string(location) + ") out vec4 " + 399 code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index));
399 GetOutputAttribute(index) + ';');
400 } 400 }
401 401
402 void DeclareConstantBuffers() { 402 void DeclareConstantBuffers() {
403 for (const auto& entry : ir.GetConstantBuffers()) { 403 for (const auto& entry : ir.GetConstantBuffers()) {
404 const auto [index, size] = entry; 404 const auto [index, size] = entry;
405 code.AddLine("layout (std140, binding = CBUF_BINDING_" + std::to_string(index) + 405 code.AddLine("layout (std140, binding = CBUF_BINDING_{}) uniform {} {{", index,
406 ") uniform " + GetConstBufferBlock(index) + " {"); 406 GetConstBufferBlock(index));
407 code.AddLine(" vec4 " + GetConstBuffer(index) + "[MAX_CONSTBUFFER_ELEMENTS];"); 407 code.AddLine(" vec4 {}[MAX_CONSTBUFFER_ELEMENTS];", GetConstBuffer(index));
408 code.AddLine("};"); 408 code.AddLine("}};");
409 code.AddNewLine(); 409 code.AddNewLine();
410 } 410 }
411 } 411 }
@@ -417,17 +417,16 @@ private:
417 // Since we don't know how the shader will use the shader, hint the driver to disable as 417 // Since we don't know how the shader will use the shader, hint the driver to disable as
418 // much optimizations as possible 418 // much optimizations as possible
419 std::string qualifier = "coherent volatile"; 419 std::string qualifier = "coherent volatile";
420 if (usage.is_read && !usage.is_written) 420 if (usage.is_read && !usage.is_written) {
421 qualifier += " readonly"; 421 qualifier += " readonly";
422 else if (usage.is_written && !usage.is_read) 422 } else if (usage.is_written && !usage.is_read) {
423 qualifier += " writeonly"; 423 qualifier += " writeonly";
424 }
424 425
425 const std::string binding = 426 code.AddLine("layout (std430, binding = GMEM_BINDING_{}_{}) {} buffer {} {{",
426 fmt::format("GMEM_BINDING_{}_{}", base.cbuf_index, base.cbuf_offset); 427 base.cbuf_index, base.cbuf_offset, qualifier, GetGlobalMemoryBlock(base));
427 code.AddLine("layout (std430, binding = " + binding + ") " + qualifier + " buffer " + 428 code.AddLine(" float {}[];", GetGlobalMemory(base));
428 GetGlobalMemoryBlock(base) + " {"); 429 code.AddLine("}};");
429 code.AddLine(" float " + GetGlobalMemory(base) + "[];");
430 code.AddLine("};");
431 code.AddNewLine(); 430 code.AddNewLine();
432 } 431 }
433 } 432 }
@@ -435,7 +434,7 @@ private:
435 void DeclareSamplers() { 434 void DeclareSamplers() {
436 const auto& samplers = ir.GetSamplers(); 435 const auto& samplers = ir.GetSamplers();
437 for (const auto& sampler : samplers) { 436 for (const auto& sampler : samplers) {
438 std::string sampler_type = [&]() { 437 std::string sampler_type = [&sampler] {
439 switch (sampler.GetType()) { 438 switch (sampler.GetType()) {
440 case Tegra::Shader::TextureType::Texture1D: 439 case Tegra::Shader::TextureType::Texture1D:
441 return "sampler1D"; 440 return "sampler1D";
@@ -450,25 +449,28 @@ private:
450 return "sampler2D"; 449 return "sampler2D";
451 } 450 }
452 }(); 451 }();
453 if (sampler.IsArray()) 452 if (sampler.IsArray()) {
454 sampler_type += "Array"; 453 sampler_type += "Array";
455 if (sampler.IsShadow()) 454 }
455 if (sampler.IsShadow()) {
456 sampler_type += "Shadow"; 456 sampler_type += "Shadow";
457 }
457 458
458 code.AddLine("layout (binding = SAMPLER_BINDING_" + std::to_string(sampler.GetIndex()) + 459 code.AddLine("layout (binding = SAMPLER_BINDING_{}) uniform {} {};", sampler.GetIndex(),
459 ") uniform " + sampler_type + ' ' + GetSampler(sampler) + ';'); 460 sampler_type, GetSampler(sampler));
460 } 461 }
461 if (!samplers.empty()) 462 if (!samplers.empty()) {
462 code.AddNewLine(); 463 code.AddNewLine();
464 }
463 } 465 }
464 466
465 void DeclarePhysicalAttributeReader() { 467 void DeclarePhysicalAttributeReader() {
466 if (!ir.HasPhysicalAttributes()) { 468 if (!ir.HasPhysicalAttributes()) {
467 return; 469 return;
468 } 470 }
469 code.AddLine("float readPhysicalAttribute(uint physical_address) {"); 471 code.AddLine("float readPhysicalAttribute(uint physical_address) {{");
470 ++code.scope; 472 ++code.scope;
471 code.AddLine("switch (physical_address) {"); 473 code.AddLine("switch (physical_address) {{");
472 474
473 // Just declare generic attributes for now. 475 // Just declare generic attributes for now.
474 const auto num_attributes{static_cast<u32>(GetNumPhysicalInputAttributes())}; 476 const auto num_attributes{static_cast<u32>(GetNumPhysicalInputAttributes())};
@@ -483,15 +485,15 @@ private:
483 const bool declared{stage != ShaderStage::Fragment || 485 const bool declared{stage != ShaderStage::Fragment ||
484 header.ps.GetAttributeUse(index) != AttributeUse::Unused}; 486 header.ps.GetAttributeUse(index) != AttributeUse::Unused};
485 const std::string value{declared ? ReadAttribute(attribute, element) : "0"}; 487 const std::string value{declared ? ReadAttribute(attribute, element) : "0"};
486 code.AddLine(fmt::format("case 0x{:x}: return {};", address, value)); 488 code.AddLine("case 0x{:x}: return {};", address, value);
487 } 489 }
488 } 490 }
489 491
490 code.AddLine("default: return 0;"); 492 code.AddLine("default: return 0;");
491 493
492 code.AddLine('}'); 494 code.AddLine("}}");
493 --code.scope; 495 --code.scope;
494 code.AddLine('}'); 496 code.AddLine("}}");
495 code.AddNewLine(); 497 code.AddNewLine();
496 } 498 }
497 499
@@ -516,23 +518,26 @@ private:
516 return {}; 518 return {};
517 } 519 }
518 return (this->*decompiler)(*operation); 520 return (this->*decompiler)(*operation);
521 }
519 522
520 } else if (const auto gpr = std::get_if<GprNode>(node)) { 523 if (const auto gpr = std::get_if<GprNode>(node)) {
521 const u32 index = gpr->GetIndex(); 524 const u32 index = gpr->GetIndex();
522 if (index == Register::ZeroIndex) { 525 if (index == Register::ZeroIndex) {
523 return "0"; 526 return "0";
524 } 527 }
525 return GetRegister(index); 528 return GetRegister(index);
529 }
526 530
527 } else if (const auto immediate = std::get_if<ImmediateNode>(node)) { 531 if (const auto immediate = std::get_if<ImmediateNode>(node)) {
528 const u32 value = immediate->GetValue(); 532 const u32 value = immediate->GetValue();
529 if (value < 10) { 533 if (value < 10) {
530 // For eyecandy avoid using hex numbers on single digits 534 // For eyecandy avoid using hex numbers on single digits
531 return fmt::format("utof({}u)", immediate->GetValue()); 535 return fmt::format("utof({}u)", immediate->GetValue());
532 } 536 }
533 return fmt::format("utof(0x{:x}u)", immediate->GetValue()); 537 return fmt::format("utof(0x{:x}u)", immediate->GetValue());
538 }
534 539
535 } else if (const auto predicate = std::get_if<PredicateNode>(node)) { 540 if (const auto predicate = std::get_if<PredicateNode>(node)) {
536 const auto value = [&]() -> std::string { 541 const auto value = [&]() -> std::string {
537 switch (const auto index = predicate->GetIndex(); index) { 542 switch (const auto index = predicate->GetIndex(); index) {
538 case Tegra::Shader::Pred::UnusedIndex: 543 case Tegra::Shader::Pred::UnusedIndex:
@@ -544,19 +549,22 @@ private:
544 } 549 }
545 }(); 550 }();
546 if (predicate->IsNegated()) { 551 if (predicate->IsNegated()) {
547 return "!(" + value + ')'; 552 return fmt::format("!({})", value);
548 } 553 }
549 return value; 554 return value;
555 }
550 556
551 } else if (const auto abuf = std::get_if<AbufNode>(node)) { 557 if (const auto abuf = std::get_if<AbufNode>(node)) {
552 UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry, 558 UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry,
553 "Physical attributes in geometry shaders are not implemented"); 559 "Physical attributes in geometry shaders are not implemented");
554 if (abuf->IsPhysicalBuffer()) { 560 if (abuf->IsPhysicalBuffer()) {
555 return "readPhysicalAttribute(ftou(" + Visit(abuf->GetPhysicalAddress()) + "))"; 561 return fmt::format("readPhysicalAttribute(ftou({}))",
562 Visit(abuf->GetPhysicalAddress()));
556 } 563 }
557 return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer()); 564 return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer());
565 }
558 566
559 } else if (const auto cbuf = std::get_if<CbufNode>(node)) { 567 if (const auto cbuf = std::get_if<CbufNode>(node)) {
560 const Node offset = cbuf->GetOffset(); 568 const Node offset = cbuf->GetOffset();
561 if (const auto immediate = std::get_if<ImmediateNode>(offset)) { 569 if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
562 // Direct access 570 // Direct access
@@ -564,57 +572,63 @@ private:
564 ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access"); 572 ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access");
565 return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), 573 return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()),
566 offset_imm / (4 * 4), (offset_imm / 4) % 4); 574 offset_imm / (4 * 4), (offset_imm / 4) % 4);
575 }
567 576
568 } else if (std::holds_alternative<OperationNode>(*offset)) { 577 if (std::holds_alternative<OperationNode>(*offset)) {
569 // Indirect access 578 // Indirect access
570 const std::string final_offset = code.GenerateTemporary(); 579 const std::string final_offset = code.GenerateTemporary();
571 code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4);"); 580 code.AddLine("uint {} = (ftou({}) / 4);", final_offset, Visit(offset));
572 return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), 581 return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
573 final_offset, final_offset); 582 final_offset, final_offset);
574
575 } else {
576 UNREACHABLE_MSG("Unmanaged offset node type");
577 } 583 }
578 584
579 } else if (const auto gmem = std::get_if<GmemNode>(node)) { 585 UNREACHABLE_MSG("Unmanaged offset node type");
586 }
587
588 if (const auto gmem = std::get_if<GmemNode>(node)) {
580 const std::string real = Visit(gmem->GetRealAddress()); 589 const std::string real = Visit(gmem->GetRealAddress());
581 const std::string base = Visit(gmem->GetBaseAddress()); 590 const std::string base = Visit(gmem->GetBaseAddress());
582 const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; 591 const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
583 return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); 592 return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
593 }
584 594
585 } else if (const auto lmem = std::get_if<LmemNode>(node)) { 595 if (const auto lmem = std::get_if<LmemNode>(node)) {
586 return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); 596 return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
597 }
587 598
588 } else if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) { 599 if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) {
589 return GetInternalFlag(internal_flag->GetFlag()); 600 return GetInternalFlag(internal_flag->GetFlag());
601 }
590 602
591 } else if (const auto conditional = std::get_if<ConditionalNode>(node)) { 603 if (const auto conditional = std::get_if<ConditionalNode>(node)) {
592 // It's invalid to call conditional on nested nodes, use an operation instead 604 // It's invalid to call conditional on nested nodes, use an operation instead
593 code.AddLine("if (" + Visit(conditional->GetCondition()) + ") {"); 605 code.AddLine("if ({}) {{", Visit(conditional->GetCondition()));
594 ++code.scope; 606 ++code.scope;
595 607
596 VisitBlock(conditional->GetCode()); 608 VisitBlock(conditional->GetCode());
597 609
598 --code.scope; 610 --code.scope;
599 code.AddLine('}'); 611 code.AddLine("}}");
600 return {}; 612 return {};
613 }
601 614
602 } else if (const auto comment = std::get_if<CommentNode>(node)) { 615 if (const auto comment = std::get_if<CommentNode>(node)) {
603 return "// " + comment->GetText(); 616 return "// " + comment->GetText();
604 } 617 }
618
605 UNREACHABLE(); 619 UNREACHABLE();
606 return {}; 620 return {};
607 } 621 }
608 622
609 std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) { 623 std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) {
610 const auto GeometryPass = [&](std::string name) { 624 const auto GeometryPass = [&](std::string_view name) {
611 if (stage == ShaderStage::Geometry && buffer) { 625 if (stage == ShaderStage::Geometry && buffer) {
612 // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games 626 // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
613 // set an 0x80000000 index for those and the shader fails to build. Find out why 627 // set an 0x80000000 index for those and the shader fails to build. Find out why
614 // this happens and what's its intent. 628 // this happens and what's its intent.
615 return "gs_" + std::move(name) + "[ftou(" + Visit(buffer) + ") % MAX_VERTEX_INPUT]"; 629 return fmt::format("gs_{}[ftou({}) % MAX_VERTEX_INPUT]", name, Visit(buffer));
616 } 630 }
617 return name; 631 return std::string(name);
618 }; 632 };
619 633
620 switch (attribute) { 634 switch (attribute) {
@@ -677,7 +691,7 @@ private:
677 const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; 691 const std::string precise = stage != ShaderStage::Fragment ? "precise " : "";
678 692
679 const std::string temporary = code.GenerateTemporary(); 693 const std::string temporary = code.GenerateTemporary();
680 code.AddLine(precise + "float " + temporary + " = " + value + ';'); 694 code.AddLine("{}float {} = {};", precise, temporary, value);
681 return temporary; 695 return temporary;
682 } 696 }
683 697
@@ -691,7 +705,7 @@ private:
691 } 705 }
692 706
693 const std::string temporary = code.GenerateTemporary(); 707 const std::string temporary = code.GenerateTemporary();
694 code.AddLine("float " + temporary + " = " + Visit(operand) + ';'); 708 code.AddLine("float {} = {};", temporary, Visit(operand));
695 return temporary; 709 return temporary;
696 } 710 }
697 711
@@ -706,31 +720,32 @@ private:
706 case Type::Float: 720 case Type::Float:
707 return value; 721 return value;
708 case Type::Int: 722 case Type::Int:
709 return "ftoi(" + value + ')'; 723 return fmt::format("ftoi({})", value);
710 case Type::Uint: 724 case Type::Uint:
711 return "ftou(" + value + ')'; 725 return fmt::format("ftou({})", value);
712 case Type::HalfFloat: 726 case Type::HalfFloat:
713 return "toHalf2(" + value + ')'; 727 return fmt::format("toHalf2({})", value);
714 } 728 }
715 UNREACHABLE(); 729 UNREACHABLE();
716 return value; 730 return value;
717 } 731 }
718 732
719 std::string BitwiseCastResult(std::string value, Type type, bool needs_parenthesis = false) { 733 std::string BitwiseCastResult(const std::string& value, Type type,
734 bool needs_parenthesis = false) {
720 switch (type) { 735 switch (type) {
721 case Type::Bool: 736 case Type::Bool:
722 case Type::Bool2: 737 case Type::Bool2:
723 case Type::Float: 738 case Type::Float:
724 if (needs_parenthesis) { 739 if (needs_parenthesis) {
725 return '(' + value + ')'; 740 return fmt::format("({})", value);
726 } 741 }
727 return value; 742 return value;
728 case Type::Int: 743 case Type::Int:
729 return "itof(" + value + ')'; 744 return fmt::format("itof({})", value);
730 case Type::Uint: 745 case Type::Uint:
731 return "utof(" + value + ')'; 746 return fmt::format("utof({})", value);
732 case Type::HalfFloat: 747 case Type::HalfFloat:
733 return "fromHalf2(" + value + ')'; 748 return fmt::format("fromHalf2({})", value);
734 } 749 }
735 UNREACHABLE(); 750 UNREACHABLE();
736 return value; 751 return value;
@@ -738,27 +753,27 @@ private:
738 753
739 std::string GenerateUnary(Operation operation, const std::string& func, Type result_type, 754 std::string GenerateUnary(Operation operation, const std::string& func, Type result_type,
740 Type type_a, bool needs_parenthesis = true) { 755 Type type_a, bool needs_parenthesis = true) {
741 return ApplyPrecise(operation, 756 const std::string op_str = fmt::format("{}({})", func, VisitOperand(operation, 0, type_a));
742 BitwiseCastResult(func + '(' + VisitOperand(operation, 0, type_a) + ')', 757
743 result_type, needs_parenthesis)); 758 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type, needs_parenthesis));
744 } 759 }
745 760
746 std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type, 761 std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type,
747 Type type_a, Type type_b) { 762 Type type_a, Type type_b) {
748 const std::string op_a = VisitOperand(operation, 0, type_a); 763 const std::string op_a = VisitOperand(operation, 0, type_a);
749 const std::string op_b = VisitOperand(operation, 1, type_b); 764 const std::string op_b = VisitOperand(operation, 1, type_b);
765 const std::string op_str = fmt::format("({} {} {})", op_a, func, op_b);
750 766
751 return ApplyPrecise( 767 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
752 operation, BitwiseCastResult('(' + op_a + ' ' + func + ' ' + op_b + ')', result_type));
753 } 768 }
754 769
755 std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type, 770 std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type,
756 Type type_a, Type type_b) { 771 Type type_a, Type type_b) {
757 const std::string op_a = VisitOperand(operation, 0, type_a); 772 const std::string op_a = VisitOperand(operation, 0, type_a);
758 const std::string op_b = VisitOperand(operation, 1, type_b); 773 const std::string op_b = VisitOperand(operation, 1, type_b);
774 const std::string op_str = fmt::format("{}({}, {})", func, op_a, op_b);
759 775
760 return ApplyPrecise(operation, 776 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
761 BitwiseCastResult(func + '(' + op_a + ", " + op_b + ')', result_type));
762 } 777 }
763 778
764 std::string GenerateTernary(Operation operation, const std::string& func, Type result_type, 779 std::string GenerateTernary(Operation operation, const std::string& func, Type result_type,
@@ -766,10 +781,9 @@ private:
766 const std::string op_a = VisitOperand(operation, 0, type_a); 781 const std::string op_a = VisitOperand(operation, 0, type_a);
767 const std::string op_b = VisitOperand(operation, 1, type_b); 782 const std::string op_b = VisitOperand(operation, 1, type_b);
768 const std::string op_c = VisitOperand(operation, 2, type_c); 783 const std::string op_c = VisitOperand(operation, 2, type_c);
784 const std::string op_str = fmt::format("{}({}, {}, {})", func, op_a, op_b, op_c);
769 785
770 return ApplyPrecise( 786 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
771 operation,
772 BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + op_c + ')', result_type));
773 } 787 }
774 788
775 std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type, 789 std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type,
@@ -778,10 +792,9 @@ private:
778 const std::string op_b = VisitOperand(operation, 1, type_b); 792 const std::string op_b = VisitOperand(operation, 1, type_b);
779 const std::string op_c = VisitOperand(operation, 2, type_c); 793 const std::string op_c = VisitOperand(operation, 2, type_c);
780 const std::string op_d = VisitOperand(operation, 3, type_d); 794 const std::string op_d = VisitOperand(operation, 3, type_d);
795 const std::string op_str = fmt::format("{}({}, {}, {}, {})", func, op_a, op_b, op_c, op_d);
781 796
782 return ApplyPrecise(operation, BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + 797 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
783 op_c + ", " + op_d + ')',
784 result_type));
785 } 798 }
786 799
787 std::string GenerateTexture(Operation operation, const std::string& function_suffix, 800 std::string GenerateTexture(Operation operation, const std::string& function_suffix,
@@ -844,7 +857,7 @@ private:
844 // required to be constant) 857 // required to be constant)
845 expr += std::to_string(static_cast<s32>(immediate->GetValue())); 858 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
846 } else { 859 } else {
847 expr += "ftoi(" + Visit(operand) + ')'; 860 expr += fmt::format("ftoi({})", Visit(operand));
848 } 861 }
849 break; 862 break;
850 case Type::Float: 863 case Type::Float:
@@ -877,7 +890,7 @@ private:
877 expr += std::to_string(static_cast<s32>(immediate->GetValue())); 890 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
878 } else if (device.HasVariableAoffi()) { 891 } else if (device.HasVariableAoffi()) {
879 // Avoid using variable AOFFI on unsupported devices. 892 // Avoid using variable AOFFI on unsupported devices.
880 expr += "ftoi(" + Visit(operand) + ')'; 893 expr += fmt::format("ftoi({})", Visit(operand));
881 } else { 894 } else {
882 // Insert 0 on devices not supporting variable AOFFI. 895 // Insert 0 on devices not supporting variable AOFFI.
883 expr += '0'; 896 expr += '0';
@@ -902,7 +915,6 @@ private:
902 return {}; 915 return {};
903 } 916 }
904 target = GetRegister(gpr->GetIndex()); 917 target = GetRegister(gpr->GetIndex());
905
906 } else if (const auto abuf = std::get_if<AbufNode>(dest)) { 918 } else if (const auto abuf = std::get_if<AbufNode>(dest)) {
907 UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer()); 919 UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
908 920
@@ -913,9 +925,9 @@ private:
913 case Attribute::Index::PointSize: 925 case Attribute::Index::PointSize:
914 return "gl_PointSize"; 926 return "gl_PointSize";
915 case Attribute::Index::ClipDistances0123: 927 case Attribute::Index::ClipDistances0123:
916 return "gl_ClipDistance[" + std::to_string(abuf->GetElement()) + ']'; 928 return fmt::format("gl_ClipDistance[{}]", abuf->GetElement());
917 case Attribute::Index::ClipDistances4567: 929 case Attribute::Index::ClipDistances4567:
918 return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']'; 930 return fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4);
919 default: 931 default:
920 if (IsGenericAttribute(attribute)) { 932 if (IsGenericAttribute(attribute)) {
921 return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()); 933 return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement());
@@ -925,21 +937,18 @@ private:
925 return "0"; 937 return "0";
926 } 938 }
927 }(); 939 }();
928
929 } else if (const auto lmem = std::get_if<LmemNode>(dest)) { 940 } else if (const auto lmem = std::get_if<LmemNode>(dest)) {
930 target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]"; 941 target = fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
931
932 } else if (const auto gmem = std::get_if<GmemNode>(dest)) { 942 } else if (const auto gmem = std::get_if<GmemNode>(dest)) {
933 const std::string real = Visit(gmem->GetRealAddress()); 943 const std::string real = Visit(gmem->GetRealAddress());
934 const std::string base = Visit(gmem->GetBaseAddress()); 944 const std::string base = Visit(gmem->GetBaseAddress());
935 const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; 945 const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
936 target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); 946 target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
937
938 } else { 947 } else {
939 UNREACHABLE_MSG("Assign called without a proper target"); 948 UNREACHABLE_MSG("Assign called without a proper target");
940 } 949 }
941 950
942 code.AddLine(target + " = " + Visit(src) + ';'); 951 code.AddLine("{} = {};", target, Visit(src));
943 return {}; 952 return {};
944 } 953 }
945 954
@@ -992,8 +1001,9 @@ private:
992 const std::string condition = Visit(operation[0]); 1001 const std::string condition = Visit(operation[0]);
993 const std::string true_case = Visit(operation[1]); 1002 const std::string true_case = Visit(operation[1]);
994 const std::string false_case = Visit(operation[2]); 1003 const std::string false_case = Visit(operation[2]);
995 return ApplyPrecise(operation, 1004 const std::string op_str = fmt::format("({} ? {} : {})", condition, true_case, false_case);
996 '(' + condition + " ? " + true_case + " : " + false_case + ')'); 1005
1006 return ApplyPrecise(operation, op_str);
997 } 1007 }
998 1008
999 std::string FCos(Operation operation) { 1009 std::string FCos(Operation operation) {
@@ -1057,9 +1067,9 @@ private:
1057 std::string ILogicalShiftRight(Operation operation) { 1067 std::string ILogicalShiftRight(Operation operation) {
1058 const std::string op_a = VisitOperand(operation, 0, Type::Uint); 1068 const std::string op_a = VisitOperand(operation, 0, Type::Uint);
1059 const std::string op_b = VisitOperand(operation, 1, Type::Uint); 1069 const std::string op_b = VisitOperand(operation, 1, Type::Uint);
1070 const std::string op_str = fmt::format("int({} >> {})", op_a, op_b);
1060 1071
1061 return ApplyPrecise(operation, 1072 return ApplyPrecise(operation, BitwiseCastResult(op_str, Type::Int));
1062 BitwiseCastResult("int(" + op_a + " >> " + op_b + ')', Type::Int));
1063 } 1073 }
1064 1074
1065 std::string IArithmeticShiftRight(Operation operation) { 1075 std::string IArithmeticShiftRight(Operation operation) {
@@ -1115,11 +1125,12 @@ private:
1115 } 1125 }
1116 1126
1117 std::string HNegate(Operation operation) { 1127 std::string HNegate(Operation operation) {
1118 const auto GetNegate = [&](std::size_t index) -> std::string { 1128 const auto GetNegate = [&](std::size_t index) {
1119 return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1"; 1129 return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1";
1120 }; 1130 };
1121 const std::string value = '(' + VisitOperand(operation, 0, Type::HalfFloat) + " * vec2(" + 1131 const std::string value =
1122 GetNegate(1) + ", " + GetNegate(2) + "))"; 1132 fmt::format("({} * vec2({}, {}))", VisitOperand(operation, 0, Type::HalfFloat),
1133 GetNegate(1), GetNegate(2));
1123 return BitwiseCastResult(value, Type::HalfFloat); 1134 return BitwiseCastResult(value, Type::HalfFloat);
1124 } 1135 }
1125 1136
@@ -1127,7 +1138,8 @@ private:
1127 const std::string value = VisitOperand(operation, 0, Type::HalfFloat); 1138 const std::string value = VisitOperand(operation, 0, Type::HalfFloat);
1128 const std::string min = VisitOperand(operation, 1, Type::Float); 1139 const std::string min = VisitOperand(operation, 1, Type::Float);
1129 const std::string max = VisitOperand(operation, 2, Type::Float); 1140 const std::string max = VisitOperand(operation, 2, Type::Float);
1130 const std::string clamped = "clamp(" + value + ", vec2(" + min + "), vec2(" + max + "))"; 1141 const std::string clamped = fmt::format("clamp({}, vec2({}), vec2({}))", value, min, max);
1142
1131 return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat)); 1143 return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat));
1132 } 1144 }
1133 1145
@@ -1138,34 +1150,35 @@ private:
1138 case Tegra::Shader::HalfType::H0_H1: 1150 case Tegra::Shader::HalfType::H0_H1:
1139 return operand; 1151 return operand;
1140 case Tegra::Shader::HalfType::F32: 1152 case Tegra::Shader::HalfType::F32:
1141 return "vec2(fromHalf2(" + operand + "))"; 1153 return fmt::format("vec2(fromHalf2({}))", operand);
1142 case Tegra::Shader::HalfType::H0_H0: 1154 case Tegra::Shader::HalfType::H0_H0:
1143 return "vec2(" + operand + "[0])"; 1155 return fmt::format("vec2({}[0])", operand);
1144 case Tegra::Shader::HalfType::H1_H1: 1156 case Tegra::Shader::HalfType::H1_H1:
1145 return "vec2(" + operand + "[1])"; 1157 return fmt::format("vec2({}[1])", operand);
1146 } 1158 }
1147 UNREACHABLE(); 1159 UNREACHABLE();
1148 return "0"; 1160 return "0";
1149 }(); 1161 }();
1150 return "fromHalf2(" + value + ')'; 1162 return fmt::format("fromHalf2({})", value);
1151 } 1163 }
1152 1164
1153 std::string HMergeF32(Operation operation) { 1165 std::string HMergeF32(Operation operation) {
1154 return "float(toHalf2(" + Visit(operation[0]) + ")[0])"; 1166 return fmt::format("float(toHalf2({})[0])", Visit(operation[0]));
1155 } 1167 }
1156 1168
1157 std::string HMergeH0(Operation operation) { 1169 std::string HMergeH0(Operation operation) {
1158 return "fromHalf2(vec2(toHalf2(" + Visit(operation[1]) + ")[0], toHalf2(" + 1170 return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[1]),
1159 Visit(operation[0]) + ")[1]))"; 1171 Visit(operation[0]));
1160 } 1172 }
1161 1173
1162 std::string HMergeH1(Operation operation) { 1174 std::string HMergeH1(Operation operation) {
1163 return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[0], toHalf2(" + 1175 return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[0]),
1164 Visit(operation[1]) + ")[1]))"; 1176 Visit(operation[1]));
1165 } 1177 }
1166 1178
1167 std::string HPack2(Operation operation) { 1179 std::string HPack2(Operation operation) {
1168 return "utof(packHalf2x16(vec2(" + Visit(operation[0]) + ", " + Visit(operation[1]) + ")))"; 1180 return fmt::format("utof(packHalf2x16(vec2({}, {})))", Visit(operation[0]),
1181 Visit(operation[1]));
1169 } 1182 }
1170 1183
1171 template <Type type> 1184 template <Type type>
@@ -1223,7 +1236,7 @@ private:
1223 target = GetInternalFlag(flag->GetFlag()); 1236 target = GetInternalFlag(flag->GetFlag());
1224 } 1237 }
1225 1238
1226 code.AddLine(target + " = " + Visit(src) + ';'); 1239 code.AddLine("{} = {};", target, Visit(src));
1227 return {}; 1240 return {};
1228 } 1241 }
1229 1242
@@ -1245,7 +1258,7 @@ private:
1245 1258
1246 std::string LogicalPick2(Operation operation) { 1259 std::string LogicalPick2(Operation operation) {
1247 const std::string pair = VisitOperand(operation, 0, Type::Bool2); 1260 const std::string pair = VisitOperand(operation, 0, Type::Bool2);
1248 return pair + '[' + VisitOperand(operation, 1, Type::Uint) + ']'; 1261 return fmt::format("{}[{}]", pair, VisitOperand(operation, 1, Type::Uint));
1249 } 1262 }
1250 1263
1251 std::string LogicalAll2(Operation operation) { 1264 std::string LogicalAll2(Operation operation) {
@@ -1257,15 +1270,15 @@ private:
1257 } 1270 }
1258 1271
1259 template <bool with_nan> 1272 template <bool with_nan>
1260 std::string GenerateHalfComparison(Operation operation, std::string compare_op) { 1273 std::string GenerateHalfComparison(Operation operation, const std::string& compare_op) {
1261 std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2, 1274 const std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2,
1262 Type::HalfFloat, Type::HalfFloat)}; 1275 Type::HalfFloat, Type::HalfFloat)};
1263 if constexpr (!with_nan) { 1276 if constexpr (!with_nan) {
1264 return comparison; 1277 return comparison;
1265 } 1278 }
1266 return "halfFloatNanComparison(" + comparison + ", " + 1279 return fmt::format("halfFloatNanComparison({}, {}, {})", comparison,
1267 VisitOperand(operation, 0, Type::HalfFloat) + ", " + 1280 VisitOperand(operation, 0, Type::HalfFloat),
1268 VisitOperand(operation, 1, Type::HalfFloat) + ')'; 1281 VisitOperand(operation, 1, Type::HalfFloat));
1269 } 1282 }
1270 1283
1271 template <bool with_nan> 1284 template <bool with_nan>
@@ -1342,12 +1355,12 @@ private:
1342 switch (meta->element) { 1355 switch (meta->element) {
1343 case 0: 1356 case 0:
1344 case 1: 1357 case 1:
1345 return "itof(int(textureSize(" + sampler + ", " + lod + ')' + 1358 return fmt::format("itof(int(textureSize({}, {}){}))", sampler, lod,
1346 GetSwizzle(meta->element) + "))"; 1359 GetSwizzle(meta->element));
1347 case 2: 1360 case 2:
1348 return "0"; 1361 return "0";
1349 case 3: 1362 case 3:
1350 return "itof(textureQueryLevels(" + sampler + "))"; 1363 return fmt::format("itof(textureQueryLevels({}))", sampler);
1351 } 1364 }
1352 UNREACHABLE(); 1365 UNREACHABLE();
1353 return "0"; 1366 return "0";
@@ -1358,8 +1371,9 @@ private:
1358 ASSERT(meta); 1371 ASSERT(meta);
1359 1372
1360 if (meta->element < 2) { 1373 if (meta->element < 2) {
1361 return "itof(int((" + GenerateTexture(operation, "QueryLod", {}) + " * vec2(256))" + 1374 return fmt::format("itof(int(({} * vec2(256)){}))",
1362 GetSwizzle(meta->element) + "))"; 1375 GenerateTexture(operation, "QueryLod", {}),
1376 GetSwizzle(meta->element));
1363 } 1377 }
1364 return "0"; 1378 return "0";
1365 } 1379 }
@@ -1398,7 +1412,7 @@ private:
1398 const auto target = std::get_if<ImmediateNode>(operation[0]); 1412 const auto target = std::get_if<ImmediateNode>(operation[0]);
1399 UNIMPLEMENTED_IF(!target); 1413 UNIMPLEMENTED_IF(!target);
1400 1414
1401 code.AddLine(fmt::format("jmp_to = 0x{:x}u;", target->GetValue())); 1415 code.AddLine("jmp_to = 0x{:x}u;", target->GetValue());
1402 code.AddLine("break;"); 1416 code.AddLine("break;");
1403 return {}; 1417 return {};
1404 } 1418 }
@@ -1407,7 +1421,7 @@ private:
1407 const auto target = std::get_if<ImmediateNode>(operation[0]); 1421 const auto target = std::get_if<ImmediateNode>(operation[0]);
1408 UNIMPLEMENTED_IF(!target); 1422 UNIMPLEMENTED_IF(!target);
1409 1423
1410 code.AddLine(fmt::format("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue())); 1424 code.AddLine("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue());
1411 return {}; 1425 return {};
1412 } 1426 }
1413 1427
@@ -1433,7 +1447,7 @@ private:
1433 1447
1434 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented"); 1448 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented");
1435 1449
1436 code.AddLine("if (alpha_test[0] != 0) {"); 1450 code.AddLine("if (alpha_test[0] != 0) {{");
1437 ++code.scope; 1451 ++code.scope;
1438 // We start on the register containing the alpha value in the first RT. 1452 // We start on the register containing the alpha value in the first RT.
1439 u32 current_reg = 3; 1453 u32 current_reg = 3;
@@ -1444,13 +1458,12 @@ private:
1444 header.ps.IsColorComponentOutputEnabled(render_target, 1) || 1458 header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
1445 header.ps.IsColorComponentOutputEnabled(render_target, 2) || 1459 header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
1446 header.ps.IsColorComponentOutputEnabled(render_target, 3)) { 1460 header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
1447 code.AddLine( 1461 code.AddLine("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg));
1448 fmt::format("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg)));
1449 current_reg += 4; 1462 current_reg += 4;
1450 } 1463 }
1451 } 1464 }
1452 --code.scope; 1465 --code.scope;
1453 code.AddLine('}'); 1466 code.AddLine("}}");
1454 1467
1455 // Write the color outputs using the data in the shader registers, disabled 1468 // Write the color outputs using the data in the shader registers, disabled
1456 // rendertargets/components are skipped in the register assignment. 1469 // rendertargets/components are skipped in the register assignment.
@@ -1459,8 +1472,8 @@ private:
1459 // TODO(Subv): Figure out how dual-source blending is configured in the Switch. 1472 // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
1460 for (u32 component = 0; component < 4; ++component) { 1473 for (u32 component = 0; component < 4; ++component) {
1461 if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { 1474 if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
1462 code.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component, 1475 code.AddLine("FragColor{}[{}] = {};", render_target, component,
1463 SafeGetRegister(current_reg))); 1476 SafeGetRegister(current_reg));
1464 ++current_reg; 1477 ++current_reg;
1465 } 1478 }
1466 } 1479 }
@@ -1469,7 +1482,7 @@ private:
1469 if (header.ps.omap.depth) { 1482 if (header.ps.omap.depth) {
1470 // The depth output is always 2 registers after the last color output, and current_reg 1483 // The depth output is always 2 registers after the last color output, and current_reg
1471 // already contains one past the last color register. 1484 // already contains one past the last color register.
1472 code.AddLine("gl_FragDepth = " + SafeGetRegister(current_reg + 1) + ';'); 1485 code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1));
1473 } 1486 }
1474 1487
1475 code.AddLine("return;"); 1488 code.AddLine("return;");
@@ -1479,11 +1492,11 @@ private:
1479 std::string Discard(Operation operation) { 1492 std::string Discard(Operation operation) {
1480 // Enclose "discard" in a conditional, so that GLSL compilation does not complain 1493 // Enclose "discard" in a conditional, so that GLSL compilation does not complain
1481 // about unexecuted instructions that may follow this. 1494 // about unexecuted instructions that may follow this.
1482 code.AddLine("if (true) {"); 1495 code.AddLine("if (true) {{");
1483 ++code.scope; 1496 ++code.scope;
1484 code.AddLine("discard;"); 1497 code.AddLine("discard;");
1485 --code.scope; 1498 --code.scope;
1486 code.AddLine("}"); 1499 code.AddLine("}}");
1487 return {}; 1500 return {};
1488 } 1501 }
1489 1502
@@ -1697,7 +1710,7 @@ private:
1697 const auto index = static_cast<u32>(flag); 1710 const auto index = static_cast<u32>(flag);
1698 ASSERT(index < static_cast<u32>(InternalFlag::Amount)); 1711 ASSERT(index < static_cast<u32>(InternalFlag::Amount));
1699 1712
1700 return std::string(InternalFlagNames[index]) + '_' + suffix; 1713 return fmt::format("{}_{}", InternalFlagNames[index], suffix);
1701 } 1714 }
1702 1715
1703 std::string GetSampler(const Sampler& sampler) const { 1716 std::string GetSampler(const Sampler& sampler) const {
@@ -1705,7 +1718,7 @@ private:
1705 } 1718 }
1706 1719
1707 std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const { 1720 std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const {
1708 return name + '_' + std::to_string(index) + '_' + suffix; 1721 return fmt::format("{}_{}_{}", name, index, suffix);
1709 } 1722 }
1710 1723
1711 u32 GetNumPhysicalInputAttributes() const { 1724 u32 GetNumPhysicalInputAttributes() const {
@@ -1733,24 +1746,25 @@ private:
1733} // Anonymous namespace 1746} // Anonymous namespace
1734 1747
1735std::string GetCommonDeclarations() { 1748std::string GetCommonDeclarations() {
1736 const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS); 1749 return fmt::format(
1737 return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" + 1750 "#define MAX_CONSTBUFFER_ELEMENTS {}\n"
1738 "#define ftoi floatBitsToInt\n" 1751 "#define ftoi floatBitsToInt\n"
1739 "#define ftou floatBitsToUint\n" 1752 "#define ftou floatBitsToUint\n"
1740 "#define itof intBitsToFloat\n" 1753 "#define itof intBitsToFloat\n"
1741 "#define utof uintBitsToFloat\n\n" 1754 "#define utof uintBitsToFloat\n\n"
1742 "float fromHalf2(vec2 pair) {\n" 1755 "float fromHalf2(vec2 pair) {{\n"
1743 " return utof(packHalf2x16(pair));\n" 1756 " return utof(packHalf2x16(pair));\n"
1744 "}\n\n" 1757 "}}\n\n"
1745 "vec2 toHalf2(float value) {\n" 1758 "vec2 toHalf2(float value) {{\n"
1746 " return unpackHalf2x16(ftou(value));\n" 1759 " return unpackHalf2x16(ftou(value));\n"
1747 "}\n\n" 1760 "}}\n\n"
1748 "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {\n" 1761 "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{\n"
1749 " bvec2 is_nan1 = isnan(pair1);\n" 1762 " bvec2 is_nan1 = isnan(pair1);\n"
1750 " bvec2 is_nan2 = isnan(pair2);\n" 1763 " bvec2 is_nan2 = isnan(pair2);\n"
1751 " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || " 1764 " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || "
1752 "is_nan2.y);\n" 1765 "is_nan2.y);\n"
1753 "}\n"; 1766 "}}\n",
1767 MAX_CONSTBUFFER_ELEMENTS);
1754} 1768}
1755 1769
1756ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage, 1770ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage,