summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/backend/glsl
diff options
context:
space:
mode:
authorGravatar bunnei2021-07-25 11:39:04 -0700
committerGravatar GitHub2021-07-25 11:39:04 -0700
commit98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f (patch)
tree816faa96c2c4d291825063433331a8ea4b3d08f1 /src/shader_recompiler/backend/glsl
parentMerge pull request #6699 from lat9nq/common-threads (diff)
parentshader: Support out of bound local memory reads and immediate writes (diff)
downloadyuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.gz
yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.xz
yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.zip
Merge pull request #6585 from ameerj/hades
Shader Decompiler Rewrite
Diffstat (limited to 'src/shader_recompiler/backend/glsl')
-rw-r--r--src/shader_recompiler/backend/glsl/emit_context.cpp715
-rw-r--r--src/shader_recompiler/backend/glsl/emit_context.h174
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.cpp252
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.h24
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp418
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp21
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp94
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp219
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp456
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp21
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp230
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp456
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp799
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h702
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp253
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp28
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp202
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp105
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_select.cpp55
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp79
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_special.cpp111
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp32
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp217
-rw-r--r--src/shader_recompiler/backend/glsl/var_alloc.cpp308
-rw-r--r--src/shader_recompiler/backend/glsl/var_alloc.h105
25 files changed, 6076 insertions, 0 deletions
diff --git a/src/shader_recompiler/backend/glsl/emit_context.cpp b/src/shader_recompiler/backend/glsl/emit_context.cpp
new file mode 100644
index 000000000..4e6f2c0fe
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_context.cpp
@@ -0,0 +1,715 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/backend/bindings.h"
6#include "shader_recompiler/backend/glsl/emit_context.h"
7#include "shader_recompiler/frontend/ir/program.h"
8#include "shader_recompiler/profile.h"
9#include "shader_recompiler/runtime_info.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13u32 CbufIndex(size_t offset) {
14 return (offset / 4) % 4;
15}
16
17char Swizzle(size_t offset) {
18 return "xyzw"[CbufIndex(offset)];
19}
20
21std::string_view InterpDecorator(Interpolation interp) {
22 switch (interp) {
23 case Interpolation::Smooth:
24 return "";
25 case Interpolation::Flat:
26 return "flat ";
27 case Interpolation::NoPerspective:
28 return "noperspective ";
29 }
30 throw InvalidArgument("Invalid interpolation {}", interp);
31}
32
33std::string_view InputArrayDecorator(Stage stage) {
34 switch (stage) {
35 case Stage::Geometry:
36 case Stage::TessellationControl:
37 case Stage::TessellationEval:
38 return "[]";
39 default:
40 return "";
41 }
42}
43
44bool StoresPerVertexAttributes(Stage stage) {
45 switch (stage) {
46 case Stage::VertexA:
47 case Stage::VertexB:
48 case Stage::Geometry:
49 case Stage::TessellationEval:
50 return true;
51 default:
52 return false;
53 }
54}
55
56std::string OutputDecorator(Stage stage, u32 size) {
57 switch (stage) {
58 case Stage::TessellationControl:
59 return fmt::format("[{}]", size);
60 default:
61 return "";
62 }
63}
64
65std::string_view SamplerType(TextureType type, bool is_depth) {
66 if (is_depth) {
67 switch (type) {
68 case TextureType::Color1D:
69 return "sampler1DShadow";
70 case TextureType::ColorArray1D:
71 return "sampler1DArrayShadow";
72 case TextureType::Color2D:
73 return "sampler2DShadow";
74 case TextureType::ColorArray2D:
75 return "sampler2DArrayShadow";
76 case TextureType::ColorCube:
77 return "samplerCubeShadow";
78 case TextureType::ColorArrayCube:
79 return "samplerCubeArrayShadow";
80 default:
81 throw NotImplementedException("Texture type: {}", type);
82 }
83 }
84 switch (type) {
85 case TextureType::Color1D:
86 return "sampler1D";
87 case TextureType::ColorArray1D:
88 return "sampler1DArray";
89 case TextureType::Color2D:
90 return "sampler2D";
91 case TextureType::ColorArray2D:
92 return "sampler2DArray";
93 case TextureType::Color3D:
94 return "sampler3D";
95 case TextureType::ColorCube:
96 return "samplerCube";
97 case TextureType::ColorArrayCube:
98 return "samplerCubeArray";
99 case TextureType::Buffer:
100 return "samplerBuffer";
101 default:
102 throw NotImplementedException("Texture type: {}", type);
103 }
104}
105
106std::string_view ImageType(TextureType type) {
107 switch (type) {
108 case TextureType::Color1D:
109 return "uimage1D";
110 case TextureType::ColorArray1D:
111 return "uimage1DArray";
112 case TextureType::Color2D:
113 return "uimage2D";
114 case TextureType::ColorArray2D:
115 return "uimage2DArray";
116 case TextureType::Color3D:
117 return "uimage3D";
118 case TextureType::ColorCube:
119 return "uimageCube";
120 case TextureType::ColorArrayCube:
121 return "uimageCubeArray";
122 case TextureType::Buffer:
123 return "uimageBuffer";
124 default:
125 throw NotImplementedException("Image type: {}", type);
126 }
127}
128
129std::string_view ImageFormatString(ImageFormat format) {
130 switch (format) {
131 case ImageFormat::Typeless:
132 return "";
133 case ImageFormat::R8_UINT:
134 return ",r8ui";
135 case ImageFormat::R8_SINT:
136 return ",r8i";
137 case ImageFormat::R16_UINT:
138 return ",r16ui";
139 case ImageFormat::R16_SINT:
140 return ",r16i";
141 case ImageFormat::R32_UINT:
142 return ",r32ui";
143 case ImageFormat::R32G32_UINT:
144 return ",rg32ui";
145 case ImageFormat::R32G32B32A32_UINT:
146 return ",rgba32ui";
147 default:
148 throw NotImplementedException("Image format: {}", format);
149 }
150}
151
152std::string_view ImageAccessQualifier(bool is_written, bool is_read) {
153 if (is_written && !is_read) {
154 return "writeonly ";
155 }
156 if (is_read && !is_written) {
157 return "readonly ";
158 }
159 return "";
160}
161
162std::string_view GetTessMode(TessPrimitive primitive) {
163 switch (primitive) {
164 case TessPrimitive::Triangles:
165 return "triangles";
166 case TessPrimitive::Quads:
167 return "quads";
168 case TessPrimitive::Isolines:
169 return "isolines";
170 }
171 throw InvalidArgument("Invalid tessellation primitive {}", primitive);
172}
173
174std::string_view GetTessSpacing(TessSpacing spacing) {
175 switch (spacing) {
176 case TessSpacing::Equal:
177 return "equal_spacing";
178 case TessSpacing::FractionalOdd:
179 return "fractional_odd_spacing";
180 case TessSpacing::FractionalEven:
181 return "fractional_even_spacing";
182 }
183 throw InvalidArgument("Invalid tessellation spacing {}", spacing);
184}
185
186std::string_view InputPrimitive(InputTopology topology) {
187 switch (topology) {
188 case InputTopology::Points:
189 return "points";
190 case InputTopology::Lines:
191 return "lines";
192 case InputTopology::LinesAdjacency:
193 return "lines_adjacency";
194 case InputTopology::Triangles:
195 return "triangles";
196 case InputTopology::TrianglesAdjacency:
197 return "triangles_adjacency";
198 }
199 throw InvalidArgument("Invalid input topology {}", topology);
200}
201
202std::string_view OutputPrimitive(OutputTopology topology) {
203 switch (topology) {
204 case OutputTopology::PointList:
205 return "points";
206 case OutputTopology::LineStrip:
207 return "line_strip";
208 case OutputTopology::TriangleStrip:
209 return "triangle_strip";
210 }
211 throw InvalidArgument("Invalid output topology {}", topology);
212}
213
214void SetupLegacyOutPerVertex(EmitContext& ctx, std::string& header) {
215 if (!ctx.info.stores.Legacy()) {
216 return;
217 }
218 if (ctx.info.stores.FixedFunctionTexture()) {
219 header += "vec4 gl_TexCoord[8];";
220 }
221 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
222 header += "vec4 gl_FrontColor;";
223 }
224 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
225 header += "vec4 gl_FrontSecondaryColor;";
226 }
227 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
228 header += "vec4 gl_BackColor;";
229 }
230 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
231 header += "vec4 gl_BackSecondaryColor;";
232 }
233}
234
235void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
236 if (!StoresPerVertexAttributes(ctx.stage)) {
237 return;
238 }
239 if (ctx.uses_geometry_passthrough) {
240 return;
241 }
242 header += "out gl_PerVertex{vec4 gl_Position;";
243 if (ctx.info.stores[IR::Attribute::PointSize]) {
244 header += "float gl_PointSize;";
245 }
246 if (ctx.info.stores.ClipDistances()) {
247 header += "float gl_ClipDistance[];";
248 }
249 if (ctx.info.stores[IR::Attribute::ViewportIndex] &&
250 ctx.profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
251 header += "int gl_ViewportIndex;";
252 }
253 SetupLegacyOutPerVertex(ctx, header);
254 header += "};";
255 if (ctx.info.stores[IR::Attribute::ViewportIndex] && ctx.stage == Stage::Geometry) {
256 header += "out int gl_ViewportIndex;";
257 }
258}
259
260void SetupInPerVertex(EmitContext& ctx, std::string& header) {
261 // Currently only required for TessellationControl to adhere to
262 // ARB_separate_shader_objects requirements
263 if (ctx.stage != Stage::TessellationControl) {
264 return;
265 }
266 const bool loads_position{ctx.info.loads.AnyComponent(IR::Attribute::PositionX)};
267 const bool loads_point_size{ctx.info.loads[IR::Attribute::PointSize]};
268 const bool loads_clip_distance{ctx.info.loads.ClipDistances()};
269 const bool loads_per_vertex{loads_position || loads_point_size || loads_clip_distance};
270 if (!loads_per_vertex) {
271 return;
272 }
273 header += "in gl_PerVertex{";
274 if (loads_position) {
275 header += "vec4 gl_Position;";
276 }
277 if (loads_point_size) {
278 header += "float gl_PointSize;";
279 }
280 if (loads_clip_distance) {
281 header += "float gl_ClipDistance[];";
282 }
283 header += "}gl_in[gl_MaxPatchVertices];";
284}
285
286void SetupLegacyInPerFragment(EmitContext& ctx, std::string& header) {
287 if (!ctx.info.loads.Legacy()) {
288 return;
289 }
290 header += "in gl_PerFragment{";
291 if (ctx.info.loads.FixedFunctionTexture()) {
292 header += "vec4 gl_TexCoord[8];";
293 }
294 if (ctx.info.loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
295 header += "vec4 gl_Color;";
296 }
297 header += "};";
298}
299
300} // Anonymous namespace
301
302EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
303 const RuntimeInfo& runtime_info_)
304 : info{program.info}, profile{profile_}, runtime_info{runtime_info_}, stage{program.stage},
305 uses_geometry_passthrough{program.is_geometry_passthrough &&
306 profile.support_geometry_shader_passthrough} {
307 if (profile.need_fastmath_off) {
308 header += "#pragma optionNV(fastmath off)\n";
309 }
310 SetupExtensions();
311 switch (program.stage) {
312 case Stage::VertexA:
313 case Stage::VertexB:
314 stage_name = "vs";
315 break;
316 case Stage::TessellationControl:
317 stage_name = "tcs";
318 header += fmt::format("layout(vertices={})out;", program.invocations);
319 break;
320 case Stage::TessellationEval:
321 stage_name = "tes";
322 header += fmt::format("layout({},{},{})in;", GetTessMode(runtime_info.tess_primitive),
323 GetTessSpacing(runtime_info.tess_spacing),
324 runtime_info.tess_clockwise ? "cw" : "ccw");
325 break;
326 case Stage::Geometry:
327 stage_name = "gs";
328 header += fmt::format("layout({})in;", InputPrimitive(runtime_info.input_topology));
329 if (uses_geometry_passthrough) {
330 header += "layout(passthrough)in gl_PerVertex{vec4 gl_Position;};";
331 break;
332 } else if (program.is_geometry_passthrough &&
333 !profile.support_geometry_shader_passthrough) {
334 LOG_WARNING(Shader_GLSL, "Passthrough geometry program used but not supported");
335 }
336 header += fmt::format(
337 "layout({},max_vertices={})out;in gl_PerVertex{{vec4 gl_Position;}}gl_in[];",
338 OutputPrimitive(program.output_topology), program.output_vertices);
339 break;
340 case Stage::Fragment:
341 stage_name = "fs";
342 position_name = "gl_FragCoord";
343 if (runtime_info.force_early_z) {
344 header += "layout(early_fragment_tests)in;";
345 }
346 if (info.uses_sample_id) {
347 header += "in int gl_SampleID;";
348 }
349 if (info.stores_sample_mask) {
350 header += "out int gl_SampleMask[];";
351 }
352 break;
353 case Stage::Compute:
354 stage_name = "cs";
355 const u32 local_x{std::max(program.workgroup_size[0], 1u)};
356 const u32 local_y{std::max(program.workgroup_size[1], 1u)};
357 const u32 local_z{std::max(program.workgroup_size[2], 1u)};
358 header += fmt::format("layout(local_size_x={},local_size_y={},local_size_z={}) in;",
359 local_x, local_y, local_z);
360 break;
361 }
362 SetupOutPerVertex(*this, header);
363 SetupInPerVertex(*this, header);
364 SetupLegacyInPerFragment(*this, header);
365
366 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
367 if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
368 continue;
369 }
370 const auto qualifier{uses_geometry_passthrough ? "passthrough"
371 : fmt::format("location={}", index)};
372 header += fmt::format("layout({}){}in vec4 in_attr{}{};", qualifier,
373 InterpDecorator(info.interpolation[index]), index,
374 InputArrayDecorator(stage));
375 }
376 for (size_t index = 0; index < info.uses_patches.size(); ++index) {
377 if (!info.uses_patches[index]) {
378 continue;
379 }
380 const auto qualifier{stage == Stage::TessellationControl ? "out" : "in"};
381 header += fmt::format("layout(location={})patch {} vec4 patch{};", index, qualifier, index);
382 }
383 if (stage == Stage::Fragment) {
384 for (size_t index = 0; index < info.stores_frag_color.size(); ++index) {
385 if (!info.stores_frag_color[index] && !profile.need_declared_frag_colors) {
386 continue;
387 }
388 header += fmt::format("layout(location={})out vec4 frag_color{};", index, index);
389 }
390 }
391 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
392 if (info.stores.Generic(index)) {
393 DefineGenericOutput(index, program.invocations);
394 }
395 }
396 DefineConstantBuffers(bindings);
397 DefineStorageBuffers(bindings);
398 SetupImages(bindings);
399 SetupTextures(bindings);
400 DefineHelperFunctions();
401 DefineConstants();
402}
403
404void EmitContext::SetupExtensions() {
405 header += "#extension GL_ARB_separate_shader_objects : enable\n";
406 if (info.uses_shadow_lod && profile.support_gl_texture_shadow_lod) {
407 header += "#extension GL_EXT_texture_shadow_lod : enable\n";
408 }
409 if (info.uses_int64 && profile.support_int64) {
410 header += "#extension GL_ARB_gpu_shader_int64 : enable\n";
411 }
412 if (info.uses_int64_bit_atomics) {
413 header += "#extension GL_NV_shader_atomic_int64 : enable\n";
414 }
415 if (info.uses_atomic_f32_add) {
416 header += "#extension GL_NV_shader_atomic_float : enable\n";
417 }
418 if (info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) {
419 header += "#extension GL_NV_shader_atomic_fp16_vector : enable\n";
420 }
421 if (info.uses_fp16) {
422 if (profile.support_gl_nv_gpu_shader_5) {
423 header += "#extension GL_NV_gpu_shader5 : enable\n";
424 }
425 if (profile.support_gl_amd_gpu_shader_half_float) {
426 header += "#extension GL_AMD_gpu_shader_half_float : enable\n";
427 }
428 }
429 if (info.uses_subgroup_invocation_id || info.uses_subgroup_mask || info.uses_subgroup_vote ||
430 info.uses_subgroup_shuffles || info.uses_fswzadd) {
431 header += "#extension GL_ARB_shader_ballot : enable\n"
432 "#extension GL_ARB_shader_group_vote : enable\n";
433 if (!info.uses_int64 && profile.support_int64) {
434 header += "#extension GL_ARB_gpu_shader_int64 : enable\n";
435 }
436 if (profile.support_gl_warp_intrinsics) {
437 header += "#extension GL_NV_shader_thread_shuffle : enable\n";
438 }
439 }
440 if ((info.stores[IR::Attribute::ViewportIndex] || info.stores[IR::Attribute::Layer]) &&
441 profile.support_viewport_index_layer_non_geometry && stage != Stage::Geometry) {
442 header += "#extension GL_ARB_shader_viewport_layer_array : enable\n";
443 }
444 if (info.uses_sparse_residency && profile.support_gl_sparse_textures) {
445 header += "#extension GL_ARB_sparse_texture2 : enable\n";
446 }
447 if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
448 header += "#extension GL_NV_viewport_array2 : enable\n";
449 }
450 if (info.uses_typeless_image_reads) {
451 header += "#extension GL_EXT_shader_image_load_formatted : enable\n";
452 }
453 if (info.uses_derivatives && profile.support_gl_derivative_control) {
454 header += "#extension GL_ARB_derivative_control : enable\n";
455 }
456 if (uses_geometry_passthrough) {
457 header += "#extension GL_NV_geometry_shader_passthrough : enable\n";
458 }
459}
460
461void EmitContext::DefineConstantBuffers(Bindings& bindings) {
462 if (info.constant_buffer_descriptors.empty()) {
463 return;
464 }
465 for (const auto& desc : info.constant_buffer_descriptors) {
466 header += fmt::format(
467 "layout(std140,binding={}) uniform {}_cbuf_{}{{vec4 {}_cbuf{}[{}];}};",
468 bindings.uniform_buffer, stage_name, desc.index, stage_name, desc.index, 4 * 1024);
469 bindings.uniform_buffer += desc.count;
470 }
471}
472
473void EmitContext::DefineStorageBuffers(Bindings& bindings) {
474 if (info.storage_buffers_descriptors.empty()) {
475 return;
476 }
477 u32 index{};
478 for (const auto& desc : info.storage_buffers_descriptors) {
479 header += fmt::format("layout(std430,binding={}) buffer {}_ssbo_{}{{uint {}_ssbo{}[];}};",
480 bindings.storage_buffer, stage_name, bindings.storage_buffer,
481 stage_name, index);
482 bindings.storage_buffer += desc.count;
483 index += desc.count;
484 }
485}
486
487void EmitContext::DefineGenericOutput(size_t index, u32 invocations) {
488 static constexpr std::string_view swizzle{"xyzw"};
489 const size_t base_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4};
490 u32 element{0};
491 while (element < 4) {
492 std::string definition{fmt::format("layout(location={}", index)};
493 const u32 remainder{4 - element};
494 const TransformFeedbackVarying* xfb_varying{};
495 if (!runtime_info.xfb_varyings.empty()) {
496 xfb_varying = &runtime_info.xfb_varyings[base_index + element];
497 xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr;
498 }
499 const u32 num_components{xfb_varying ? xfb_varying->components : remainder};
500 if (element > 0) {
501 definition += fmt::format(",component={}", element);
502 }
503 if (xfb_varying) {
504 definition +=
505 fmt::format(",xfb_buffer={},xfb_stride={},xfb_offset={}", xfb_varying->buffer,
506 xfb_varying->stride, xfb_varying->offset);
507 }
508 std::string name{fmt::format("out_attr{}", index)};
509 if (num_components < 4 || element > 0) {
510 name += fmt::format("_{}", swizzle.substr(element, num_components));
511 }
512 const auto type{num_components == 1 ? "float" : fmt::format("vec{}", num_components)};
513 definition += fmt::format(")out {} {}{};", type, name, OutputDecorator(stage, invocations));
514 header += definition;
515
516 const GenericElementInfo element_info{
517 .name = name,
518 .first_element = element,
519 .num_components = num_components,
520 };
521 std::fill_n(output_generics[index].begin() + element, num_components, element_info);
522 element += num_components;
523 }
524}
525
526void EmitContext::DefineHelperFunctions() {
527 header += "\n#define ftoi floatBitsToInt\n#define ftou floatBitsToUint\n"
528 "#define itof intBitsToFloat\n#define utof uintBitsToFloat\n";
529 if (info.uses_global_increment || info.uses_shared_increment) {
530 header += "uint CasIncrement(uint op_a,uint op_b){return op_a>=op_b?0u:(op_a+1u);}";
531 }
532 if (info.uses_global_decrement || info.uses_shared_decrement) {
533 header += "uint CasDecrement(uint op_a,uint op_b){"
534 "return op_a==0||op_a>op_b?op_b:(op_a-1u);}";
535 }
536 if (info.uses_atomic_f32_add) {
537 header += "uint CasFloatAdd(uint op_a,float op_b){"
538 "return ftou(utof(op_a)+op_b);}";
539 }
540 if (info.uses_atomic_f32x2_add) {
541 header += "uint CasFloatAdd32x2(uint op_a,vec2 op_b){"
542 "return packHalf2x16(unpackHalf2x16(op_a)+op_b);}";
543 }
544 if (info.uses_atomic_f32x2_min) {
545 header += "uint CasFloatMin32x2(uint op_a,vec2 op_b){return "
546 "packHalf2x16(min(unpackHalf2x16(op_a),op_b));}";
547 }
548 if (info.uses_atomic_f32x2_max) {
549 header += "uint CasFloatMax32x2(uint op_a,vec2 op_b){return "
550 "packHalf2x16(max(unpackHalf2x16(op_a),op_b));}";
551 }
552 if (info.uses_atomic_f16x2_add) {
553 header += "uint CasFloatAdd16x2(uint op_a,f16vec2 op_b){return "
554 "packFloat2x16(unpackFloat2x16(op_a)+op_b);}";
555 }
556 if (info.uses_atomic_f16x2_min) {
557 header += "uint CasFloatMin16x2(uint op_a,f16vec2 op_b){return "
558 "packFloat2x16(min(unpackFloat2x16(op_a),op_b));}";
559 }
560 if (info.uses_atomic_f16x2_max) {
561 header += "uint CasFloatMax16x2(uint op_a,f16vec2 op_b){return "
562 "packFloat2x16(max(unpackFloat2x16(op_a),op_b));}";
563 }
564 if (info.uses_atomic_s32_min) {
565 header += "uint CasMinS32(uint op_a,uint op_b){return uint(min(int(op_a),int(op_b)));}";
566 }
567 if (info.uses_atomic_s32_max) {
568 header += "uint CasMaxS32(uint op_a,uint op_b){return uint(max(int(op_a),int(op_b)));}";
569 }
570 if (info.uses_global_memory && profile.support_int64) {
571 header += DefineGlobalMemoryFunctions();
572 }
573 if (info.loads_indexed_attributes) {
574 const bool is_array{stage == Stage::Geometry};
575 const auto vertex_arg{is_array ? ",uint vertex" : ""};
576 std::string func{
577 fmt::format("float IndexedAttrLoad(int offset{}){{int base_index=offset>>2;uint "
578 "masked_index=uint(base_index)&3u;switch(base_index>>2){{",
579 vertex_arg)};
580 if (info.loads.AnyComponent(IR::Attribute::PositionX)) {
581 const auto position_idx{is_array ? "gl_in[vertex]." : ""};
582 func += fmt::format("case {}:return {}{}[masked_index];",
583 static_cast<u32>(IR::Attribute::PositionX) >> 2, position_idx,
584 position_name);
585 }
586 const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2;
587 for (u32 index = 0; index < IR::NUM_GENERICS; ++index) {
588 if (!info.loads.Generic(index)) {
589 continue;
590 }
591 const auto vertex_idx{is_array ? "[vertex]" : ""};
592 func += fmt::format("case {}:return in_attr{}{}[masked_index];",
593 base_attribute_value + index, index, vertex_idx);
594 }
595 func += "default: return 0.0;}}";
596 header += func;
597 }
598 if (info.stores_indexed_attributes) {
599 // TODO
600 }
601}
602
603std::string EmitContext::DefineGlobalMemoryFunctions() {
604 const auto define_body{[&](std::string& func, size_t index, std::string_view return_statement) {
605 const auto& ssbo{info.storage_buffers_descriptors[index]};
606 const u32 size_cbuf_offset{ssbo.cbuf_offset + 8};
607 const auto ssbo_addr{fmt::format("ssbo_addr{}", index)};
608 const auto cbuf{fmt::format("{}_cbuf{}", stage_name, ssbo.cbuf_index)};
609 std::array<std::string, 2> addr_xy;
610 std::array<std::string, 2> size_xy;
611 for (size_t i = 0; i < addr_xy.size(); ++i) {
612 const auto addr_loc{ssbo.cbuf_offset + 4 * i};
613 const auto size_loc{size_cbuf_offset + 4 * i};
614 addr_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, addr_loc / 16, Swizzle(addr_loc));
615 size_xy[i] = fmt::format("ftou({}[{}].{})", cbuf, size_loc / 16, Swizzle(size_loc));
616 }
617 const auto addr_pack{fmt::format("packUint2x32(uvec2({},{}))", addr_xy[0], addr_xy[1])};
618 const auto addr_statment{fmt::format("uint64_t {}={};", ssbo_addr, addr_pack)};
619 func += addr_statment;
620
621 const auto size_vec{fmt::format("uvec2({},{})", size_xy[0], size_xy[1])};
622 const auto comp_lhs{fmt::format("(addr>={})", ssbo_addr)};
623 const auto comp_rhs{fmt::format("(addr<({}+uint64_t({})))", ssbo_addr, size_vec)};
624 const auto comparison{fmt::format("if({}&&{}){{", comp_lhs, comp_rhs)};
625 func += comparison;
626
627 const auto ssbo_name{fmt::format("{}_ssbo{}", stage_name, index)};
628 func += fmt::format(fmt::runtime(return_statement), ssbo_name, ssbo_addr);
629 }};
630 std::string write_func{"void WriteGlobal32(uint64_t addr,uint data){"};
631 std::string write_func_64{"void WriteGlobal64(uint64_t addr,uvec2 data){"};
632 std::string write_func_128{"void WriteGlobal128(uint64_t addr,uvec4 data){"};
633 std::string load_func{"uint LoadGlobal32(uint64_t addr){"};
634 std::string load_func_64{"uvec2 LoadGlobal64(uint64_t addr){"};
635 std::string load_func_128{"uvec4 LoadGlobal128(uint64_t addr){"};
636 const size_t num_buffers{info.storage_buffers_descriptors.size()};
637 for (size_t index = 0; index < num_buffers; ++index) {
638 if (!info.nvn_buffer_used[index]) {
639 continue;
640 }
641 define_body(write_func, index, "{0}[uint(addr-{1})>>2]=data;return;}}");
642 define_body(write_func_64, index,
643 "{0}[uint(addr-{1})>>2]=data.x;{0}[uint(addr-{1}+4)>>2]=data.y;return;}}");
644 define_body(write_func_128, index,
645 "{0}[uint(addr-{1})>>2]=data.x;{0}[uint(addr-{1}+4)>>2]=data.y;{0}[uint("
646 "addr-{1}+8)>>2]=data.z;{0}[uint(addr-{1}+12)>>2]=data.w;return;}}");
647 define_body(load_func, index, "return {0}[uint(addr-{1})>>2];}}");
648 define_body(load_func_64, index,
649 "return uvec2({0}[uint(addr-{1})>>2],{0}[uint(addr-{1}+4)>>2]);}}");
650 define_body(load_func_128, index,
651 "return uvec4({0}[uint(addr-{1})>>2],{0}[uint(addr-{1}+4)>>2],{0}["
652 "uint(addr-{1}+8)>>2],{0}[uint(addr-{1}+12)>>2]);}}");
653 }
654 write_func += '}';
655 write_func_64 += '}';
656 write_func_128 += '}';
657 load_func += "return 0u;}";
658 load_func_64 += "return uvec2(0);}";
659 load_func_128 += "return uvec4(0);}";
660 return write_func + write_func_64 + write_func_128 + load_func + load_func_64 + load_func_128;
661}
662
663void EmitContext::SetupImages(Bindings& bindings) {
664 image_buffers.reserve(info.image_buffer_descriptors.size());
665 for (const auto& desc : info.image_buffer_descriptors) {
666 image_buffers.push_back({bindings.image, desc.count});
667 const auto format{ImageFormatString(desc.format)};
668 const auto qualifier{ImageAccessQualifier(desc.is_written, desc.is_read)};
669 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
670 header += fmt::format("layout(binding={}{}) uniform {}uimageBuffer img{}{};",
671 bindings.image, format, qualifier, bindings.image, array_decorator);
672 bindings.image += desc.count;
673 }
674 images.reserve(info.image_descriptors.size());
675 for (const auto& desc : info.image_descriptors) {
676 images.push_back({bindings.image, desc.count});
677 const auto format{ImageFormatString(desc.format)};
678 const auto image_type{ImageType(desc.type)};
679 const auto qualifier{ImageAccessQualifier(desc.is_written, desc.is_read)};
680 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
681 header += fmt::format("layout(binding={}{})uniform {}{} img{}{};", bindings.image, format,
682 qualifier, image_type, bindings.image, array_decorator);
683 bindings.image += desc.count;
684 }
685}
686
687void EmitContext::SetupTextures(Bindings& bindings) {
688 texture_buffers.reserve(info.texture_buffer_descriptors.size());
689 for (const auto& desc : info.texture_buffer_descriptors) {
690 texture_buffers.push_back({bindings.texture, desc.count});
691 const auto sampler_type{SamplerType(TextureType::Buffer, false)};
692 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
693 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
694 sampler_type, bindings.texture, array_decorator);
695 bindings.texture += desc.count;
696 }
697 textures.reserve(info.texture_descriptors.size());
698 for (const auto& desc : info.texture_descriptors) {
699 textures.push_back({bindings.texture, desc.count});
700 const auto sampler_type{SamplerType(desc.type, desc.is_depth)};
701 const auto array_decorator{desc.count > 1 ? fmt::format("[{}]", desc.count) : ""};
702 header += fmt::format("layout(binding={}) uniform {} tex{}{};", bindings.texture,
703 sampler_type, bindings.texture, array_decorator);
704 bindings.texture += desc.count;
705 }
706}
707
708void EmitContext::DefineConstants() {
709 if (info.uses_fswzadd) {
710 header += "const float FSWZ_A[]=float[4](-1.f,1.f,-1.f,0.f);"
711 "const float FSWZ_B[]=float[4](-1.f,-1.f,1.f,-1.f);";
712 }
713}
714
715} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_context.h b/src/shader_recompiler/backend/glsl/emit_context.h
new file mode 100644
index 000000000..d9b639d29
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_context.h
@@ -0,0 +1,174 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <utility>
9#include <vector>
10
11#include <fmt/format.h>
12
13#include "shader_recompiler/backend/glsl/var_alloc.h"
14#include "shader_recompiler/stage.h"
15
16namespace Shader {
17struct Info;
18struct Profile;
19struct RuntimeInfo;
20} // namespace Shader
21
22namespace Shader::Backend {
23struct Bindings;
24}
25
26namespace Shader::IR {
27class Inst;
28struct Program;
29} // namespace Shader::IR
30
31namespace Shader::Backend::GLSL {
32
33struct GenericElementInfo {
34 std::string name;
35 u32 first_element{};
36 u32 num_components{};
37};
38
39struct TextureImageDefinition {
40 u32 binding;
41 u32 count;
42};
43
44class EmitContext {
45public:
46 explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
47 const RuntimeInfo& runtime_info_);
48
49 template <GlslVarType type, typename... Args>
50 void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
51 const auto var_def{var_alloc.AddDefine(inst, type)};
52 if (var_def.empty()) {
53 // skip assigment.
54 code += fmt::format(fmt::runtime(format_str + 3), std::forward<Args>(args)...);
55 } else {
56 code += fmt::format(fmt::runtime(format_str), var_def, std::forward<Args>(args)...);
57 }
58 // TODO: Remove this
59 code += '\n';
60 }
61
62 template <typename... Args>
63 void AddU1(const char* format_str, IR::Inst& inst, Args&&... args) {
64 Add<GlslVarType::U1>(format_str, inst, args...);
65 }
66
67 template <typename... Args>
68 void AddF16x2(const char* format_str, IR::Inst& inst, Args&&... args) {
69 Add<GlslVarType::F16x2>(format_str, inst, args...);
70 }
71
72 template <typename... Args>
73 void AddU32(const char* format_str, IR::Inst& inst, Args&&... args) {
74 Add<GlslVarType::U32>(format_str, inst, args...);
75 }
76
77 template <typename... Args>
78 void AddF32(const char* format_str, IR::Inst& inst, Args&&... args) {
79 Add<GlslVarType::F32>(format_str, inst, args...);
80 }
81
82 template <typename... Args>
83 void AddU64(const char* format_str, IR::Inst& inst, Args&&... args) {
84 Add<GlslVarType::U64>(format_str, inst, args...);
85 }
86
87 template <typename... Args>
88 void AddF64(const char* format_str, IR::Inst& inst, Args&&... args) {
89 Add<GlslVarType::F64>(format_str, inst, args...);
90 }
91
92 template <typename... Args>
93 void AddU32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
94 Add<GlslVarType::U32x2>(format_str, inst, args...);
95 }
96
97 template <typename... Args>
98 void AddF32x2(const char* format_str, IR::Inst& inst, Args&&... args) {
99 Add<GlslVarType::F32x2>(format_str, inst, args...);
100 }
101
102 template <typename... Args>
103 void AddU32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
104 Add<GlslVarType::U32x3>(format_str, inst, args...);
105 }
106
107 template <typename... Args>
108 void AddF32x3(const char* format_str, IR::Inst& inst, Args&&... args) {
109 Add<GlslVarType::F32x3>(format_str, inst, args...);
110 }
111
112 template <typename... Args>
113 void AddU32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
114 Add<GlslVarType::U32x4>(format_str, inst, args...);
115 }
116
117 template <typename... Args>
118 void AddF32x4(const char* format_str, IR::Inst& inst, Args&&... args) {
119 Add<GlslVarType::F32x4>(format_str, inst, args...);
120 }
121
122 template <typename... Args>
123 void AddPrecF32(const char* format_str, IR::Inst& inst, Args&&... args) {
124 Add<GlslVarType::PrecF32>(format_str, inst, args...);
125 }
126
127 template <typename... Args>
128 void AddPrecF64(const char* format_str, IR::Inst& inst, Args&&... args) {
129 Add<GlslVarType::PrecF64>(format_str, inst, args...);
130 }
131
132 template <typename... Args>
133 void Add(const char* format_str, Args&&... args) {
134 code += fmt::format(fmt::runtime(format_str), std::forward<Args>(args)...);
135 // TODO: Remove this
136 code += '\n';
137 }
138
139 std::string header;
140 std::string code;
141 VarAlloc var_alloc;
142 const Info& info;
143 const Profile& profile;
144 const RuntimeInfo& runtime_info;
145
146 Stage stage{};
147 std::string_view stage_name = "invalid";
148 std::string_view position_name = "gl_Position";
149
150 std::vector<TextureImageDefinition> texture_buffers;
151 std::vector<TextureImageDefinition> image_buffers;
152 std::vector<TextureImageDefinition> textures;
153 std::vector<TextureImageDefinition> images;
154 std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
155
156 u32 num_safety_loop_vars{};
157
158 bool uses_y_direction{};
159 bool uses_cc_carry{};
160 bool uses_geometry_passthrough{};
161
162private:
163 void SetupExtensions();
164 void DefineConstantBuffers(Bindings& bindings);
165 void DefineStorageBuffers(Bindings& bindings);
166 void DefineGenericOutput(size_t index, u32 invocations);
167 void DefineHelperFunctions();
168 void DefineConstants();
169 std::string DefineGlobalMemoryFunctions();
170 void SetupImages(Bindings& bindings);
171 void SetupTextures(Bindings& bindings);
172};
173
174} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
new file mode 100644
index 000000000..8a430d573
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
@@ -0,0 +1,252 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <string>
7#include <tuple>
8#include <type_traits>
9
10#include "common/div_ceil.h"
11#include "common/settings.h"
12#include "shader_recompiler/backend/glsl/emit_context.h"
13#include "shader_recompiler/backend/glsl/emit_glsl.h"
14#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
15#include "shader_recompiler/frontend/ir/ir_emitter.h"
16
17namespace Shader::Backend::GLSL {
18namespace {
19template <class Func>
20struct FuncTraits {};
21
22template <class ReturnType_, class... Args>
23struct FuncTraits<ReturnType_ (*)(Args...)> {
24 using ReturnType = ReturnType_;
25
26 static constexpr size_t NUM_ARGS = sizeof...(Args);
27
28 template <size_t I>
29 using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
30};
31
32template <auto func, typename... Args>
33void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
34 inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
35}
36
37template <typename ArgType>
38auto Arg(EmitContext& ctx, const IR::Value& arg) {
39 if constexpr (std::is_same_v<ArgType, std::string_view>) {
40 return ctx.var_alloc.Consume(arg);
41 } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
42 return arg;
43 } else if constexpr (std::is_same_v<ArgType, u32>) {
44 return arg.U32();
45 } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
46 return arg.Attribute();
47 } else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
48 return arg.Patch();
49 } else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
50 return arg.Reg();
51 }
52}
53
54template <auto func, bool is_first_arg_inst, size_t... I>
55void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
56 using Traits = FuncTraits<decltype(func)>;
57 if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
58 if constexpr (is_first_arg_inst) {
59 SetDefinition<func>(
60 ctx, inst, *inst,
61 Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
62 } else {
63 SetDefinition<func>(
64 ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
65 }
66 } else {
67 if constexpr (is_first_arg_inst) {
68 func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
69 } else {
70 func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
71 }
72 }
73}
74
75template <auto func>
76void Invoke(EmitContext& ctx, IR::Inst* inst) {
77 using Traits = FuncTraits<decltype(func)>;
78 static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
79 if constexpr (Traits::NUM_ARGS == 1) {
80 Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
81 } else {
82 using FirstArgType = typename Traits::template ArgType<1>;
83 static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
84 using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
85 Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
86 }
87}
88
89void EmitInst(EmitContext& ctx, IR::Inst* inst) {
90 switch (inst->GetOpcode()) {
91#define OPCODE(name, result_type, ...) \
92 case IR::Opcode::name: \
93 return Invoke<&Emit##name>(ctx, inst);
94#include "shader_recompiler/frontend/ir/opcodes.inc"
95#undef OPCODE
96 }
97 throw LogicError("Invalid opcode {}", inst->GetOpcode());
98}
99
100bool IsReference(IR::Inst& inst) {
101 return inst.GetOpcode() == IR::Opcode::Reference;
102}
103
104void PrecolorInst(IR::Inst& phi) {
105 // Insert phi moves before references to avoid overwritting other phis
106 const size_t num_args{phi.NumArgs()};
107 for (size_t i = 0; i < num_args; ++i) {
108 IR::Block& phi_block{*phi.PhiBlock(i)};
109 auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
110 IR::IREmitter ir{phi_block, it};
111 const IR::Value arg{phi.Arg(i)};
112 if (arg.IsImmediate()) {
113 ir.PhiMove(phi, arg);
114 } else {
115 ir.PhiMove(phi, IR::Value{arg.InstRecursive()});
116 }
117 }
118 for (size_t i = 0; i < num_args; ++i) {
119 IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
120 }
121}
122
123void Precolor(const IR::Program& program) {
124 for (IR::Block* const block : program.blocks) {
125 for (IR::Inst& phi : block->Instructions()) {
126 if (!IR::IsPhi(phi)) {
127 break;
128 }
129 PrecolorInst(phi);
130 }
131 }
132}
133
134void EmitCode(EmitContext& ctx, const IR::Program& program) {
135 for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
136 switch (node.type) {
137 case IR::AbstractSyntaxNode::Type::Block:
138 for (IR::Inst& inst : node.data.block->Instructions()) {
139 EmitInst(ctx, &inst);
140 }
141 break;
142 case IR::AbstractSyntaxNode::Type::If:
143 ctx.Add("if({}){{", ctx.var_alloc.Consume(node.data.if_node.cond));
144 break;
145 case IR::AbstractSyntaxNode::Type::EndIf:
146 ctx.Add("}}");
147 break;
148 case IR::AbstractSyntaxNode::Type::Break:
149 if (node.data.break_node.cond.IsImmediate()) {
150 if (node.data.break_node.cond.U1()) {
151 ctx.Add("break;");
152 }
153 } else {
154 ctx.Add("if({}){{break;}}", ctx.var_alloc.Consume(node.data.break_node.cond));
155 }
156 break;
157 case IR::AbstractSyntaxNode::Type::Return:
158 case IR::AbstractSyntaxNode::Type::Unreachable:
159 ctx.Add("return;");
160 break;
161 case IR::AbstractSyntaxNode::Type::Loop:
162 ctx.Add("for(;;){{");
163 break;
164 case IR::AbstractSyntaxNode::Type::Repeat:
165 if (Settings::values.disable_shader_loop_safety_checks) {
166 ctx.Add("if(!{}){{break;}}}}", ctx.var_alloc.Consume(node.data.repeat.cond));
167 } else {
168 ctx.Add("if(--loop{}<0 || !{}){{break;}}}}", ctx.num_safety_loop_vars++,
169 ctx.var_alloc.Consume(node.data.repeat.cond));
170 }
171 break;
172 default:
173 throw NotImplementedException("AbstractSyntaxNode Type {}", node.type);
174 }
175 }
176}
177
178std::string GlslVersionSpecifier(const EmitContext& ctx) {
179 if (ctx.uses_y_direction || ctx.info.stores.Legacy() || ctx.info.loads.Legacy()) {
180 return " compatibility";
181 }
182 return "";
183}
184
185bool IsPreciseType(GlslVarType type) {
186 switch (type) {
187 case GlslVarType::PrecF32:
188 case GlslVarType::PrecF64:
189 return true;
190 default:
191 return false;
192 }
193}
194
195void DefineVariables(const EmitContext& ctx, std::string& header) {
196 for (u32 i = 0; i < static_cast<u32>(GlslVarType::Void); ++i) {
197 const auto type{static_cast<GlslVarType>(i)};
198 const auto& tracker{ctx.var_alloc.GetUseTracker(type)};
199 const auto type_name{ctx.var_alloc.GetGlslType(type)};
200 const bool has_precise_bug{ctx.stage == Stage::Fragment && ctx.profile.has_gl_precise_bug};
201 const auto precise{!has_precise_bug && IsPreciseType(type) ? "precise " : ""};
202 // Temps/return types that are never used are stored at index 0
203 if (tracker.uses_temp) {
204 header += fmt::format("{}{} t{}={}(0);", precise, type_name,
205 ctx.var_alloc.Representation(0, type), type_name);
206 }
207 for (u32 index = 0; index < tracker.num_used; ++index) {
208 header += fmt::format("{}{} {}={}(0);", precise, type_name,
209 ctx.var_alloc.Representation(index, type), type_name);
210 }
211 }
212 for (u32 i = 0; i < ctx.num_safety_loop_vars; ++i) {
213 header += fmt::format("int loop{}=0x2000;", i);
214 }
215}
216} // Anonymous namespace
217
218std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
219 Bindings& bindings) {
220 EmitContext ctx{program, bindings, profile, runtime_info};
221 Precolor(program);
222 EmitCode(ctx, program);
223 const std::string version{fmt::format("#version 450{}\n", GlslVersionSpecifier(ctx))};
224 ctx.header.insert(0, version);
225 if (program.shared_memory_size > 0) {
226 const auto requested_size{program.shared_memory_size};
227 const auto max_size{profile.gl_max_compute_smem_size};
228 const bool needs_clamp{requested_size > max_size};
229 if (needs_clamp) {
230 LOG_WARNING(Shader_GLSL, "Requested shared memory size ({}) exceeds device limit ({})",
231 requested_size, max_size);
232 }
233 const auto smem_size{needs_clamp ? max_size : requested_size};
234 ctx.header += fmt::format("shared uint smem[{}];", Common::DivCeil(smem_size, 4U));
235 }
236 ctx.header += "void main(){\n";
237 if (program.local_memory_size > 0) {
238 ctx.header += fmt::format("uint lmem[{}];", Common::DivCeil(program.local_memory_size, 4U));
239 }
240 DefineVariables(ctx, ctx.header);
241 if (ctx.uses_cc_carry) {
242 ctx.header += "uint carry;";
243 }
244 if (program.info.uses_subgroup_shuffles) {
245 ctx.header += "bool shfl_in_bounds;";
246 }
247 ctx.code.insert(0, ctx.header);
248 ctx.code += '}';
249 return ctx.code;
250}
251
252} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.h b/src/shader_recompiler/backend/glsl/emit_glsl.h
new file mode 100644
index 000000000..20e5719e6
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.h
@@ -0,0 +1,24 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8
9#include "shader_recompiler/backend/bindings.h"
10#include "shader_recompiler/frontend/ir/program.h"
11#include "shader_recompiler/profile.h"
12#include "shader_recompiler/runtime_info.h"
13
14namespace Shader::Backend::GLSL {
15
16[[nodiscard]] std::string EmitGLSL(const Profile& profile, const RuntimeInfo& runtime_info,
17 IR::Program& program, Bindings& bindings);
18
19[[nodiscard]] inline std::string EmitGLSL(const Profile& profile, IR::Program& program) {
20 Bindings binding;
21 return EmitGLSL(profile, {}, program, binding);
22}
23
24} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
new file mode 100644
index 000000000..772acc5a4
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_atomic.cpp
@@ -0,0 +1,418 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13constexpr char cas_loop[]{
14 "for (;;){{uint old={};{}=atomicCompSwap({},old,{}({},{}));if({}==old){{break;}}}}"};
15
16void SharedCasFunction(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
17 std::string_view value, std::string_view function) {
18 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
19 const std::string smem{fmt::format("smem[{}>>2]", offset)};
20 ctx.Add(cas_loop, smem, ret, smem, function, smem, value, ret);
21}
22
23void SsboCasFunction(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
24 const IR::Value& offset, std::string_view value, std::string_view function) {
25 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
26 const std::string ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(),
27 ctx.var_alloc.Consume(offset))};
28 ctx.Add(cas_loop, ssbo, ret, ssbo, function, ssbo, value, ret);
29}
30
31void SsboCasFunctionF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
32 const IR::Value& offset, std::string_view value,
33 std::string_view function) {
34 const std::string ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(),
35 ctx.var_alloc.Consume(offset))};
36 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
37 ctx.Add(cas_loop, ssbo, ret, ssbo, function, ssbo, value, ret);
38 ctx.AddF32("{}=utof({});", inst, ret);
39}
40} // Anonymous namespace
41
42void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
43 std::string_view value) {
44 ctx.AddU32("{}=atomicAdd(smem[{}>>2],{});", inst, pointer_offset, value);
45}
46
47void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
48 std::string_view value) {
49 const std::string u32_value{fmt::format("uint({})", value)};
50 SharedCasFunction(ctx, inst, pointer_offset, u32_value, "CasMinS32");
51}
52
53void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
54 std::string_view value) {
55 ctx.AddU32("{}=atomicMin(smem[{}>>2],{});", inst, pointer_offset, value);
56}
57
58void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
59 std::string_view value) {
60 const std::string u32_value{fmt::format("uint({})", value)};
61 SharedCasFunction(ctx, inst, pointer_offset, u32_value, "CasMaxS32");
62}
63
64void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
65 std::string_view value) {
66 ctx.AddU32("{}=atomicMax(smem[{}>>2],{});", inst, pointer_offset, value);
67}
68
69void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
70 std::string_view value) {
71 SharedCasFunction(ctx, inst, pointer_offset, value, "CasIncrement");
72}
73
74void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
75 std::string_view value) {
76 SharedCasFunction(ctx, inst, pointer_offset, value, "CasDecrement");
77}
78
79void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
80 std::string_view value) {
81 ctx.AddU32("{}=atomicAnd(smem[{}>>2],{});", inst, pointer_offset, value);
82}
83
84void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
85 std::string_view value) {
86 ctx.AddU32("{}=atomicOr(smem[{}>>2],{});", inst, pointer_offset, value);
87}
88
89void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
90 std::string_view value) {
91 ctx.AddU32("{}=atomicXor(smem[{}>>2],{});", inst, pointer_offset, value);
92}
93
94void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
95 std::string_view value) {
96 ctx.AddU32("{}=atomicExchange(smem[{}>>2],{});", inst, pointer_offset, value);
97}
98
99void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
100 std::string_view value) {
101 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
102 ctx.AddU64("{}=packUint2x32(uvec2(smem[{}>>2],smem[({}+4)>>2]));", inst, pointer_offset,
103 pointer_offset);
104 ctx.Add("smem[{}>>2]=unpackUint2x32({}).x;smem[({}+4)>>2]=unpackUint2x32({}).y;",
105 pointer_offset, value, pointer_offset, value);
106}
107
108void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
109 const IR::Value& offset, std::string_view value) {
110 ctx.AddU32("{}=atomicAdd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
111 ctx.var_alloc.Consume(offset), value);
112}
113
114void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
115 const IR::Value& offset, std::string_view value) {
116 const std::string u32_value{fmt::format("uint({})", value)};
117 SsboCasFunction(ctx, inst, binding, offset, u32_value, "CasMinS32");
118}
119
120void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
121 const IR::Value& offset, std::string_view value) {
122 ctx.AddU32("{}=atomicMin({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
123 ctx.var_alloc.Consume(offset), value);
124}
125
126void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
127 const IR::Value& offset, std::string_view value) {
128 const std::string u32_value{fmt::format("uint({})", value)};
129 SsboCasFunction(ctx, inst, binding, offset, u32_value, "CasMaxS32");
130}
131
132void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
133 const IR::Value& offset, std::string_view value) {
134 ctx.AddU32("{}=atomicMax({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
135 ctx.var_alloc.Consume(offset), value);
136}
137
138void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
139 const IR::Value& offset, std::string_view value) {
140 SsboCasFunction(ctx, inst, binding, offset, value, "CasIncrement");
141}
142
143void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
144 const IR::Value& offset, std::string_view value) {
145 SsboCasFunction(ctx, inst, binding, offset, value, "CasDecrement");
146}
147
148void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
149 const IR::Value& offset, std::string_view value) {
150 ctx.AddU32("{}=atomicAnd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
151 ctx.var_alloc.Consume(offset), value);
152}
153
154void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
155 const IR::Value& offset, std::string_view value) {
156 ctx.AddU32("{}=atomicOr({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
157 ctx.var_alloc.Consume(offset), value);
158}
159
160void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
161 const IR::Value& offset, std::string_view value) {
162 ctx.AddU32("{}=atomicXor({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
163 ctx.var_alloc.Consume(offset), value);
164}
165
166void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
167 const IR::Value& offset, std::string_view value) {
168 ctx.AddU32("{}=atomicExchange({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
169 ctx.var_alloc.Consume(offset), value);
170}
171
172void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
173 const IR::Value& offset, std::string_view value) {
174 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
175 ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
176 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
177 binding.U32(), ctx.var_alloc.Consume(offset));
178 ctx.Add("{}_ssbo{}[{}>>2]+=unpackUint2x32({}).x;{}_ssbo{}[({}>>2)+1]+=unpackUint2x32({}).y;",
179 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
180 binding.U32(), ctx.var_alloc.Consume(offset), value);
181}
182
183void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
184 const IR::Value& offset, std::string_view value) {
185 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
186 ctx.AddU64("{}=packInt2x32(ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
187 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
188 binding.U32(), ctx.var_alloc.Consume(offset));
189 ctx.Add("for(int i=0;i<2;++i){{ "
190 "{}_ssbo{}[({}>>2)+i]=uint(min(int({}_ssbo{}[({}>>2)+i]),unpackInt2x32(int64_t({}))[i])"
191 ");}}",
192 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
193 binding.U32(), ctx.var_alloc.Consume(offset), value);
194}
195
196void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
197 const IR::Value& offset, std::string_view value) {
198 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
199 ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
200 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
201 binding.U32(), ctx.var_alloc.Consume(offset));
202 ctx.Add("for(int i=0;i<2;++i){{ "
203 "{}_ssbo{}[({}>>2)+i]=min({}_ssbo{}[({}>>2)+i],unpackUint2x32(uint64_t({}))[i]);}}",
204 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
205 binding.U32(), ctx.var_alloc.Consume(offset), value);
206}
207
208void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
209 const IR::Value& offset, std::string_view value) {
210 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
211 ctx.AddU64("{}=packInt2x32(ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
212 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
213 binding.U32(), ctx.var_alloc.Consume(offset));
214 ctx.Add("for(int i=0;i<2;++i){{ "
215 "{}_ssbo{}[({}>>2)+i]=uint(max(int({}_ssbo{}[({}>>2)+i]),unpackInt2x32(int64_t({}))[i])"
216 ");}}",
217 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
218 binding.U32(), ctx.var_alloc.Consume(offset), value);
219}
220
221void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
222 const IR::Value& offset, std::string_view value) {
223 LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
224 ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
225 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
226 binding.U32(), ctx.var_alloc.Consume(offset));
227 ctx.Add("for(int "
228 "i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=max({}_ssbo{}[({}>>2)+i],unpackUint2x32(uint64_t({}"
229 "))[i]);}}",
230 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
231 binding.U32(), ctx.var_alloc.Consume(offset), value);
232}
233
234void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
235 const IR::Value& offset, std::string_view value) {
236 ctx.AddU64(
237 "{}=packUint2x32(uvec2(atomicAnd({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicAnd({}_"
238 "ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
239 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
240 binding.U32(), ctx.var_alloc.Consume(offset), value);
241}
242
243void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
244 const IR::Value& offset, std::string_view value) {
245 ctx.AddU64("{}=packUint2x32(uvec2(atomicOr({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicOr({}_"
246 "ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
247 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
248 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
249}
250
251void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
252 const IR::Value& offset, std::string_view value) {
253 ctx.AddU64(
254 "{}=packUint2x32(uvec2(atomicXor({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicXor({}_"
255 "ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
256 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
257 binding.U32(), ctx.var_alloc.Consume(offset), value);
258}
259
260void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
261 const IR::Value& offset, std::string_view value) {
262 ctx.AddU64("{}=packUint2x32(uvec2(atomicExchange({}_ssbo{}[{}>>2],unpackUint2x32({}).x),"
263 "atomicExchange({}_ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
264 inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
265 ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
266}
267
268void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
269 const IR::Value& offset, std::string_view value) {
270 SsboCasFunctionF32(ctx, inst, binding, offset, value, "CasFloatAdd");
271}
272
273void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
274 const IR::Value& offset, std::string_view value) {
275 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatAdd16x2");
276}
277
278void EmitStorageAtomicAddF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
279 const IR::Value& offset, std::string_view value) {
280 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatAdd32x2");
281}
282
283void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
284 const IR::Value& offset, std::string_view value) {
285 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMin16x2");
286}
287
288void EmitStorageAtomicMinF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
289 const IR::Value& offset, std::string_view value) {
290 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMin32x2");
291}
292
293void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
294 const IR::Value& offset, std::string_view value) {
295 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMax16x2");
296}
297
298void EmitStorageAtomicMaxF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
299 const IR::Value& offset, std::string_view value) {
300 SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMax32x2");
301}
302
303void EmitGlobalAtomicIAdd32(EmitContext&) {
304 throw NotImplementedException("GLSL Instrucion");
305}
306
307void EmitGlobalAtomicSMin32(EmitContext&) {
308 throw NotImplementedException("GLSL Instrucion");
309}
310
311void EmitGlobalAtomicUMin32(EmitContext&) {
312 throw NotImplementedException("GLSL Instrucion");
313}
314
315void EmitGlobalAtomicSMax32(EmitContext&) {
316 throw NotImplementedException("GLSL Instrucion");
317}
318
319void EmitGlobalAtomicUMax32(EmitContext&) {
320 throw NotImplementedException("GLSL Instrucion");
321}
322
323void EmitGlobalAtomicInc32(EmitContext&) {
324 throw NotImplementedException("GLSL Instrucion");
325}
326
327void EmitGlobalAtomicDec32(EmitContext&) {
328 throw NotImplementedException("GLSL Instrucion");
329}
330
331void EmitGlobalAtomicAnd32(EmitContext&) {
332 throw NotImplementedException("GLSL Instrucion");
333}
334
335void EmitGlobalAtomicOr32(EmitContext&) {
336 throw NotImplementedException("GLSL Instrucion");
337}
338
339void EmitGlobalAtomicXor32(EmitContext&) {
340 throw NotImplementedException("GLSL Instrucion");
341}
342
343void EmitGlobalAtomicExchange32(EmitContext&) {
344 throw NotImplementedException("GLSL Instrucion");
345}
346
347void EmitGlobalAtomicIAdd64(EmitContext&) {
348 throw NotImplementedException("GLSL Instrucion");
349}
350
351void EmitGlobalAtomicSMin64(EmitContext&) {
352 throw NotImplementedException("GLSL Instrucion");
353}
354
355void EmitGlobalAtomicUMin64(EmitContext&) {
356 throw NotImplementedException("GLSL Instrucion");
357}
358
359void EmitGlobalAtomicSMax64(EmitContext&) {
360 throw NotImplementedException("GLSL Instrucion");
361}
362
363void EmitGlobalAtomicUMax64(EmitContext&) {
364 throw NotImplementedException("GLSL Instrucion");
365}
366
367void EmitGlobalAtomicInc64(EmitContext&) {
368 throw NotImplementedException("GLSL Instrucion");
369}
370
371void EmitGlobalAtomicDec64(EmitContext&) {
372 throw NotImplementedException("GLSL Instrucion");
373}
374
375void EmitGlobalAtomicAnd64(EmitContext&) {
376 throw NotImplementedException("GLSL Instrucion");
377}
378
379void EmitGlobalAtomicOr64(EmitContext&) {
380 throw NotImplementedException("GLSL Instrucion");
381}
382
383void EmitGlobalAtomicXor64(EmitContext&) {
384 throw NotImplementedException("GLSL Instrucion");
385}
386
387void EmitGlobalAtomicExchange64(EmitContext&) {
388 throw NotImplementedException("GLSL Instrucion");
389}
390
391void EmitGlobalAtomicAddF32(EmitContext&) {
392 throw NotImplementedException("GLSL Instrucion");
393}
394
395void EmitGlobalAtomicAddF16x2(EmitContext&) {
396 throw NotImplementedException("GLSL Instrucion");
397}
398
399void EmitGlobalAtomicAddF32x2(EmitContext&) {
400 throw NotImplementedException("GLSL Instrucion");
401}
402
403void EmitGlobalAtomicMinF16x2(EmitContext&) {
404 throw NotImplementedException("GLSL Instrucion");
405}
406
407void EmitGlobalAtomicMinF32x2(EmitContext&) {
408 throw NotImplementedException("GLSL Instrucion");
409}
410
411void EmitGlobalAtomicMaxF16x2(EmitContext&) {
412 throw NotImplementedException("GLSL Instrucion");
413}
414
415void EmitGlobalAtomicMaxF32x2(EmitContext&) {
416 throw NotImplementedException("GLSL Instrucion");
417}
418} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
new file mode 100644
index 000000000..e1d1b558e
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_barriers.cpp
@@ -0,0 +1,21 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/backend/glsl/emit_context.h"
6#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
7#include "shader_recompiler/frontend/ir/value.h"
8
9namespace Shader::Backend::GLSL {
10void EmitBarrier(EmitContext& ctx) {
11 ctx.Add("barrier();");
12}
13
14void EmitWorkgroupMemoryBarrier(EmitContext& ctx) {
15 ctx.Add("groupMemoryBarrier();");
16}
17
18void EmitDeviceMemoryBarrier(EmitContext& ctx) {
19 ctx.Add("memoryBarrier();");
20}
21} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
new file mode 100644
index 000000000..3c1714e89
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_bitwise_conversion.cpp
@@ -0,0 +1,94 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13void Alias(IR::Inst& inst, const IR::Value& value) {
14 if (value.IsImmediate()) {
15 return;
16 }
17 IR::Inst& value_inst{*value.InstRecursive()};
18 value_inst.DestructiveAddUsage(inst.UseCount());
19 value_inst.DestructiveRemoveUsage();
20 inst.SetDefinition(value_inst.Definition<Id>());
21}
22} // Anonymous namespace
23
24void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) {
25 Alias(inst, value);
26}
27
28void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value) {
29 // Fake one usage to get a real variable out of the condition
30 inst.DestructiveAddUsage(1);
31 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U1)};
32 const auto input{ctx.var_alloc.Consume(value)};
33 if (ret != input) {
34 ctx.Add("{}={};", ret, input);
35 }
36}
37
38void EmitBitCastU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
39 NotImplemented();
40}
41
42void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
43 ctx.AddU32("{}=ftou({});", inst, value);
44}
45
46void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
47 ctx.AddU64("{}=doubleBitsToUint64({});", inst, value);
48}
49
50void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) {
51 NotImplemented();
52}
53
54void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
55 ctx.AddF32("{}=utof({});", inst, value);
56}
57
58void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
59 ctx.AddF64("{}=uint64BitsToDouble({});", inst, value);
60}
61
62void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
63 ctx.AddU64("{}=packUint2x32({});", inst, value);
64}
65
66void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
67 ctx.AddU32x2("{}=unpackUint2x32({});", inst, value);
68}
69
70void EmitPackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
71 ctx.AddU32("{}=packFloat2x16({});", inst, value);
72}
73
74void EmitUnpackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
75 ctx.AddF16x2("{}=unpackFloat2x16({});", inst, value);
76}
77
78void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
79 ctx.AddU32("{}=packHalf2x16({});", inst, value);
80}
81
82void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
83 ctx.AddF32x2("{}=unpackHalf2x16({});", inst, value);
84}
85
86void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
87 ctx.AddF64("{}=packDouble2x32({});", inst, value);
88}
89
90void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
91 ctx.AddU32x2("{}=unpackDouble2x32({});", inst, value);
92}
93
94} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp
new file mode 100644
index 000000000..49a66e3ec
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_composite.cpp
@@ -0,0 +1,219 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13constexpr std::string_view SWIZZLE{"xyzw"};
14void CompositeInsert(EmitContext& ctx, std::string_view result, std::string_view composite,
15 std::string_view object, u32 index) {
16 if (result == composite) {
17 // The result is aliased with the composite
18 ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
19 } else {
20 ctx.Add("{}={};{}.{}={};", result, composite, result, SWIZZLE[index], object);
21 }
22}
23} // Anonymous namespace
24
25void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
26 std::string_view e2) {
27 ctx.AddU32x2("{}=uvec2({},{});", inst, e1, e2);
28}
29
30void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
31 std::string_view e2, std::string_view e3) {
32 ctx.AddU32x3("{}=uvec3({},{},{});", inst, e1, e2, e3);
33}
34
35void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
36 std::string_view e2, std::string_view e3, std::string_view e4) {
37 ctx.AddU32x4("{}=uvec4({},{},{},{});", inst, e1, e2, e3, e4);
38}
39
40void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
41 u32 index) {
42 ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
43}
44
45void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
46 u32 index) {
47 ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
48}
49
50void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
51 u32 index) {
52 ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]);
53}
54
55void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
56 std::string_view object, u32 index) {
57 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
58 CompositeInsert(ctx, ret, composite, object, index);
59}
60
61void EmitCompositeInsertU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
62 std::string_view object, u32 index) {
63 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x3)};
64 CompositeInsert(ctx, ret, composite, object, index);
65}
66
67void EmitCompositeInsertU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
68 std::string_view object, u32 index) {
69 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x4)};
70 CompositeInsert(ctx, ret, composite, object, index);
71}
72
73void EmitCompositeConstructF16x2([[maybe_unused]] EmitContext& ctx,
74 [[maybe_unused]] std::string_view e1,
75 [[maybe_unused]] std::string_view e2) {
76 NotImplemented();
77}
78
79void EmitCompositeConstructF16x3([[maybe_unused]] EmitContext& ctx,
80 [[maybe_unused]] std::string_view e1,
81 [[maybe_unused]] std::string_view e2,
82 [[maybe_unused]] std::string_view e3) {
83 NotImplemented();
84}
85
86void EmitCompositeConstructF16x4([[maybe_unused]] EmitContext& ctx,
87 [[maybe_unused]] std::string_view e1,
88 [[maybe_unused]] std::string_view e2,
89 [[maybe_unused]] std::string_view e3,
90 [[maybe_unused]] std::string_view e4) {
91 NotImplemented();
92}
93
94void EmitCompositeExtractF16x2([[maybe_unused]] EmitContext& ctx,
95 [[maybe_unused]] std::string_view composite,
96 [[maybe_unused]] u32 index) {
97 NotImplemented();
98}
99
100void EmitCompositeExtractF16x3([[maybe_unused]] EmitContext& ctx,
101 [[maybe_unused]] std::string_view composite,
102 [[maybe_unused]] u32 index) {
103 NotImplemented();
104}
105
106void EmitCompositeExtractF16x4([[maybe_unused]] EmitContext& ctx,
107 [[maybe_unused]] std::string_view composite,
108 [[maybe_unused]] u32 index) {
109 NotImplemented();
110}
111
112void EmitCompositeInsertF16x2([[maybe_unused]] EmitContext& ctx,
113 [[maybe_unused]] std::string_view composite,
114 [[maybe_unused]] std::string_view object,
115 [[maybe_unused]] u32 index) {
116 NotImplemented();
117}
118
119void EmitCompositeInsertF16x3([[maybe_unused]] EmitContext& ctx,
120 [[maybe_unused]] std::string_view composite,
121 [[maybe_unused]] std::string_view object,
122 [[maybe_unused]] u32 index) {
123 NotImplemented();
124}
125
126void EmitCompositeInsertF16x4([[maybe_unused]] EmitContext& ctx,
127 [[maybe_unused]] std::string_view composite,
128 [[maybe_unused]] std::string_view object,
129 [[maybe_unused]] u32 index) {
130 NotImplemented();
131}
132
133void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
134 std::string_view e2) {
135 ctx.AddF32x2("{}=vec2({},{});", inst, e1, e2);
136}
137
138void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
139 std::string_view e2, std::string_view e3) {
140 ctx.AddF32x3("{}=vec3({},{},{});", inst, e1, e2, e3);
141}
142
143void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
144 std::string_view e2, std::string_view e3, std::string_view e4) {
145 ctx.AddF32x4("{}=vec4({},{},{},{});", inst, e1, e2, e3, e4);
146}
147
148void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
149 u32 index) {
150 ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
151}
152
153void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
154 u32 index) {
155 ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
156}
157
158void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
159 u32 index) {
160 ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]);
161}
162
163void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
164 std::string_view object, u32 index) {
165 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x2)};
166 CompositeInsert(ctx, ret, composite, object, index);
167}
168
169void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
170 std::string_view object, u32 index) {
171 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x3)};
172 CompositeInsert(ctx, ret, composite, object, index);
173}
174
175void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
176 std::string_view object, u32 index) {
177 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
178 CompositeInsert(ctx, ret, composite, object, index);
179}
180
181void EmitCompositeConstructF64x2([[maybe_unused]] EmitContext& ctx) {
182 NotImplemented();
183}
184
185void EmitCompositeConstructF64x3([[maybe_unused]] EmitContext& ctx) {
186 NotImplemented();
187}
188
189void EmitCompositeConstructF64x4([[maybe_unused]] EmitContext& ctx) {
190 NotImplemented();
191}
192
193void EmitCompositeExtractF64x2([[maybe_unused]] EmitContext& ctx) {
194 NotImplemented();
195}
196
197void EmitCompositeExtractF64x3([[maybe_unused]] EmitContext& ctx) {
198 NotImplemented();
199}
200
201void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
202 NotImplemented();
203}
204
205void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object,
206 u32 index) {
207 ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
208}
209
210void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object,
211 u32 index) {
212 ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
213}
214
215void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object,
216 u32 index) {
217 ctx.Add("{}.{}={};", composite, SWIZZLE[index], object);
218}
219} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
new file mode 100644
index 000000000..580063fa9
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -0,0 +1,456 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h"
11#include "shader_recompiler/runtime_info.h"
12
13namespace Shader::Backend::GLSL {
14namespace {
15constexpr char SWIZZLE[]{"xyzw"};
16
17u32 CbufIndex(u32 offset) {
18 return (offset / 4) % 4;
19}
20
21char OffsetSwizzle(u32 offset) {
22 return SWIZZLE[CbufIndex(offset)];
23}
24
25bool IsInputArray(Stage stage) {
26 return stage == Stage::Geometry || stage == Stage::TessellationControl ||
27 stage == Stage::TessellationEval;
28}
29
30std::string InputVertexIndex(EmitContext& ctx, std::string_view vertex) {
31 return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : "";
32}
33
34std::string_view OutputVertexIndex(EmitContext& ctx) {
35 return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
36}
37
38void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding,
39 const IR::Value& offset, u32 num_bits, std::string_view cast = {},
40 std::string_view bit_offset = {}) {
41 const bool is_immediate{offset.IsImmediate()};
42 const bool component_indexing_bug{!is_immediate && ctx.profile.has_gl_component_indexing_bug};
43 if (is_immediate) {
44 const s32 signed_offset{static_cast<s32>(offset.U32())};
45 static constexpr u32 cbuf_size{0x10000};
46 if (signed_offset < 0 || offset.U32() > cbuf_size) {
47 LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
48 ctx.Add("{}=0u;", ret);
49 return;
50 }
51 }
52 const auto offset_var{ctx.var_alloc.Consume(offset)};
53 const auto index{is_immediate ? fmt::format("{}", offset.U32() / 16)
54 : fmt::format("{}>>4", offset_var)};
55 const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32()))
56 : fmt::format("[({}>>2)%4]", offset_var)};
57
58 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
59 const auto cbuf_cast{fmt::format("{}({}[{}]{{}})", cast, cbuf, index)};
60 const auto extraction{num_bits == 32 ? cbuf_cast
61 : fmt ::format("bitfieldExtract({},int({}),{})", cbuf_cast,
62 bit_offset, num_bits)};
63 if (!component_indexing_bug) {
64 const auto result{fmt::format(fmt::runtime(extraction), swizzle)};
65 ctx.Add("{}={};", ret, result);
66 return;
67 }
68 const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
69 for (u32 i = 0; i < 4; ++i) {
70 const auto swizzle_string{fmt::format(".{}", "xyzw"[i])};
71 const auto result{fmt::format(fmt::runtime(extraction), swizzle_string)};
72 ctx.Add("if(({}&3)=={}){}={};", cbuf_offset, i, ret, result);
73 }
74}
75
76void GetCbuf8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
77 std::string_view cast) {
78 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
79 if (offset.IsImmediate()) {
80 const auto bit_offset{fmt::format("{}", (offset.U32() % 4) * 8)};
81 GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
82 } else {
83 const auto offset_var{ctx.var_alloc.Consume(offset)};
84 const auto bit_offset{fmt::format("({}%4)*8", offset_var)};
85 GetCbuf(ctx, ret, binding, offset, 8, cast, bit_offset);
86 }
87}
88
89void GetCbuf16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset,
90 std::string_view cast) {
91 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
92 if (offset.IsImmediate()) {
93 const auto bit_offset{fmt::format("{}", ((offset.U32() / 2) % 2) * 16)};
94 GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
95 } else {
96 const auto offset_var{ctx.var_alloc.Consume(offset)};
97 const auto bit_offset{fmt::format("(({}>>1)%2)*16", offset_var)};
98 GetCbuf(ctx, ret, binding, offset, 16, cast, bit_offset);
99 }
100}
101
102u32 TexCoordIndex(IR::Attribute attr) {
103 return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4;
104}
105} // Anonymous namespace
106
107void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
108 const IR::Value& offset) {
109 GetCbuf8(ctx, inst, binding, offset, "ftou");
110}
111
112void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
113 const IR::Value& offset) {
114 GetCbuf8(ctx, inst, binding, offset, "ftoi");
115}
116
117void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
118 const IR::Value& offset) {
119 GetCbuf16(ctx, inst, binding, offset, "ftou");
120}
121
122void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
123 const IR::Value& offset) {
124 GetCbuf16(ctx, inst, binding, offset, "ftoi");
125}
126
127void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
128 const IR::Value& offset) {
129 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
130 GetCbuf(ctx, ret, binding, offset, 32, "ftou");
131}
132
133void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
134 const IR::Value& offset) {
135 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::F32)};
136 GetCbuf(ctx, ret, binding, offset, 32);
137}
138
139void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
140 const IR::Value& offset) {
141 const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())};
142 if (offset.IsImmediate()) {
143 static constexpr u32 cbuf_size{0x10000};
144 const u32 u32_offset{offset.U32()};
145 const s32 signed_offset{static_cast<s32>(offset.U32())};
146 if (signed_offset < 0 || u32_offset > cbuf_size) {
147 LOG_WARNING(Shader_GLSL, "Immediate constant buffer offset is out of bounds");
148 ctx.AddU32x2("{}=uvec2(0u);", inst);
149 return;
150 }
151 if (u32_offset % 2 == 0) {
152 ctx.AddU32x2("{}=ftou({}[{}].{}{});", inst, cbuf, u32_offset / 16,
153 OffsetSwizzle(u32_offset), OffsetSwizzle(u32_offset + 4));
154 } else {
155 ctx.AddU32x2("{}=uvec2(ftou({}[{}].{}),ftou({}[{}].{}));", inst, cbuf, u32_offset / 16,
156 OffsetSwizzle(u32_offset), cbuf, (u32_offset + 4) / 16,
157 OffsetSwizzle(u32_offset + 4));
158 }
159 return;
160 }
161 const auto offset_var{ctx.var_alloc.Consume(offset)};
162 if (!ctx.profile.has_gl_component_indexing_bug) {
163 ctx.AddU32x2("{}=uvec2(ftou({}[{}>>4][({}>>2)%4]),ftou({}[({}+4)>>4][(({}+4)>>2)%4]));",
164 inst, cbuf, offset_var, offset_var, cbuf, offset_var, offset_var);
165 return;
166 }
167 const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)};
168 const auto cbuf_offset{fmt::format("{}>>2", offset_var)};
169 for (u32 swizzle = 0; swizzle < 4; ++swizzle) {
170 ctx.Add("if(({}&3)=={}){}=uvec2(ftou({}[{}>>4].{}),ftou({}[({}+4)>>4].{}));", cbuf_offset,
171 swizzle, ret, cbuf, offset_var, "xyzw"[swizzle], cbuf, offset_var,
172 "xyzw"[(swizzle + 1) % 4]);
173 }
174}
175
176void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
177 std::string_view vertex) {
178 const u32 element{static_cast<u32>(attr) % 4};
179 const char swizzle{"xyzw"[element]};
180 if (IR::IsGeneric(attr)) {
181 const u32 index{IR::GenericAttributeIndex(attr)};
182 if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
183 if (element == 3) {
184 ctx.AddF32("{}=1.f;", inst, attr);
185 } else {
186 ctx.AddF32("{}=0.f;", inst, attr);
187 }
188 return;
189 }
190 ctx.AddF32("{}=in_attr{}{}.{};", inst, index, InputVertexIndex(ctx, vertex), swizzle);
191 return;
192 }
193 // GLSL only exposes 8 legacy texcoords
194 if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
195 LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
196 TexCoordIndex(attr));
197 ctx.AddF32("{}=0.f;", inst);
198 return;
199 }
200 if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
201 const u32 index{TexCoordIndex(attr)};
202 ctx.AddF32("{}=gl_TexCoord[{}].{};", inst, index, swizzle);
203 return;
204 }
205 switch (attr) {
206 case IR::Attribute::PrimitiveId:
207 ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
208 break;
209 case IR::Attribute::PositionX:
210 case IR::Attribute::PositionY:
211 case IR::Attribute::PositionZ:
212 case IR::Attribute::PositionW: {
213 const bool is_array{IsInputArray(ctx.stage)};
214 const auto input_decorator{is_array ? fmt::format("gl_in[{}].", vertex) : ""};
215 ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle);
216 break;
217 }
218 case IR::Attribute::ColorFrontDiffuseR:
219 case IR::Attribute::ColorFrontDiffuseG:
220 case IR::Attribute::ColorFrontDiffuseB:
221 case IR::Attribute::ColorFrontDiffuseA:
222 if (ctx.stage == Stage::Fragment) {
223 ctx.AddF32("{}=gl_Color.{};", inst, swizzle);
224 } else {
225 ctx.AddF32("{}=gl_FrontColor.{};", inst, swizzle);
226 }
227 break;
228 case IR::Attribute::PointSpriteS:
229 case IR::Attribute::PointSpriteT:
230 ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle);
231 break;
232 case IR::Attribute::TessellationEvaluationPointU:
233 case IR::Attribute::TessellationEvaluationPointV:
234 ctx.AddF32("{}=gl_TessCoord.{};", inst, swizzle);
235 break;
236 case IR::Attribute::InstanceId:
237 ctx.AddF32("{}=itof(gl_InstanceID);", inst);
238 break;
239 case IR::Attribute::VertexId:
240 ctx.AddF32("{}=itof(gl_VertexID);", inst);
241 break;
242 case IR::Attribute::FrontFace:
243 ctx.AddF32("{}=itof(gl_FrontFacing?-1:0);", inst);
244 break;
245 default:
246 throw NotImplementedException("Get attribute {}", attr);
247 }
248}
249
250void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
251 [[maybe_unused]] std::string_view vertex) {
252 if (IR::IsGeneric(attr)) {
253 const u32 index{IR::GenericAttributeIndex(attr)};
254 const u32 attr_element{IR::GenericAttributeElement(attr)};
255 const GenericElementInfo& info{ctx.output_generics.at(index).at(attr_element)};
256 const auto output_decorator{OutputVertexIndex(ctx)};
257 if (info.num_components == 1) {
258 ctx.Add("{}{}={};", info.name, output_decorator, value);
259 } else {
260 const u32 index_element{attr_element - info.first_element};
261 ctx.Add("{}{}.{}={};", info.name, output_decorator, "xyzw"[index_element], value);
262 }
263 return;
264 }
265 const u32 element{static_cast<u32>(attr) % 4};
266 const char swizzle{"xyzw"[element]};
267 // GLSL only exposes 8 legacy texcoords
268 if (attr >= IR::Attribute::FixedFncTexture8S && attr <= IR::Attribute::FixedFncTexture9Q) {
269 LOG_WARNING(Shader_GLSL, "GLSL does not allow access to gl_TexCoord[{}]",
270 TexCoordIndex(attr));
271 return;
272 }
273 if (attr >= IR::Attribute::FixedFncTexture0S && attr <= IR::Attribute::FixedFncTexture7Q) {
274 const u32 index{TexCoordIndex(attr)};
275 ctx.Add("gl_TexCoord[{}].{}={};", index, swizzle, value);
276 return;
277 }
278 switch (attr) {
279 case IR::Attribute::Layer:
280 if (ctx.stage != Stage::Geometry &&
281 !ctx.profile.support_viewport_index_layer_non_geometry) {
282 LOG_WARNING(Shader_GLSL, "Shader stores viewport layer but device does not support "
283 "viewport layer extension");
284 break;
285 }
286 ctx.Add("gl_Layer=ftoi({});", value);
287 break;
288 case IR::Attribute::ViewportIndex:
289 if (ctx.stage != Stage::Geometry &&
290 !ctx.profile.support_viewport_index_layer_non_geometry) {
291 LOG_WARNING(Shader_GLSL, "Shader stores viewport index but device does not support "
292 "viewport layer extension");
293 break;
294 }
295 ctx.Add("gl_ViewportIndex=ftoi({});", value);
296 break;
297 case IR::Attribute::ViewportMask:
298 if (ctx.stage != Stage::Geometry && !ctx.profile.support_viewport_mask) {
299 LOG_WARNING(
300 Shader_GLSL,
301 "Shader stores viewport mask but device does not support viewport mask extension");
302 break;
303 }
304 ctx.Add("gl_ViewportMask[0]=ftoi({});", value);
305 break;
306 case IR::Attribute::PointSize:
307 ctx.Add("gl_PointSize={};", value);
308 break;
309 case IR::Attribute::PositionX:
310 case IR::Attribute::PositionY:
311 case IR::Attribute::PositionZ:
312 case IR::Attribute::PositionW:
313 ctx.Add("gl_Position.{}={};", swizzle, value);
314 break;
315 case IR::Attribute::ColorFrontDiffuseR:
316 case IR::Attribute::ColorFrontDiffuseG:
317 case IR::Attribute::ColorFrontDiffuseB:
318 case IR::Attribute::ColorFrontDiffuseA:
319 ctx.Add("gl_FrontColor.{}={};", swizzle, value);
320 break;
321 case IR::Attribute::ColorFrontSpecularR:
322 case IR::Attribute::ColorFrontSpecularG:
323 case IR::Attribute::ColorFrontSpecularB:
324 case IR::Attribute::ColorFrontSpecularA:
325 ctx.Add("gl_FrontSecondaryColor.{}={};", swizzle, value);
326 break;
327 case IR::Attribute::ColorBackDiffuseR:
328 case IR::Attribute::ColorBackDiffuseG:
329 case IR::Attribute::ColorBackDiffuseB:
330 case IR::Attribute::ColorBackDiffuseA:
331 ctx.Add("gl_BackColor.{}={};", swizzle, value);
332 break;
333 case IR::Attribute::ColorBackSpecularR:
334 case IR::Attribute::ColorBackSpecularG:
335 case IR::Attribute::ColorBackSpecularB:
336 case IR::Attribute::ColorBackSpecularA:
337 ctx.Add("gl_BackSecondaryColor.{}={};", swizzle, value);
338 break;
339 case IR::Attribute::FogCoordinate:
340 ctx.Add("gl_FogFragCoord={};", value);
341 break;
342 case IR::Attribute::ClipDistance0:
343 case IR::Attribute::ClipDistance1:
344 case IR::Attribute::ClipDistance2:
345 case IR::Attribute::ClipDistance3:
346 case IR::Attribute::ClipDistance4:
347 case IR::Attribute::ClipDistance5:
348 case IR::Attribute::ClipDistance6:
349 case IR::Attribute::ClipDistance7: {
350 const u32 index{static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::ClipDistance0)};
351 ctx.Add("gl_ClipDistance[{}]={};", index, value);
352 break;
353 }
354 default:
355 throw NotImplementedException("Set attribute {}", attr);
356 }
357}
358
359void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
360 std::string_view vertex) {
361 const bool is_array{ctx.stage == Stage::Geometry};
362 const auto vertex_arg{is_array ? fmt::format(",{}", vertex) : ""};
363 ctx.AddF32("{}=IndexedAttrLoad(int({}){});", inst, offset, vertex_arg);
364}
365
366void EmitSetAttributeIndexed([[maybe_unused]] EmitContext& ctx,
367 [[maybe_unused]] std::string_view offset,
368 [[maybe_unused]] std::string_view value,
369 [[maybe_unused]] std::string_view vertex) {
370 NotImplemented();
371}
372
373void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch) {
374 if (!IR::IsGeneric(patch)) {
375 throw NotImplementedException("Non-generic patch load");
376 }
377 const u32 index{IR::GenericPatchIndex(patch)};
378 const u32 element{IR::GenericPatchElement(patch)};
379 const char swizzle{"xyzw"[element]};
380 ctx.AddF32("{}=patch{}.{};", inst, index, swizzle);
381}
382
383void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value) {
384 if (IR::IsGeneric(patch)) {
385 const u32 index{IR::GenericPatchIndex(patch)};
386 const u32 element{IR::GenericPatchElement(patch)};
387 ctx.Add("patch{}.{}={};", index, "xyzw"[element], value);
388 return;
389 }
390 switch (patch) {
391 case IR::Patch::TessellationLodLeft:
392 case IR::Patch::TessellationLodRight:
393 case IR::Patch::TessellationLodTop:
394 case IR::Patch::TessellationLodBottom: {
395 const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
396 ctx.Add("gl_TessLevelOuter[{}]={};", index, value);
397 break;
398 }
399 case IR::Patch::TessellationLodInteriorU:
400 ctx.Add("gl_TessLevelInner[0]={};", value);
401 break;
402 case IR::Patch::TessellationLodInteriorV:
403 ctx.Add("gl_TessLevelInner[1]={};", value);
404 break;
405 default:
406 throw NotImplementedException("Patch {}", patch);
407 }
408}
409
410void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value) {
411 const char swizzle{"xyzw"[component]};
412 ctx.Add("frag_color{}.{}={};", index, swizzle, value);
413}
414
415void EmitSetSampleMask(EmitContext& ctx, std::string_view value) {
416 ctx.Add("gl_SampleMask[0]=int({});", value);
417}
418
419void EmitSetFragDepth(EmitContext& ctx, std::string_view value) {
420 ctx.Add("gl_FragDepth={};", value);
421}
422
423void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) {
424 ctx.AddU32x3("{}=gl_LocalInvocationID;", inst);
425}
426
427void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) {
428 ctx.AddU32x3("{}=gl_WorkGroupID;", inst);
429}
430
431void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
432 ctx.AddU32("{}=uint(gl_InvocationID);", inst);
433}
434
435void EmitSampleId(EmitContext& ctx, IR::Inst& inst) {
436 ctx.AddU32("{}=uint(gl_SampleID);", inst);
437}
438
439void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) {
440 ctx.AddU1("{}=gl_HelperInvocation;", inst);
441}
442
443void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
444 ctx.uses_y_direction = true;
445 ctx.AddF32("{}=gl_FrontMaterial.ambient.a;", inst);
446}
447
448void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) {
449 ctx.AddU32("{}=lmem[{}];", inst, word_offset);
450}
451
452void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value) {
453 ctx.Add("lmem[{}]={};", word_offset, value);
454}
455
456} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
new file mode 100644
index 000000000..53f8896be
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_control_flow.cpp
@@ -0,0 +1,21 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/exception.h"
10
11namespace Shader::Backend::GLSL {
12
13void EmitJoin(EmitContext&) {
14 throw NotImplementedException("Join shouldn't be emitted");
15}
16
17void EmitDemoteToHelperInvocation(EmitContext& ctx) {
18 ctx.Add("discard;");
19}
20
21} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp
new file mode 100644
index 000000000..eeae6562c
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_convert.cpp
@@ -0,0 +1,230 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12void EmitConvertS16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
13 [[maybe_unused]] std::string_view value) {
14 NotImplemented();
15}
16
17void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
18 ctx.AddU32("{}=(int({})&0xffff)|(bitfieldExtract(int({}),31,1)<<15);", inst, value, value);
19}
20
21void EmitConvertS16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
22 [[maybe_unused]] std::string_view value) {
23 NotImplemented();
24}
25
26void EmitConvertS32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
27 [[maybe_unused]] std::string_view value) {
28 NotImplemented();
29}
30
31void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
32 ctx.AddU32("{}=int({});", inst, value);
33}
34
35void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
36 ctx.AddU32("{}=int({});", inst, value);
37}
38
39void EmitConvertS64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
40 [[maybe_unused]] std::string_view value) {
41 NotImplemented();
42}
43
44void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
45 ctx.AddU64("{}=int64_t({});", inst, value);
46}
47
48void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
49 ctx.AddU64("{}=int64_t({});", inst, value);
50}
51
52void EmitConvertU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
53 [[maybe_unused]] std::string_view value) {
54 NotImplemented();
55}
56
57void EmitConvertU16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
58 [[maybe_unused]] std::string_view value) {
59 NotImplemented();
60}
61
62void EmitConvertU16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
63 [[maybe_unused]] std::string_view value) {
64 NotImplemented();
65}
66
67void EmitConvertU32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
68 [[maybe_unused]] std::string_view value) {
69 NotImplemented();
70}
71
72void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
73 ctx.AddU32("{}=uint({});", inst, value);
74}
75
76void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
77 ctx.AddU32("{}=uint({});", inst, value);
78}
79
80void EmitConvertU64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
81 [[maybe_unused]] std::string_view value) {
82 NotImplemented();
83}
84
85void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
86 ctx.AddU64("{}=uint64_t({});", inst, value);
87}
88
89void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
90 ctx.AddU64("{}=uint64_t({});", inst, value);
91}
92
93void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
94 ctx.AddU64("{}=uint64_t({});", inst, value);
95}
96
97void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
98 ctx.AddU32("{}=uint({});", inst, value);
99}
100
101void EmitConvertF16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
102 [[maybe_unused]] std::string_view value) {
103 NotImplemented();
104}
105
106void EmitConvertF32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
107 [[maybe_unused]] std::string_view value) {
108 NotImplemented();
109}
110
111void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
112 ctx.AddF32("{}=float({});", inst, value);
113}
114
115void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
116 ctx.AddF64("{}=double({});", inst, value);
117}
118
119void EmitConvertF16S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
120 [[maybe_unused]] std::string_view value) {
121 NotImplemented();
122}
123
124void EmitConvertF16S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
125 [[maybe_unused]] std::string_view value) {
126 NotImplemented();
127}
128
129void EmitConvertF16S32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
130 [[maybe_unused]] std::string_view value) {
131 NotImplemented();
132}
133
134void EmitConvertF16S64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
135 [[maybe_unused]] std::string_view value) {
136 NotImplemented();
137}
138
139void EmitConvertF16U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
140 [[maybe_unused]] std::string_view value) {
141 NotImplemented();
142}
143
144void EmitConvertF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
145 [[maybe_unused]] std::string_view value) {
146 NotImplemented();
147}
148
149void EmitConvertF16U32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
150 [[maybe_unused]] std::string_view value) {
151 NotImplemented();
152}
153
154void EmitConvertF16U64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
155 [[maybe_unused]] std::string_view value) {
156 NotImplemented();
157}
158
159void EmitConvertF32S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
160 [[maybe_unused]] std::string_view value) {
161 NotImplemented();
162}
163
164void EmitConvertF32S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
165 [[maybe_unused]] std::string_view value) {
166 NotImplemented();
167}
168
169void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
170 ctx.AddF32("{}=float(int({}));", inst, value);
171}
172
173void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
174 ctx.AddF32("{}=float(int64_t({}));", inst, value);
175}
176
177void EmitConvertF32U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
178 [[maybe_unused]] std::string_view value) {
179 NotImplemented();
180}
181
182void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
183 ctx.AddF32("{}=float({}&0xffff);", inst, value);
184}
185
186void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
187 ctx.AddF32("{}=float({});", inst, value);
188}
189
190void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
191 ctx.AddF32("{}=float({});", inst, value);
192}
193
194void EmitConvertF64S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
195 [[maybe_unused]] std::string_view value) {
196 NotImplemented();
197}
198
199void EmitConvertF64S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
200 [[maybe_unused]] std::string_view value) {
201 NotImplemented();
202}
203
204void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
205 ctx.AddF64("{}=double(int({}));", inst, value);
206}
207
208void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
209 ctx.AddF64("{}=double(int64_t({}));", inst, value);
210}
211
212void EmitConvertF64U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
213 [[maybe_unused]] std::string_view value) {
214 NotImplemented();
215}
216
217void EmitConvertF64U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
218 [[maybe_unused]] std::string_view value) {
219 NotImplemented();
220}
221
222void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
223 ctx.AddF64("{}=double({});", inst, value);
224}
225
226void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
227 ctx.AddF64("{}=double({});", inst, value);
228}
229
230} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
new file mode 100644
index 000000000..d423bfb1b
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_floating_point.cpp
@@ -0,0 +1,456 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h"
11
12namespace Shader::Backend::GLSL {
13namespace {
14void Compare(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs,
15 std::string_view op, bool ordered) {
16 const auto nan_op{ordered ? "&&!" : "||"};
17 ctx.AddU1("{}={}{}{}"
18 "{}isnan({}){}isnan({});",
19 inst, lhs, op, rhs, nan_op, lhs, nan_op, rhs);
20}
21
22bool IsPrecise(const IR::Inst& inst) {
23 return inst.Flags<IR::FpControl>().no_contraction;
24}
25} // Anonymous namespace
26
27void EmitFPAbs16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
28 [[maybe_unused]] std::string_view value) {
29 NotImplemented();
30}
31
32void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
33 ctx.AddF32("{}=abs({});", inst, value);
34}
35
36void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
37 ctx.AddF64("{}=abs({});", inst, value);
38}
39
40void EmitFPAdd16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
41 [[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
42 NotImplemented();
43}
44
45void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
46 if (IsPrecise(inst)) {
47 ctx.AddPrecF32("{}={}+{};", inst, a, b);
48 } else {
49 ctx.AddF32("{}={}+{};", inst, a, b);
50 }
51}
52
53void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
54 if (IsPrecise(inst)) {
55 ctx.AddPrecF64("{}={}+{};", inst, a, b);
56 } else {
57 ctx.AddF64("{}={}+{};", inst, a, b);
58 }
59}
60
61void EmitFPFma16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
62 [[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b,
63 [[maybe_unused]] std::string_view c) {
64 NotImplemented();
65}
66
67void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
68 std::string_view c) {
69 if (IsPrecise(inst)) {
70 ctx.AddPrecF32("{}=fma({},{},{});", inst, a, b, c);
71 } else {
72 ctx.AddF32("{}=fma({},{},{});", inst, a, b, c);
73 }
74}
75
76void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
77 std::string_view c) {
78 if (IsPrecise(inst)) {
79 ctx.AddPrecF64("{}=fma({},{},{});", inst, a, b, c);
80 } else {
81 ctx.AddF64("{}=fma({},{},{});", inst, a, b, c);
82 }
83}
84
85void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
86 ctx.AddF32("{}=max({},{});", inst, a, b);
87}
88
89void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
90 ctx.AddF64("{}=max({},{});", inst, a, b);
91}
92
93void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
94 ctx.AddF32("{}=min({},{});", inst, a, b);
95}
96
97void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
98 ctx.AddF64("{}=min({},{});", inst, a, b);
99}
100
101void EmitFPMul16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
102 [[maybe_unused]] std::string_view a, [[maybe_unused]] std::string_view b) {
103 NotImplemented();
104}
105
106void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
107 if (IsPrecise(inst)) {
108 ctx.AddPrecF32("{}={}*{};", inst, a, b);
109 } else {
110 ctx.AddF32("{}={}*{};", inst, a, b);
111 }
112}
113
114void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
115 if (IsPrecise(inst)) {
116 ctx.AddPrecF64("{}={}*{};", inst, a, b);
117 } else {
118 ctx.AddF64("{}={}*{};", inst, a, b);
119 }
120}
121
122void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
123 [[maybe_unused]] std::string_view value) {
124 NotImplemented();
125}
126
127void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
128 ctx.AddF32("{}=-({});", inst, value);
129}
130
131void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
132 ctx.AddF64("{}=-({});", inst, value);
133}
134
135void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
136 ctx.AddF32("{}=sin({});", inst, value);
137}
138
139void EmitFPCos(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
140 ctx.AddF32("{}=cos({});", inst, value);
141}
142
143void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
144 ctx.AddF32("{}=exp2({});", inst, value);
145}
146
147void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
148 ctx.AddF32("{}=log2({});", inst, value);
149}
150
151void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
152 ctx.AddF32("{}=(1.0f)/{};", inst, value);
153}
154
155void EmitFPRecip64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
156 ctx.AddF64("{}=1.0/{};", inst, value);
157}
158
159void EmitFPRecipSqrt32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
160 [[maybe_unused]] std::string_view value) {
161 ctx.AddF32("{}=inversesqrt({});", inst, value);
162}
163
164void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
165 [[maybe_unused]] std::string_view value) {
166 NotImplemented();
167}
168
169void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
170 ctx.AddF32("{}=sqrt({});", inst, value);
171}
172
173void EmitFPSaturate16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
174 [[maybe_unused]] std::string_view value) {
175 NotImplemented();
176}
177
178void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
179 ctx.AddF32("{}=min(max({},0.0),1.0);", inst, value);
180}
181
182void EmitFPSaturate64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
183 ctx.AddF64("{}=min(max({},0.0),1.0);", inst, value);
184}
185
186void EmitFPClamp16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
187 [[maybe_unused]] std::string_view value,
188 [[maybe_unused]] std::string_view min_value,
189 [[maybe_unused]] std::string_view max_value) {
190 NotImplemented();
191}
192
193void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value,
194 std::string_view min_value, std::string_view max_value) {
195 // GLSL's clamp does not produce desirable results
196 ctx.AddF32("{}=min(max({},float({})),float({}));", inst, value, min_value, max_value);
197}
198
199void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, std::string_view value,
200 std::string_view min_value, std::string_view max_value) {
201 // GLSL's clamp does not produce desirable results
202 ctx.AddF64("{}=min(max({},double({})),double({}));", inst, value, min_value, max_value);
203}
204
205void EmitFPRoundEven16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
206 [[maybe_unused]] std::string_view value) {
207 NotImplemented();
208}
209
210void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
211 ctx.AddF32("{}=roundEven({});", inst, value);
212}
213
214void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
215 ctx.AddF64("{}=roundEven({});", inst, value);
216}
217
218void EmitFPFloor16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
219 [[maybe_unused]] std::string_view value) {
220 NotImplemented();
221}
222
223void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
224 ctx.AddF32("{}=floor({});", inst, value);
225}
226
227void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
228 ctx.AddF64("{}=floor({});", inst, value);
229}
230
231void EmitFPCeil16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
232 [[maybe_unused]] std::string_view value) {
233 NotImplemented();
234}
235
236void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
237 ctx.AddF32("{}=ceil({});", inst, value);
238}
239
240void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
241 ctx.AddF64("{}=ceil({});", inst, value);
242}
243
244void EmitFPTrunc16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
245 [[maybe_unused]] std::string_view value) {
246 NotImplemented();
247}
248
249void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
250 ctx.AddF32("{}=trunc({});", inst, value);
251}
252
253void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
254 ctx.AddF64("{}=trunc({});", inst, value);
255}
256
257void EmitFPOrdEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
258 [[maybe_unused]] std::string_view rhs) {
259 NotImplemented();
260}
261
262void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
263 std::string_view rhs) {
264 Compare(ctx, inst, lhs, rhs, "==", true);
265}
266
267void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
268 std::string_view rhs) {
269 Compare(ctx, inst, lhs, rhs, "==", true);
270}
271
272void EmitFPUnordEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
273 [[maybe_unused]] std::string_view rhs) {
274 NotImplemented();
275}
276
277void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
278 std::string_view rhs) {
279 Compare(ctx, inst, lhs, rhs, "==", false);
280}
281
282void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
283 std::string_view rhs) {
284 Compare(ctx, inst, lhs, rhs, "==", false);
285}
286
287void EmitFPOrdNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
288 [[maybe_unused]] std::string_view rhs) {
289 NotImplemented();
290}
291
292void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
293 std::string_view rhs) {
294 Compare(ctx, inst, lhs, rhs, "!=", true);
295}
296
297void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
298 std::string_view rhs) {
299 Compare(ctx, inst, lhs, rhs, "!=", true);
300}
301
302void EmitFPUnordNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
303 [[maybe_unused]] std::string_view rhs) {
304 NotImplemented();
305}
306
307void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
308 std::string_view rhs) {
309 Compare(ctx, inst, lhs, rhs, "!=", false);
310}
311
312void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
313 std::string_view rhs) {
314 Compare(ctx, inst, lhs, rhs, "!=", false);
315}
316
317void EmitFPOrdLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
318 [[maybe_unused]] std::string_view rhs) {
319 NotImplemented();
320}
321
322void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
323 std::string_view rhs) {
324 Compare(ctx, inst, lhs, rhs, "<", true);
325}
326
327void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
328 std::string_view rhs) {
329 Compare(ctx, inst, lhs, rhs, "<", true);
330}
331
332void EmitFPUnordLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs,
333 [[maybe_unused]] std::string_view rhs) {
334 NotImplemented();
335}
336
337void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
338 std::string_view rhs) {
339 Compare(ctx, inst, lhs, rhs, "<", false);
340}
341
342void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
343 std::string_view rhs) {
344 Compare(ctx, inst, lhs, rhs, "<", false);
345}
346
347void EmitFPOrdGreaterThan16([[maybe_unused]] EmitContext& ctx,
348 [[maybe_unused]] std::string_view lhs,
349 [[maybe_unused]] std::string_view rhs) {
350 NotImplemented();
351}
352
353void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
354 std::string_view rhs) {
355 Compare(ctx, inst, lhs, rhs, ">", true);
356}
357
358void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
359 std::string_view rhs) {
360 Compare(ctx, inst, lhs, rhs, ">", true);
361}
362
363void EmitFPUnordGreaterThan16([[maybe_unused]] EmitContext& ctx,
364 [[maybe_unused]] std::string_view lhs,
365 [[maybe_unused]] std::string_view rhs) {
366 NotImplemented();
367}
368
369void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
370 std::string_view rhs) {
371 Compare(ctx, inst, lhs, rhs, ">", false);
372}
373
374void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
375 std::string_view rhs) {
376 Compare(ctx, inst, lhs, rhs, ">", false);
377}
378
379void EmitFPOrdLessThanEqual16([[maybe_unused]] EmitContext& ctx,
380 [[maybe_unused]] std::string_view lhs,
381 [[maybe_unused]] std::string_view rhs) {
382 NotImplemented();
383}
384
385void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
386 std::string_view rhs) {
387 Compare(ctx, inst, lhs, rhs, "<=", true);
388}
389
390void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
391 std::string_view rhs) {
392 Compare(ctx, inst, lhs, rhs, "<=", true);
393}
394
395void EmitFPUnordLessThanEqual16([[maybe_unused]] EmitContext& ctx,
396 [[maybe_unused]] std::string_view lhs,
397 [[maybe_unused]] std::string_view rhs) {
398 NotImplemented();
399}
400
401void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
402 std::string_view rhs) {
403 Compare(ctx, inst, lhs, rhs, "<=", false);
404}
405
406void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
407 std::string_view rhs) {
408 Compare(ctx, inst, lhs, rhs, "<=", false);
409}
410
411void EmitFPOrdGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
412 [[maybe_unused]] std::string_view lhs,
413 [[maybe_unused]] std::string_view rhs) {
414 NotImplemented();
415}
416
417void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
418 std::string_view rhs) {
419 Compare(ctx, inst, lhs, rhs, ">=", true);
420}
421
422void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
423 std::string_view rhs) {
424 Compare(ctx, inst, lhs, rhs, ">=", true);
425}
426
427void EmitFPUnordGreaterThanEqual16([[maybe_unused]] EmitContext& ctx,
428 [[maybe_unused]] std::string_view lhs,
429 [[maybe_unused]] std::string_view rhs) {
430 NotImplemented();
431}
432
433void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
434 std::string_view rhs) {
435 Compare(ctx, inst, lhs, rhs, ">=", false);
436}
437
438void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
439 std::string_view rhs) {
440 Compare(ctx, inst, lhs, rhs, ">=", false);
441}
442
443void EmitFPIsNan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
444 [[maybe_unused]] std::string_view value) {
445 NotImplemented();
446}
447
448void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
449 ctx.AddU1("{}=isnan({});", inst, value);
450}
451
452void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
453 ctx.AddU1("{}=isnan({});", inst, value);
454}
455
456} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
new file mode 100644
index 000000000..447eb8e0a
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -0,0 +1,799 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/modifiers.h"
10#include "shader_recompiler/frontend/ir/value.h"
11#include "shader_recompiler/profile.h"
12
13namespace Shader::Backend::GLSL {
14namespace {
15std::string Texture(EmitContext& ctx, const IR::TextureInstInfo& info, const IR::Value& index) {
16 const auto def{info.type == TextureType::Buffer ? ctx.texture_buffers.at(info.descriptor_index)
17 : ctx.textures.at(info.descriptor_index)};
18 const auto index_offset{def.count > 1 ? fmt::format("[{}]", ctx.var_alloc.Consume(index)) : ""};
19 return fmt::format("tex{}{}", def.binding, index_offset);
20}
21
22std::string Image(EmitContext& ctx, const IR::TextureInstInfo& info, const IR::Value& index) {
23 const auto def{info.type == TextureType::Buffer ? ctx.image_buffers.at(info.descriptor_index)
24 : ctx.images.at(info.descriptor_index)};
25 const auto index_offset{def.count > 1 ? fmt::format("[{}]", ctx.var_alloc.Consume(index)) : ""};
26 return fmt::format("img{}{}", def.binding, index_offset);
27}
28
29std::string CastToIntVec(std::string_view value, const IR::TextureInstInfo& info) {
30 switch (info.type) {
31 case TextureType::Color1D:
32 case TextureType::Buffer:
33 return fmt::format("int({})", value);
34 case TextureType::ColorArray1D:
35 case TextureType::Color2D:
36 case TextureType::ColorArray2D:
37 return fmt::format("ivec2({})", value);
38 case TextureType::Color3D:
39 case TextureType::ColorCube:
40 return fmt::format("ivec3({})", value);
41 case TextureType::ColorArrayCube:
42 return fmt::format("ivec4({})", value);
43 default:
44 throw NotImplementedException("Integer cast for TextureType {}", info.type.Value());
45 }
46}
47
48std::string CoordsCastToInt(std::string_view value, const IR::TextureInstInfo& info) {
49 switch (info.type) {
50 case TextureType::Color1D:
51 case TextureType::Buffer:
52 return fmt::format("int({})", value);
53 case TextureType::ColorArray1D:
54 case TextureType::Color2D:
55 return fmt::format("ivec2({})", value);
56 case TextureType::ColorArray2D:
57 case TextureType::Color3D:
58 case TextureType::ColorCube:
59 return fmt::format("ivec3({})", value);
60 case TextureType::ColorArrayCube:
61 return fmt::format("ivec4({})", value);
62 default:
63 throw NotImplementedException("TexelFetchCast type {}", info.type.Value());
64 }
65}
66
67bool NeedsShadowLodExt(TextureType type) {
68 switch (type) {
69 case TextureType::ColorArray2D:
70 case TextureType::ColorCube:
71 case TextureType::ColorArrayCube:
72 return true;
73 default:
74 return false;
75 }
76}
77
78std::string GetOffsetVec(EmitContext& ctx, const IR::Value& offset) {
79 if (offset.IsImmediate()) {
80 return fmt::format("int({})", offset.U32());
81 }
82 IR::Inst* const inst{offset.InstRecursive()};
83 if (inst->AreAllArgsImmediates()) {
84 switch (inst->GetOpcode()) {
85 case IR::Opcode::CompositeConstructU32x2:
86 return fmt::format("ivec2({},{})", inst->Arg(0).U32(), inst->Arg(1).U32());
87 case IR::Opcode::CompositeConstructU32x3:
88 return fmt::format("ivec3({},{},{})", inst->Arg(0).U32(), inst->Arg(1).U32(),
89 inst->Arg(2).U32());
90 case IR::Opcode::CompositeConstructU32x4:
91 return fmt::format("ivec4({},{},{},{})", inst->Arg(0).U32(), inst->Arg(1).U32(),
92 inst->Arg(2).U32(), inst->Arg(3).U32());
93 default:
94 break;
95 }
96 }
97 const bool has_var_aoffi{ctx.profile.support_gl_variable_aoffi};
98 if (!has_var_aoffi) {
99 LOG_WARNING(Shader_GLSL, "Device does not support variable texture offsets, STUBBING");
100 }
101 const auto offset_str{has_var_aoffi ? ctx.var_alloc.Consume(offset) : "0"};
102 switch (offset.Type()) {
103 case IR::Type::U32:
104 return fmt::format("int({})", offset_str);
105 case IR::Type::U32x2:
106 return fmt::format("ivec2({})", offset_str);
107 case IR::Type::U32x3:
108 return fmt::format("ivec3({})", offset_str);
109 case IR::Type::U32x4:
110 return fmt::format("ivec4({})", offset_str);
111 default:
112 throw NotImplementedException("Offset type {}", offset.Type());
113 }
114}
115
116std::string PtpOffsets(const IR::Value& offset, const IR::Value& offset2) {
117 const std::array values{offset.InstRecursive(), offset2.InstRecursive()};
118 if (!values[0]->AreAllArgsImmediates() || !values[1]->AreAllArgsImmediates()) {
119 LOG_WARNING(Shader_GLSL, "Not all arguments in PTP are immediate, STUBBING");
120 return "ivec2[](ivec2(0), ivec2(1), ivec2(2), ivec2(3))";
121 }
122 const IR::Opcode opcode{values[0]->GetOpcode()};
123 if (opcode != values[1]->GetOpcode() || opcode != IR::Opcode::CompositeConstructU32x4) {
124 throw LogicError("Invalid PTP arguments");
125 }
126 auto read{[&](unsigned int a, unsigned int b) { return values[a]->Arg(b).U32(); }};
127
128 return fmt::format("ivec2[](ivec2({},{}),ivec2({},{}),ivec2({},{}),ivec2({},{}))", read(0, 0),
129 read(0, 1), read(0, 2), read(0, 3), read(1, 0), read(1, 1), read(1, 2),
130 read(1, 3));
131}
132
133IR::Inst* PrepareSparse(IR::Inst& inst) {
134 const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
135 if (sparse_inst) {
136 sparse_inst->Invalidate();
137 }
138 return sparse_inst;
139}
140} // Anonymous namespace
141
142void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
143 std::string_view coords, std::string_view bias_lc,
144 const IR::Value& offset) {
145 const auto info{inst.Flags<IR::TextureInstInfo>()};
146 if (info.has_lod_clamp) {
147 throw NotImplementedException("EmitImageSampleImplicitLod Lod clamp samples");
148 }
149 const auto texture{Texture(ctx, info, index)};
150 const auto bias{info.has_bias ? fmt::format(",{}", bias_lc) : ""};
151 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
152 const auto sparse_inst{PrepareSparse(inst)};
153 const bool supports_sparse{ctx.profile.support_gl_sparse_textures};
154 if (sparse_inst && !supports_sparse) {
155 LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
156 ctx.AddU1("{}=true;", *sparse_inst);
157 }
158 if (!sparse_inst || !supports_sparse) {
159 if (!offset.IsEmpty()) {
160 const auto offset_str{GetOffsetVec(ctx, offset)};
161 if (ctx.stage == Stage::Fragment) {
162 ctx.Add("{}=textureOffset({},{},{}{});", texel, texture, coords, offset_str, bias);
163 } else {
164 ctx.Add("{}=textureLodOffset({},{},0.0,{});", texel, texture, coords, offset_str);
165 }
166 } else {
167 if (ctx.stage == Stage::Fragment) {
168 ctx.Add("{}=texture({},{}{});", texel, texture, coords, bias);
169 } else {
170 ctx.Add("{}=textureLod({},{},0.0);", texel, texture, coords);
171 }
172 }
173 return;
174 }
175 if (!offset.IsEmpty()) {
176 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureOffsetARB({},{},{},{}{}));",
177 *sparse_inst, texture, coords, GetOffsetVec(ctx, offset), texel, bias);
178 } else {
179 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureARB({},{},{}{}));", *sparse_inst,
180 texture, coords, texel, bias);
181 }
182}
183
184void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
185 std::string_view coords, std::string_view lod_lc,
186 const IR::Value& offset) {
187 const auto info{inst.Flags<IR::TextureInstInfo>()};
188 if (info.has_bias) {
189 throw NotImplementedException("EmitImageSampleExplicitLod Bias texture samples");
190 }
191 if (info.has_lod_clamp) {
192 throw NotImplementedException("EmitImageSampleExplicitLod Lod clamp samples");
193 }
194 const auto texture{Texture(ctx, info, index)};
195 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
196 const auto sparse_inst{PrepareSparse(inst)};
197 const bool supports_sparse{ctx.profile.support_gl_sparse_textures};
198 if (sparse_inst && !supports_sparse) {
199 LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
200 ctx.AddU1("{}=true;", *sparse_inst);
201 }
202 if (!sparse_inst || !supports_sparse) {
203 if (!offset.IsEmpty()) {
204 ctx.Add("{}=textureLodOffset({},{},{},{});", texel, texture, coords, lod_lc,
205 GetOffsetVec(ctx, offset));
206 } else {
207 ctx.Add("{}=textureLod({},{},{});", texel, texture, coords, lod_lc);
208 }
209 return;
210 }
211 if (!offset.IsEmpty()) {
212 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));",
213 *sparse_inst, texture, CastToIntVec(coords, info), lod_lc,
214 GetOffsetVec(ctx, offset), texel);
215 } else {
216 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureLodARB({},{},{},{}));", *sparse_inst,
217 texture, coords, lod_lc, texel);
218 }
219}
220
221void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
222 std::string_view coords, std::string_view dref,
223 std::string_view bias_lc, const IR::Value& offset) {
224 const auto info{inst.Flags<IR::TextureInstInfo>()};
225 const auto sparse_inst{PrepareSparse(inst)};
226 if (sparse_inst) {
227 throw NotImplementedException("EmitImageSampleDrefImplicitLod Sparse texture samples");
228 }
229 if (info.has_bias) {
230 throw NotImplementedException("EmitImageSampleDrefImplicitLod Bias texture samples");
231 }
232 if (info.has_lod_clamp) {
233 throw NotImplementedException("EmitImageSampleDrefImplicitLod Lod clamp samples");
234 }
235 const auto texture{Texture(ctx, info, index)};
236 const auto bias{info.has_bias ? fmt::format(",{}", bias_lc) : ""};
237 const bool needs_shadow_ext{NeedsShadowLodExt(info.type)};
238 const auto cast{needs_shadow_ext ? "vec4" : "vec3"};
239 const bool use_grad{!ctx.profile.support_gl_texture_shadow_lod &&
240 ctx.stage != Stage::Fragment && needs_shadow_ext};
241 if (use_grad) {
242 LOG_WARNING(Shader_GLSL,
243 "Device lacks GL_EXT_texture_shadow_lod. Using textureGrad fallback");
244 if (info.type == TextureType::ColorArrayCube) {
245 LOG_WARNING(Shader_GLSL, "textureGrad does not support ColorArrayCube. Stubbing");
246 ctx.AddF32("{}=0.0f;", inst);
247 return;
248 }
249 const auto d_cast{info.type == TextureType::ColorArray2D ? "vec2" : "vec3"};
250 ctx.AddF32("{}=textureGrad({},{}({},{}),{}(0),{}(0));", inst, texture, cast, coords, dref,
251 d_cast, d_cast);
252 return;
253 }
254 if (!offset.IsEmpty()) {
255 const auto offset_str{GetOffsetVec(ctx, offset)};
256 if (ctx.stage == Stage::Fragment) {
257 ctx.AddF32("{}=textureOffset({},{}({},{}),{}{});", inst, texture, cast, coords, dref,
258 offset_str, bias);
259 } else {
260 ctx.AddF32("{}=textureLodOffset({},{}({},{}),0.0,{});", inst, texture, cast, coords,
261 dref, offset_str);
262 }
263 } else {
264 if (ctx.stage == Stage::Fragment) {
265 if (info.type == TextureType::ColorArrayCube) {
266 ctx.AddF32("{}=texture({},vec4({}),{});", inst, texture, coords, dref);
267 } else {
268 ctx.AddF32("{}=texture({},{}({},{}){});", inst, texture, cast, coords, dref, bias);
269 }
270 } else {
271 ctx.AddF32("{}=textureLod({},{}({},{}),0.0);", inst, texture, cast, coords, dref);
272 }
273 }
274}
275
276void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
277 std::string_view coords, std::string_view dref,
278 std::string_view lod_lc, const IR::Value& offset) {
279 const auto info{inst.Flags<IR::TextureInstInfo>()};
280 const auto sparse_inst{PrepareSparse(inst)};
281 if (sparse_inst) {
282 throw NotImplementedException("EmitImageSampleDrefExplicitLod Sparse texture samples");
283 }
284 if (info.has_bias) {
285 throw NotImplementedException("EmitImageSampleDrefExplicitLod Bias texture samples");
286 }
287 if (info.has_lod_clamp) {
288 throw NotImplementedException("EmitImageSampleDrefExplicitLod Lod clamp samples");
289 }
290 const auto texture{Texture(ctx, info, index)};
291 const bool needs_shadow_ext{NeedsShadowLodExt(info.type)};
292 const bool use_grad{!ctx.profile.support_gl_texture_shadow_lod && needs_shadow_ext};
293 const auto cast{needs_shadow_ext ? "vec4" : "vec3"};
294 if (use_grad) {
295 LOG_WARNING(Shader_GLSL,
296 "Device lacks GL_EXT_texture_shadow_lod. Using textureGrad fallback");
297 if (info.type == TextureType::ColorArrayCube) {
298 LOG_WARNING(Shader_GLSL, "textureGrad does not support ColorArrayCube. Stubbing");
299 ctx.AddF32("{}=0.0f;", inst);
300 return;
301 }
302 const auto d_cast{info.type == TextureType::ColorArray2D ? "vec2" : "vec3"};
303 ctx.AddF32("{}=textureGrad({},{}({},{}),{}(0),{}(0));", inst, texture, cast, coords, dref,
304 d_cast, d_cast);
305 return;
306 }
307 if (!offset.IsEmpty()) {
308 const auto offset_str{GetOffsetVec(ctx, offset)};
309 if (info.type == TextureType::ColorArrayCube) {
310 ctx.AddF32("{}=textureLodOffset({},{},{},{},{});", inst, texture, coords, dref, lod_lc,
311 offset_str);
312 } else {
313 ctx.AddF32("{}=textureLodOffset({},{}({},{}),{},{});", inst, texture, cast, coords,
314 dref, lod_lc, offset_str);
315 }
316 } else {
317 if (info.type == TextureType::ColorArrayCube) {
318 ctx.AddF32("{}=textureLod({},{},{},{});", inst, texture, coords, dref, lod_lc);
319 } else {
320 ctx.AddF32("{}=textureLod({},{}({},{}),{});", inst, texture, cast, coords, dref,
321 lod_lc);
322 }
323 }
324}
325
326void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
327 std::string_view coords, const IR::Value& offset, const IR::Value& offset2) {
328 const auto info{inst.Flags<IR::TextureInstInfo>()};
329 const auto texture{Texture(ctx, info, index)};
330 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
331 const auto sparse_inst{PrepareSparse(inst)};
332 const bool supports_sparse{ctx.profile.support_gl_sparse_textures};
333 if (sparse_inst && !supports_sparse) {
334 LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
335 ctx.AddU1("{}=true;", *sparse_inst);
336 }
337 if (!sparse_inst || !supports_sparse) {
338 if (offset.IsEmpty()) {
339 ctx.Add("{}=textureGather({},{},int({}));", texel, texture, coords,
340 info.gather_component);
341 return;
342 }
343 if (offset2.IsEmpty()) {
344 ctx.Add("{}=textureGatherOffset({},{},{},int({}));", texel, texture, coords,
345 GetOffsetVec(ctx, offset), info.gather_component);
346 return;
347 }
348 // PTP
349 const auto offsets{PtpOffsets(offset, offset2)};
350 ctx.Add("{}=textureGatherOffsets({},{},{},int({}));", texel, texture, coords, offsets,
351 info.gather_component);
352 return;
353 }
354 if (offset.IsEmpty()) {
355 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherARB({},{},{},int({})));",
356 *sparse_inst, texture, coords, texel, info.gather_component);
357 return;
358 }
359 if (offset2.IsEmpty()) {
360 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},{},int({})));",
361 *sparse_inst, texture, CastToIntVec(coords, info), GetOffsetVec(ctx, offset),
362 texel, info.gather_component);
363 return;
364 }
365 // PTP
366 const auto offsets{PtpOffsets(offset, offset2)};
367 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},{},int({})));",
368 *sparse_inst, texture, CastToIntVec(coords, info), offsets, texel,
369 info.gather_component);
370}
371
372void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
373 std::string_view coords, const IR::Value& offset, const IR::Value& offset2,
374 std::string_view dref) {
375 const auto info{inst.Flags<IR::TextureInstInfo>()};
376 const auto texture{Texture(ctx, info, index)};
377 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
378 const auto sparse_inst{PrepareSparse(inst)};
379 const bool supports_sparse{ctx.profile.support_gl_sparse_textures};
380 if (sparse_inst && !supports_sparse) {
381 LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
382 ctx.AddU1("{}=true;", *sparse_inst);
383 }
384 if (!sparse_inst || !supports_sparse) {
385 if (offset.IsEmpty()) {
386 ctx.Add("{}=textureGather({},{},{});", texel, texture, coords, dref);
387 return;
388 }
389 if (offset2.IsEmpty()) {
390 ctx.Add("{}=textureGatherOffset({},{},{},{});", texel, texture, coords, dref,
391 GetOffsetVec(ctx, offset));
392 return;
393 }
394 // PTP
395 const auto offsets{PtpOffsets(offset, offset2)};
396 ctx.Add("{}=textureGatherOffsets({},{},{},{});", texel, texture, coords, dref, offsets);
397 return;
398 }
399 if (offset.IsEmpty()) {
400 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherARB({},{},{},{}));", *sparse_inst,
401 texture, coords, dref, texel);
402 return;
403 }
404 if (offset2.IsEmpty()) {
405 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},,{},{}));",
406 *sparse_inst, texture, CastToIntVec(coords, info), dref,
407 GetOffsetVec(ctx, offset), texel);
408 return;
409 }
410 // PTP
411 const auto offsets{PtpOffsets(offset, offset2)};
412 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},,{},{}));",
413 *sparse_inst, texture, CastToIntVec(coords, info), dref, offsets, texel);
414}
415
416void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
417 std::string_view coords, std::string_view offset, std::string_view lod,
418 [[maybe_unused]] std::string_view ms) {
419 const auto info{inst.Flags<IR::TextureInstInfo>()};
420 if (info.has_bias) {
421 throw NotImplementedException("EmitImageFetch Bias texture samples");
422 }
423 if (info.has_lod_clamp) {
424 throw NotImplementedException("EmitImageFetch Lod clamp samples");
425 }
426 const auto texture{Texture(ctx, info, index)};
427 const auto sparse_inst{PrepareSparse(inst)};
428 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
429 const bool supports_sparse{ctx.profile.support_gl_sparse_textures};
430 if (sparse_inst && !supports_sparse) {
431 LOG_WARNING(Shader_GLSL, "Device does not support sparse texture queries. STUBBING");
432 ctx.AddU1("{}=true;", *sparse_inst);
433 }
434 if (!sparse_inst || !supports_sparse) {
435 if (!offset.empty()) {
436 ctx.Add("{}=texelFetchOffset({},{},int({}),{});", texel, texture,
437 CoordsCastToInt(coords, info), lod, CoordsCastToInt(offset, info));
438 } else {
439 if (info.type == TextureType::Buffer) {
440 ctx.Add("{}=texelFetch({},int({}));", texel, texture, coords);
441 } else {
442 ctx.Add("{}=texelFetch({},{},int({}));", texel, texture,
443 CoordsCastToInt(coords, info), lod);
444 }
445 }
446 return;
447 }
448 if (!offset.empty()) {
449 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchOffsetARB({},{},int({}),{},{}));",
450 *sparse_inst, texture, CastToIntVec(coords, info), lod,
451 CastToIntVec(offset, info), texel);
452 } else {
453 ctx.AddU1("{}=sparseTexelsResidentARB(sparseTexelFetchARB({},{},int({}),{}));",
454 *sparse_inst, texture, CastToIntVec(coords, info), lod, texel);
455 }
456}
457
458void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
459 std::string_view lod) {
460 const auto info{inst.Flags<IR::TextureInstInfo>()};
461 const auto texture{Texture(ctx, info, index)};
462 switch (info.type) {
463 case TextureType::Color1D:
464 return ctx.AddU32x4(
465 "{}=uvec4(uint(textureSize({},int({}))),0u,0u,uint(textureQueryLevels({})));", inst,
466 texture, lod, texture);
467 case TextureType::ColorArray1D:
468 case TextureType::Color2D:
469 case TextureType::ColorCube:
470 return ctx.AddU32x4(
471 "{}=uvec4(uvec2(textureSize({},int({}))),0u,uint(textureQueryLevels({})));", inst,
472 texture, lod, texture);
473 case TextureType::ColorArray2D:
474 case TextureType::Color3D:
475 case TextureType::ColorArrayCube:
476 return ctx.AddU32x4(
477 "{}=uvec4(uvec3(textureSize({},int({}))),uint(textureQueryLevels({})));", inst, texture,
478 lod, texture);
479 case TextureType::Buffer:
480 throw NotImplementedException("EmitImageQueryDimensions Texture buffers");
481 }
482 throw LogicError("Unspecified image type {}", info.type.Value());
483}
484
485void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
486 std::string_view coords) {
487 const auto info{inst.Flags<IR::TextureInstInfo>()};
488 const auto texture{Texture(ctx, info, index)};
489 return ctx.AddF32x4("{}=vec4(textureQueryLod({},{}),0.0,0.0);", inst, texture, coords);
490}
491
492void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
493 std::string_view coords, const IR::Value& derivatives,
494 const IR::Value& offset, [[maybe_unused]] const IR::Value& lod_clamp) {
495 const auto info{inst.Flags<IR::TextureInstInfo>()};
496 if (info.has_lod_clamp) {
497 throw NotImplementedException("EmitImageGradient Lod clamp samples");
498 }
499 const auto sparse_inst{PrepareSparse(inst)};
500 if (sparse_inst) {
501 throw NotImplementedException("EmitImageGradient Sparse");
502 }
503 if (!offset.IsEmpty()) {
504 throw NotImplementedException("EmitImageGradient offset");
505 }
506 const auto texture{Texture(ctx, info, index)};
507 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
508 const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp};
509 const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)};
510 if (multi_component) {
511 ctx.Add("{}=textureGrad({},{},vec2({}.xz),vec2({}.yz));", texel, texture, coords,
512 derivatives_vec, derivatives_vec);
513 } else {
514 ctx.Add("{}=textureGrad({},{},float({}.x),float({}.y));", texel, texture, coords,
515 derivatives_vec, derivatives_vec);
516 }
517}
518
519void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
520 std::string_view coords) {
521 const auto info{inst.Flags<IR::TextureInstInfo>()};
522 const auto sparse_inst{PrepareSparse(inst)};
523 if (sparse_inst) {
524 throw NotImplementedException("EmitImageRead Sparse");
525 }
526 const auto image{Image(ctx, info, index)};
527 ctx.AddU32x4("{}=uvec4(imageLoad({},{}));", inst, image, CoordsCastToInt(coords, info));
528}
529
530void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
531 std::string_view coords, std::string_view color) {
532 const auto info{inst.Flags<IR::TextureInstInfo>()};
533 const auto image{Image(ctx, info, index)};
534 ctx.Add("imageStore({},{},{});", image, CoordsCastToInt(coords, info), color);
535}
536
537void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
538 std::string_view coords, std::string_view value) {
539 const auto info{inst.Flags<IR::TextureInstInfo>()};
540 const auto image{Image(ctx, info, index)};
541 ctx.AddU32("{}=imageAtomicAdd({},{},{});", inst, image, CoordsCastToInt(coords, info), value);
542}
543
544void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
545 std::string_view coords, std::string_view value) {
546 const auto info{inst.Flags<IR::TextureInstInfo>()};
547 const auto image{Image(ctx, info, index)};
548 ctx.AddU32("{}=imageAtomicMin({},{},int({}));", inst, image, CoordsCastToInt(coords, info),
549 value);
550}
551
552void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
553 std::string_view coords, std::string_view value) {
554 const auto info{inst.Flags<IR::TextureInstInfo>()};
555 const auto image{Image(ctx, info, index)};
556 ctx.AddU32("{}=imageAtomicMin({},{},uint({}));", inst, image, CoordsCastToInt(coords, info),
557 value);
558}
559
560void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
561 std::string_view coords, std::string_view value) {
562 const auto info{inst.Flags<IR::TextureInstInfo>()};
563 const auto image{Image(ctx, info, index)};
564 ctx.AddU32("{}=imageAtomicMax({},{},int({}));", inst, image, CoordsCastToInt(coords, info),
565 value);
566}
567
568void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
569 std::string_view coords, std::string_view value) {
570 const auto info{inst.Flags<IR::TextureInstInfo>()};
571 const auto image{Image(ctx, info, index)};
572 ctx.AddU32("{}=imageAtomicMax({},{},uint({}));", inst, image, CoordsCastToInt(coords, info),
573 value);
574}
575
576void EmitImageAtomicInc32(EmitContext&, IR::Inst&, const IR::Value&, std::string_view,
577 std::string_view) {
578 NotImplemented();
579}
580
581void EmitImageAtomicDec32(EmitContext&, IR::Inst&, const IR::Value&, std::string_view,
582 std::string_view) {
583 NotImplemented();
584}
585
586void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
587 std::string_view coords, std::string_view value) {
588 const auto info{inst.Flags<IR::TextureInstInfo>()};
589 const auto image{Image(ctx, info, index)};
590 ctx.AddU32("{}=imageAtomicAnd({},{},{});", inst, image, CoordsCastToInt(coords, info), value);
591}
592
593void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
594 std::string_view coords, std::string_view value) {
595 const auto info{inst.Flags<IR::TextureInstInfo>()};
596 const auto image{Image(ctx, info, index)};
597 ctx.AddU32("{}=imageAtomicOr({},{},{});", inst, image, CoordsCastToInt(coords, info), value);
598}
599
600void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
601 std::string_view coords, std::string_view value) {
602 const auto info{inst.Flags<IR::TextureInstInfo>()};
603 const auto image{Image(ctx, info, index)};
604 ctx.AddU32("{}=imageAtomicXor({},{},{});", inst, image, CoordsCastToInt(coords, info), value);
605}
606
607void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
608 std::string_view coords, std::string_view value) {
609 const auto info{inst.Flags<IR::TextureInstInfo>()};
610 const auto image{Image(ctx, info, index)};
611 ctx.AddU32("{}=imageAtomicExchange({},{},{});", inst, image, CoordsCastToInt(coords, info),
612 value);
613}
614
615void EmitBindlessImageSampleImplicitLod(EmitContext&) {
616 NotImplemented();
617}
618
619void EmitBindlessImageSampleExplicitLod(EmitContext&) {
620 NotImplemented();
621}
622
623void EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
624 NotImplemented();
625}
626
627void EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
628 NotImplemented();
629}
630
631void EmitBindlessImageGather(EmitContext&) {
632 NotImplemented();
633}
634
635void EmitBindlessImageGatherDref(EmitContext&) {
636 NotImplemented();
637}
638
639void EmitBindlessImageFetch(EmitContext&) {
640 NotImplemented();
641}
642
643void EmitBindlessImageQueryDimensions(EmitContext&) {
644 NotImplemented();
645}
646
647void EmitBindlessImageQueryLod(EmitContext&) {
648 NotImplemented();
649}
650
651void EmitBindlessImageGradient(EmitContext&) {
652 NotImplemented();
653}
654
655void EmitBindlessImageRead(EmitContext&) {
656 NotImplemented();
657}
658
659void EmitBindlessImageWrite(EmitContext&) {
660 NotImplemented();
661}
662
663void EmitBoundImageSampleImplicitLod(EmitContext&) {
664 NotImplemented();
665}
666
667void EmitBoundImageSampleExplicitLod(EmitContext&) {
668 NotImplemented();
669}
670
671void EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
672 NotImplemented();
673}
674
675void EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
676 NotImplemented();
677}
678
679void EmitBoundImageGather(EmitContext&) {
680 NotImplemented();
681}
682
683void EmitBoundImageGatherDref(EmitContext&) {
684 NotImplemented();
685}
686
687void EmitBoundImageFetch(EmitContext&) {
688 NotImplemented();
689}
690
691void EmitBoundImageQueryDimensions(EmitContext&) {
692 NotImplemented();
693}
694
695void EmitBoundImageQueryLod(EmitContext&) {
696 NotImplemented();
697}
698
699void EmitBoundImageGradient(EmitContext&) {
700 NotImplemented();
701}
702
703void EmitBoundImageRead(EmitContext&) {
704 NotImplemented();
705}
706
707void EmitBoundImageWrite(EmitContext&) {
708 NotImplemented();
709}
710
711void EmitBindlessImageAtomicIAdd32(EmitContext&) {
712 NotImplemented();
713}
714
715void EmitBindlessImageAtomicSMin32(EmitContext&) {
716 NotImplemented();
717}
718
719void EmitBindlessImageAtomicUMin32(EmitContext&) {
720 NotImplemented();
721}
722
723void EmitBindlessImageAtomicSMax32(EmitContext&) {
724 NotImplemented();
725}
726
727void EmitBindlessImageAtomicUMax32(EmitContext&) {
728 NotImplemented();
729}
730
731void EmitBindlessImageAtomicInc32(EmitContext&) {
732 NotImplemented();
733}
734
735void EmitBindlessImageAtomicDec32(EmitContext&) {
736 NotImplemented();
737}
738
739void EmitBindlessImageAtomicAnd32(EmitContext&) {
740 NotImplemented();
741}
742
743void EmitBindlessImageAtomicOr32(EmitContext&) {
744 NotImplemented();
745}
746
747void EmitBindlessImageAtomicXor32(EmitContext&) {
748 NotImplemented();
749}
750
751void EmitBindlessImageAtomicExchange32(EmitContext&) {
752 NotImplemented();
753}
754
755void EmitBoundImageAtomicIAdd32(EmitContext&) {
756 NotImplemented();
757}
758
759void EmitBoundImageAtomicSMin32(EmitContext&) {
760 NotImplemented();
761}
762
763void EmitBoundImageAtomicUMin32(EmitContext&) {
764 NotImplemented();
765}
766
767void EmitBoundImageAtomicSMax32(EmitContext&) {
768 NotImplemented();
769}
770
771void EmitBoundImageAtomicUMax32(EmitContext&) {
772 NotImplemented();
773}
774
775void EmitBoundImageAtomicInc32(EmitContext&) {
776 NotImplemented();
777}
778
779void EmitBoundImageAtomicDec32(EmitContext&) {
780 NotImplemented();
781}
782
783void EmitBoundImageAtomicAnd32(EmitContext&) {
784 NotImplemented();
785}
786
787void EmitBoundImageAtomicOr32(EmitContext&) {
788 NotImplemented();
789}
790
791void EmitBoundImageAtomicXor32(EmitContext&) {
792 NotImplemented();
793}
794
795void EmitBoundImageAtomicExchange32(EmitContext&) {
796 NotImplemented();
797}
798
799} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
new file mode 100644
index 000000000..5936d086f
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -0,0 +1,702 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string_view>
8
9#include "common/common_types.h"
10
11namespace Shader::IR {
12enum class Attribute : u64;
13enum class Patch : u64;
14class Inst;
15class Value;
16} // namespace Shader::IR
17
18namespace Shader::Backend::GLSL {
19class EmitContext;
20
21#define NotImplemented() throw NotImplementedException("GLSL instruction {}", __func__)
22
23// Microinstruction emitters
24void EmitPhi(EmitContext& ctx, IR::Inst& inst);
25void EmitVoid(EmitContext& ctx);
26void EmitIdentity(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
27void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
28void EmitReference(EmitContext& ctx, const IR::Value& value);
29void EmitPhiMove(EmitContext& ctx, const IR::Value& phi, const IR::Value& value);
30void EmitJoin(EmitContext& ctx);
31void EmitDemoteToHelperInvocation(EmitContext& ctx);
32void EmitBarrier(EmitContext& ctx);
33void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
34void EmitDeviceMemoryBarrier(EmitContext& ctx);
35void EmitPrologue(EmitContext& ctx);
36void EmitEpilogue(EmitContext& ctx);
37void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream);
38void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream);
39void EmitGetRegister(EmitContext& ctx);
40void EmitSetRegister(EmitContext& ctx);
41void EmitGetPred(EmitContext& ctx);
42void EmitSetPred(EmitContext& ctx);
43void EmitSetGotoVariable(EmitContext& ctx);
44void EmitGetGotoVariable(EmitContext& ctx);
45void EmitSetIndirectBranchVariable(EmitContext& ctx);
46void EmitGetIndirectBranchVariable(EmitContext& ctx);
47void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
48 const IR::Value& offset);
49void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
50 const IR::Value& offset);
51void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
52 const IR::Value& offset);
53void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
54 const IR::Value& offset);
55void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
56 const IR::Value& offset);
57void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
58 const IR::Value& offset);
59void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
60 const IR::Value& offset);
61void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
62 std::string_view vertex);
63void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value,
64 std::string_view vertex);
65void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
66 std::string_view vertex);
67void EmitSetAttributeIndexed(EmitContext& ctx, std::string_view offset, std::string_view value,
68 std::string_view vertex);
69void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch);
70void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value);
71void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value);
72void EmitSetSampleMask(EmitContext& ctx, std::string_view value);
73void EmitSetFragDepth(EmitContext& ctx, std::string_view value);
74void EmitGetZFlag(EmitContext& ctx);
75void EmitGetSFlag(EmitContext& ctx);
76void EmitGetCFlag(EmitContext& ctx);
77void EmitGetOFlag(EmitContext& ctx);
78void EmitSetZFlag(EmitContext& ctx);
79void EmitSetSFlag(EmitContext& ctx);
80void EmitSetCFlag(EmitContext& ctx);
81void EmitSetOFlag(EmitContext& ctx);
82void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst);
83void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst);
84void EmitInvocationId(EmitContext& ctx, IR::Inst& inst);
85void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
86void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
87void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
88void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset);
89void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value);
90void EmitUndefU1(EmitContext& ctx, IR::Inst& inst);
91void EmitUndefU8(EmitContext& ctx, IR::Inst& inst);
92void EmitUndefU16(EmitContext& ctx, IR::Inst& inst);
93void EmitUndefU32(EmitContext& ctx, IR::Inst& inst);
94void EmitUndefU64(EmitContext& ctx, IR::Inst& inst);
95void EmitLoadGlobalU8(EmitContext& ctx);
96void EmitLoadGlobalS8(EmitContext& ctx);
97void EmitLoadGlobalU16(EmitContext& ctx);
98void EmitLoadGlobalS16(EmitContext& ctx);
99void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, std::string_view address);
100void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, std::string_view address);
101void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, std::string_view address);
102void EmitWriteGlobalU8(EmitContext& ctx);
103void EmitWriteGlobalS8(EmitContext& ctx);
104void EmitWriteGlobalU16(EmitContext& ctx);
105void EmitWriteGlobalS16(EmitContext& ctx);
106void EmitWriteGlobal32(EmitContext& ctx, std::string_view address, std::string_view value);
107void EmitWriteGlobal64(EmitContext& ctx, std::string_view address, std::string_view value);
108void EmitWriteGlobal128(EmitContext& ctx, std::string_view address, std::string_view value);
109void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
110 const IR::Value& offset);
111void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
112 const IR::Value& offset);
113void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
114 const IR::Value& offset);
115void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
116 const IR::Value& offset);
117void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
118 const IR::Value& offset);
119void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
120 const IR::Value& offset);
121void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
122 const IR::Value& offset);
123void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
124 std::string_view value);
125void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
126 std::string_view value);
127void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
128 std::string_view value);
129void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
130 std::string_view value);
131void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
132 std::string_view value);
133void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
134 std::string_view value);
135void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
136 std::string_view value);
137void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
138void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
139void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
140void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
141void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
142void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
143void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, std::string_view offset);
144void EmitWriteSharedU8(EmitContext& ctx, std::string_view offset, std::string_view value);
145void EmitWriteSharedU16(EmitContext& ctx, std::string_view offset, std::string_view value);
146void EmitWriteSharedU32(EmitContext& ctx, std::string_view offset, std::string_view value);
147void EmitWriteSharedU64(EmitContext& ctx, std::string_view offset, std::string_view value);
148void EmitWriteSharedU128(EmitContext& ctx, std::string_view offset, std::string_view value);
149void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
150 std::string_view e2);
151void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
152 std::string_view e2, std::string_view e3);
153void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
154 std::string_view e2, std::string_view e3, std::string_view e4);
155void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
156 u32 index);
157void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
158 u32 index);
159void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
160 u32 index);
161void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
162 std::string_view object, u32 index);
163void EmitCompositeInsertU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
164 std::string_view object, u32 index);
165void EmitCompositeInsertU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
166 std::string_view object, u32 index);
167void EmitCompositeConstructF16x2(EmitContext& ctx, std::string_view e1, std::string_view e2);
168void EmitCompositeConstructF16x3(EmitContext& ctx, std::string_view e1, std::string_view e2,
169 std::string_view e3);
170void EmitCompositeConstructF16x4(EmitContext& ctx, std::string_view e1, std::string_view e2,
171 std::string_view e3, std::string_view e4);
172void EmitCompositeExtractF16x2(EmitContext& ctx, std::string_view composite, u32 index);
173void EmitCompositeExtractF16x3(EmitContext& ctx, std::string_view composite, u32 index);
174void EmitCompositeExtractF16x4(EmitContext& ctx, std::string_view composite, u32 index);
175void EmitCompositeInsertF16x2(EmitContext& ctx, std::string_view composite, std::string_view object,
176 u32 index);
177void EmitCompositeInsertF16x3(EmitContext& ctx, std::string_view composite, std::string_view object,
178 u32 index);
179void EmitCompositeInsertF16x4(EmitContext& ctx, std::string_view composite, std::string_view object,
180 u32 index);
181void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
182 std::string_view e2);
183void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
184 std::string_view e2, std::string_view e3);
185void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1,
186 std::string_view e2, std::string_view e3, std::string_view e4);
187void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
188 u32 index);
189void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
190 u32 index);
191void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
192 u32 index);
193void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
194 std::string_view object, u32 index);
195void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
196 std::string_view object, u32 index);
197void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
198 std::string_view object, u32 index);
199void EmitCompositeConstructF64x2(EmitContext& ctx);
200void EmitCompositeConstructF64x3(EmitContext& ctx);
201void EmitCompositeConstructF64x4(EmitContext& ctx);
202void EmitCompositeExtractF64x2(EmitContext& ctx);
203void EmitCompositeExtractF64x3(EmitContext& ctx);
204void EmitCompositeExtractF64x4(EmitContext& ctx);
205void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object,
206 u32 index);
207void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object,
208 u32 index);
209void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object,
210 u32 index);
211void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
212 std::string_view true_value, std::string_view false_value);
213void EmitSelectU8(EmitContext& ctx, std::string_view cond, std::string_view true_value,
214 std::string_view false_value);
215void EmitSelectU16(EmitContext& ctx, std::string_view cond, std::string_view true_value,
216 std::string_view false_value);
217void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
218 std::string_view true_value, std::string_view false_value);
219void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
220 std::string_view true_value, std::string_view false_value);
221void EmitSelectF16(EmitContext& ctx, std::string_view cond, std::string_view true_value,
222 std::string_view false_value);
223void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
224 std::string_view true_value, std::string_view false_value);
225void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
226 std::string_view true_value, std::string_view false_value);
227void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst);
228void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
229void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
230void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst);
231void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
232void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
233void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
234void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
235void EmitPackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
236void EmitUnpackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
237void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
238void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
239void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
240void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
241void EmitGetZeroFromOp(EmitContext& ctx);
242void EmitGetSignFromOp(EmitContext& ctx);
243void EmitGetCarryFromOp(EmitContext& ctx);
244void EmitGetOverflowFromOp(EmitContext& ctx);
245void EmitGetSparseFromOp(EmitContext& ctx);
246void EmitGetInBoundsFromOp(EmitContext& ctx);
247void EmitFPAbs16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
248void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
249void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
250void EmitFPAdd16(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
251void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
252void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
253void EmitFPFma16(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
254 std::string_view c);
255void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
256 std::string_view c);
257void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
258 std::string_view c);
259void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
260void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
261void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
262void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
263void EmitFPMul16(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
264void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
265void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
266void EmitFPNeg16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
267void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
268void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
269void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value);
270void EmitFPCos(EmitContext& ctx, IR::Inst& inst, std::string_view value);
271void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, std::string_view value);
272void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, std::string_view value);
273void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
274void EmitFPRecip64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
275void EmitFPRecipSqrt32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
276void EmitFPRecipSqrt64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
277void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, std::string_view value);
278void EmitFPSaturate16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
279void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
280void EmitFPSaturate64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
281void EmitFPClamp16(EmitContext& ctx, IR::Inst& inst, std::string_view value,
282 std::string_view min_value, std::string_view max_value);
283void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value,
284 std::string_view min_value, std::string_view max_value);
285void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, std::string_view value,
286 std::string_view min_value, std::string_view max_value);
287void EmitFPRoundEven16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
288void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
289void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
290void EmitFPFloor16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
291void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
292void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
293void EmitFPCeil16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
294void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
295void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
296void EmitFPTrunc16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
297void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
298void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
299void EmitFPOrdEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
300void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
301void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
302void EmitFPUnordEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
303void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
304 std::string_view rhs);
305void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
306 std::string_view rhs);
307void EmitFPOrdNotEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
308void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
309 std::string_view rhs);
310void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
311 std::string_view rhs);
312void EmitFPUnordNotEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
313void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
314 std::string_view rhs);
315void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
316 std::string_view rhs);
317void EmitFPOrdLessThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
318void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
319 std::string_view rhs);
320void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
321 std::string_view rhs);
322void EmitFPUnordLessThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
323void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
324 std::string_view rhs);
325void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
326 std::string_view rhs);
327void EmitFPOrdGreaterThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
328void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
329 std::string_view rhs);
330void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
331 std::string_view rhs);
332void EmitFPUnordGreaterThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
333void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
334 std::string_view rhs);
335void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
336 std::string_view rhs);
337void EmitFPOrdLessThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
338void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
339 std::string_view rhs);
340void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
341 std::string_view rhs);
342void EmitFPUnordLessThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
343void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
344 std::string_view rhs);
345void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
346 std::string_view rhs);
347void EmitFPOrdGreaterThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
348void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
349 std::string_view rhs);
350void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
351 std::string_view rhs);
352void EmitFPUnordGreaterThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs);
353void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
354 std::string_view rhs);
355void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
356 std::string_view rhs);
357void EmitFPIsNan16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
358void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
359void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
360void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
361void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
362void EmitISub32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
363void EmitISub64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
364void EmitIMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
365void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
366void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
367void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
368void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
369 std::string_view shift);
370void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
371 std::string_view shift);
372void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
373 std::string_view shift);
374void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
375 std::string_view shift);
376void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
377 std::string_view shift);
378void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
379 std::string_view shift);
380void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
381void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
382void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
383void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, std::string_view base,
384 std::string_view insert, std::string_view offset, std::string_view count);
385void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
386 std::string_view offset, std::string_view count);
387void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
388 std::string_view offset, std::string_view count);
389void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
390void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
391void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
392void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
393void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
394void EmitSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
395void EmitUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
396void EmitSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
397void EmitUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
398void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
399 std::string_view max);
400void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
401 std::string_view max);
402void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
403void EmitULessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
404void EmitIEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
405void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
406 std::string_view rhs);
407void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
408 std::string_view rhs);
409void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
410void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
411void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs);
412void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
413 std::string_view rhs);
414void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
415 std::string_view rhs);
416void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
417 std::string_view value);
418void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
419 std::string_view value);
420void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
421 std::string_view value);
422void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
423 std::string_view value);
424void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
425 std::string_view value);
426void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
427 std::string_view value);
428void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
429 std::string_view value);
430void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
431 std::string_view value);
432void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
433 std::string_view value);
434void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
435 std::string_view value);
436void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
437 std::string_view value);
438void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
439 std::string_view value);
440void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
441 const IR::Value& offset, std::string_view value);
442void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
443 const IR::Value& offset, std::string_view value);
444void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
445 const IR::Value& offset, std::string_view value);
446void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
447 const IR::Value& offset, std::string_view value);
448void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
449 const IR::Value& offset, std::string_view value);
450void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
451 const IR::Value& offset, std::string_view value);
452void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
453 const IR::Value& offset, std::string_view value);
454void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
455 const IR::Value& offset, std::string_view value);
456void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
457 const IR::Value& offset, std::string_view value);
458void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
459 const IR::Value& offset, std::string_view value);
460void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
461 const IR::Value& offset, std::string_view value);
462void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
463 const IR::Value& offset, std::string_view value);
464void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
465 const IR::Value& offset, std::string_view value);
466void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
467 const IR::Value& offset, std::string_view value);
468void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
469 const IR::Value& offset, std::string_view value);
470void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
471 const IR::Value& offset, std::string_view value);
472void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
473 const IR::Value& offset, std::string_view value);
474void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
475 const IR::Value& offset, std::string_view value);
476void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
477 const IR::Value& offset, std::string_view value);
478void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
479 const IR::Value& offset, std::string_view value);
480void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
481 const IR::Value& offset, std::string_view value);
482void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
483 const IR::Value& offset, std::string_view value);
484void EmitStorageAtomicAddF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
485 const IR::Value& offset, std::string_view value);
486void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
487 const IR::Value& offset, std::string_view value);
488void EmitStorageAtomicMinF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
489 const IR::Value& offset, std::string_view value);
490void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
491 const IR::Value& offset, std::string_view value);
492void EmitStorageAtomicMaxF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
493 const IR::Value& offset, std::string_view value);
494void EmitGlobalAtomicIAdd32(EmitContext& ctx);
495void EmitGlobalAtomicSMin32(EmitContext& ctx);
496void EmitGlobalAtomicUMin32(EmitContext& ctx);
497void EmitGlobalAtomicSMax32(EmitContext& ctx);
498void EmitGlobalAtomicUMax32(EmitContext& ctx);
499void EmitGlobalAtomicInc32(EmitContext& ctx);
500void EmitGlobalAtomicDec32(EmitContext& ctx);
501void EmitGlobalAtomicAnd32(EmitContext& ctx);
502void EmitGlobalAtomicOr32(EmitContext& ctx);
503void EmitGlobalAtomicXor32(EmitContext& ctx);
504void EmitGlobalAtomicExchange32(EmitContext& ctx);
505void EmitGlobalAtomicIAdd64(EmitContext& ctx);
506void EmitGlobalAtomicSMin64(EmitContext& ctx);
507void EmitGlobalAtomicUMin64(EmitContext& ctx);
508void EmitGlobalAtomicSMax64(EmitContext& ctx);
509void EmitGlobalAtomicUMax64(EmitContext& ctx);
510void EmitGlobalAtomicInc64(EmitContext& ctx);
511void EmitGlobalAtomicDec64(EmitContext& ctx);
512void EmitGlobalAtomicAnd64(EmitContext& ctx);
513void EmitGlobalAtomicOr64(EmitContext& ctx);
514void EmitGlobalAtomicXor64(EmitContext& ctx);
515void EmitGlobalAtomicExchange64(EmitContext& ctx);
516void EmitGlobalAtomicAddF32(EmitContext& ctx);
517void EmitGlobalAtomicAddF16x2(EmitContext& ctx);
518void EmitGlobalAtomicAddF32x2(EmitContext& ctx);
519void EmitGlobalAtomicMinF16x2(EmitContext& ctx);
520void EmitGlobalAtomicMinF32x2(EmitContext& ctx);
521void EmitGlobalAtomicMaxF16x2(EmitContext& ctx);
522void EmitGlobalAtomicMaxF32x2(EmitContext& ctx);
523void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
524void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
525void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b);
526void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, std::string_view value);
527void EmitConvertS16F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
528void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
529void EmitConvertS16F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
530void EmitConvertS32F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
531void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
532void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
533void EmitConvertS64F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
534void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
535void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
536void EmitConvertU16F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
537void EmitConvertU16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
538void EmitConvertU16F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
539void EmitConvertU32F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
540void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
541void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
542void EmitConvertU64F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
543void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
544void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
545void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
546void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
547void EmitConvertF16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
548void EmitConvertF32F16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
549void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
550void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
551void EmitConvertF16S8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
552void EmitConvertF16S16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
553void EmitConvertF16S32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
554void EmitConvertF16S64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
555void EmitConvertF16U8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
556void EmitConvertF16U16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
557void EmitConvertF16U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
558void EmitConvertF16U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
559void EmitConvertF32S8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
560void EmitConvertF32S16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
561void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
562void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
563void EmitConvertF32U8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
564void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
565void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
566void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
567void EmitConvertF64S8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
568void EmitConvertF64S16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
569void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
570void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
571void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, std::string_view value);
572void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, std::string_view value);
573void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value);
574void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value);
575void EmitBindlessImageSampleImplicitLod(EmitContext&);
576void EmitBindlessImageSampleExplicitLod(EmitContext&);
577void EmitBindlessImageSampleDrefImplicitLod(EmitContext&);
578void EmitBindlessImageSampleDrefExplicitLod(EmitContext&);
579void EmitBindlessImageGather(EmitContext&);
580void EmitBindlessImageGatherDref(EmitContext&);
581void EmitBindlessImageFetch(EmitContext&);
582void EmitBindlessImageQueryDimensions(EmitContext&);
583void EmitBindlessImageQueryLod(EmitContext&);
584void EmitBindlessImageGradient(EmitContext&);
585void EmitBindlessImageRead(EmitContext&);
586void EmitBindlessImageWrite(EmitContext&);
587void EmitBoundImageSampleImplicitLod(EmitContext&);
588void EmitBoundImageSampleExplicitLod(EmitContext&);
589void EmitBoundImageSampleDrefImplicitLod(EmitContext&);
590void EmitBoundImageSampleDrefExplicitLod(EmitContext&);
591void EmitBoundImageGather(EmitContext&);
592void EmitBoundImageGatherDref(EmitContext&);
593void EmitBoundImageFetch(EmitContext&);
594void EmitBoundImageQueryDimensions(EmitContext&);
595void EmitBoundImageQueryLod(EmitContext&);
596void EmitBoundImageGradient(EmitContext&);
597void EmitBoundImageRead(EmitContext&);
598void EmitBoundImageWrite(EmitContext&);
599void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
600 std::string_view coords, std::string_view bias_lc,
601 const IR::Value& offset);
602void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
603 std::string_view coords, std::string_view lod_lc,
604 const IR::Value& offset);
605void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
606 std::string_view coords, std::string_view dref,
607 std::string_view bias_lc, const IR::Value& offset);
608void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
609 std::string_view coords, std::string_view dref,
610 std::string_view lod_lc, const IR::Value& offset);
611void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
612 std::string_view coords, const IR::Value& offset, const IR::Value& offset2);
613void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
614 std::string_view coords, const IR::Value& offset, const IR::Value& offset2,
615 std::string_view dref);
616void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
617 std::string_view coords, std::string_view offset, std::string_view lod,
618 std::string_view ms);
619void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
620 std::string_view lod);
621void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
622 std::string_view coords);
623void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
624 std::string_view coords, const IR::Value& derivatives,
625 const IR::Value& offset, const IR::Value& lod_clamp);
626void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
627 std::string_view coords);
628void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
629 std::string_view coords, std::string_view color);
630void EmitBindlessImageAtomicIAdd32(EmitContext&);
631void EmitBindlessImageAtomicSMin32(EmitContext&);
632void EmitBindlessImageAtomicUMin32(EmitContext&);
633void EmitBindlessImageAtomicSMax32(EmitContext&);
634void EmitBindlessImageAtomicUMax32(EmitContext&);
635void EmitBindlessImageAtomicInc32(EmitContext&);
636void EmitBindlessImageAtomicDec32(EmitContext&);
637void EmitBindlessImageAtomicAnd32(EmitContext&);
638void EmitBindlessImageAtomicOr32(EmitContext&);
639void EmitBindlessImageAtomicXor32(EmitContext&);
640void EmitBindlessImageAtomicExchange32(EmitContext&);
641void EmitBoundImageAtomicIAdd32(EmitContext&);
642void EmitBoundImageAtomicSMin32(EmitContext&);
643void EmitBoundImageAtomicUMin32(EmitContext&);
644void EmitBoundImageAtomicSMax32(EmitContext&);
645void EmitBoundImageAtomicUMax32(EmitContext&);
646void EmitBoundImageAtomicInc32(EmitContext&);
647void EmitBoundImageAtomicDec32(EmitContext&);
648void EmitBoundImageAtomicAnd32(EmitContext&);
649void EmitBoundImageAtomicOr32(EmitContext&);
650void EmitBoundImageAtomicXor32(EmitContext&);
651void EmitBoundImageAtomicExchange32(EmitContext&);
652void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
653 std::string_view coords, std::string_view value);
654void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
655 std::string_view coords, std::string_view value);
656void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
657 std::string_view coords, std::string_view value);
658void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
659 std::string_view coords, std::string_view value);
660void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
661 std::string_view coords, std::string_view value);
662void EmitImageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
663 std::string_view coords, std::string_view value);
664void EmitImageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
665 std::string_view coords, std::string_view value);
666void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
667 std::string_view coords, std::string_view value);
668void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
669 std::string_view coords, std::string_view value);
670void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
671 std::string_view coords, std::string_view value);
672void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
673 std::string_view coords, std::string_view value);
674void EmitLaneId(EmitContext& ctx, IR::Inst& inst);
675void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred);
676void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred);
677void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred);
678void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred);
679void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst);
680void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst);
681void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst);
682void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst);
683void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst);
684void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
685 std::string_view index, std::string_view clamp,
686 std::string_view segmentation_mask);
687void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index,
688 std::string_view clamp, std::string_view segmentation_mask);
689void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
690 std::string_view index, std::string_view clamp,
691 std::string_view segmentation_mask);
692void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
693 std::string_view index, std::string_view clamp,
694 std::string_view segmentation_mask);
695void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
696 std::string_view swizzle);
697void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
698void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
699void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
700void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
701
702} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
new file mode 100644
index 000000000..38419f88f
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_integer.cpp
@@ -0,0 +1,253 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13void SetZeroFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
14 IR::Inst* const zero{inst.GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
15 if (!zero) {
16 return;
17 }
18 ctx.AddU1("{}={}==0;", *zero, result);
19 zero->Invalidate();
20}
21
22void SetSignFlag(EmitContext& ctx, IR::Inst& inst, std::string_view result) {
23 IR::Inst* const sign{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
24 if (!sign) {
25 return;
26 }
27 ctx.AddU1("{}=int({})<0;", *sign, result);
28 sign->Invalidate();
29}
30
31void BitwiseLogicalOp(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b,
32 char lop) {
33 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
34 ctx.Add("{}={}{}{};", result, a, lop, b);
35 SetZeroFlag(ctx, inst, result);
36 SetSignFlag(ctx, inst, result);
37}
38} // Anonymous namespace
39
40void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
41 // Compute the overflow CC first as it requires the original operand values,
42 // which may be overwritten by the result of the addition
43 if (IR::Inst * overflow{inst.GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp)}) {
44 // https://stackoverflow.com/questions/55468823/how-to-detect-integer-overflow-in-c
45 constexpr u32 s32_max{static_cast<u32>(std::numeric_limits<s32>::max())};
46 const auto sub_a{fmt::format("{}u-{}", s32_max, a)};
47 const auto positive_result{fmt::format("int({})>int({})", b, sub_a)};
48 const auto negative_result{fmt::format("int({})<int({})", b, sub_a)};
49 ctx.AddU1("{}=int({})>=0?{}:{};", *overflow, a, positive_result, negative_result);
50 overflow->Invalidate();
51 }
52 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
53 if (IR::Inst* const carry{inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
54 ctx.uses_cc_carry = true;
55 ctx.Add("{}=uaddCarry({},{},carry);", result, a, b);
56 ctx.AddU1("{}=carry!=0;", *carry);
57 carry->Invalidate();
58 } else {
59 ctx.Add("{}={}+{};", result, a, b);
60 }
61 SetZeroFlag(ctx, inst, result);
62 SetSignFlag(ctx, inst, result);
63}
64
65void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
66 ctx.AddU64("{}={}+{};", inst, a, b);
67}
68
69void EmitISub32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
70 ctx.AddU32("{}={}-{};", inst, a, b);
71}
72
73void EmitISub64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
74 ctx.AddU64("{}={}-{};", inst, a, b);
75}
76
77void EmitIMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
78 ctx.AddU32("{}=uint({}*{});", inst, a, b);
79}
80
81void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
82 ctx.AddU32("{}=uint(-({}));", inst, value);
83}
84
85void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
86 ctx.AddU64("{}=-({});", inst, value);
87}
88
89void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
90 ctx.AddU32("{}=abs(int({}));", inst, value);
91}
92
93void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
94 std::string_view shift) {
95 ctx.AddU32("{}={}<<{};", inst, base, shift);
96}
97
98void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
99 std::string_view shift) {
100 ctx.AddU64("{}={}<<{};", inst, base, shift);
101}
102
103void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
104 std::string_view shift) {
105 ctx.AddU32("{}={}>>{};", inst, base, shift);
106}
107
108void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
109 std::string_view shift) {
110 ctx.AddU64("{}={}>>{};", inst, base, shift);
111}
112
113void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, std::string_view base,
114 std::string_view shift) {
115 ctx.AddU32("{}=int({})>>{};", inst, base, shift);
116}
117
118void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, std::string_view base,
119 std::string_view shift) {
120 ctx.AddU64("{}=int64_t({})>>{};", inst, base, shift);
121}
122
123void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
124 BitwiseLogicalOp(ctx, inst, a, b, '&');
125}
126
127void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
128 BitwiseLogicalOp(ctx, inst, a, b, '|');
129}
130
131void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
132 BitwiseLogicalOp(ctx, inst, a, b, '^');
133}
134
135void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, std::string_view base,
136 std::string_view insert, std::string_view offset, std::string_view count) {
137 ctx.AddU32("{}=bitfieldInsert({},{},int({}),int({}));", inst, base, insert, offset, count);
138}
139
140void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
141 std::string_view offset, std::string_view count) {
142 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
143 ctx.Add("{}=uint(bitfieldExtract(int({}),int({}),int({})));", result, base, offset, count);
144 SetZeroFlag(ctx, inst, result);
145 SetSignFlag(ctx, inst, result);
146}
147
148void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base,
149 std::string_view offset, std::string_view count) {
150 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
151 ctx.Add("{}=uint(bitfieldExtract(uint({}),int({}),int({})));", result, base, offset, count);
152 SetZeroFlag(ctx, inst, result);
153 SetSignFlag(ctx, inst, result);
154}
155
156void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
157 ctx.AddU32("{}=bitfieldReverse({});", inst, value);
158}
159
160void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
161 ctx.AddU32("{}=bitCount({});", inst, value);
162}
163
164void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
165 ctx.AddU32("{}=~{};", inst, value);
166}
167
168void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
169 ctx.AddU32("{}=findMSB(int({}));", inst, value);
170}
171
172void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
173 ctx.AddU32("{}=findMSB(uint({}));", inst, value);
174}
175
176void EmitSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
177 ctx.AddU32("{}=min(int({}),int({}));", inst, a, b);
178}
179
180void EmitUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
181 ctx.AddU32("{}=min(uint({}),uint({}));", inst, a, b);
182}
183
184void EmitSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
185 ctx.AddU32("{}=max(int({}),int({}));", inst, a, b);
186}
187
188void EmitUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
189 ctx.AddU32("{}=max(uint({}),uint({}));", inst, a, b);
190}
191
192void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
193 std::string_view max) {
194 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
195 ctx.Add("{}=clamp(int({}),int({}),int({}));", result, value, min, max);
196 SetZeroFlag(ctx, inst, result);
197 SetSignFlag(ctx, inst, result);
198}
199
200void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min,
201 std::string_view max) {
202 const auto result{ctx.var_alloc.Define(inst, GlslVarType::U32)};
203 ctx.Add("{}=clamp(uint({}),uint({}),uint({}));", result, value, min, max);
204 SetZeroFlag(ctx, inst, result);
205 SetSignFlag(ctx, inst, result);
206}
207
208void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
209 ctx.AddU1("{}=int({})<int({});", inst, lhs, rhs);
210}
211
212void EmitULessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
213 ctx.AddU1("{}=uint({})<uint({});", inst, lhs, rhs);
214}
215
216void EmitIEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
217 ctx.AddU1("{}={}=={};", inst, lhs, rhs);
218}
219
220void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
221 std::string_view rhs) {
222 ctx.AddU1("{}=int({})<=int({});", inst, lhs, rhs);
223}
224
225void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
226 std::string_view rhs) {
227 ctx.AddU1("{}=uint({})<=uint({});", inst, lhs, rhs);
228}
229
230void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
231 std::string_view rhs) {
232 ctx.AddU1("{}=int({})>int({});", inst, lhs, rhs);
233}
234
235void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
236 std::string_view rhs) {
237 ctx.AddU1("{}=uint({})>uint({});", inst, lhs, rhs);
238}
239
240void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) {
241 ctx.AddU1("{}={}!={};", inst, lhs, rhs);
242}
243
244void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
245 std::string_view rhs) {
246 ctx.AddU1("{}=int({})>=int({});", inst, lhs, rhs);
247}
248
249void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs,
250 std::string_view rhs) {
251 ctx.AddU1("{}=uint({})>=uint({});", inst, lhs, rhs);
252}
253} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
new file mode 100644
index 000000000..338ff4bd6
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_logical.cpp
@@ -0,0 +1,28 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12
13void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
14 ctx.AddU1("{}={}||{};", inst, a, b);
15}
16
17void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
18 ctx.AddU1("{}={}&&{};", inst, a, b);
19}
20
21void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) {
22 ctx.AddU1("{}={}^^{};", inst, a, b);
23}
24
25void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, std::string_view value) {
26 ctx.AddU1("{}=!{};", inst, value);
27}
28} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp
new file mode 100644
index 000000000..e3957491f
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_memory.cpp
@@ -0,0 +1,202 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h"
11
12namespace Shader::Backend::GLSL {
13namespace {
14constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
15 "cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
16 "if(cas_result==old_value){{break;}}}}"};
17
18void SsboWriteCas(EmitContext& ctx, const IR::Value& binding, std::string_view offset_var,
19 std::string_view value, std::string_view bit_offset, u32 num_bits) {
20 const auto ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(), offset_var)};
21 ctx.Add(cas_loop, ssbo, ssbo, ssbo, value, bit_offset, num_bits);
22}
23} // Anonymous namespace
24
25void EmitLoadGlobalU8(EmitContext&) {
26 NotImplemented();
27}
28
29void EmitLoadGlobalS8(EmitContext&) {
30 NotImplemented();
31}
32
33void EmitLoadGlobalU16(EmitContext&) {
34 NotImplemented();
35}
36
37void EmitLoadGlobalS16(EmitContext&) {
38 NotImplemented();
39}
40
41void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
42 if (ctx.profile.support_int64) {
43 return ctx.AddU32("{}=LoadGlobal32({});", inst, address);
44 }
45 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
46 ctx.AddU32("{}=0u;", inst);
47}
48
49void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
50 if (ctx.profile.support_int64) {
51 return ctx.AddU32x2("{}=LoadGlobal64({});", inst, address);
52 }
53 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
54 ctx.AddU32x2("{}=uvec2(0);", inst);
55}
56
57void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, std::string_view address) {
58 if (ctx.profile.support_int64) {
59 return ctx.AddU32x4("{}=LoadGlobal128({});", inst, address);
60 }
61 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
62 ctx.AddU32x4("{}=uvec4(0);", inst);
63}
64
65void EmitWriteGlobalU8(EmitContext&) {
66 NotImplemented();
67}
68
69void EmitWriteGlobalS8(EmitContext&) {
70 NotImplemented();
71}
72
73void EmitWriteGlobalU16(EmitContext&) {
74 NotImplemented();
75}
76
77void EmitWriteGlobalS16(EmitContext&) {
78 NotImplemented();
79}
80
81void EmitWriteGlobal32(EmitContext& ctx, std::string_view address, std::string_view value) {
82 if (ctx.profile.support_int64) {
83 return ctx.Add("WriteGlobal32({},{});", address, value);
84 }
85 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
86}
87
88void EmitWriteGlobal64(EmitContext& ctx, std::string_view address, std::string_view value) {
89 if (ctx.profile.support_int64) {
90 return ctx.Add("WriteGlobal64({},{});", address, value);
91 }
92 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
93}
94
95void EmitWriteGlobal128(EmitContext& ctx, std::string_view address, std::string_view value) {
96 if (ctx.profile.support_int64) {
97 return ctx.Add("WriteGlobal128({},{});", address, value);
98 }
99 LOG_WARNING(Shader_GLSL, "Int64 not supported, ignoring memory operation");
100}
101
102void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
103 const IR::Value& offset) {
104 const auto offset_var{ctx.var_alloc.Consume(offset)};
105 ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int({}%4)*8,8);", inst, ctx.stage_name,
106 binding.U32(), offset_var, offset_var);
107}
108
109void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
110 const IR::Value& offset) {
111 const auto offset_var{ctx.var_alloc.Consume(offset)};
112 ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int({}%4)*8,8);", inst, ctx.stage_name,
113 binding.U32(), offset_var, offset_var);
114}
115
116void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
117 const IR::Value& offset) {
118 const auto offset_var{ctx.var_alloc.Consume(offset)};
119 ctx.AddU32("{}=bitfieldExtract({}_ssbo{}[{}>>2],int(({}>>1)%2)*16,16);", inst, ctx.stage_name,
120 binding.U32(), offset_var, offset_var);
121}
122
123void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
124 const IR::Value& offset) {
125 const auto offset_var{ctx.var_alloc.Consume(offset)};
126 ctx.AddU32("{}=bitfieldExtract(int({}_ssbo{}[{}>>2]),int(({}>>1)%2)*16,16);", inst,
127 ctx.stage_name, binding.U32(), offset_var, offset_var);
128}
129
130void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
131 const IR::Value& offset) {
132 const auto offset_var{ctx.var_alloc.Consume(offset)};
133 ctx.AddU32("{}={}_ssbo{}[{}>>2];", inst, ctx.stage_name, binding.U32(), offset_var);
134}
135
136void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
137 const IR::Value& offset) {
138 const auto offset_var{ctx.var_alloc.Consume(offset)};
139 ctx.AddU32x2("{}=uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2]);", inst, ctx.stage_name,
140 binding.U32(), offset_var, ctx.stage_name, binding.U32(), offset_var);
141}
142
143void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
144 const IR::Value& offset) {
145 const auto offset_var{ctx.var_alloc.Consume(offset)};
146 ctx.AddU32x4("{}=uvec4({}_ssbo{}[{}>>2],{}_ssbo{}[({}+4)>>2],{}_ssbo{}[({}+8)>>2],{}_ssbo{}[({}"
147 "+12)>>2]);",
148 inst, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name, binding.U32(),
149 offset_var, ctx.stage_name, binding.U32(), offset_var, ctx.stage_name,
150 binding.U32(), offset_var);
151}
152
153void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
154 std::string_view value) {
155 const auto offset_var{ctx.var_alloc.Consume(offset)};
156 const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
157 SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
158}
159
160void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
161 std::string_view value) {
162 const auto offset_var{ctx.var_alloc.Consume(offset)};
163 const auto bit_offset{fmt::format("int({}%4)*8", offset_var)};
164 SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 8);
165}
166
167void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
168 std::string_view value) {
169 const auto offset_var{ctx.var_alloc.Consume(offset)};
170 const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
171 SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
172}
173
174void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
175 std::string_view value) {
176 const auto offset_var{ctx.var_alloc.Consume(offset)};
177 const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset_var)};
178 SsboWriteCas(ctx, binding, offset_var, value, bit_offset, 16);
179}
180
181void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
182 std::string_view value) {
183 const auto offset_var{ctx.var_alloc.Consume(offset)};
184 ctx.Add("{}_ssbo{}[{}>>2]={};", ctx.stage_name, binding.U32(), offset_var, value);
185}
186
187void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
188 std::string_view value) {
189 const auto offset_var{ctx.var_alloc.Consume(offset)};
190 ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
191 ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
192}
193
194void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
195 std::string_view value) {
196 const auto offset_var{ctx.var_alloc.Consume(offset)};
197 ctx.Add("{}_ssbo{}[{}>>2]={}.x;", ctx.stage_name, binding.U32(), offset_var, value);
198 ctx.Add("{}_ssbo{}[({}+4)>>2]={}.y;", ctx.stage_name, binding.U32(), offset_var, value);
199 ctx.Add("{}_ssbo{}[({}+8)>>2]={}.z;", ctx.stage_name, binding.U32(), offset_var, value);
200 ctx.Add("{}_ssbo{}[({}+12)>>2]={}.w;", ctx.stage_name, binding.U32(), offset_var, value);
201}
202} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
new file mode 100644
index 000000000..f420fe388
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
@@ -0,0 +1,105 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11#ifdef _MSC_VER
12#pragma warning(disable : 4100)
13#endif
14
15namespace Shader::Backend::GLSL {
16
17void EmitGetRegister(EmitContext& ctx) {
18 NotImplemented();
19}
20
21void EmitSetRegister(EmitContext& ctx) {
22 NotImplemented();
23}
24
25void EmitGetPred(EmitContext& ctx) {
26 NotImplemented();
27}
28
29void EmitSetPred(EmitContext& ctx) {
30 NotImplemented();
31}
32
33void EmitSetGotoVariable(EmitContext& ctx) {
34 NotImplemented();
35}
36
37void EmitGetGotoVariable(EmitContext& ctx) {
38 NotImplemented();
39}
40
41void EmitSetIndirectBranchVariable(EmitContext& ctx) {
42 NotImplemented();
43}
44
45void EmitGetIndirectBranchVariable(EmitContext& ctx) {
46 NotImplemented();
47}
48
49void EmitGetZFlag(EmitContext& ctx) {
50 NotImplemented();
51}
52
53void EmitGetSFlag(EmitContext& ctx) {
54 NotImplemented();
55}
56
57void EmitGetCFlag(EmitContext& ctx) {
58 NotImplemented();
59}
60
61void EmitGetOFlag(EmitContext& ctx) {
62 NotImplemented();
63}
64
65void EmitSetZFlag(EmitContext& ctx) {
66 NotImplemented();
67}
68
69void EmitSetSFlag(EmitContext& ctx) {
70 NotImplemented();
71}
72
73void EmitSetCFlag(EmitContext& ctx) {
74 NotImplemented();
75}
76
77void EmitSetOFlag(EmitContext& ctx) {
78 NotImplemented();
79}
80
81void EmitGetZeroFromOp(EmitContext& ctx) {
82 NotImplemented();
83}
84
85void EmitGetSignFromOp(EmitContext& ctx) {
86 NotImplemented();
87}
88
89void EmitGetCarryFromOp(EmitContext& ctx) {
90 NotImplemented();
91}
92
93void EmitGetOverflowFromOp(EmitContext& ctx) {
94 NotImplemented();
95}
96
97void EmitGetSparseFromOp(EmitContext& ctx) {
98 NotImplemented();
99}
100
101void EmitGetInBoundsFromOp(EmitContext& ctx) {
102 NotImplemented();
103}
104
105} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp
new file mode 100644
index 000000000..49fba9073
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_select.cpp
@@ -0,0 +1,55 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
13 std::string_view true_value, std::string_view false_value) {
14 ctx.AddU1("{}={}?{}:{};", inst, cond, true_value, false_value);
15}
16
17void EmitSelectU8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
18 [[maybe_unused]] std::string_view true_value,
19 [[maybe_unused]] std::string_view false_value) {
20 NotImplemented();
21}
22
23void EmitSelectU16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
24 [[maybe_unused]] std::string_view true_value,
25 [[maybe_unused]] std::string_view false_value) {
26 NotImplemented();
27}
28
29void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
30 std::string_view true_value, std::string_view false_value) {
31 ctx.AddU32("{}={}?{}:{};", inst, cond, true_value, false_value);
32}
33
34void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
35 std::string_view true_value, std::string_view false_value) {
36 ctx.AddU64("{}={}?{}:{};", inst, cond, true_value, false_value);
37}
38
39void EmitSelectF16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view cond,
40 [[maybe_unused]] std::string_view true_value,
41 [[maybe_unused]] std::string_view false_value) {
42 NotImplemented();
43}
44
45void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
46 std::string_view true_value, std::string_view false_value) {
47 ctx.AddF32("{}={}?{}:{};", inst, cond, true_value, false_value);
48}
49
50void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond,
51 std::string_view true_value, std::string_view false_value) {
52 ctx.AddF64("{}={}?{}:{};", inst, cond, true_value, false_value);
53}
54
55} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
new file mode 100644
index 000000000..518b78f06
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_shared_memory.cpp
@@ -0,0 +1,79 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::Backend::GLSL {
12namespace {
13constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
14 "cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
15 "if(cas_result==old_value){{break;}}}}"};
16
17void SharedWriteCas(EmitContext& ctx, std::string_view offset, std::string_view value,
18 std::string_view bit_offset, u32 num_bits) {
19 const auto smem{fmt::format("smem[{}>>2]", offset)};
20 ctx.Add(cas_loop, smem, smem, smem, value, bit_offset, num_bits);
21}
22} // Anonymous namespace
23
24void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
25 ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int({}%4)*8,8);", inst, offset, offset);
26}
27
28void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
29 ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int({}%4)*8,8);", inst, offset, offset);
30}
31
32void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
33 ctx.AddU32("{}=bitfieldExtract(smem[{}>>2],int(({}>>1)%2)*16,16);", inst, offset, offset);
34}
35
36void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
37 ctx.AddU32("{}=bitfieldExtract(int(smem[{}>>2]),int(({}>>1)%2)*16,16);", inst, offset, offset);
38}
39
40void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
41 ctx.AddU32("{}=smem[{}>>2];", inst, offset);
42}
43
44void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
45 ctx.AddU32x2("{}=uvec2(smem[{}>>2],smem[({}+4)>>2]);", inst, offset, offset);
46}
47
48void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, std::string_view offset) {
49 ctx.AddU32x4("{}=uvec4(smem[{}>>2],smem[({}+4)>>2],smem[({}+8)>>2],smem[({}+12)>>2]);", inst,
50 offset, offset, offset, offset);
51}
52
53void EmitWriteSharedU8(EmitContext& ctx, std::string_view offset, std::string_view value) {
54 const auto bit_offset{fmt::format("int({}%4)*8", offset)};
55 SharedWriteCas(ctx, offset, value, bit_offset, 8);
56}
57
58void EmitWriteSharedU16(EmitContext& ctx, std::string_view offset, std::string_view value) {
59 const auto bit_offset{fmt::format("int(({}>>1)%2)*16", offset)};
60 SharedWriteCas(ctx, offset, value, bit_offset, 16);
61}
62
63void EmitWriteSharedU32(EmitContext& ctx, std::string_view offset, std::string_view value) {
64 ctx.Add("smem[{}>>2]={};", offset, value);
65}
66
67void EmitWriteSharedU64(EmitContext& ctx, std::string_view offset, std::string_view value) {
68 ctx.Add("smem[{}>>2]={}.x;", offset, value);
69 ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
70}
71
72void EmitWriteSharedU128(EmitContext& ctx, std::string_view offset, std::string_view value) {
73 ctx.Add("smem[{}>>2]={}.x;", offset, value);
74 ctx.Add("smem[({}+4)>>2]={}.y;", offset, value);
75 ctx.Add("smem[({}+8)>>2]={}.z;", offset, value);
76 ctx.Add("smem[({}+12)>>2]={}.w;", offset, value);
77}
78
79} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
new file mode 100644
index 000000000..9b866f889
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
@@ -0,0 +1,111 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/program.h"
10#include "shader_recompiler/frontend/ir/value.h"
11#include "shader_recompiler/profile.h"
12
13namespace Shader::Backend::GLSL {
14namespace {
15std::string_view OutputVertexIndex(EmitContext& ctx) {
16 return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : "";
17}
18
19void InitializeOutputVaryings(EmitContext& ctx) {
20 if (ctx.uses_geometry_passthrough) {
21 return;
22 }
23 if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
24 ctx.Add("gl_Position=vec4(0,0,0,1);");
25 }
26 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
27 if (!ctx.info.stores.Generic(index)) {
28 continue;
29 }
30 const auto& info_array{ctx.output_generics.at(index)};
31 const auto output_decorator{OutputVertexIndex(ctx)};
32 size_t element{};
33 while (element < info_array.size()) {
34 const auto& info{info_array.at(element)};
35 const auto varying_name{fmt::format("{}{}", info.name, output_decorator)};
36 switch (info.num_components) {
37 case 1: {
38 const char value{element == 3 ? '1' : '0'};
39 ctx.Add("{}={}.f;", varying_name, value);
40 break;
41 }
42 case 2:
43 case 3:
44 if (element + info.num_components < 4) {
45 ctx.Add("{}=vec{}(0);", varying_name, info.num_components);
46 } else {
47 // last element is the w component, must be initialized to 1
48 const auto zeros{info.num_components == 3 ? "0,0," : "0,"};
49 ctx.Add("{}=vec{}({}1);", varying_name, info.num_components, zeros);
50 }
51 break;
52 case 4:
53 ctx.Add("{}=vec4(0,0,0,1);", varying_name);
54 break;
55 default:
56 break;
57 }
58 element += info.num_components;
59 }
60 }
61}
62} // Anonymous namespace
63
64void EmitPhi(EmitContext& ctx, IR::Inst& phi) {
65 const size_t num_args{phi.NumArgs()};
66 for (size_t i = 0; i < num_args; ++i) {
67 ctx.var_alloc.Consume(phi.Arg(i));
68 }
69 if (!phi.Definition<Id>().is_valid) {
70 // The phi node wasn't forward defined
71 ctx.var_alloc.PhiDefine(phi, phi.Arg(0).Type());
72 }
73}
74
75void EmitVoid(EmitContext&) {}
76
77void EmitReference(EmitContext& ctx, const IR::Value& value) {
78 ctx.var_alloc.Consume(value);
79}
80
81void EmitPhiMove(EmitContext& ctx, const IR::Value& phi_value, const IR::Value& value) {
82 IR::Inst& phi{*phi_value.InstRecursive()};
83 const auto phi_type{phi.Arg(0).Type()};
84 if (!phi.Definition<Id>().is_valid) {
85 // The phi node wasn't forward defined
86 ctx.var_alloc.PhiDefine(phi, phi_type);
87 }
88 const auto phi_reg{ctx.var_alloc.Consume(IR::Value{&phi})};
89 const auto val_reg{ctx.var_alloc.Consume(value)};
90 if (phi_reg == val_reg) {
91 return;
92 }
93 ctx.Add("{}={};", phi_reg, val_reg);
94}
95
96void EmitPrologue(EmitContext& ctx) {
97 InitializeOutputVaryings(ctx);
98}
99
100void EmitEpilogue(EmitContext&) {}
101
102void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
103 ctx.Add("EmitStreamVertex(int({}));", ctx.var_alloc.Consume(stream));
104 InitializeOutputVaryings(ctx);
105}
106
107void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
108 ctx.Add("EndStreamPrimitive(int({}));", ctx.var_alloc.Consume(stream));
109}
110
111} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
new file mode 100644
index 000000000..15bf02dd6
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_undefined.cpp
@@ -0,0 +1,32 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9
10namespace Shader::Backend::GLSL {
11
12void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) {
13 ctx.AddU1("{}=false;", inst);
14}
15
16void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) {
17 ctx.AddU32("{}=0u;", inst);
18}
19
20void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) {
21 ctx.AddU32("{}=0u;", inst);
22}
23
24void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) {
25 ctx.AddU32("{}=0u;", inst);
26}
27
28void EmitUndefU64(EmitContext& ctx, IR::Inst& inst) {
29 ctx.AddU64("{}=0u;", inst);
30}
31
32} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
new file mode 100644
index 000000000..a982dd8a2
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_warp.cpp
@@ -0,0 +1,217 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string_view>
6
7#include "shader_recompiler/backend/glsl/emit_context.h"
8#include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
9#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/profile.h"
11
12namespace Shader::Backend::GLSL {
13namespace {
14void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) {
15 IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
16 if (!in_bounds) {
17 return;
18 }
19 ctx.AddU1("{}=shfl_in_bounds;", *in_bounds);
20 in_bounds->Invalidate();
21}
22
23std::string ComputeMinThreadId(std::string_view thread_id, std::string_view segmentation_mask) {
24 return fmt::format("({}&{})", thread_id, segmentation_mask);
25}
26
27std::string ComputeMaxThreadId(std::string_view min_thread_id, std::string_view clamp,
28 std::string_view not_seg_mask) {
29 return fmt::format("({})|({}&{})", min_thread_id, clamp, not_seg_mask);
30}
31
32std::string GetMaxThreadId(std::string_view thread_id, std::string_view clamp,
33 std::string_view segmentation_mask) {
34 const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)};
35 const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)};
36 return ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask);
37}
38
39void UseShuffleNv(EmitContext& ctx, IR::Inst& inst, std::string_view shfl_op,
40 std::string_view value, std::string_view index,
41 [[maybe_unused]] std::string_view clamp, std::string_view segmentation_mask) {
42 const auto width{fmt::format("32u>>(bitCount({}&31u))", segmentation_mask)};
43 ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width);
44 SetInBoundsFlag(ctx, inst);
45}
46} // Anonymous namespace
47
48void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
49 ctx.AddU32("{}=gl_SubGroupInvocationARB&31u;", inst);
50}
51
52void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
53 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
54 ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
55 } else {
56 const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
57 const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
58 ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask);
59 }
60}
61
62void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
63 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
64 ctx.AddU1("{}=anyInvocationARB({});", inst, pred);
65 } else {
66 const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
67 const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
68 ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask);
69 }
70}
71
72void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
73 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
74 ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
75 } else {
76 const auto active_mask{fmt::format("uvec2(ballotARB(true))[gl_SubGroupInvocationARB]")};
77 const auto ballot{fmt::format("uvec2(ballotARB({}))[gl_SubGroupInvocationARB]", pred)};
78 const auto value{fmt::format("({}^{})", ballot, active_mask)};
79 ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask);
80 }
81}
82
83void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
84 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
85 ctx.AddU32("{}=uvec2(ballotARB({})).x;", inst, pred);
86 } else {
87 ctx.AddU32("{}=uvec2(ballotARB({}))[gl_SubGroupInvocationARB];", inst, pred);
88 }
89}
90
91void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
92 ctx.AddU32("{}=uint(gl_SubGroupEqMaskARB.x);", inst);
93}
94
95void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
96 ctx.AddU32("{}=uint(gl_SubGroupLtMaskARB.x);", inst);
97}
98
99void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
100 ctx.AddU32("{}=uint(gl_SubGroupLeMaskARB.x);", inst);
101}
102
103void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
104 ctx.AddU32("{}=uint(gl_SubGroupGtMaskARB.x);", inst);
105}
106
107void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
108 ctx.AddU32("{}=uint(gl_SubGroupGeMaskARB.x);", inst);
109}
110
111void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
112 std::string_view index, std::string_view clamp,
113 std::string_view segmentation_mask) {
114 if (ctx.profile.support_gl_warp_intrinsics) {
115 UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, segmentation_mask);
116 return;
117 }
118 const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)};
119 const auto thread_id{"gl_SubGroupInvocationARB"};
120 const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)};
121 const auto max_thread_id{ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask)};
122
123 const auto lhs{fmt::format("({}&{})", index, not_seg_mask)};
124 const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)};
125 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
126 SetInBoundsFlag(ctx, inst);
127 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
128}
129
130void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index,
131 std::string_view clamp, std::string_view segmentation_mask) {
132 if (ctx.profile.support_gl_warp_intrinsics) {
133 UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, segmentation_mask);
134 return;
135 }
136 const auto thread_id{"gl_SubGroupInvocationARB"};
137 const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)};
138 const auto src_thread_id{fmt::format("({}-{})", thread_id, index)};
139 ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id);
140 SetInBoundsFlag(ctx, inst);
141 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
142}
143
144void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
145 std::string_view index, std::string_view clamp,
146 std::string_view segmentation_mask) {
147 if (ctx.profile.support_gl_warp_intrinsics) {
148 UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, segmentation_mask);
149 return;
150 }
151 const auto thread_id{"gl_SubGroupInvocationARB"};
152 const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)};
153 const auto src_thread_id{fmt::format("({}+{})", thread_id, index)};
154 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
155 SetInBoundsFlag(ctx, inst);
156 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
157}
158
159void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
160 std::string_view index, std::string_view clamp,
161 std::string_view segmentation_mask) {
162 if (ctx.profile.support_gl_warp_intrinsics) {
163 UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, segmentation_mask);
164 return;
165 }
166 const auto thread_id{"gl_SubGroupInvocationARB"};
167 const auto max_thread_id{GetMaxThreadId(thread_id, clamp, segmentation_mask)};
168 const auto src_thread_id{fmt::format("({}^{})", thread_id, index)};
169 ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
170 SetInBoundsFlag(ctx, inst);
171 ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
172}
173
174void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
175 std::string_view swizzle) {
176 const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
177 const std::string modifier_a = fmt::format("FSWZ_A[{}]", mask);
178 const std::string modifier_b = fmt::format("FSWZ_B[{}]", mask);
179 ctx.AddF32("{}=({}*{})+({}*{});", inst, op_a, modifier_a, op_b, modifier_b);
180}
181
182void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
183 if (ctx.profile.support_gl_derivative_control) {
184 ctx.AddF32("{}=dFdxFine({});", inst, op_a);
185 } else {
186 LOG_WARNING(Shader_GLSL, "Device does not support dFdxFine, fallback to dFdx");
187 ctx.AddF32("{}=dFdx({});", inst, op_a);
188 }
189}
190
191void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
192 if (ctx.profile.support_gl_derivative_control) {
193 ctx.AddF32("{}=dFdyFine({});", inst, op_a);
194 } else {
195 LOG_WARNING(Shader_GLSL, "Device does not support dFdyFine, fallback to dFdy");
196 ctx.AddF32("{}=dFdy({});", inst, op_a);
197 }
198}
199
200void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
201 if (ctx.profile.support_gl_derivative_control) {
202 ctx.AddF32("{}=dFdxCoarse({});", inst, op_a);
203 } else {
204 LOG_WARNING(Shader_GLSL, "Device does not support dFdxCoarse, fallback to dFdx");
205 ctx.AddF32("{}=dFdx({});", inst, op_a);
206 }
207}
208
209void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
210 if (ctx.profile.support_gl_derivative_control) {
211 ctx.AddF32("{}=dFdyCoarse({});", inst, op_a);
212 } else {
213 LOG_WARNING(Shader_GLSL, "Device does not support dFdyCoarse, fallback to dFdy");
214 ctx.AddF32("{}=dFdy({});", inst, op_a);
215 }
216}
217} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/var_alloc.cpp b/src/shader_recompiler/backend/glsl/var_alloc.cpp
new file mode 100644
index 000000000..194f926ca
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/var_alloc.cpp
@@ -0,0 +1,308 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6#include <string_view>
7
8#include <fmt/format.h>
9
10#include "shader_recompiler/backend/glsl/var_alloc.h"
11#include "shader_recompiler/exception.h"
12#include "shader_recompiler/frontend/ir/value.h"
13
14namespace Shader::Backend::GLSL {
15namespace {
16std::string TypePrefix(GlslVarType type) {
17 switch (type) {
18 case GlslVarType::U1:
19 return "b_";
20 case GlslVarType::F16x2:
21 return "f16x2_";
22 case GlslVarType::U32:
23 return "u_";
24 case GlslVarType::F32:
25 return "f_";
26 case GlslVarType::U64:
27 return "u64_";
28 case GlslVarType::F64:
29 return "d_";
30 case GlslVarType::U32x2:
31 return "u2_";
32 case GlslVarType::F32x2:
33 return "f2_";
34 case GlslVarType::U32x3:
35 return "u3_";
36 case GlslVarType::F32x3:
37 return "f3_";
38 case GlslVarType::U32x4:
39 return "u4_";
40 case GlslVarType::F32x4:
41 return "f4_";
42 case GlslVarType::PrecF32:
43 return "pf_";
44 case GlslVarType::PrecF64:
45 return "pd_";
46 case GlslVarType::Void:
47 return "";
48 default:
49 throw NotImplementedException("Type {}", type);
50 }
51}
52
53std::string FormatFloat(std::string_view value, IR::Type type) {
54 // TODO: Confirm FP64 nan/inf
55 if (type == IR::Type::F32) {
56 if (value == "nan") {
57 return "utof(0x7fc00000)";
58 }
59 if (value == "inf") {
60 return "utof(0x7f800000)";
61 }
62 if (value == "-inf") {
63 return "utof(0xff800000)";
64 }
65 }
66 if (value.find_first_of('e') != std::string_view::npos) {
67 // scientific notation
68 const auto cast{type == IR::Type::F32 ? "float" : "double"};
69 return fmt::format("{}({})", cast, value);
70 }
71 const bool needs_dot{value.find_first_of('.') == std::string_view::npos};
72 const bool needs_suffix{!value.ends_with('f')};
73 const auto suffix{type == IR::Type::F32 ? "f" : "lf"};
74 return fmt::format("{}{}{}", value, needs_dot ? "." : "", needs_suffix ? suffix : "");
75}
76
77std::string MakeImm(const IR::Value& value) {
78 switch (value.Type()) {
79 case IR::Type::U1:
80 return fmt::format("{}", value.U1() ? "true" : "false");
81 case IR::Type::U32:
82 return fmt::format("{}u", value.U32());
83 case IR::Type::F32:
84 return FormatFloat(fmt::format("{}", value.F32()), IR::Type::F32);
85 case IR::Type::U64:
86 return fmt::format("{}ul", value.U64());
87 case IR::Type::F64:
88 return FormatFloat(fmt::format("{}", value.F64()), IR::Type::F64);
89 case IR::Type::Void:
90 return "";
91 default:
92 throw NotImplementedException("Immediate type {}", value.Type());
93 }
94}
95} // Anonymous namespace
96
97std::string VarAlloc::Representation(u32 index, GlslVarType type) const {
98 const auto prefix{TypePrefix(type)};
99 return fmt::format("{}{}", prefix, index);
100}
101
102std::string VarAlloc::Representation(Id id) const {
103 return Representation(id.index, id.type);
104}
105
106std::string VarAlloc::Define(IR::Inst& inst, GlslVarType type) {
107 if (inst.HasUses()) {
108 inst.SetDefinition<Id>(Alloc(type));
109 return Representation(inst.Definition<Id>());
110 } else {
111 Id id{};
112 id.type.Assign(type);
113 GetUseTracker(type).uses_temp = true;
114 inst.SetDefinition<Id>(id);
115 return 't' + Representation(inst.Definition<Id>());
116 }
117}
118
119std::string VarAlloc::Define(IR::Inst& inst, IR::Type type) {
120 return Define(inst, RegType(type));
121}
122
123std::string VarAlloc::PhiDefine(IR::Inst& inst, IR::Type type) {
124 return AddDefine(inst, RegType(type));
125}
126
127std::string VarAlloc::AddDefine(IR::Inst& inst, GlslVarType type) {
128 if (inst.HasUses()) {
129 inst.SetDefinition<Id>(Alloc(type));
130 return Representation(inst.Definition<Id>());
131 } else {
132 return "";
133 }
134 return Representation(inst.Definition<Id>());
135}
136
137std::string VarAlloc::Consume(const IR::Value& value) {
138 return value.IsImmediate() ? MakeImm(value) : ConsumeInst(*value.InstRecursive());
139}
140
141std::string VarAlloc::ConsumeInst(IR::Inst& inst) {
142 inst.DestructiveRemoveUsage();
143 if (!inst.HasUses()) {
144 Free(inst.Definition<Id>());
145 }
146 return Representation(inst.Definition<Id>());
147}
148
149std::string VarAlloc::GetGlslType(IR::Type type) const {
150 return GetGlslType(RegType(type));
151}
152
153Id VarAlloc::Alloc(GlslVarType type) {
154 auto& use_tracker{GetUseTracker(type)};
155 const auto num_vars{use_tracker.var_use.size()};
156 for (size_t var = 0; var < num_vars; ++var) {
157 if (use_tracker.var_use[var]) {
158 continue;
159 }
160 use_tracker.num_used = std::max(use_tracker.num_used, var + 1);
161 use_tracker.var_use[var] = true;
162 Id ret{};
163 ret.is_valid.Assign(1);
164 ret.type.Assign(type);
165 ret.index.Assign(static_cast<u32>(var));
166 return ret;
167 }
168 // Allocate a new variable
169 use_tracker.var_use.push_back(true);
170 Id ret{};
171 ret.is_valid.Assign(1);
172 ret.type.Assign(type);
173 ret.index.Assign(static_cast<u32>(use_tracker.num_used));
174 ++use_tracker.num_used;
175 return ret;
176}
177
178void VarAlloc::Free(Id id) {
179 if (id.is_valid == 0) {
180 throw LogicError("Freeing invalid variable");
181 }
182 auto& use_tracker{GetUseTracker(id.type)};
183 use_tracker.var_use[id.index] = false;
184}
185
186GlslVarType VarAlloc::RegType(IR::Type type) const {
187 switch (type) {
188 case IR::Type::U1:
189 return GlslVarType::U1;
190 case IR::Type::U32:
191 return GlslVarType::U32;
192 case IR::Type::F32:
193 return GlslVarType::F32;
194 case IR::Type::U64:
195 return GlslVarType::U64;
196 case IR::Type::F64:
197 return GlslVarType::F64;
198 default:
199 throw NotImplementedException("IR type {}", type);
200 }
201}
202
203std::string VarAlloc::GetGlslType(GlslVarType type) const {
204 switch (type) {
205 case GlslVarType::U1:
206 return "bool";
207 case GlslVarType::F16x2:
208 return "f16vec2";
209 case GlslVarType::U32:
210 return "uint";
211 case GlslVarType::F32:
212 case GlslVarType::PrecF32:
213 return "float";
214 case GlslVarType::U64:
215 return "uint64_t";
216 case GlslVarType::F64:
217 case GlslVarType::PrecF64:
218 return "double";
219 case GlslVarType::U32x2:
220 return "uvec2";
221 case GlslVarType::F32x2:
222 return "vec2";
223 case GlslVarType::U32x3:
224 return "uvec3";
225 case GlslVarType::F32x3:
226 return "vec3";
227 case GlslVarType::U32x4:
228 return "uvec4";
229 case GlslVarType::F32x4:
230 return "vec4";
231 case GlslVarType::Void:
232 return "";
233 default:
234 throw NotImplementedException("Type {}", type);
235 }
236}
237
238VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) {
239 switch (type) {
240 case GlslVarType::U1:
241 return var_bool;
242 case GlslVarType::F16x2:
243 return var_f16x2;
244 case GlslVarType::U32:
245 return var_u32;
246 case GlslVarType::F32:
247 return var_f32;
248 case GlslVarType::U64:
249 return var_u64;
250 case GlslVarType::F64:
251 return var_f64;
252 case GlslVarType::U32x2:
253 return var_u32x2;
254 case GlslVarType::F32x2:
255 return var_f32x2;
256 case GlslVarType::U32x3:
257 return var_u32x3;
258 case GlslVarType::F32x3:
259 return var_f32x3;
260 case GlslVarType::U32x4:
261 return var_u32x4;
262 case GlslVarType::F32x4:
263 return var_f32x4;
264 case GlslVarType::PrecF32:
265 return var_precf32;
266 case GlslVarType::PrecF64:
267 return var_precf64;
268 default:
269 throw NotImplementedException("Type {}", type);
270 }
271}
272
273const VarAlloc::UseTracker& VarAlloc::GetUseTracker(GlslVarType type) const {
274 switch (type) {
275 case GlslVarType::U1:
276 return var_bool;
277 case GlslVarType::F16x2:
278 return var_f16x2;
279 case GlslVarType::U32:
280 return var_u32;
281 case GlslVarType::F32:
282 return var_f32;
283 case GlslVarType::U64:
284 return var_u64;
285 case GlslVarType::F64:
286 return var_f64;
287 case GlslVarType::U32x2:
288 return var_u32x2;
289 case GlslVarType::F32x2:
290 return var_f32x2;
291 case GlslVarType::U32x3:
292 return var_u32x3;
293 case GlslVarType::F32x3:
294 return var_f32x3;
295 case GlslVarType::U32x4:
296 return var_u32x4;
297 case GlslVarType::F32x4:
298 return var_f32x4;
299 case GlslVarType::PrecF32:
300 return var_precf32;
301 case GlslVarType::PrecF64:
302 return var_precf64;
303 default:
304 throw NotImplementedException("Type {}", type);
305 }
306}
307
308} // namespace Shader::Backend::GLSL
diff --git a/src/shader_recompiler/backend/glsl/var_alloc.h b/src/shader_recompiler/backend/glsl/var_alloc.h
new file mode 100644
index 000000000..8b49f32a6
--- /dev/null
+++ b/src/shader_recompiler/backend/glsl/var_alloc.h
@@ -0,0 +1,105 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <bitset>
8#include <string>
9#include <vector>
10
11#include "common/bit_field.h"
12#include "common/common_types.h"
13
14namespace Shader::IR {
15class Inst;
16class Value;
17enum class Type;
18} // namespace Shader::IR
19
20namespace Shader::Backend::GLSL {
21enum class GlslVarType : u32 {
22 U1,
23 F16x2,
24 U32,
25 F32,
26 U64,
27 F64,
28 U32x2,
29 F32x2,
30 U32x3,
31 F32x3,
32 U32x4,
33 F32x4,
34 PrecF32,
35 PrecF64,
36 Void,
37};
38
39struct Id {
40 union {
41 u32 raw;
42 BitField<0, 1, u32> is_valid;
43 BitField<1, 4, GlslVarType> type;
44 BitField<6, 26, u32> index;
45 };
46
47 bool operator==(Id rhs) const noexcept {
48 return raw == rhs.raw;
49 }
50 bool operator!=(Id rhs) const noexcept {
51 return !operator==(rhs);
52 }
53};
54static_assert(sizeof(Id) == sizeof(u32));
55
56class VarAlloc {
57public:
58 struct UseTracker {
59 bool uses_temp{};
60 size_t num_used{};
61 std::vector<bool> var_use;
62 };
63
64 /// Used for explicit usages of variables, may revert to temporaries
65 std::string Define(IR::Inst& inst, GlslVarType type);
66 std::string Define(IR::Inst& inst, IR::Type type);
67
68 /// Used to assign variables used by the IR. May return a blank string if
69 /// the instruction's result is unused in the IR.
70 std::string AddDefine(IR::Inst& inst, GlslVarType type);
71 std::string PhiDefine(IR::Inst& inst, IR::Type type);
72
73 std::string Consume(const IR::Value& value);
74 std::string ConsumeInst(IR::Inst& inst);
75
76 std::string GetGlslType(GlslVarType type) const;
77 std::string GetGlslType(IR::Type type) const;
78
79 const UseTracker& GetUseTracker(GlslVarType type) const;
80 std::string Representation(u32 index, GlslVarType type) const;
81
82private:
83 GlslVarType RegType(IR::Type type) const;
84 Id Alloc(GlslVarType type);
85 void Free(Id id);
86 UseTracker& GetUseTracker(GlslVarType type);
87 std::string Representation(Id id) const;
88
89 UseTracker var_bool{};
90 UseTracker var_f16x2{};
91 UseTracker var_u32{};
92 UseTracker var_u32x2{};
93 UseTracker var_u32x3{};
94 UseTracker var_u32x4{};
95 UseTracker var_f32{};
96 UseTracker var_f32x2{};
97 UseTracker var_f32x3{};
98 UseTracker var_f32x4{};
99 UseTracker var_u64{};
100 UseTracker var_f64{};
101 UseTracker var_precf32{};
102 UseTracker var_precf64{};
103};
104
105} // namespace Shader::Backend::GLSL