summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp921
1 files changed, 505 insertions, 416 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 359d58cbe..f73bf6392 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -39,7 +39,7 @@ using namespace VideoCommon::Shader;
39using Maxwell = Tegra::Engines::Maxwell3D::Regs; 39using Maxwell = Tegra::Engines::Maxwell3D::Regs;
40using Operation = const OperationNode&; 40using Operation = const OperationNode&;
41 41
42enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat }; 42enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat };
43 43
44struct TextureAoffi {}; 44struct TextureAoffi {};
45using TextureArgument = std::pair<Type, Node>; 45using TextureArgument = std::pair<Type, Node>;
@@ -48,7 +48,7 @@ using TextureIR = std::variant<TextureAoffi, TextureArgument>;
48constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 48constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
49 static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float)); 49 static_cast<u32>(Maxwell::MaxConstBufferSize) / (4 * sizeof(float));
50 50
51class ShaderWriter { 51class ShaderWriter final {
52public: 52public:
53 void AddExpression(std::string_view text) { 53 void AddExpression(std::string_view text) {
54 DEBUG_ASSERT(scope >= 0); 54 DEBUG_ASSERT(scope >= 0);
@@ -93,9 +93,157 @@ private:
93 u32 temporary_index = 1; 93 u32 temporary_index = 1;
94}; 94};
95 95
96class Expression final {
97public:
98 Expression(std::string code, Type type) : code{std::move(code)}, type{type} {
99 ASSERT(type != Type::Void);
100 }
101 Expression() : type{Type::Void} {}
102
103 Type GetType() const {
104 return type;
105 }
106
107 std::string GetCode() const {
108 return code;
109 }
110
111 void CheckVoid() const {
112 ASSERT(type == Type::Void);
113 }
114
115 std::string As(Type type) const {
116 switch (type) {
117 case Type::Bool:
118 return AsBool();
119 case Type::Bool2:
120 return AsBool2();
121 case Type::Float:
122 return AsFloat();
123 case Type::Int:
124 return AsInt();
125 case Type::Uint:
126 return AsUint();
127 case Type::HalfFloat:
128 return AsHalfFloat();
129 default:
130 UNREACHABLE_MSG("Invalid type");
131 return code;
132 }
133 }
134
135 std::string AsBool() const {
136 switch (type) {
137 case Type::Bool:
138 return code;
139 default:
140 UNREACHABLE_MSG("Incompatible types");
141 return code;
142 }
143 }
144
145 std::string AsBool2() const {
146 switch (type) {
147 case Type::Bool2:
148 return code;
149 default:
150 UNREACHABLE_MSG("Incompatible types");
151 return code;
152 }
153 }
154
155 std::string AsFloat() const {
156 switch (type) {
157 case Type::Float:
158 return code;
159 case Type::Uint:
160 return fmt::format("utof({})", code);
161 case Type::Int:
162 return fmt::format("itof({})", code);
163 case Type::HalfFloat:
164 return fmt::format("utof(packHalf2x16({}))", code);
165 default:
166 UNREACHABLE_MSG("Incompatible types");
167 return code;
168 }
169 }
170
171 std::string AsInt() const {
172 switch (type) {
173 case Type::Float:
174 return fmt::format("ftoi({})", code);
175 case Type::Uint:
176 return fmt::format("int({})", code);
177 case Type::Int:
178 return code;
179 case Type::HalfFloat:
180 return fmt::format("int(packHalf2x16({}))", code);
181 default:
182 UNREACHABLE_MSG("Incompatible types");
183 return code;
184 }
185 }
186
187 std::string AsUint() const {
188 switch (type) {
189 case Type::Float:
190 return fmt::format("ftou({})", code);
191 case Type::Uint:
192 return code;
193 case Type::Int:
194 return fmt::format("uint({})", code);
195 case Type::HalfFloat:
196 return fmt::format("packHalf2x16({})", code);
197 default:
198 UNREACHABLE_MSG("Incompatible types");
199 return code;
200 }
201 }
202
203 std::string AsHalfFloat() const {
204 switch (type) {
205 case Type::Float:
206 return fmt::format("unpackHalf2x16(ftou({}))", code);
207 case Type::Uint:
208 return fmt::format("unpackHalf2x16({})", code);
209 case Type::Int:
210 return fmt::format("unpackHalf2x16(int({}))", code);
211 case Type::HalfFloat:
212 return code;
213 default:
214 UNREACHABLE_MSG("Incompatible types");
215 return code;
216 }
217 }
218
219private:
220 std::string code;
221 Type type{};
222};
223
224constexpr const char* GetTypeString(Type type) {
225 switch (type) {
226 case Type::Bool:
227 return "bool";
228 case Type::Bool2:
229 return "bvec2";
230 case Type::Float:
231 return "float";
232 case Type::Int:
233 return "int";
234 case Type::Uint:
235 return "uint";
236 case Type::HalfFloat:
237 return "vec2";
238 default:
239 UNREACHABLE_MSG("Invalid type");
240 return "<invalid type>";
241 }
242}
243
96/// Generates code to use for a swizzle operation. 244/// Generates code to use for a swizzle operation.
97constexpr const char* GetSwizzle(u32 element) { 245constexpr const char* GetSwizzle(u32 element) {
98 constexpr std::array<const char*, 4> swizzle = {".x", ".y", ".z", ".w"}; 246 constexpr std::array swizzle = {".x", ".y", ".z", ".w"};
99 return swizzle.at(element); 247 return swizzle.at(element);
100} 248}
101 249
@@ -134,8 +282,8 @@ constexpr bool IsGenericAttribute(Attribute::Index index) {
134 return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31; 282 return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31;
135} 283}
136 284
137constexpr Attribute::Index ToGenericAttribute(u32 value) { 285constexpr Attribute::Index ToGenericAttribute(u64 value) {
138 return static_cast<Attribute::Index>(value + static_cast<u32>(Attribute::Index::Attribute_0)); 286 return static_cast<Attribute::Index>(value + static_cast<u64>(Attribute::Index::Attribute_0));
139} 287}
140 288
141u32 GetGenericAttributeIndex(Attribute::Index index) { 289u32 GetGenericAttributeIndex(Attribute::Index index) {
@@ -191,7 +339,7 @@ public:
191 339
192 // VM's program counter 340 // VM's program counter
193 const auto first_address = ir.GetBasicBlocks().begin()->first; 341 const auto first_address = ir.GetBasicBlocks().begin()->first;
194 code.AddLine("uint jmp_to = {}u;", first_address); 342 code.AddLine("uint jmp_to = {}U;", first_address);
195 343
196 // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems 344 // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
197 // unlikely that shaders will use 20 nested SSYs and PBKs. 345 // unlikely that shaders will use 20 nested SSYs and PBKs.
@@ -199,7 +347,7 @@ public:
199 constexpr u32 FLOW_STACK_SIZE = 20; 347 constexpr u32 FLOW_STACK_SIZE = 20;
200 for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { 348 for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
201 code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); 349 code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
202 code.AddLine("uint {} = 0u;", FlowStackTopName(stack)); 350 code.AddLine("uint {} = 0U;", FlowStackTopName(stack));
203 } 351 }
204 } 352 }
205 353
@@ -210,7 +358,7 @@ public:
210 358
211 for (const auto& pair : ir.GetBasicBlocks()) { 359 for (const auto& pair : ir.GetBasicBlocks()) {
212 const auto [address, bb] = pair; 360 const auto [address, bb] = pair;
213 code.AddLine("case 0x{:x}u: {{", address); 361 code.AddLine("case 0x{:X}U: {{", address);
214 ++code.scope; 362 ++code.scope;
215 363
216 VisitBlock(bb); 364 VisitBlock(bb);
@@ -322,7 +470,7 @@ private:
322 void DeclareRegisters() { 470 void DeclareRegisters() {
323 const auto& registers = ir.GetRegisters(); 471 const auto& registers = ir.GetRegisters();
324 for (const u32 gpr : registers) { 472 for (const u32 gpr : registers) {
325 code.AddLine("float {} = 0;", GetRegister(gpr)); 473 code.AddLine("float {} = 0.0f;", GetRegister(gpr));
326 } 474 }
327 if (!registers.empty()) { 475 if (!registers.empty()) {
328 code.AddNewLine(); 476 code.AddNewLine();
@@ -348,7 +496,7 @@ private:
348 return; 496 return;
349 } 497 }
350 const auto element_count = Common::AlignUp(local_memory_size, 4) / 4; 498 const auto element_count = Common::AlignUp(local_memory_size, 4) / 4;
351 code.AddLine("float {}[{}];", GetLocalMemory(), element_count); 499 code.AddLine("uint {}[{}];", GetLocalMemory(), element_count);
352 code.AddNewLine(); 500 code.AddNewLine();
353 } 501 }
354 502
@@ -371,8 +519,6 @@ private:
371 return "noperspective "; 519 return "noperspective ";
372 default: 520 default:
373 case AttributeUse::Unused: 521 case AttributeUse::Unused:
374 UNREACHABLE_MSG("Unused attribute being fetched");
375 return {};
376 UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute)); 522 UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute));
377 return {}; 523 return {};
378 } 524 }
@@ -449,7 +595,7 @@ private:
449 const auto [index, size] = entry; 595 const auto [index, size] = entry;
450 code.AddLine("layout (std140, binding = CBUF_BINDING_{}) uniform {} {{", index, 596 code.AddLine("layout (std140, binding = CBUF_BINDING_{}) uniform {} {{", index,
451 GetConstBufferBlock(index)); 597 GetConstBufferBlock(index));
452 code.AddLine(" vec4 {}[MAX_CONSTBUFFER_ELEMENTS];", GetConstBuffer(index)); 598 code.AddLine(" uvec4 {}[{}];", GetConstBuffer(index), MAX_CONSTBUFFER_ELEMENTS);
453 code.AddLine("}};"); 599 code.AddLine("}};");
454 code.AddNewLine(); 600 code.AddNewLine();
455 } 601 }
@@ -470,7 +616,7 @@ private:
470 616
471 code.AddLine("layout (std430, binding = GMEM_BINDING_{}_{}) {} buffer {} {{", 617 code.AddLine("layout (std430, binding = GMEM_BINDING_{}_{}) {} buffer {} {{",
472 base.cbuf_index, base.cbuf_offset, qualifier, GetGlobalMemoryBlock(base)); 618 base.cbuf_index, base.cbuf_offset, qualifier, GetGlobalMemoryBlock(base));
473 code.AddLine(" float {}[];", GetGlobalMemory(base)); 619 code.AddLine(" uint {}[];", GetGlobalMemory(base));
474 code.AddLine("}};"); 620 code.AddLine("}};");
475 code.AddNewLine(); 621 code.AddNewLine();
476 } 622 }
@@ -528,7 +674,7 @@ private:
528 if (!ir.HasPhysicalAttributes()) { 674 if (!ir.HasPhysicalAttributes()) {
529 return; 675 return;
530 } 676 }
531 code.AddLine("float readPhysicalAttribute(uint physical_address) {{"); 677 code.AddLine("float ReadPhysicalAttribute(uint physical_address) {{");
532 ++code.scope; 678 ++code.scope;
533 code.AddLine("switch (physical_address) {{"); 679 code.AddLine("switch (physical_address) {{");
534 680
@@ -537,15 +683,16 @@ private:
537 for (u32 index = 0; index < num_attributes; ++index) { 683 for (u32 index = 0; index < num_attributes; ++index) {
538 const auto attribute{ToGenericAttribute(index)}; 684 const auto attribute{ToGenericAttribute(index)};
539 for (u32 element = 0; element < 4; ++element) { 685 for (u32 element = 0; element < 4; ++element) {
540 constexpr u32 generic_base{0x80}; 686 constexpr u32 generic_base = 0x80;
541 constexpr u32 generic_stride{16}; 687 constexpr u32 generic_stride = 16;
542 constexpr u32 element_stride{4}; 688 constexpr u32 element_stride = 4;
543 const u32 address{generic_base + index * generic_stride + element * element_stride}; 689 const u32 address{generic_base + index * generic_stride + element * element_stride};
544 690
545 const bool declared{stage != ProgramType::Fragment || 691 const bool declared = stage != ProgramType::Fragment ||
546 header.ps.GetAttributeUse(index) != AttributeUse::Unused}; 692 header.ps.GetAttributeUse(index) != AttributeUse::Unused;
547 const std::string value{declared ? ReadAttribute(attribute, element) : "0"}; 693 const std::string value =
548 code.AddLine("case 0x{:x}: return {};", address, value); 694 declared ? ReadAttribute(attribute, element).AsFloat() : "0.0f";
695 code.AddLine("case 0x{:X}U: return {};", address, value);
549 } 696 }
550 } 697 }
551 698
@@ -590,13 +737,11 @@ private:
590 737
591 void VisitBlock(const NodeBlock& bb) { 738 void VisitBlock(const NodeBlock& bb) {
592 for (const auto& node : bb) { 739 for (const auto& node : bb) {
593 if (const std::string expr = Visit(node); !expr.empty()) { 740 Visit(node).CheckVoid();
594 code.AddLine(expr);
595 }
596 } 741 }
597 } 742 }
598 743
599 std::string Visit(const Node& node) { 744 Expression Visit(const Node& node) {
600 if (const auto operation = std::get_if<OperationNode>(&*node)) { 745 if (const auto operation = std::get_if<OperationNode>(&*node)) {
601 const auto operation_index = static_cast<std::size_t>(operation->GetCode()); 746 const auto operation_index = static_cast<std::size_t>(operation->GetCode());
602 if (operation_index >= operation_decompilers.size()) { 747 if (operation_index >= operation_decompilers.size()) {
@@ -614,18 +759,18 @@ private:
614 if (const auto gpr = std::get_if<GprNode>(&*node)) { 759 if (const auto gpr = std::get_if<GprNode>(&*node)) {
615 const u32 index = gpr->GetIndex(); 760 const u32 index = gpr->GetIndex();
616 if (index == Register::ZeroIndex) { 761 if (index == Register::ZeroIndex) {
617 return "0"; 762 return {"0U", Type::Uint};
618 } 763 }
619 return GetRegister(index); 764 return {GetRegister(index), Type::Float};
620 } 765 }
621 766
622 if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { 767 if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
623 const u32 value = immediate->GetValue(); 768 const u32 value = immediate->GetValue();
624 if (value < 10) { 769 if (value < 10) {
625 // For eyecandy avoid using hex numbers on single digits 770 // For eyecandy avoid using hex numbers on single digits
626 return fmt::format("utof({}u)", immediate->GetValue()); 771 return {fmt::format("{}U", immediate->GetValue()), Type::Uint};
627 } 772 }
628 return fmt::format("utof(0x{:x}u)", immediate->GetValue()); 773 return {fmt::format("0x{:X}U", immediate->GetValue()), Type::Uint};
629 } 774 }
630 775
631 if (const auto predicate = std::get_if<PredicateNode>(&*node)) { 776 if (const auto predicate = std::get_if<PredicateNode>(&*node)) {
@@ -640,17 +785,18 @@ private:
640 } 785 }
641 }(); 786 }();
642 if (predicate->IsNegated()) { 787 if (predicate->IsNegated()) {
643 return fmt::format("!({})", value); 788 return {fmt::format("!({})", value), Type::Bool};
644 } 789 }
645 return value; 790 return {value, Type::Bool};
646 } 791 }
647 792
648 if (const auto abuf = std::get_if<AbufNode>(&*node)) { 793 if (const auto abuf = std::get_if<AbufNode>(&*node)) {
649 UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ProgramType::Geometry, 794 UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ProgramType::Geometry,
650 "Physical attributes in geometry shaders are not implemented"); 795 "Physical attributes in geometry shaders are not implemented");
651 if (abuf->IsPhysicalBuffer()) { 796 if (abuf->IsPhysicalBuffer()) {
652 return fmt::format("readPhysicalAttribute(ftou({}))", 797 return {fmt::format("ReadPhysicalAttribute({})",
653 Visit(abuf->GetPhysicalAddress())); 798 Visit(abuf->GetPhysicalAddress()).AsUint()),
799 Type::Float};
654 } 800 }
655 return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer()); 801 return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer());
656 } 802 }
@@ -661,18 +807,20 @@ private:
661 // Direct access 807 // Direct access
662 const u32 offset_imm = immediate->GetValue(); 808 const u32 offset_imm = immediate->GetValue();
663 ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access"); 809 ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access");
664 return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), 810 return {fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()),
665 offset_imm / (4 * 4), (offset_imm / 4) % 4); 811 offset_imm / (4 * 4), (offset_imm / 4) % 4),
812 Type::Uint};
666 } 813 }
667 814
668 if (std::holds_alternative<OperationNode>(*offset)) { 815 if (std::holds_alternative<OperationNode>(*offset)) {
669 // Indirect access 816 // Indirect access
670 const std::string final_offset = code.GenerateTemporary(); 817 const std::string final_offset = code.GenerateTemporary();
671 code.AddLine("uint {} = ftou({}) >> 2;", final_offset, Visit(offset)); 818 code.AddLine("uint {} = {} >> 2;", final_offset, Visit(offset).AsUint());
672 819
673 if (!device.HasComponentIndexingBug()) { 820 if (!device.HasComponentIndexingBug()) {
674 return fmt::format("{}[{} >> 2][{} & 3]", GetConstBuffer(cbuf->GetIndex()), 821 return {fmt::format("{}[{} >> 2][{} & 3]", GetConstBuffer(cbuf->GetIndex()),
675 final_offset, final_offset); 822 final_offset, final_offset),
823 Type::Uint};
676 } 824 }
677 825
678 // AMD's proprietary GLSL compiler emits ill code for variable component access. 826 // AMD's proprietary GLSL compiler emits ill code for variable component access.
@@ -687,33 +835,36 @@ private:
687 code.AddLine("if (({} & 3) == {}) {} = {}{};", final_offset, swizzle, result, 835 code.AddLine("if (({} & 3) == {}) {} = {}{};", final_offset, swizzle, result,
688 pack, GetSwizzle(swizzle)); 836 pack, GetSwizzle(swizzle));
689 } 837 }
690 return result; 838 return {result, Type::Uint};
691 } 839 }
692 840
693 UNREACHABLE_MSG("Unmanaged offset node type"); 841 UNREACHABLE_MSG("Unmanaged offset node type");
694 } 842 }
695 843
696 if (const auto gmem = std::get_if<GmemNode>(&*node)) { 844 if (const auto gmem = std::get_if<GmemNode>(&*node)) {
697 const std::string real = Visit(gmem->GetRealAddress()); 845 const std::string real = Visit(gmem->GetRealAddress()).AsUint();
698 const std::string base = Visit(gmem->GetBaseAddress()); 846 const std::string base = Visit(gmem->GetBaseAddress()).AsUint();
699 const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base); 847 const std::string final_offset = fmt::format("({} - {}) >> 2", real, base);
700 return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); 848 return {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset),
849 Type::Uint};
701 } 850 }
702 851
703 if (const auto lmem = std::get_if<LmemNode>(&*node)) { 852 if (const auto lmem = std::get_if<LmemNode>(&*node)) {
704 if (stage == ProgramType::Compute) { 853 if (stage == ProgramType::Compute) {
705 LOG_WARNING(Render_OpenGL, "Local memory is stubbed on compute shaders"); 854 LOG_WARNING(Render_OpenGL, "Local memory is stubbed on compute shaders");
706 } 855 }
707 return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); 856 return {
857 fmt::format("{}[{} >> 2]", GetLocalMemory(), Visit(lmem->GetAddress()).AsUint()),
858 Type::Uint};
708 } 859 }
709 860
710 if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) { 861 if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
711 return GetInternalFlag(internal_flag->GetFlag()); 862 return {GetInternalFlag(internal_flag->GetFlag()), Type::Bool};
712 } 863 }
713 864
714 if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { 865 if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
715 // It's invalid to call conditional on nested nodes, use an operation instead 866 // It's invalid to call conditional on nested nodes, use an operation instead
716 code.AddLine("if ({}) {{", Visit(conditional->GetCondition())); 867 code.AddLine("if ({}) {{", Visit(conditional->GetCondition()).AsBool());
717 ++code.scope; 868 ++code.scope;
718 869
719 VisitBlock(conditional->GetCode()); 870 VisitBlock(conditional->GetCode());
@@ -724,20 +875,21 @@ private:
724 } 875 }
725 876
726 if (const auto comment = std::get_if<CommentNode>(&*node)) { 877 if (const auto comment = std::get_if<CommentNode>(&*node)) {
727 return "// " + comment->GetText(); 878 code.AddLine("// " + comment->GetText());
879 return {};
728 } 880 }
729 881
730 UNREACHABLE(); 882 UNREACHABLE();
731 return {}; 883 return {};
732 } 884 }
733 885
734 std::string ReadAttribute(Attribute::Index attribute, u32 element, const Node& buffer = {}) { 886 Expression ReadAttribute(Attribute::Index attribute, u32 element, const Node& buffer = {}) {
735 const auto GeometryPass = [&](std::string_view name) { 887 const auto GeometryPass = [&](std::string_view name) {
736 if (stage == ProgramType::Geometry && buffer) { 888 if (stage == ProgramType::Geometry && buffer) {
737 // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games 889 // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
738 // set an 0x80000000 index for those and the shader fails to build. Find out why 890 // set an 0x80000000 index for those and the shader fails to build. Find out why
739 // this happens and what's its intent. 891 // this happens and what's its intent.
740 return fmt::format("gs_{}[ftou({}) % MAX_VERTEX_INPUT]", name, Visit(buffer)); 892 return fmt::format("gs_{}[{} % MAX_VERTEX_INPUT]", name, Visit(buffer).AsUint());
741 } 893 }
742 return std::string(name); 894 return std::string(name);
743 }; 895 };
@@ -746,25 +898,27 @@ private:
746 case Attribute::Index::Position: 898 case Attribute::Index::Position:
747 switch (stage) { 899 switch (stage) {
748 case ProgramType::Geometry: 900 case ProgramType::Geometry:
749 return fmt::format("gl_in[ftou({})].gl_Position{}", Visit(buffer), 901 return {fmt::format("gl_in[{}].gl_Position{}", Visit(buffer).AsUint(),
750 GetSwizzle(element)); 902 GetSwizzle(element)),
903 Type::Float};
751 case ProgramType::Fragment: 904 case ProgramType::Fragment:
752 return element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element)); 905 return {element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element)),
906 Type::Float};
753 default: 907 default:
754 UNREACHABLE(); 908 UNREACHABLE();
755 } 909 }
756 case Attribute::Index::PointCoord: 910 case Attribute::Index::PointCoord:
757 switch (element) { 911 switch (element) {
758 case 0: 912 case 0:
759 return "gl_PointCoord.x"; 913 return {"gl_PointCoord.x", Type::Float};
760 case 1: 914 case 1:
761 return "gl_PointCoord.y"; 915 return {"gl_PointCoord.y", Type::Float};
762 case 2: 916 case 2:
763 case 3: 917 case 3:
764 return "0"; 918 return {"0.0f", Type::Float};
765 } 919 }
766 UNREACHABLE(); 920 UNREACHABLE();
767 return "0"; 921 return {"0", Type::Int};
768 case Attribute::Index::TessCoordInstanceIDVertexID: 922 case Attribute::Index::TessCoordInstanceIDVertexID:
769 // TODO(Subv): Find out what the values are for the first two elements when inside a 923 // TODO(Subv): Find out what the values are for the first two elements when inside a
770 // vertex shader, and what's the value of the fourth element when inside a Tess Eval 924 // vertex shader, and what's the value of the fourth element when inside a Tess Eval
@@ -773,44 +927,42 @@ private:
773 switch (element) { 927 switch (element) {
774 case 2: 928 case 2:
775 // Config pack's first value is instance_id. 929 // Config pack's first value is instance_id.
776 return "uintBitsToFloat(config_pack[0])"; 930 return {"config_pack[0]", Type::Uint};
777 case 3: 931 case 3:
778 return "uintBitsToFloat(gl_VertexID)"; 932 return {"gl_VertexID", Type::Int};
779 } 933 }
780 UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element); 934 UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
781 return "0"; 935 return {"0", Type::Int};
782 case Attribute::Index::FrontFacing: 936 case Attribute::Index::FrontFacing:
783 // TODO(Subv): Find out what the values are for the other elements. 937 // TODO(Subv): Find out what the values are for the other elements.
784 ASSERT(stage == ProgramType::Fragment); 938 ASSERT(stage == ProgramType::Fragment);
785 switch (element) { 939 switch (element) {
786 case 3: 940 case 3:
787 return "itof(gl_FrontFacing ? -1 : 0)"; 941 return {"(gl_FrontFacing ? -1 : 0)", Type::Int};
788 } 942 }
789 UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element); 943 UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
790 return "0"; 944 return {"0", Type::Int};
791 default: 945 default:
792 if (IsGenericAttribute(attribute)) { 946 if (IsGenericAttribute(attribute)) {
793 return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element); 947 return {GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element),
948 Type::Float};
794 } 949 }
795 break; 950 break;
796 } 951 }
797 UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); 952 UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
798 return "0"; 953 return {"0", Type::Int};
799 } 954 }
800 955
801 std::string ApplyPrecise(Operation operation, const std::string& value) { 956 Expression ApplyPrecise(Operation operation, std::string value, Type type) {
802 if (!IsPrecise(operation)) { 957 if (!IsPrecise(operation)) {
803 return value; 958 return {std::move(value), type};
804 } 959 }
805 // There's a bug in NVidia's proprietary drivers that makes precise fail on fragment shaders 960 std::string temporary = code.GenerateTemporary();
806 const std::string precise = stage != ProgramType::Fragment ? "precise " : ""; 961 code.AddLine("precise {} {} = {};", GetTypeString(type), temporary, value);
807 962 return {std::move(temporary), type};
808 const std::string temporary = code.GenerateTemporary();
809 code.AddLine("{}float {} = {};", precise, temporary, value);
810 return temporary;
811 } 963 }
812 964
813 std::string VisitOperand(Operation operation, std::size_t operand_index) { 965 Expression VisitOperand(Operation operation, std::size_t operand_index) {
814 const auto& operand = operation[operand_index]; 966 const auto& operand = operation[operand_index];
815 const bool parent_precise = IsPrecise(operation); 967 const bool parent_precise = IsPrecise(operation);
816 const bool child_precise = IsPrecise(operand); 968 const bool child_precise = IsPrecise(operand);
@@ -819,19 +971,16 @@ private:
819 return Visit(operand); 971 return Visit(operand);
820 } 972 }
821 973
822 const std::string temporary = code.GenerateTemporary(); 974 Expression value = Visit(operand);
823 code.AddLine("float {} = {};", temporary, Visit(operand)); 975 std::string temporary = code.GenerateTemporary();
824 return temporary; 976 code.AddLine("{} {} = {};", GetTypeString(value.GetType()), temporary, value.GetCode());
825 } 977 return {std::move(temporary), value.GetType()};
826
827 std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) {
828 return CastOperand(VisitOperand(operation, operand_index), type);
829 } 978 }
830 979
831 std::optional<std::pair<std::string, bool>> GetOutputAttribute(const AbufNode* abuf) { 980 Expression GetOutputAttribute(const AbufNode* abuf) {
832 switch (const auto attribute = abuf->GetIndex()) { 981 switch (const auto attribute = abuf->GetIndex()) {
833 case Attribute::Index::Position: 982 case Attribute::Index::Position:
834 return std::make_pair("gl_Position"s + GetSwizzle(abuf->GetElement()), false); 983 return {"gl_Position"s + GetSwizzle(abuf->GetElement()), Type::Float};
835 case Attribute::Index::LayerViewportPointSize: 984 case Attribute::Index::LayerViewportPointSize:
836 switch (abuf->GetElement()) { 985 switch (abuf->GetElement()) {
837 case 0: 986 case 0:
@@ -841,119 +990,79 @@ private:
841 if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) { 990 if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
842 return {}; 991 return {};
843 } 992 }
844 return std::make_pair("gl_Layer", true); 993 return {"gl_Layer", Type::Int};
845 case 2: 994 case 2:
846 if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) { 995 if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
847 return {}; 996 return {};
848 } 997 }
849 return std::make_pair("gl_ViewportIndex", true); 998 return {"gl_ViewportIndex", Type::Int};
850 case 3: 999 case 3:
851 UNIMPLEMENTED_MSG("Requires some state changes for gl_PointSize to work in shader"); 1000 UNIMPLEMENTED_MSG("Requires some state changes for gl_PointSize to work in shader");
852 return std::make_pair("gl_PointSize", false); 1001 return {"gl_PointSize", Type::Float};
853 } 1002 }
854 return {}; 1003 return {};
855 case Attribute::Index::ClipDistances0123: 1004 case Attribute::Index::ClipDistances0123:
856 return std::make_pair(fmt::format("gl_ClipDistance[{}]", abuf->GetElement()), false); 1005 return {fmt::format("gl_ClipDistance[{}]", abuf->GetElement()), Type::Float};
857 case Attribute::Index::ClipDistances4567: 1006 case Attribute::Index::ClipDistances4567:
858 return std::make_pair(fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), 1007 return {fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float};
859 false);
860 default: 1008 default:
861 if (IsGenericAttribute(attribute)) { 1009 if (IsGenericAttribute(attribute)) {
862 return std::make_pair( 1010 return {GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()),
863 GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()), false); 1011 Type::Float};
864 } 1012 }
865 UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute)); 1013 UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
866 return {}; 1014 return {};
867 } 1015 }
868 } 1016 }
869 1017
870 std::string CastOperand(const std::string& value, Type type) const { 1018 Expression GenerateUnary(Operation operation, std::string_view func, Type result_type,
871 switch (type) { 1019 Type type_a) {
872 case Type::Bool: 1020 std::string op_str = fmt::format("{}({})", func, VisitOperand(operation, 0).As(type_a));
873 case Type::Bool2: 1021 return ApplyPrecise(operation, std::move(op_str), result_type);
874 case Type::Float:
875 return value;
876 case Type::Int:
877 return fmt::format("ftoi({})", value);
878 case Type::Uint:
879 return fmt::format("ftou({})", value);
880 case Type::HalfFloat:
881 return fmt::format("toHalf2({})", value);
882 }
883 UNREACHABLE();
884 return value;
885 } 1022 }
886 1023
887 std::string BitwiseCastResult(const std::string& value, Type type, 1024 Expression GenerateBinaryInfix(Operation operation, std::string_view func, Type result_type,
888 bool needs_parenthesis = false) { 1025 Type type_a, Type type_b) {
889 switch (type) { 1026 const std::string op_a = VisitOperand(operation, 0).As(type_a);
890 case Type::Bool: 1027 const std::string op_b = VisitOperand(operation, 1).As(type_b);
891 case Type::Bool2: 1028 std::string op_str = fmt::format("({} {} {})", op_a, func, op_b);
892 case Type::Float:
893 if (needs_parenthesis) {
894 return fmt::format("({})", value);
895 }
896 return value;
897 case Type::Int:
898 return fmt::format("itof({})", value);
899 case Type::Uint:
900 return fmt::format("utof({})", value);
901 case Type::HalfFloat:
902 return fmt::format("fromHalf2({})", value);
903 }
904 UNREACHABLE();
905 return value;
906 }
907
908 std::string GenerateUnary(Operation operation, const std::string& func, Type result_type,
909 Type type_a, bool needs_parenthesis = true) {
910 const std::string op_str = fmt::format("{}({})", func, VisitOperand(operation, 0, type_a));
911
912 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type, needs_parenthesis));
913 }
914
915 std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type,
916 Type type_a, Type type_b) {
917 const std::string op_a = VisitOperand(operation, 0, type_a);
918 const std::string op_b = VisitOperand(operation, 1, type_b);
919 const std::string op_str = fmt::format("({} {} {})", op_a, func, op_b);
920 1029
921 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); 1030 return ApplyPrecise(operation, std::move(op_str), result_type);
922 } 1031 }
923 1032
924 std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type, 1033 Expression GenerateBinaryCall(Operation operation, std::string_view func, Type result_type,
925 Type type_a, Type type_b) { 1034 Type type_a, Type type_b) {
926 const std::string op_a = VisitOperand(operation, 0, type_a); 1035 const std::string op_a = VisitOperand(operation, 0).As(type_a);
927 const std::string op_b = VisitOperand(operation, 1, type_b); 1036 const std::string op_b = VisitOperand(operation, 1).As(type_b);
928 const std::string op_str = fmt::format("{}({}, {})", func, op_a, op_b); 1037 std::string op_str = fmt::format("{}({}, {})", func, op_a, op_b);
929 1038
930 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); 1039 return ApplyPrecise(operation, std::move(op_str), result_type);
931 } 1040 }
932 1041
933 std::string GenerateTernary(Operation operation, const std::string& func, Type result_type, 1042 Expression GenerateTernary(Operation operation, std::string_view func, Type result_type,
934 Type type_a, Type type_b, Type type_c) { 1043 Type type_a, Type type_b, Type type_c) {
935 const std::string op_a = VisitOperand(operation, 0, type_a); 1044 const std::string op_a = VisitOperand(operation, 0).As(type_a);
936 const std::string op_b = VisitOperand(operation, 1, type_b); 1045 const std::string op_b = VisitOperand(operation, 1).As(type_b);
937 const std::string op_c = VisitOperand(operation, 2, type_c); 1046 const std::string op_c = VisitOperand(operation, 2).As(type_c);
938 const std::string op_str = fmt::format("{}({}, {}, {})", func, op_a, op_b, op_c); 1047 std::string op_str = fmt::format("{}({}, {}, {})", func, op_a, op_b, op_c);
939 1048
940 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); 1049 return ApplyPrecise(operation, std::move(op_str), result_type);
941 } 1050 }
942 1051
943 std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type, 1052 Expression GenerateQuaternary(Operation operation, const std::string& func, Type result_type,
944 Type type_a, Type type_b, Type type_c, Type type_d) { 1053 Type type_a, Type type_b, Type type_c, Type type_d) {
945 const std::string op_a = VisitOperand(operation, 0, type_a); 1054 const std::string op_a = VisitOperand(operation, 0).As(type_a);
946 const std::string op_b = VisitOperand(operation, 1, type_b); 1055 const std::string op_b = VisitOperand(operation, 1).As(type_b);
947 const std::string op_c = VisitOperand(operation, 2, type_c); 1056 const std::string op_c = VisitOperand(operation, 2).As(type_c);
948 const std::string op_d = VisitOperand(operation, 3, type_d); 1057 const std::string op_d = VisitOperand(operation, 3).As(type_d);
949 const std::string op_str = fmt::format("{}({}, {}, {}, {})", func, op_a, op_b, op_c, op_d); 1058 std::string op_str = fmt::format("{}({}, {}, {}, {})", func, op_a, op_b, op_c, op_d);
950 1059
951 return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); 1060 return ApplyPrecise(operation, std::move(op_str), result_type);
952 } 1061 }
953 1062
954 std::string GenerateTexture(Operation operation, const std::string& function_suffix, 1063 std::string GenerateTexture(Operation operation, const std::string& function_suffix,
955 const std::vector<TextureIR>& extras) { 1064 const std::vector<TextureIR>& extras) {
956 constexpr std::array<const char*, 4> coord_constructors = {"float", "vec2", "vec3", "vec4"}; 1065 constexpr std::array coord_constructors = {"float", "vec2", "vec3", "vec4"};
957 1066
958 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1067 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
959 ASSERT(meta); 1068 ASSERT(meta);
@@ -970,17 +1079,17 @@ private:
970 expr += coord_constructors.at(count + (has_array ? 1 : 0) + (has_shadow ? 1 : 0) - 1); 1079 expr += coord_constructors.at(count + (has_array ? 1 : 0) + (has_shadow ? 1 : 0) - 1);
971 expr += '('; 1080 expr += '(';
972 for (std::size_t i = 0; i < count; ++i) { 1081 for (std::size_t i = 0; i < count; ++i) {
973 expr += Visit(operation[i]); 1082 expr += Visit(operation[i]).AsFloat();
974 1083
975 const std::size_t next = i + 1; 1084 const std::size_t next = i + 1;
976 if (next < count) 1085 if (next < count)
977 expr += ", "; 1086 expr += ", ";
978 } 1087 }
979 if (has_array) { 1088 if (has_array) {
980 expr += ", float(ftoi(" + Visit(meta->array) + "))"; 1089 expr += ", float(" + Visit(meta->array).AsInt() + ')';
981 } 1090 }
982 if (has_shadow) { 1091 if (has_shadow) {
983 expr += ", " + Visit(meta->depth_compare); 1092 expr += ", " + Visit(meta->depth_compare).AsFloat();
984 } 1093 }
985 expr += ')'; 1094 expr += ')';
986 1095
@@ -1011,11 +1120,11 @@ private:
1011 // required to be constant) 1120 // required to be constant)
1012 expr += std::to_string(static_cast<s32>(immediate->GetValue())); 1121 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
1013 } else { 1122 } else {
1014 expr += fmt::format("ftoi({})", Visit(operand)); 1123 expr += Visit(operand).AsInt();
1015 } 1124 }
1016 break; 1125 break;
1017 case Type::Float: 1126 case Type::Float:
1018 expr += Visit(operand); 1127 expr += Visit(operand).AsFloat();
1019 break; 1128 break;
1020 default: { 1129 default: {
1021 const auto type_int = static_cast<u32>(type); 1130 const auto type_int = static_cast<u32>(type);
@@ -1031,7 +1140,7 @@ private:
1031 if (aoffi.empty()) { 1140 if (aoffi.empty()) {
1032 return {}; 1141 return {};
1033 } 1142 }
1034 constexpr std::array<const char*, 3> coord_constructors = {"int", "ivec2", "ivec3"}; 1143 constexpr std::array coord_constructors = {"int", "ivec2", "ivec3"};
1035 std::string expr = ", "; 1144 std::string expr = ", ";
1036 expr += coord_constructors.at(aoffi.size() - 1); 1145 expr += coord_constructors.at(aoffi.size() - 1);
1037 expr += '('; 1146 expr += '(';
@@ -1044,7 +1153,7 @@ private:
1044 expr += std::to_string(static_cast<s32>(immediate->GetValue())); 1153 expr += std::to_string(static_cast<s32>(immediate->GetValue()));
1045 } else if (device.HasVariableAoffi()) { 1154 } else if (device.HasVariableAoffi()) {
1046 // Avoid using variable AOFFI on unsupported devices. 1155 // Avoid using variable AOFFI on unsupported devices.
1047 expr += fmt::format("ftoi({})", Visit(operand)); 1156 expr += Visit(operand).AsInt();
1048 } else { 1157 } else {
1049 // Insert 0 on devices not supporting variable AOFFI. 1158 // Insert 0 on devices not supporting variable AOFFI.
1050 expr += '0'; 1159 expr += '0';
@@ -1058,328 +1167,314 @@ private:
1058 return expr; 1167 return expr;
1059 } 1168 }
1060 1169
1061 std::string Assign(Operation operation) { 1170 Expression Assign(Operation operation) {
1062 const Node& dest = operation[0]; 1171 const Node& dest = operation[0];
1063 const Node& src = operation[1]; 1172 const Node& src = operation[1];
1064 1173
1065 std::string target; 1174 Expression target;
1066 bool is_integer = false;
1067
1068 if (const auto gpr = std::get_if<GprNode>(&*dest)) { 1175 if (const auto gpr = std::get_if<GprNode>(&*dest)) {
1069 if (gpr->GetIndex() == Register::ZeroIndex) { 1176 if (gpr->GetIndex() == Register::ZeroIndex) {
1070 // Writing to Register::ZeroIndex is a no op 1177 // Writing to Register::ZeroIndex is a no op
1071 return {}; 1178 return {};
1072 } 1179 }
1073 target = GetRegister(gpr->GetIndex()); 1180 target = {GetRegister(gpr->GetIndex()), Type::Float};
1074 } else if (const auto abuf = std::get_if<AbufNode>(&*dest)) { 1181 } else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
1075 UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer()); 1182 UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
1076 const auto result = GetOutputAttribute(abuf); 1183 target = GetOutputAttribute(abuf);
1077 if (!result) {
1078 return {};
1079 }
1080 target = result->first;
1081 is_integer = result->second;
1082 } else if (const auto lmem = std::get_if<LmemNode>(&*dest)) { 1184 } else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
1083 if (stage == ProgramType::Compute) { 1185 if (stage == ProgramType::Compute) {
1084 LOG_WARNING(Render_OpenGL, "Local memory is stubbed on compute shaders"); 1186 LOG_WARNING(Render_OpenGL, "Local memory is stubbed on compute shaders");
1085 } 1187 }
1086 target = fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); 1188 target = {
1189 fmt::format("{}[{} >> 2]", GetLocalMemory(), Visit(lmem->GetAddress()).AsUint()),
1190 Type::Uint};
1087 } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) { 1191 } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
1088 const std::string real = Visit(gmem->GetRealAddress()); 1192 const std::string real = Visit(gmem->GetRealAddress()).AsUint();
1089 const std::string base = Visit(gmem->GetBaseAddress()); 1193 const std::string base = Visit(gmem->GetBaseAddress()).AsUint();
1090 const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base); 1194 const std::string final_offset = fmt::format("({} - {}) >> 2", real, base);
1091 target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); 1195 target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset),
1196 Type::Uint};
1092 } else { 1197 } else {
1093 UNREACHABLE_MSG("Assign called without a proper target"); 1198 UNREACHABLE_MSG("Assign called without a proper target");
1094 } 1199 }
1095 1200
1096 if (is_integer) { 1201 code.AddLine("{} = {};", target.GetCode(), Visit(src).As(target.GetType()));
1097 code.AddLine("{} = ftoi({});", target, Visit(src));
1098 } else {
1099 code.AddLine("{} = {};", target, Visit(src));
1100 }
1101 return {}; 1202 return {};
1102 } 1203 }
1103 1204
1104 template <Type type> 1205 template <Type type>
1105 std::string Add(Operation operation) { 1206 Expression Add(Operation operation) {
1106 return GenerateBinaryInfix(operation, "+", type, type, type); 1207 return GenerateBinaryInfix(operation, "+", type, type, type);
1107 } 1208 }
1108 1209
1109 template <Type type> 1210 template <Type type>
1110 std::string Mul(Operation operation) { 1211 Expression Mul(Operation operation) {
1111 return GenerateBinaryInfix(operation, "*", type, type, type); 1212 return GenerateBinaryInfix(operation, "*", type, type, type);
1112 } 1213 }
1113 1214
1114 template <Type type> 1215 template <Type type>
1115 std::string Div(Operation operation) { 1216 Expression Div(Operation operation) {
1116 return GenerateBinaryInfix(operation, "/", type, type, type); 1217 return GenerateBinaryInfix(operation, "/", type, type, type);
1117 } 1218 }
1118 1219
1119 template <Type type> 1220 template <Type type>
1120 std::string Fma(Operation operation) { 1221 Expression Fma(Operation operation) {
1121 return GenerateTernary(operation, "fma", type, type, type, type); 1222 return GenerateTernary(operation, "fma", type, type, type, type);
1122 } 1223 }
1123 1224
1124 template <Type type> 1225 template <Type type>
1125 std::string Negate(Operation operation) { 1226 Expression Negate(Operation operation) {
1126 return GenerateUnary(operation, "-", type, type, true); 1227 return GenerateUnary(operation, "-", type, type);
1127 } 1228 }
1128 1229
1129 template <Type type> 1230 template <Type type>
1130 std::string Absolute(Operation operation) { 1231 Expression Absolute(Operation operation) {
1131 return GenerateUnary(operation, "abs", type, type, false); 1232 return GenerateUnary(operation, "abs", type, type);
1132 } 1233 }
1133 1234
1134 std::string FClamp(Operation operation) { 1235 Expression FClamp(Operation operation) {
1135 return GenerateTernary(operation, "clamp", Type::Float, Type::Float, Type::Float, 1236 return GenerateTernary(operation, "clamp", Type::Float, Type::Float, Type::Float,
1136 Type::Float); 1237 Type::Float);
1137 } 1238 }
1138 1239
1139 std::string FCastHalf0(Operation operation) { 1240 Expression FCastHalf0(Operation operation) {
1140 const std::string op_a = VisitOperand(operation, 0, Type::HalfFloat); 1241 return {fmt::format("({})[0]", VisitOperand(operation, 0).AsHalfFloat()), Type::Float};
1141 return fmt::format("({})[0]", op_a);
1142 } 1242 }
1143 1243
1144 std::string FCastHalf1(Operation operation) { 1244 Expression FCastHalf1(Operation operation) {
1145 const std::string op_a = VisitOperand(operation, 0, Type::HalfFloat); 1245 return {fmt::format("({})[1]", VisitOperand(operation, 0).AsHalfFloat()), Type::Float};
1146 return fmt::format("({})[1]", op_a);
1147 } 1246 }
1148 1247
1149 template <Type type> 1248 template <Type type>
1150 std::string Min(Operation operation) { 1249 Expression Min(Operation operation) {
1151 return GenerateBinaryCall(operation, "min", type, type, type); 1250 return GenerateBinaryCall(operation, "min", type, type, type);
1152 } 1251 }
1153 1252
1154 template <Type type> 1253 template <Type type>
1155 std::string Max(Operation operation) { 1254 Expression Max(Operation operation) {
1156 return GenerateBinaryCall(operation, "max", type, type, type); 1255 return GenerateBinaryCall(operation, "max", type, type, type);
1157 } 1256 }
1158 1257
1159 std::string Select(Operation operation) { 1258 Expression Select(Operation operation) {
1160 const std::string condition = Visit(operation[0]); 1259 const std::string condition = Visit(operation[0]).AsBool();
1161 const std::string true_case = Visit(operation[1]); 1260 const std::string true_case = Visit(operation[1]).AsUint();
1162 const std::string false_case = Visit(operation[2]); 1261 const std::string false_case = Visit(operation[2]).AsUint();
1163 const std::string op_str = fmt::format("({} ? {} : {})", condition, true_case, false_case); 1262 std::string op_str = fmt::format("({} ? {} : {})", condition, true_case, false_case);
1164 1263
1165 return ApplyPrecise(operation, op_str); 1264 return ApplyPrecise(operation, std::move(op_str), Type::Uint);
1166 } 1265 }
1167 1266
1168 std::string FCos(Operation operation) { 1267 Expression FCos(Operation operation) {
1169 return GenerateUnary(operation, "cos", Type::Float, Type::Float, false); 1268 return GenerateUnary(operation, "cos", Type::Float, Type::Float);
1170 } 1269 }
1171 1270
1172 std::string FSin(Operation operation) { 1271 Expression FSin(Operation operation) {
1173 return GenerateUnary(operation, "sin", Type::Float, Type::Float, false); 1272 return GenerateUnary(operation, "sin", Type::Float, Type::Float);
1174 } 1273 }
1175 1274
1176 std::string FExp2(Operation operation) { 1275 Expression FExp2(Operation operation) {
1177 return GenerateUnary(operation, "exp2", Type::Float, Type::Float, false); 1276 return GenerateUnary(operation, "exp2", Type::Float, Type::Float);
1178 } 1277 }
1179 1278
1180 std::string FLog2(Operation operation) { 1279 Expression FLog2(Operation operation) {
1181 return GenerateUnary(operation, "log2", Type::Float, Type::Float, false); 1280 return GenerateUnary(operation, "log2", Type::Float, Type::Float);
1182 } 1281 }
1183 1282
1184 std::string FInverseSqrt(Operation operation) { 1283 Expression FInverseSqrt(Operation operation) {
1185 return GenerateUnary(operation, "inversesqrt", Type::Float, Type::Float, false); 1284 return GenerateUnary(operation, "inversesqrt", Type::Float, Type::Float);
1186 } 1285 }
1187 1286
1188 std::string FSqrt(Operation operation) { 1287 Expression FSqrt(Operation operation) {
1189 return GenerateUnary(operation, "sqrt", Type::Float, Type::Float, false); 1288 return GenerateUnary(operation, "sqrt", Type::Float, Type::Float);
1190 } 1289 }
1191 1290
1192 std::string FRoundEven(Operation operation) { 1291 Expression FRoundEven(Operation operation) {
1193 return GenerateUnary(operation, "roundEven", Type::Float, Type::Float, false); 1292 return GenerateUnary(operation, "roundEven", Type::Float, Type::Float);
1194 } 1293 }
1195 1294
1196 std::string FFloor(Operation operation) { 1295 Expression FFloor(Operation operation) {
1197 return GenerateUnary(operation, "floor", Type::Float, Type::Float, false); 1296 return GenerateUnary(operation, "floor", Type::Float, Type::Float);
1198 } 1297 }
1199 1298
1200 std::string FCeil(Operation operation) { 1299 Expression FCeil(Operation operation) {
1201 return GenerateUnary(operation, "ceil", Type::Float, Type::Float, false); 1300 return GenerateUnary(operation, "ceil", Type::Float, Type::Float);
1202 } 1301 }
1203 1302
1204 std::string FTrunc(Operation operation) { 1303 Expression FTrunc(Operation operation) {
1205 return GenerateUnary(operation, "trunc", Type::Float, Type::Float, false); 1304 return GenerateUnary(operation, "trunc", Type::Float, Type::Float);
1206 } 1305 }
1207 1306
1208 template <Type type> 1307 template <Type type>
1209 std::string FCastInteger(Operation operation) { 1308 Expression FCastInteger(Operation operation) {
1210 return GenerateUnary(operation, "float", Type::Float, type, false); 1309 return GenerateUnary(operation, "float", Type::Float, type);
1211 } 1310 }
1212 1311
1213 std::string ICastFloat(Operation operation) { 1312 Expression ICastFloat(Operation operation) {
1214 return GenerateUnary(operation, "int", Type::Int, Type::Float, false); 1313 return GenerateUnary(operation, "int", Type::Int, Type::Float);
1215 } 1314 }
1216 1315
1217 std::string ICastUnsigned(Operation operation) { 1316 Expression ICastUnsigned(Operation operation) {
1218 return GenerateUnary(operation, "int", Type::Int, Type::Uint, false); 1317 return GenerateUnary(operation, "int", Type::Int, Type::Uint);
1219 } 1318 }
1220 1319
1221 template <Type type> 1320 template <Type type>
1222 std::string LogicalShiftLeft(Operation operation) { 1321 Expression LogicalShiftLeft(Operation operation) {
1223 return GenerateBinaryInfix(operation, "<<", type, type, Type::Uint); 1322 return GenerateBinaryInfix(operation, "<<", type, type, Type::Uint);
1224 } 1323 }
1225 1324
1226 std::string ILogicalShiftRight(Operation operation) { 1325 Expression ILogicalShiftRight(Operation operation) {
1227 const std::string op_a = VisitOperand(operation, 0, Type::Uint); 1326 const std::string op_a = VisitOperand(operation, 0).AsUint();
1228 const std::string op_b = VisitOperand(operation, 1, Type::Uint); 1327 const std::string op_b = VisitOperand(operation, 1).AsUint();
1229 const std::string op_str = fmt::format("int({} >> {})", op_a, op_b); 1328 std::string op_str = fmt::format("int({} >> {})", op_a, op_b);
1230 1329
1231 return ApplyPrecise(operation, BitwiseCastResult(op_str, Type::Int)); 1330 return ApplyPrecise(operation, std::move(op_str), Type::Int);
1232 } 1331 }
1233 1332
1234 std::string IArithmeticShiftRight(Operation operation) { 1333 Expression IArithmeticShiftRight(Operation operation) {
1235 return GenerateBinaryInfix(operation, ">>", Type::Int, Type::Int, Type::Uint); 1334 return GenerateBinaryInfix(operation, ">>", Type::Int, Type::Int, Type::Uint);
1236 } 1335 }
1237 1336
1238 template <Type type> 1337 template <Type type>
1239 std::string BitwiseAnd(Operation operation) { 1338 Expression BitwiseAnd(Operation operation) {
1240 return GenerateBinaryInfix(operation, "&", type, type, type); 1339 return GenerateBinaryInfix(operation, "&", type, type, type);
1241 } 1340 }
1242 1341
1243 template <Type type> 1342 template <Type type>
1244 std::string BitwiseOr(Operation operation) { 1343 Expression BitwiseOr(Operation operation) {
1245 return GenerateBinaryInfix(operation, "|", type, type, type); 1344 return GenerateBinaryInfix(operation, "|", type, type, type);
1246 } 1345 }
1247 1346
1248 template <Type type> 1347 template <Type type>
1249 std::string BitwiseXor(Operation operation) { 1348 Expression BitwiseXor(Operation operation) {
1250 return GenerateBinaryInfix(operation, "^", type, type, type); 1349 return GenerateBinaryInfix(operation, "^", type, type, type);
1251 } 1350 }
1252 1351
1253 template <Type type> 1352 template <Type type>
1254 std::string BitwiseNot(Operation operation) { 1353 Expression BitwiseNot(Operation operation) {
1255 return GenerateUnary(operation, "~", type, type, false); 1354 return GenerateUnary(operation, "~", type, type);
1256 } 1355 }
1257 1356
1258 std::string UCastFloat(Operation operation) { 1357 Expression UCastFloat(Operation operation) {
1259 return GenerateUnary(operation, "uint", Type::Uint, Type::Float, false); 1358 return GenerateUnary(operation, "uint", Type::Uint, Type::Float);
1260 } 1359 }
1261 1360
1262 std::string UCastSigned(Operation operation) { 1361 Expression UCastSigned(Operation operation) {
1263 return GenerateUnary(operation, "uint", Type::Uint, Type::Int, false); 1362 return GenerateUnary(operation, "uint", Type::Uint, Type::Int);
1264 } 1363 }
1265 1364
1266 std::string UShiftRight(Operation operation) { 1365 Expression UShiftRight(Operation operation) {
1267 return GenerateBinaryInfix(operation, ">>", Type::Uint, Type::Uint, Type::Uint); 1366 return GenerateBinaryInfix(operation, ">>", Type::Uint, Type::Uint, Type::Uint);
1268 } 1367 }
1269 1368
1270 template <Type type> 1369 template <Type type>
1271 std::string BitfieldInsert(Operation operation) { 1370 Expression BitfieldInsert(Operation operation) {
1272 return GenerateQuaternary(operation, "bitfieldInsert", type, type, type, Type::Int, 1371 return GenerateQuaternary(operation, "bitfieldInsert", type, type, type, Type::Int,
1273 Type::Int); 1372 Type::Int);
1274 } 1373 }
1275 1374
1276 template <Type type> 1375 template <Type type>
1277 std::string BitfieldExtract(Operation operation) { 1376 Expression BitfieldExtract(Operation operation) {
1278 return GenerateTernary(operation, "bitfieldExtract", type, type, Type::Int, Type::Int); 1377 return GenerateTernary(operation, "bitfieldExtract", type, type, Type::Int, Type::Int);
1279 } 1378 }
1280 1379
1281 template <Type type> 1380 template <Type type>
1282 std::string BitCount(Operation operation) { 1381 Expression BitCount(Operation operation) {
1283 return GenerateUnary(operation, "bitCount", type, type, false); 1382 return GenerateUnary(operation, "bitCount", type, type);
1284 } 1383 }
1285 1384
1286 std::string HNegate(Operation operation) { 1385 Expression HNegate(Operation operation) {
1287 const auto GetNegate = [&](std::size_t index) { 1386 const auto GetNegate = [&](std::size_t index) {
1288 return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1"; 1387 return VisitOperand(operation, index).AsBool() + " ? -1 : 1";
1289 }; 1388 };
1290 const std::string value = 1389 return {fmt::format("({} * vec2({}, {}))", VisitOperand(operation, 0).AsHalfFloat(),
1291 fmt::format("({} * vec2({}, {}))", VisitOperand(operation, 0, Type::HalfFloat), 1390 GetNegate(1), GetNegate(2)),
1292 GetNegate(1), GetNegate(2)); 1391 Type::HalfFloat};
1293 return BitwiseCastResult(value, Type::HalfFloat); 1392 }
1294 } 1393
1295 1394 Expression HClamp(Operation operation) {
1296 std::string HClamp(Operation operation) { 1395 const std::string value = VisitOperand(operation, 0).AsHalfFloat();
1297 const std::string value = VisitOperand(operation, 0, Type::HalfFloat); 1396 const std::string min = VisitOperand(operation, 1).AsFloat();
1298 const std::string min = VisitOperand(operation, 1, Type::Float); 1397 const std::string max = VisitOperand(operation, 2).AsFloat();
1299 const std::string max = VisitOperand(operation, 2, Type::Float); 1398 std::string clamped = fmt::format("clamp({}, vec2({}), vec2({}))", value, min, max);
1300 const std::string clamped = fmt::format("clamp({}, vec2({}), vec2({}))", value, min, max); 1399
1301 1400 return ApplyPrecise(operation, std::move(clamped), Type::HalfFloat);
1302 return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat)); 1401 }
1303 } 1402
1304 1403 Expression HCastFloat(Operation operation) {
1305 std::string HCastFloat(Operation operation) { 1404 return {fmt::format("vec2({})", VisitOperand(operation, 0).AsFloat()), Type::HalfFloat};
1306 const std::string op_a = VisitOperand(operation, 0, Type::Float); 1405 }
1307 return fmt::format("fromHalf2(vec2({}, 0.0f))", op_a); 1406
1308 } 1407 Expression HUnpack(Operation operation) {
1309 1408 Expression operand = VisitOperand(operation, 0);
1310 std::string HUnpack(Operation operation) { 1409 switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) {
1311 const std::string operand{VisitOperand(operation, 0, Type::HalfFloat)}; 1410 case Tegra::Shader::HalfType::H0_H1:
1312 const auto value = [&]() -> std::string { 1411 return operand;
1313 switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) { 1412 case Tegra::Shader::HalfType::F32:
1314 case Tegra::Shader::HalfType::H0_H1: 1413 return {fmt::format("vec2({})", operand.AsFloat()), Type::HalfFloat};
1315 return operand; 1414 case Tegra::Shader::HalfType::H0_H0:
1316 case Tegra::Shader::HalfType::F32: 1415 return {fmt::format("vec2({}[0])", operand.AsHalfFloat()), Type::HalfFloat};
1317 return fmt::format("vec2(fromHalf2({}))", operand); 1416 case Tegra::Shader::HalfType::H1_H1:
1318 case Tegra::Shader::HalfType::H0_H0: 1417 return {fmt::format("vec2({}[1])", operand.AsHalfFloat()), Type::HalfFloat};
1319 return fmt::format("vec2({}[0])", operand); 1418 }
1320 case Tegra::Shader::HalfType::H1_H1:
1321 return fmt::format("vec2({}[1])", operand);
1322 }
1323 UNREACHABLE();
1324 return "0";
1325 }();
1326 return fmt::format("fromHalf2({})", value);
1327 } 1419 }
1328 1420
1329 std::string HMergeF32(Operation operation) { 1421 Expression HMergeF32(Operation operation) {
1330 return fmt::format("float(toHalf2({})[0])", Visit(operation[0])); 1422 return {fmt::format("float({}[0])", VisitOperand(operation, 0).AsHalfFloat()), Type::Float};
1331 } 1423 }
1332 1424
1333 std::string HMergeH0(Operation operation) { 1425 Expression HMergeH0(Operation operation) {
1334 return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[1]), 1426 std::string dest = VisitOperand(operation, 0).AsUint();
1335 Visit(operation[0])); 1427 std::string src = VisitOperand(operation, 1).AsUint();
1428 return {fmt::format("(({} & 0x0000FFFFU) | ({} & 0xFFFF0000U))", src, dest), Type::Uint};
1336 } 1429 }
1337 1430
1338 std::string HMergeH1(Operation operation) { 1431 Expression HMergeH1(Operation operation) {
1339 return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[0]), 1432 std::string dest = VisitOperand(operation, 0).AsUint();
1340 Visit(operation[1])); 1433 std::string src = VisitOperand(operation, 1).AsUint();
1434 return {fmt::format("(({} & 0x0000FFFFU) | ({} & 0xFFFF0000U))", dest, src), Type::Uint};
1341 } 1435 }
1342 1436
1343 std::string HPack2(Operation operation) { 1437 Expression HPack2(Operation operation) {
1344 return fmt::format("utof(packHalf2x16(vec2({}, {})))", Visit(operation[0]), 1438 return {fmt::format("vec2({}, {})", VisitOperand(operation, 0).AsFloat(),
1345 Visit(operation[1])); 1439 VisitOperand(operation, 1).AsFloat()),
1440 Type::HalfFloat};
1346 } 1441 }
1347 1442
1348 template <Type type> 1443 template <Type type>
1349 std::string LogicalLessThan(Operation operation) { 1444 Expression LogicalLessThan(Operation operation) {
1350 return GenerateBinaryInfix(operation, "<", Type::Bool, type, type); 1445 return GenerateBinaryInfix(operation, "<", Type::Bool, type, type);
1351 } 1446 }
1352 1447
1353 template <Type type> 1448 template <Type type>
1354 std::string LogicalEqual(Operation operation) { 1449 Expression LogicalEqual(Operation operation) {
1355 return GenerateBinaryInfix(operation, "==", Type::Bool, type, type); 1450 return GenerateBinaryInfix(operation, "==", Type::Bool, type, type);
1356 } 1451 }
1357 1452
1358 template <Type type> 1453 template <Type type>
1359 std::string LogicalLessEqual(Operation operation) { 1454 Expression LogicalLessEqual(Operation operation) {
1360 return GenerateBinaryInfix(operation, "<=", Type::Bool, type, type); 1455 return GenerateBinaryInfix(operation, "<=", Type::Bool, type, type);
1361 } 1456 }
1362 1457
1363 template <Type type> 1458 template <Type type>
1364 std::string LogicalGreaterThan(Operation operation) { 1459 Expression LogicalGreaterThan(Operation operation) {
1365 return GenerateBinaryInfix(operation, ">", Type::Bool, type, type); 1460 return GenerateBinaryInfix(operation, ">", Type::Bool, type, type);
1366 } 1461 }
1367 1462
1368 template <Type type> 1463 template <Type type>
1369 std::string LogicalNotEqual(Operation operation) { 1464 Expression LogicalNotEqual(Operation operation) {
1370 return GenerateBinaryInfix(operation, "!=", Type::Bool, type, type); 1465 return GenerateBinaryInfix(operation, "!=", Type::Bool, type, type);
1371 } 1466 }
1372 1467
1373 template <Type type> 1468 template <Type type>
1374 std::string LogicalGreaterEqual(Operation operation) { 1469 Expression LogicalGreaterEqual(Operation operation) {
1375 return GenerateBinaryInfix(operation, ">=", Type::Bool, type, type); 1470 return GenerateBinaryInfix(operation, ">=", Type::Bool, type, type);
1376 } 1471 }
1377 1472
1378 std::string LogicalFIsNan(Operation operation) { 1473 Expression LogicalFIsNan(Operation operation) {
1379 return GenerateUnary(operation, "isnan", Type::Bool, Type::Float, false); 1474 return GenerateUnary(operation, "isnan", Type::Bool, Type::Float);
1380 } 1475 }
1381 1476
1382 std::string LogicalAssign(Operation operation) { 1477 Expression LogicalAssign(Operation operation) {
1383 const Node& dest = operation[0]; 1478 const Node& dest = operation[0];
1384 const Node& src = operation[1]; 1479 const Node& src = operation[1];
1385 1480
@@ -1400,78 +1495,80 @@ private:
1400 target = GetInternalFlag(flag->GetFlag()); 1495 target = GetInternalFlag(flag->GetFlag());
1401 } 1496 }
1402 1497
1403 code.AddLine("{} = {};", target, Visit(src)); 1498 code.AddLine("{} = {};", target, Visit(src).AsBool());
1404 return {}; 1499 return {};
1405 } 1500 }
1406 1501
1407 std::string LogicalAnd(Operation operation) { 1502 Expression LogicalAnd(Operation operation) {
1408 return GenerateBinaryInfix(operation, "&&", Type::Bool, Type::Bool, Type::Bool); 1503 return GenerateBinaryInfix(operation, "&&", Type::Bool, Type::Bool, Type::Bool);
1409 } 1504 }
1410 1505
1411 std::string LogicalOr(Operation operation) { 1506 Expression LogicalOr(Operation operation) {
1412 return GenerateBinaryInfix(operation, "||", Type::Bool, Type::Bool, Type::Bool); 1507 return GenerateBinaryInfix(operation, "||", Type::Bool, Type::Bool, Type::Bool);
1413 } 1508 }
1414 1509
1415 std::string LogicalXor(Operation operation) { 1510 Expression LogicalXor(Operation operation) {
1416 return GenerateBinaryInfix(operation, "^^", Type::Bool, Type::Bool, Type::Bool); 1511 return GenerateBinaryInfix(operation, "^^", Type::Bool, Type::Bool, Type::Bool);
1417 } 1512 }
1418 1513
1419 std::string LogicalNegate(Operation operation) { 1514 Expression LogicalNegate(Operation operation) {
1420 return GenerateUnary(operation, "!", Type::Bool, Type::Bool, false); 1515 return GenerateUnary(operation, "!", Type::Bool, Type::Bool);
1421 } 1516 }
1422 1517
1423 std::string LogicalPick2(Operation operation) { 1518 Expression LogicalPick2(Operation operation) {
1424 const std::string pair = VisitOperand(operation, 0, Type::Bool2); 1519 return {fmt::format("{}[{}]", VisitOperand(operation, 0).AsBool2(),
1425 return fmt::format("{}[{}]", pair, VisitOperand(operation, 1, Type::Uint)); 1520 VisitOperand(operation, 1).AsUint()),
1521 Type::Bool};
1426 } 1522 }
1427 1523
1428 std::string LogicalAnd2(Operation operation) { 1524 Expression LogicalAnd2(Operation operation) {
1429 return GenerateUnary(operation, "all", Type::Bool, Type::Bool2); 1525 return GenerateUnary(operation, "all", Type::Bool, Type::Bool2);
1430 } 1526 }
1431 1527
1432 template <bool with_nan> 1528 template <bool with_nan>
1433 std::string GenerateHalfComparison(Operation operation, const std::string& compare_op) { 1529 Expression GenerateHalfComparison(Operation operation, std::string_view compare_op) {
1434 const std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2, 1530 Expression comparison = GenerateBinaryCall(operation, compare_op, Type::Bool2,
1435 Type::HalfFloat, Type::HalfFloat)}; 1531 Type::HalfFloat, Type::HalfFloat);
1436 if constexpr (!with_nan) { 1532 if constexpr (!with_nan) {
1437 return comparison; 1533 return comparison;
1438 } 1534 }
1439 return fmt::format("halfFloatNanComparison({}, {}, {})", comparison, 1535 return {fmt::format("HalfFloatNanComparison({}, {}, {})", comparison.AsBool2(),
1440 VisitOperand(operation, 0, Type::HalfFloat), 1536 VisitOperand(operation, 0).AsHalfFloat(),
1441 VisitOperand(operation, 1, Type::HalfFloat)); 1537 VisitOperand(operation, 1).AsHalfFloat()),
1538 Type::Bool2};
1442 } 1539 }
1443 1540
1444 template <bool with_nan> 1541 template <bool with_nan>
1445 std::string Logical2HLessThan(Operation operation) { 1542 Expression Logical2HLessThan(Operation operation) {
1446 return GenerateHalfComparison<with_nan>(operation, "lessThan"); 1543 return GenerateHalfComparison<with_nan>(operation, "lessThan");
1447 } 1544 }
1448 1545
1449 template <bool with_nan> 1546 template <bool with_nan>
1450 std::string Logical2HEqual(Operation operation) { 1547 Expression Logical2HEqual(Operation operation) {
1451 return GenerateHalfComparison<with_nan>(operation, "equal"); 1548 return GenerateHalfComparison<with_nan>(operation, "equal");
1452 } 1549 }
1453 1550
1454 template <bool with_nan> 1551 template <bool with_nan>
1455 std::string Logical2HLessEqual(Operation operation) { 1552 Expression Logical2HLessEqual(Operation operation) {
1456 return GenerateHalfComparison<with_nan>(operation, "lessThanEqual"); 1553 return GenerateHalfComparison<with_nan>(operation, "lessThanEqual");
1457 } 1554 }
1458 1555
1459 template <bool with_nan> 1556 template <bool with_nan>
1460 std::string Logical2HGreaterThan(Operation operation) { 1557 Expression Logical2HGreaterThan(Operation operation) {
1461 return GenerateHalfComparison<with_nan>(operation, "greaterThan"); 1558 return GenerateHalfComparison<with_nan>(operation, "greaterThan");
1462 } 1559 }
1463 1560
1464 template <bool with_nan> 1561 template <bool with_nan>
1465 std::string Logical2HNotEqual(Operation operation) { 1562 Expression Logical2HNotEqual(Operation operation) {
1466 return GenerateHalfComparison<with_nan>(operation, "notEqual"); 1563 return GenerateHalfComparison<with_nan>(operation, "notEqual");
1467 } 1564 }
1468 1565
1469 template <bool with_nan> 1566 template <bool with_nan>
1470 std::string Logical2HGreaterEqual(Operation operation) { 1567 Expression Logical2HGreaterEqual(Operation operation) {
1471 return GenerateHalfComparison<with_nan>(operation, "greaterThanEqual"); 1568 return GenerateHalfComparison<with_nan>(operation, "greaterThanEqual");
1472 } 1569 }
1473 1570
1474 std::string Texture(Operation operation) { 1571 Expression Texture(Operation operation) {
1475 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1572 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
1476 ASSERT(meta); 1573 ASSERT(meta);
1477 1574
@@ -1480,10 +1577,10 @@ private:
1480 if (meta->sampler.IsShadow()) { 1577 if (meta->sampler.IsShadow()) {
1481 expr = "vec4(" + expr + ')'; 1578 expr = "vec4(" + expr + ')';
1482 } 1579 }
1483 return expr + GetSwizzle(meta->element); 1580 return {expr + GetSwizzle(meta->element), Type::Float};
1484 } 1581 }
1485 1582
1486 std::string TextureLod(Operation operation) { 1583 Expression TextureLod(Operation operation) {
1487 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1584 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
1488 ASSERT(meta); 1585 ASSERT(meta);
1489 1586
@@ -1492,54 +1589,54 @@ private:
1492 if (meta->sampler.IsShadow()) { 1589 if (meta->sampler.IsShadow()) {
1493 expr = "vec4(" + expr + ')'; 1590 expr = "vec4(" + expr + ')';
1494 } 1591 }
1495 return expr + GetSwizzle(meta->element); 1592 return {expr + GetSwizzle(meta->element), Type::Float};
1496 } 1593 }
1497 1594
1498 std::string TextureGather(Operation operation) { 1595 Expression TextureGather(Operation operation) {
1499 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1596 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
1500 ASSERT(meta); 1597 ASSERT(meta);
1501 1598
1502 const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int; 1599 const auto type = meta->sampler.IsShadow() ? Type::Float : Type::Int;
1503 return GenerateTexture(operation, "Gather", 1600 return {GenerateTexture(operation, "Gather",
1504 {TextureArgument{type, meta->component}, TextureAoffi{}}) + 1601 {TextureArgument{type, meta->component}, TextureAoffi{}}) +
1505 GetSwizzle(meta->element); 1602 GetSwizzle(meta->element),
1603 Type::Float};
1506 } 1604 }
1507 1605
1508 std::string TextureQueryDimensions(Operation operation) { 1606 Expression TextureQueryDimensions(Operation operation) {
1509 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1607 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
1510 ASSERT(meta); 1608 ASSERT(meta);
1511 1609
1512 const std::string sampler = GetSampler(meta->sampler); 1610 const std::string sampler = GetSampler(meta->sampler);
1513 const std::string lod = VisitOperand(operation, 0, Type::Int); 1611 const std::string lod = VisitOperand(operation, 0).AsInt();
1514 1612
1515 switch (meta->element) { 1613 switch (meta->element) {
1516 case 0: 1614 case 0:
1517 case 1: 1615 case 1:
1518 return fmt::format("itof(int(textureSize({}, {}){}))", sampler, lod, 1616 return {fmt::format("textureSize({}, {}){}", sampler, lod, GetSwizzle(meta->element)),
1519 GetSwizzle(meta->element)); 1617 Type::Int};
1520 case 2:
1521 return "0";
1522 case 3: 1618 case 3:
1523 return fmt::format("itof(textureQueryLevels({}))", sampler); 1619 return {fmt::format("textureQueryLevels({})", sampler), Type::Int};
1524 } 1620 }
1525 UNREACHABLE(); 1621 UNREACHABLE();
1526 return "0"; 1622 return {"0", Type::Int};
1527 } 1623 }
1528 1624
1529 std::string TextureQueryLod(Operation operation) { 1625 Expression TextureQueryLod(Operation operation) {
1530 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1626 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
1531 ASSERT(meta); 1627 ASSERT(meta);
1532 1628
1533 if (meta->element < 2) { 1629 if (meta->element < 2) {
1534 return fmt::format("itof(int(({} * vec2(256)){}))", 1630 return {fmt::format("int(({} * vec2(256)){})",
1535 GenerateTexture(operation, "QueryLod", {}), 1631 GenerateTexture(operation, "QueryLod", {}),
1536 GetSwizzle(meta->element)); 1632 GetSwizzle(meta->element)),
1633 Type::Int};
1537 } 1634 }
1538 return "0"; 1635 return {"0", Type::Int};
1539 } 1636 }
1540 1637
1541 std::string TexelFetch(Operation operation) { 1638 Expression TexelFetch(Operation operation) {
1542 constexpr std::array<const char*, 4> constructors = {"int", "ivec2", "ivec3", "ivec4"}; 1639 constexpr std::array constructors = {"int", "ivec2", "ivec3", "ivec4"};
1543 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); 1640 const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
1544 ASSERT(meta); 1641 ASSERT(meta);
1545 UNIMPLEMENTED_IF(meta->sampler.IsArray()); 1642 UNIMPLEMENTED_IF(meta->sampler.IsArray());
@@ -1552,7 +1649,7 @@ private:
1552 expr += constructors.at(operation.GetOperandsCount() - 1); 1649 expr += constructors.at(operation.GetOperandsCount() - 1);
1553 expr += '('; 1650 expr += '(';
1554 for (std::size_t i = 0; i < count; ++i) { 1651 for (std::size_t i = 0; i < count; ++i) {
1555 expr += VisitOperand(operation, i, Type::Int); 1652 expr += VisitOperand(operation, i).AsInt();
1556 const std::size_t next = i + 1; 1653 const std::size_t next = i + 1;
1557 if (next == count) 1654 if (next == count)
1558 expr += ')'; 1655 expr += ')';
@@ -1565,7 +1662,7 @@ private:
1565 1662
1566 if (meta->lod) { 1663 if (meta->lod) {
1567 expr += ", "; 1664 expr += ", ";
1568 expr += CastOperand(Visit(meta->lod), Type::Int); 1665 expr += Visit(meta->lod).AsInt();
1569 } 1666 }
1570 expr += ')'; 1667 expr += ')';
1571 expr += GetSwizzle(meta->element); 1668 expr += GetSwizzle(meta->element);
@@ -1580,11 +1677,11 @@ private:
1580 code.AddLine("float {} = {};", tmp, expr); 1677 code.AddLine("float {} = {};", tmp, expr);
1581 code.AddLine("#endif"); 1678 code.AddLine("#endif");
1582 1679
1583 return tmp; 1680 return {tmp, Type::Float};
1584 } 1681 }
1585 1682
1586 std::string ImageStore(Operation operation) { 1683 Expression ImageStore(Operation operation) {
1587 constexpr std::array<const char*, 4> constructors{"int(", "ivec2(", "ivec3(", "ivec4("}; 1684 constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
1588 const auto meta{std::get<MetaImage>(operation.GetMeta())}; 1685 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1589 1686
1590 std::string expr = "imageStore("; 1687 std::string expr = "imageStore(";
@@ -1594,7 +1691,7 @@ private:
1594 const std::size_t coords_count{operation.GetOperandsCount()}; 1691 const std::size_t coords_count{operation.GetOperandsCount()};
1595 expr += constructors.at(coords_count - 1); 1692 expr += constructors.at(coords_count - 1);
1596 for (std::size_t i = 0; i < coords_count; ++i) { 1693 for (std::size_t i = 0; i < coords_count; ++i) {
1597 expr += VisitOperand(operation, i, Type::Int); 1694 expr += VisitOperand(operation, i).AsInt();
1598 if (i + 1 < coords_count) { 1695 if (i + 1 < coords_count) {
1599 expr += ", "; 1696 expr += ", ";
1600 } 1697 }
@@ -1605,7 +1702,7 @@ private:
1605 UNIMPLEMENTED_IF(values_count != 4); 1702 UNIMPLEMENTED_IF(values_count != 4);
1606 expr += "vec4("; 1703 expr += "vec4(";
1607 for (std::size_t i = 0; i < values_count; ++i) { 1704 for (std::size_t i = 0; i < values_count; ++i) {
1608 expr += Visit(meta.values.at(i)); 1705 expr += Visit(meta.values.at(i)).AsFloat();
1609 if (i + 1 < values_count) { 1706 if (i + 1 < values_count) {
1610 expr += ", "; 1707 expr += ", ";
1611 } 1708 }
@@ -1616,52 +1713,52 @@ private:
1616 return {}; 1713 return {};
1617 } 1714 }
1618 1715
1619 std::string Branch(Operation operation) { 1716 Expression Branch(Operation operation) {
1620 const auto target = std::get_if<ImmediateNode>(&*operation[0]); 1717 const auto target = std::get_if<ImmediateNode>(&*operation[0]);
1621 UNIMPLEMENTED_IF(!target); 1718 UNIMPLEMENTED_IF(!target);
1622 1719
1623 code.AddLine("jmp_to = 0x{:x}u;", target->GetValue()); 1720 code.AddLine("jmp_to = 0x{:X}U;", target->GetValue());
1624 code.AddLine("break;"); 1721 code.AddLine("break;");
1625 return {}; 1722 return {};
1626 } 1723 }
1627 1724
1628 std::string BranchIndirect(Operation operation) { 1725 Expression BranchIndirect(Operation operation) {
1629 const std::string op_a = VisitOperand(operation, 0, Type::Uint); 1726 const std::string op_a = VisitOperand(operation, 0).AsUint();
1630 1727
1631 code.AddLine("jmp_to = {};", op_a); 1728 code.AddLine("jmp_to = {};", op_a);
1632 code.AddLine("break;"); 1729 code.AddLine("break;");
1633 return {}; 1730 return {};
1634 } 1731 }
1635 1732
1636 std::string PushFlowStack(Operation operation) { 1733 Expression PushFlowStack(Operation operation) {
1637 const auto stack = std::get<MetaStackClass>(operation.GetMeta()); 1734 const auto stack = std::get<MetaStackClass>(operation.GetMeta());
1638 const auto target = std::get_if<ImmediateNode>(&*operation[0]); 1735 const auto target = std::get_if<ImmediateNode>(&*operation[0]);
1639 UNIMPLEMENTED_IF(!target); 1736 UNIMPLEMENTED_IF(!target);
1640 1737
1641 code.AddLine("{}[{}++] = 0x{:x}u;", FlowStackName(stack), FlowStackTopName(stack), 1738 code.AddLine("{}[{}++] = 0x{:X}U;", FlowStackName(stack), FlowStackTopName(stack),
1642 target->GetValue()); 1739 target->GetValue());
1643 return {}; 1740 return {};
1644 } 1741 }
1645 1742
1646 std::string PopFlowStack(Operation operation) { 1743 Expression PopFlowStack(Operation operation) {
1647 const auto stack = std::get<MetaStackClass>(operation.GetMeta()); 1744 const auto stack = std::get<MetaStackClass>(operation.GetMeta());
1648 code.AddLine("jmp_to = {}[--{}];", FlowStackName(stack), FlowStackTopName(stack)); 1745 code.AddLine("jmp_to = {}[--{}];", FlowStackName(stack), FlowStackTopName(stack));
1649 code.AddLine("break;"); 1746 code.AddLine("break;");
1650 return {}; 1747 return {};
1651 } 1748 }
1652 1749
1653 std::string Exit(Operation operation) { 1750 Expression Exit(Operation operation) {
1654 if (stage != ProgramType::Fragment) { 1751 if (stage != ProgramType::Fragment) {
1655 code.AddLine("return;"); 1752 code.AddLine("return;");
1656 return {}; 1753 return {};
1657 } 1754 }
1658 const auto& used_registers = ir.GetRegisters(); 1755 const auto& used_registers = ir.GetRegisters();
1659 const auto SafeGetRegister = [&](u32 reg) -> std::string { 1756 const auto SafeGetRegister = [&](u32 reg) -> Expression {
1660 // TODO(Rodrigo): Replace with contains once C++20 releases 1757 // TODO(Rodrigo): Replace with contains once C++20 releases
1661 if (used_registers.find(reg) != used_registers.end()) { 1758 if (used_registers.find(reg) != used_registers.end()) {
1662 return GetRegister(reg); 1759 return {GetRegister(reg), Type::Float};
1663 } 1760 }
1664 return "0.0f"; 1761 return {"0.0f", Type::Float};
1665 }; 1762 };
1666 1763
1667 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented"); 1764 UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented");
@@ -1674,7 +1771,7 @@ private:
1674 for (u32 component = 0; component < 4; ++component) { 1771 for (u32 component = 0; component < 4; ++component) {
1675 if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { 1772 if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
1676 code.AddLine("FragColor{}[{}] = {};", render_target, component, 1773 code.AddLine("FragColor{}[{}] = {};", render_target, component,
1677 SafeGetRegister(current_reg)); 1774 SafeGetRegister(current_reg).AsFloat());
1678 ++current_reg; 1775 ++current_reg;
1679 } 1776 }
1680 } 1777 }
@@ -1683,14 +1780,14 @@ private:
1683 if (header.ps.omap.depth) { 1780 if (header.ps.omap.depth) {
1684 // The depth output is always 2 registers after the last color output, and current_reg 1781 // The depth output is always 2 registers after the last color output, and current_reg
1685 // already contains one past the last color register. 1782 // already contains one past the last color register.
1686 code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1)); 1783 code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1).AsFloat());
1687 } 1784 }
1688 1785
1689 code.AddLine("return;"); 1786 code.AddLine("return;");
1690 return {}; 1787 return {};
1691 } 1788 }
1692 1789
1693 std::string Discard(Operation operation) { 1790 Expression Discard(Operation operation) {
1694 // Enclose "discard" in a conditional, so that GLSL compilation does not complain 1791 // Enclose "discard" in a conditional, so that GLSL compilation does not complain
1695 // about unexecuted instructions that may follow this. 1792 // about unexecuted instructions that may follow this.
1696 code.AddLine("if (true) {{"); 1793 code.AddLine("if (true) {{");
@@ -1701,7 +1798,7 @@ private:
1701 return {}; 1798 return {};
1702 } 1799 }
1703 1800
1704 std::string EmitVertex(Operation operation) { 1801 Expression EmitVertex(Operation operation) {
1705 ASSERT_MSG(stage == ProgramType::Geometry, 1802 ASSERT_MSG(stage == ProgramType::Geometry,
1706 "EmitVertex is expected to be used in a geometry shader."); 1803 "EmitVertex is expected to be used in a geometry shader.");
1707 1804
@@ -1712,7 +1809,7 @@ private:
1712 return {}; 1809 return {};
1713 } 1810 }
1714 1811
1715 std::string EndPrimitive(Operation operation) { 1812 Expression EndPrimitive(Operation operation) {
1716 ASSERT_MSG(stage == ProgramType::Geometry, 1813 ASSERT_MSG(stage == ProgramType::Geometry,
1717 "EndPrimitive is expected to be used in a geometry shader."); 1814 "EndPrimitive is expected to be used in a geometry shader.");
1718 1815
@@ -1720,59 +1817,59 @@ private:
1720 return {}; 1817 return {};
1721 } 1818 }
1722 1819
1723 std::string YNegate(Operation operation) { 1820 Expression YNegate(Operation operation) {
1724 // Config pack's third value is Y_NEGATE's state. 1821 // Config pack's third value is Y_NEGATE's state.
1725 return "uintBitsToFloat(config_pack[2])"; 1822 return {"config_pack[2]", Type::Uint};
1726 } 1823 }
1727 1824
1728 template <u32 element> 1825 template <u32 element>
1729 std::string LocalInvocationId(Operation) { 1826 Expression LocalInvocationId(Operation) {
1730 return "utof(gl_LocalInvocationID"s + GetSwizzle(element) + ')'; 1827 return {"gl_LocalInvocationID"s + GetSwizzle(element), Type::Uint};
1731 } 1828 }
1732 1829
1733 template <u32 element> 1830 template <u32 element>
1734 std::string WorkGroupId(Operation) { 1831 Expression WorkGroupId(Operation) {
1735 return "utof(gl_WorkGroupID"s + GetSwizzle(element) + ')'; 1832 return {"gl_WorkGroupID"s + GetSwizzle(element), Type::Uint};
1736 } 1833 }
1737 1834
1738 std::string BallotThread(Operation operation) { 1835 Expression BallotThread(Operation operation) {
1739 const std::string value = VisitOperand(operation, 0, Type::Bool); 1836 const std::string value = VisitOperand(operation, 0).AsBool();
1740 if (!device.HasWarpIntrinsics()) { 1837 if (!device.HasWarpIntrinsics()) {
1741 LOG_ERROR(Render_OpenGL, 1838 LOG_ERROR(Render_OpenGL,
1742 "Nvidia warp intrinsics are not available and its required by a shader"); 1839 "Nvidia warp intrinsics are not available and its required by a shader");
1743 // Stub on non-Nvidia devices by simulating all threads voting the same as the active 1840 // Stub on non-Nvidia devices by simulating all threads voting the same as the active
1744 // one. 1841 // one.
1745 return fmt::format("utof({} ? 0xFFFFFFFFU : 0U)", value); 1842 return {fmt::format("({} ? 0xFFFFFFFFU : 0U)", value), Type::Uint};
1746 } 1843 }
1747 return fmt::format("utof(ballotThreadNV({}))", value); 1844 return {fmt::format("ballotThreadNV({})", value), Type::Uint};
1748 } 1845 }
1749 1846
1750 std::string Vote(Operation operation, const char* func) { 1847 Expression Vote(Operation operation, const char* func) {
1751 const std::string value = VisitOperand(operation, 0, Type::Bool); 1848 const std::string value = VisitOperand(operation, 0).AsBool();
1752 if (!device.HasWarpIntrinsics()) { 1849 if (!device.HasWarpIntrinsics()) {
1753 LOG_ERROR(Render_OpenGL, 1850 LOG_ERROR(Render_OpenGL,
1754 "Nvidia vote intrinsics are not available and its required by a shader"); 1851 "Nvidia vote intrinsics are not available and its required by a shader");
1755 // Stub with a warp size of one. 1852 // Stub with a warp size of one.
1756 return value; 1853 return {value, Type::Bool};
1757 } 1854 }
1758 return fmt::format("{}({})", func, value); 1855 return {fmt::format("{}({})", func, value), Type::Bool};
1759 } 1856 }
1760 1857
1761 std::string VoteAll(Operation operation) { 1858 Expression VoteAll(Operation operation) {
1762 return Vote(operation, "allThreadsNV"); 1859 return Vote(operation, "allThreadsNV");
1763 } 1860 }
1764 1861
1765 std::string VoteAny(Operation operation) { 1862 Expression VoteAny(Operation operation) {
1766 return Vote(operation, "anyThreadNV"); 1863 return Vote(operation, "anyThreadNV");
1767 } 1864 }
1768 1865
1769 std::string VoteEqual(Operation operation) { 1866 Expression VoteEqual(Operation operation) {
1770 if (!device.HasWarpIntrinsics()) { 1867 if (!device.HasWarpIntrinsics()) {
1771 LOG_ERROR(Render_OpenGL, 1868 LOG_ERROR(Render_OpenGL,
1772 "Nvidia vote intrinsics are not available and its required by a shader"); 1869 "Nvidia vote intrinsics are not available and its required by a shader");
1773 // We must return true here since a stub for a theoretical warp size of 1 will always 1870 // We must return true here since a stub for a theoretical warp size of 1 will always
1774 // return an equal result for all its votes. 1871 // return an equal result for all its votes.
1775 return "true"; 1872 return {"true", Type::Bool};
1776 } 1873 }
1777 return Vote(operation, "allThreadsEqualNV"); 1874 return Vote(operation, "allThreadsEqualNV");
1778 } 1875 }
@@ -1973,8 +2070,8 @@ private:
1973 } 2070 }
1974 2071
1975 std::string GetInternalFlag(InternalFlag flag) const { 2072 std::string GetInternalFlag(InternalFlag flag) const {
1976 constexpr std::array<const char*, 4> InternalFlagNames = {"zero_flag", "sign_flag", 2073 constexpr std::array InternalFlagNames = {"zero_flag", "sign_flag", "carry_flag",
1977 "carry_flag", "overflow_flag"}; 2074 "overflow_flag"};
1978 const auto index = static_cast<u32>(flag); 2075 const auto index = static_cast<u32>(flag);
1979 ASSERT(index < static_cast<u32>(InternalFlag::Amount)); 2076 ASSERT(index < static_cast<u32>(InternalFlag::Amount));
1980 2077
@@ -2022,24 +2119,16 @@ private:
2022 2119
2023std::string GetCommonDeclarations() { 2120std::string GetCommonDeclarations() {
2024 return fmt::format( 2121 return fmt::format(
2025 "#define MAX_CONSTBUFFER_ELEMENTS {}\n"
2026 "#define ftoi floatBitsToInt\n" 2122 "#define ftoi floatBitsToInt\n"
2027 "#define ftou floatBitsToUint\n" 2123 "#define ftou floatBitsToUint\n"
2028 "#define itof intBitsToFloat\n" 2124 "#define itof intBitsToFloat\n"
2029 "#define utof uintBitsToFloat\n\n" 2125 "#define utof uintBitsToFloat\n\n"
2030 "float fromHalf2(vec2 pair) {{\n" 2126 "bvec2 HalfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{\n"
2031 " return utof(packHalf2x16(pair));\n"
2032 "}}\n\n"
2033 "vec2 toHalf2(float value) {{\n"
2034 " return unpackHalf2x16(ftou(value));\n"
2035 "}}\n\n"
2036 "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{\n"
2037 " bvec2 is_nan1 = isnan(pair1);\n" 2127 " bvec2 is_nan1 = isnan(pair1);\n"
2038 " bvec2 is_nan2 = isnan(pair2);\n" 2128 " bvec2 is_nan2 = isnan(pair2);\n"
2039 " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || " 2129 " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || "
2040 "is_nan2.y);\n" 2130 "is_nan2.y);\n"
2041 "}}\n", 2131 "}}\n\n");
2042 MAX_CONSTBUFFER_ELEMENTS);
2043} 2132}
2044 2133
2045ProgramResult Decompile(const Device& device, const ShaderIR& ir, ProgramType stage, 2134ProgramResult Decompile(const Device& device, const ShaderIR& ir, ProgramType stage,