diff options
| author | 2021-07-25 11:39:04 -0700 | |
|---|---|---|
| committer | 2021-07-25 11:39:04 -0700 | |
| commit | 98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f (patch) | |
| tree | 816faa96c2c4d291825063433331a8ea4b3d08f1 /src/shader_recompiler/backend/glsl | |
| parent | Merge pull request #6699 from lat9nq/common-threads (diff) | |
| parent | shader: Support out of bound local memory reads and immediate writes (diff) | |
| download | yuzu-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')
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 | |||
| 11 | namespace Shader::Backend::GLSL { | ||
| 12 | namespace { | ||
| 13 | u32 CbufIndex(size_t offset) { | ||
| 14 | return (offset / 4) % 4; | ||
| 15 | } | ||
| 16 | |||
| 17 | char Swizzle(size_t offset) { | ||
| 18 | return "xyzw"[CbufIndex(offset)]; | ||
| 19 | } | ||
| 20 | |||
| 21 | std::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 | |||
| 33 | std::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 | |||
| 44 | bool 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 | |||
| 56 | std::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 | |||
| 65 | std::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 | |||
| 106 | std::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 | |||
| 129 | std::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 | |||
| 152 | std::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 | |||
| 162 | std::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 | |||
| 174 | std::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 | |||
| 186 | std::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 | |||
| 202 | std::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 | |||
| 214 | void 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 | |||
| 235 | void 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 | |||
| 260 | void 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 | |||
| 286 | void 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 | |||
| 302 | EmitContext::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 | |||
| 404 | void 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 | |||
| 461 | void 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 | |||
| 473 | void 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 | |||
| 487 | void 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 | |||
| 526 | void 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 | |||
| 603 | std::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 | |||
| 663 | void 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 | |||
| 687 | void 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 | |||
| 708 | void 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 | |||
| 16 | namespace Shader { | ||
| 17 | struct Info; | ||
| 18 | struct Profile; | ||
| 19 | struct RuntimeInfo; | ||
| 20 | } // namespace Shader | ||
| 21 | |||
| 22 | namespace Shader::Backend { | ||
| 23 | struct Bindings; | ||
| 24 | } | ||
| 25 | |||
| 26 | namespace Shader::IR { | ||
| 27 | class Inst; | ||
| 28 | struct Program; | ||
| 29 | } // namespace Shader::IR | ||
| 30 | |||
| 31 | namespace Shader::Backend::GLSL { | ||
| 32 | |||
| 33 | struct GenericElementInfo { | ||
| 34 | std::string name; | ||
| 35 | u32 first_element{}; | ||
| 36 | u32 num_components{}; | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct TextureImageDefinition { | ||
| 40 | u32 binding; | ||
| 41 | u32 count; | ||
| 42 | }; | ||
| 43 | |||
| 44 | class EmitContext { | ||
| 45 | public: | ||
| 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 | |||
| 162 | private: | ||
| 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 | |||
| 17 | namespace Shader::Backend::GLSL { | ||
| 18 | namespace { | ||
| 19 | template <class Func> | ||
| 20 | struct FuncTraits {}; | ||
| 21 | |||
| 22 | template <class ReturnType_, class... Args> | ||
| 23 | struct 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 | |||
| 32 | template <auto func, typename... Args> | ||
| 33 | void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) { | ||
| 34 | inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...)); | ||
| 35 | } | ||
| 36 | |||
| 37 | template <typename ArgType> | ||
| 38 | auto 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 | |||
| 54 | template <auto func, bool is_first_arg_inst, size_t... I> | ||
| 55 | void 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 | |||
| 75 | template <auto func> | ||
| 76 | void 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 | |||
| 89 | void 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 | |||
| 100 | bool IsReference(IR::Inst& inst) { | ||
| 101 | return inst.GetOpcode() == IR::Opcode::Reference; | ||
| 102 | } | ||
| 103 | |||
| 104 | void 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 | |||
| 123 | void 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 | |||
| 134 | void 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 | |||
| 178 | std::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 | |||
| 185 | bool 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 | |||
| 195 | void 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 | |||
| 218 | std::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 | |||
| 14 | namespace 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 | |||
| 11 | namespace Shader::Backend::GLSL { | ||
| 12 | namespace { | ||
| 13 | constexpr char cas_loop[]{ | ||
| 14 | "for (;;){{uint old={};{}=atomicCompSwap({},old,{}({},{}));if({}==old){{break;}}}}"}; | ||
| 15 | |||
| 16 | void 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 | |||
| 23 | void 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 | |||
| 31 | void 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 | |||
| 42 | void 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 | |||
| 47 | void 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 | |||
| 53 | void 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 | |||
| 58 | void 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 | |||
| 64 | void 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 | |||
| 69 | void 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 | |||
| 74 | void 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 | |||
| 79 | void 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 | |||
| 84 | void 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 | |||
| 89 | void 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 | |||
| 94 | void 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 | |||
| 99 | void 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 | |||
| 108 | void 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 | |||
| 114 | void 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 | |||
| 120 | void 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 | |||
| 126 | void 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 | |||
| 132 | void 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 | |||
| 138 | void 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 | |||
| 143 | void 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 | |||
| 148 | void 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 | |||
| 154 | void 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 | |||
| 160 | void 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 | |||
| 166 | void 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 | |||
| 172 | void 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 | |||
| 183 | void 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 | |||
| 196 | void 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 | |||
| 208 | void 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 | |||
| 221 | void 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 | |||
| 234 | void 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 | |||
| 243 | void 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 | |||
| 251 | void 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 | |||
| 260 | void 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 | |||
| 268 | void 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 | |||
| 273 | void 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 | |||
| 278 | void 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 | |||
| 283 | void 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 | |||
| 288 | void 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 | |||
| 293 | void 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 | |||
| 298 | void 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 | |||
| 303 | void EmitGlobalAtomicIAdd32(EmitContext&) { | ||
| 304 | throw NotImplementedException("GLSL Instrucion"); | ||
| 305 | } | ||
| 306 | |||
| 307 | void EmitGlobalAtomicSMin32(EmitContext&) { | ||
| 308 | throw NotImplementedException("GLSL Instrucion"); | ||
| 309 | } | ||
| 310 | |||
| 311 | void EmitGlobalAtomicUMin32(EmitContext&) { | ||
| 312 | throw NotImplementedException("GLSL Instrucion"); | ||
| 313 | } | ||
| 314 | |||
| 315 | void EmitGlobalAtomicSMax32(EmitContext&) { | ||
| 316 | throw NotImplementedException("GLSL Instrucion"); | ||
| 317 | } | ||
| 318 | |||
| 319 | void EmitGlobalAtomicUMax32(EmitContext&) { | ||
| 320 | throw NotImplementedException("GLSL Instrucion"); | ||
| 321 | } | ||
| 322 | |||
| 323 | void EmitGlobalAtomicInc32(EmitContext&) { | ||
| 324 | throw NotImplementedException("GLSL Instrucion"); | ||
| 325 | } | ||
| 326 | |||
| 327 | void EmitGlobalAtomicDec32(EmitContext&) { | ||
| 328 | throw NotImplementedException("GLSL Instrucion"); | ||
| 329 | } | ||
| 330 | |||
| 331 | void EmitGlobalAtomicAnd32(EmitContext&) { | ||
| 332 | throw NotImplementedException("GLSL Instrucion"); | ||
| 333 | } | ||
| 334 | |||
| 335 | void EmitGlobalAtomicOr32(EmitContext&) { | ||
| 336 | throw NotImplementedException("GLSL Instrucion"); | ||
| 337 | } | ||
| 338 | |||
| 339 | void EmitGlobalAtomicXor32(EmitContext&) { | ||
| 340 | throw NotImplementedException("GLSL Instrucion"); | ||
| 341 | } | ||
| 342 | |||
| 343 | void EmitGlobalAtomicExchange32(EmitContext&) { | ||
| 344 | throw NotImplementedException("GLSL Instrucion"); | ||
| 345 | } | ||
| 346 | |||
| 347 | void EmitGlobalAtomicIAdd64(EmitContext&) { | ||
| 348 | throw NotImplementedException("GLSL Instrucion"); | ||
| 349 | } | ||
| 350 | |||
| 351 | void EmitGlobalAtomicSMin64(EmitContext&) { | ||
| 352 | throw NotImplementedException("GLSL Instrucion"); | ||
| 353 | } | ||
| 354 | |||
| 355 | void EmitGlobalAtomicUMin64(EmitContext&) { | ||
| 356 | throw NotImplementedException("GLSL Instrucion"); | ||
| 357 | } | ||
| 358 | |||
| 359 | void EmitGlobalAtomicSMax64(EmitContext&) { | ||
| 360 | throw NotImplementedException("GLSL Instrucion"); | ||
| 361 | } | ||
| 362 | |||
| 363 | void EmitGlobalAtomicUMax64(EmitContext&) { | ||
| 364 | throw NotImplementedException("GLSL Instrucion"); | ||
| 365 | } | ||
| 366 | |||
| 367 | void EmitGlobalAtomicInc64(EmitContext&) { | ||
| 368 | throw NotImplementedException("GLSL Instrucion"); | ||
| 369 | } | ||
| 370 | |||
| 371 | void EmitGlobalAtomicDec64(EmitContext&) { | ||
| 372 | throw NotImplementedException("GLSL Instrucion"); | ||
| 373 | } | ||
| 374 | |||
| 375 | void EmitGlobalAtomicAnd64(EmitContext&) { | ||
| 376 | throw NotImplementedException("GLSL Instrucion"); | ||
| 377 | } | ||
| 378 | |||
| 379 | void EmitGlobalAtomicOr64(EmitContext&) { | ||
| 380 | throw NotImplementedException("GLSL Instrucion"); | ||
| 381 | } | ||
| 382 | |||
| 383 | void EmitGlobalAtomicXor64(EmitContext&) { | ||
| 384 | throw NotImplementedException("GLSL Instrucion"); | ||
| 385 | } | ||
| 386 | |||
| 387 | void EmitGlobalAtomicExchange64(EmitContext&) { | ||
| 388 | throw NotImplementedException("GLSL Instrucion"); | ||
| 389 | } | ||
| 390 | |||
| 391 | void EmitGlobalAtomicAddF32(EmitContext&) { | ||
| 392 | throw NotImplementedException("GLSL Instrucion"); | ||
| 393 | } | ||
| 394 | |||
| 395 | void EmitGlobalAtomicAddF16x2(EmitContext&) { | ||
| 396 | throw NotImplementedException("GLSL Instrucion"); | ||
| 397 | } | ||
| 398 | |||
| 399 | void EmitGlobalAtomicAddF32x2(EmitContext&) { | ||
| 400 | throw NotImplementedException("GLSL Instrucion"); | ||
| 401 | } | ||
| 402 | |||
| 403 | void EmitGlobalAtomicMinF16x2(EmitContext&) { | ||
| 404 | throw NotImplementedException("GLSL Instrucion"); | ||
| 405 | } | ||
| 406 | |||
| 407 | void EmitGlobalAtomicMinF32x2(EmitContext&) { | ||
| 408 | throw NotImplementedException("GLSL Instrucion"); | ||
| 409 | } | ||
| 410 | |||
| 411 | void EmitGlobalAtomicMaxF16x2(EmitContext&) { | ||
| 412 | throw NotImplementedException("GLSL Instrucion"); | ||
| 413 | } | ||
| 414 | |||
| 415 | void 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 | |||
| 9 | namespace Shader::Backend::GLSL { | ||
| 10 | void EmitBarrier(EmitContext& ctx) { | ||
| 11 | ctx.Add("barrier();"); | ||
| 12 | } | ||
| 13 | |||
| 14 | void EmitWorkgroupMemoryBarrier(EmitContext& ctx) { | ||
| 15 | ctx.Add("groupMemoryBarrier();"); | ||
| 16 | } | ||
| 17 | |||
| 18 | void 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 | |||
| 11 | namespace Shader::Backend::GLSL { | ||
| 12 | namespace { | ||
| 13 | void 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 | |||
| 24 | void EmitIdentity(EmitContext&, IR::Inst& inst, const IR::Value& value) { | ||
| 25 | Alias(inst, value); | ||
| 26 | } | ||
| 27 | |||
| 28 | void 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 | |||
| 38 | void EmitBitCastU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) { | ||
| 39 | NotImplemented(); | ||
| 40 | } | ||
| 41 | |||
| 42 | void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 43 | ctx.AddU32("{}=ftou({});", inst, value); | ||
| 44 | } | ||
| 45 | |||
| 46 | void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 47 | ctx.AddU64("{}=doubleBitsToUint64({});", inst, value); | ||
| 48 | } | ||
| 49 | |||
| 50 | void EmitBitCastF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst) { | ||
| 51 | NotImplemented(); | ||
| 52 | } | ||
| 53 | |||
| 54 | void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 55 | ctx.AddF32("{}=utof({});", inst, value); | ||
| 56 | } | ||
| 57 | |||
| 58 | void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 59 | ctx.AddF64("{}=uint64BitsToDouble({});", inst, value); | ||
| 60 | } | ||
| 61 | |||
| 62 | void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 63 | ctx.AddU64("{}=packUint2x32({});", inst, value); | ||
| 64 | } | ||
| 65 | |||
| 66 | void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 67 | ctx.AddU32x2("{}=unpackUint2x32({});", inst, value); | ||
| 68 | } | ||
| 69 | |||
| 70 | void EmitPackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 71 | ctx.AddU32("{}=packFloat2x16({});", inst, value); | ||
| 72 | } | ||
| 73 | |||
| 74 | void EmitUnpackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 75 | ctx.AddF16x2("{}=unpackFloat2x16({});", inst, value); | ||
| 76 | } | ||
| 77 | |||
| 78 | void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 79 | ctx.AddU32("{}=packHalf2x16({});", inst, value); | ||
| 80 | } | ||
| 81 | |||
| 82 | void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 83 | ctx.AddF32x2("{}=unpackHalf2x16({});", inst, value); | ||
| 84 | } | ||
| 85 | |||
| 86 | void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 87 | ctx.AddF64("{}=packDouble2x32({});", inst, value); | ||
| 88 | } | ||
| 89 | |||
| 90 | void 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 | |||
| 11 | namespace Shader::Backend::GLSL { | ||
| 12 | namespace { | ||
| 13 | constexpr std::string_view SWIZZLE{"xyzw"}; | ||
| 14 | void 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 | |||
| 25 | void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1, | ||
| 26 | std::string_view e2) { | ||
| 27 | ctx.AddU32x2("{}=uvec2({},{});", inst, e1, e2); | ||
| 28 | } | ||
| 29 | |||
| 30 | void 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 | |||
| 35 | void 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 | |||
| 40 | void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 41 | u32 index) { | ||
| 42 | ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]); | ||
| 43 | } | ||
| 44 | |||
| 45 | void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 46 | u32 index) { | ||
| 47 | ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]); | ||
| 48 | } | ||
| 49 | |||
| 50 | void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 51 | u32 index) { | ||
| 52 | ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]); | ||
| 53 | } | ||
| 54 | |||
| 55 | void 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 | |||
| 61 | void 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 | |||
| 67 | void 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 | |||
| 73 | void EmitCompositeConstructF16x2([[maybe_unused]] EmitContext& ctx, | ||
| 74 | [[maybe_unused]] std::string_view e1, | ||
| 75 | [[maybe_unused]] std::string_view e2) { | ||
| 76 | NotImplemented(); | ||
| 77 | } | ||
| 78 | |||
| 79 | void 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 | |||
| 86 | void 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 | |||
| 94 | void EmitCompositeExtractF16x2([[maybe_unused]] EmitContext& ctx, | ||
| 95 | [[maybe_unused]] std::string_view composite, | ||
| 96 | [[maybe_unused]] u32 index) { | ||
| 97 | NotImplemented(); | ||
| 98 | } | ||
| 99 | |||
| 100 | void EmitCompositeExtractF16x3([[maybe_unused]] EmitContext& ctx, | ||
| 101 | [[maybe_unused]] std::string_view composite, | ||
| 102 | [[maybe_unused]] u32 index) { | ||
| 103 | NotImplemented(); | ||
| 104 | } | ||
| 105 | |||
| 106 | void EmitCompositeExtractF16x4([[maybe_unused]] EmitContext& ctx, | ||
| 107 | [[maybe_unused]] std::string_view composite, | ||
| 108 | [[maybe_unused]] u32 index) { | ||
| 109 | NotImplemented(); | ||
| 110 | } | ||
| 111 | |||
| 112 | void 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 | |||
| 119 | void 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 | |||
| 126 | void 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 | |||
| 133 | void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1, | ||
| 134 | std::string_view e2) { | ||
| 135 | ctx.AddF32x2("{}=vec2({},{});", inst, e1, e2); | ||
| 136 | } | ||
| 137 | |||
| 138 | void 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 | |||
| 143 | void 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 | |||
| 148 | void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 149 | u32 index) { | ||
| 150 | ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]); | ||
| 151 | } | ||
| 152 | |||
| 153 | void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 154 | u32 index) { | ||
| 155 | ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]); | ||
| 156 | } | ||
| 157 | |||
| 158 | void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 159 | u32 index) { | ||
| 160 | ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]); | ||
| 161 | } | ||
| 162 | |||
| 163 | void 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 | |||
| 169 | void 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 | |||
| 175 | void 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 | |||
| 181 | void EmitCompositeConstructF64x2([[maybe_unused]] EmitContext& ctx) { | ||
| 182 | NotImplemented(); | ||
| 183 | } | ||
| 184 | |||
| 185 | void EmitCompositeConstructF64x3([[maybe_unused]] EmitContext& ctx) { | ||
| 186 | NotImplemented(); | ||
| 187 | } | ||
| 188 | |||
| 189 | void EmitCompositeConstructF64x4([[maybe_unused]] EmitContext& ctx) { | ||
| 190 | NotImplemented(); | ||
| 191 | } | ||
| 192 | |||
| 193 | void EmitCompositeExtractF64x2([[maybe_unused]] EmitContext& ctx) { | ||
| 194 | NotImplemented(); | ||
| 195 | } | ||
| 196 | |||
| 197 | void EmitCompositeExtractF64x3([[maybe_unused]] EmitContext& ctx) { | ||
| 198 | NotImplemented(); | ||
| 199 | } | ||
| 200 | |||
| 201 | void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) { | ||
| 202 | NotImplemented(); | ||
| 203 | } | ||
| 204 | |||
| 205 | void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object, | ||
| 206 | u32 index) { | ||
| 207 | ctx.Add("{}.{}={};", composite, SWIZZLE[index], object); | ||
| 208 | } | ||
| 209 | |||
| 210 | void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object, | ||
| 211 | u32 index) { | ||
| 212 | ctx.Add("{}.{}={};", composite, SWIZZLE[index], object); | ||
| 213 | } | ||
| 214 | |||
| 215 | void 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 | |||
| 13 | namespace Shader::Backend::GLSL { | ||
| 14 | namespace { | ||
| 15 | constexpr char SWIZZLE[]{"xyzw"}; | ||
| 16 | |||
| 17 | u32 CbufIndex(u32 offset) { | ||
| 18 | return (offset / 4) % 4; | ||
| 19 | } | ||
| 20 | |||
| 21 | char OffsetSwizzle(u32 offset) { | ||
| 22 | return SWIZZLE[CbufIndex(offset)]; | ||
| 23 | } | ||
| 24 | |||
| 25 | bool IsInputArray(Stage stage) { | ||
| 26 | return stage == Stage::Geometry || stage == Stage::TessellationControl || | ||
| 27 | stage == Stage::TessellationEval; | ||
| 28 | } | ||
| 29 | |||
| 30 | std::string InputVertexIndex(EmitContext& ctx, std::string_view vertex) { | ||
| 31 | return IsInputArray(ctx.stage) ? fmt::format("[{}]", vertex) : ""; | ||
| 32 | } | ||
| 33 | |||
| 34 | std::string_view OutputVertexIndex(EmitContext& ctx) { | ||
| 35 | return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : ""; | ||
| 36 | } | ||
| 37 | |||
| 38 | void 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 | |||
| 76 | void 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 | |||
| 89 | void 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 | |||
| 102 | u32 TexCoordIndex(IR::Attribute attr) { | ||
| 103 | return (static_cast<u32>(attr) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4; | ||
| 104 | } | ||
| 105 | } // Anonymous namespace | ||
| 106 | |||
| 107 | void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 108 | const IR::Value& offset) { | ||
| 109 | GetCbuf8(ctx, inst, binding, offset, "ftou"); | ||
| 110 | } | ||
| 111 | |||
| 112 | void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 113 | const IR::Value& offset) { | ||
| 114 | GetCbuf8(ctx, inst, binding, offset, "ftoi"); | ||
| 115 | } | ||
| 116 | |||
| 117 | void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 118 | const IR::Value& offset) { | ||
| 119 | GetCbuf16(ctx, inst, binding, offset, "ftou"); | ||
| 120 | } | ||
| 121 | |||
| 122 | void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 123 | const IR::Value& offset) { | ||
| 124 | GetCbuf16(ctx, inst, binding, offset, "ftoi"); | ||
| 125 | } | ||
| 126 | |||
| 127 | void 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 | |||
| 133 | void 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 | |||
| 139 | void 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 | |||
| 176 | void 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 | |||
| 250 | void 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 | |||
| 359 | void 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 | |||
| 366 | void 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 | |||
| 373 | void 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 | |||
| 383 | void 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 | |||
| 410 | void 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 | |||
| 415 | void EmitSetSampleMask(EmitContext& ctx, std::string_view value) { | ||
| 416 | ctx.Add("gl_SampleMask[0]=int({});", value); | ||
| 417 | } | ||
| 418 | |||
| 419 | void EmitSetFragDepth(EmitContext& ctx, std::string_view value) { | ||
| 420 | ctx.Add("gl_FragDepth={};", value); | ||
| 421 | } | ||
| 422 | |||
| 423 | void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst) { | ||
| 424 | ctx.AddU32x3("{}=gl_LocalInvocationID;", inst); | ||
| 425 | } | ||
| 426 | |||
| 427 | void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst) { | ||
| 428 | ctx.AddU32x3("{}=gl_WorkGroupID;", inst); | ||
| 429 | } | ||
| 430 | |||
| 431 | void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) { | ||
| 432 | ctx.AddU32("{}=uint(gl_InvocationID);", inst); | ||
| 433 | } | ||
| 434 | |||
| 435 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst) { | ||
| 436 | ctx.AddU32("{}=uint(gl_SampleID);", inst); | ||
| 437 | } | ||
| 438 | |||
| 439 | void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst) { | ||
| 440 | ctx.AddU1("{}=gl_HelperInvocation;", inst); | ||
| 441 | } | ||
| 442 | |||
| 443 | void EmitYDirection(EmitContext& ctx, IR::Inst& inst) { | ||
| 444 | ctx.uses_y_direction = true; | ||
| 445 | ctx.AddF32("{}=gl_FrontMaterial.ambient.a;", inst); | ||
| 446 | } | ||
| 447 | |||
| 448 | void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) { | ||
| 449 | ctx.AddU32("{}=lmem[{}];", inst, word_offset); | ||
| 450 | } | ||
| 451 | |||
| 452 | void 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 | |||
| 11 | namespace Shader::Backend::GLSL { | ||
| 12 | |||
| 13 | void EmitJoin(EmitContext&) { | ||
| 14 | throw NotImplementedException("Join shouldn't be emitted"); | ||
| 15 | } | ||
| 16 | |||
| 17 | void 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 | |||
| 11 | namespace Shader::Backend::GLSL { | ||
| 12 | void EmitConvertS16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 13 | [[maybe_unused]] std::string_view value) { | ||
| 14 | NotImplemented(); | ||
| 15 | } | ||
| 16 | |||
| 17 | void 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 | |||
| 21 | void EmitConvertS16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 22 | [[maybe_unused]] std::string_view value) { | ||
| 23 | NotImplemented(); | ||
| 24 | } | ||
| 25 | |||
| 26 | void EmitConvertS32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 27 | [[maybe_unused]] std::string_view value) { | ||
| 28 | NotImplemented(); | ||
| 29 | } | ||
| 30 | |||
| 31 | void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 32 | ctx.AddU32("{}=int({});", inst, value); | ||
| 33 | } | ||
| 34 | |||
| 35 | void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 36 | ctx.AddU32("{}=int({});", inst, value); | ||
| 37 | } | ||
| 38 | |||
| 39 | void EmitConvertS64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 40 | [[maybe_unused]] std::string_view value) { | ||
| 41 | NotImplemented(); | ||
| 42 | } | ||
| 43 | |||
| 44 | void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 45 | ctx.AddU64("{}=int64_t({});", inst, value); | ||
| 46 | } | ||
| 47 | |||
| 48 | void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 49 | ctx.AddU64("{}=int64_t({});", inst, value); | ||
| 50 | } | ||
| 51 | |||
| 52 | void EmitConvertU16F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 53 | [[maybe_unused]] std::string_view value) { | ||
| 54 | NotImplemented(); | ||
| 55 | } | ||
| 56 | |||
| 57 | void EmitConvertU16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 58 | [[maybe_unused]] std::string_view value) { | ||
| 59 | NotImplemented(); | ||
| 60 | } | ||
| 61 | |||
| 62 | void EmitConvertU16F64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 63 | [[maybe_unused]] std::string_view value) { | ||
| 64 | NotImplemented(); | ||
| 65 | } | ||
| 66 | |||
| 67 | void EmitConvertU32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 68 | [[maybe_unused]] std::string_view value) { | ||
| 69 | NotImplemented(); | ||
| 70 | } | ||
| 71 | |||
| 72 | void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 73 | ctx.AddU32("{}=uint({});", inst, value); | ||
| 74 | } | ||
| 75 | |||
| 76 | void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 77 | ctx.AddU32("{}=uint({});", inst, value); | ||
| 78 | } | ||
| 79 | |||
| 80 | void EmitConvertU64F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 81 | [[maybe_unused]] std::string_view value) { | ||
| 82 | NotImplemented(); | ||
| 83 | } | ||
| 84 | |||
| 85 | void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 86 | ctx.AddU64("{}=uint64_t({});", inst, value); | ||
| 87 | } | ||
| 88 | |||
| 89 | void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 90 | ctx.AddU64("{}=uint64_t({});", inst, value); | ||
| 91 | } | ||
| 92 | |||
| 93 | void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 94 | ctx.AddU64("{}=uint64_t({});", inst, value); | ||
| 95 | } | ||
| 96 | |||
| 97 | void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 98 | ctx.AddU32("{}=uint({});", inst, value); | ||
| 99 | } | ||
| 100 | |||
| 101 | void EmitConvertF16F32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 102 | [[maybe_unused]] std::string_view value) { | ||
| 103 | NotImplemented(); | ||
| 104 | } | ||
| 105 | |||
| 106 | void EmitConvertF32F16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 107 | [[maybe_unused]] std::string_view value) { | ||
| 108 | NotImplemented(); | ||
| 109 | } | ||
| 110 | |||
| 111 | void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 112 | ctx.AddF32("{}=float({});", inst, value); | ||
| 113 | } | ||
| 114 | |||
| 115 | void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 116 | ctx.AddF64("{}=double({});", inst, value); | ||
| 117 | } | ||
| 118 | |||
| 119 | void EmitConvertF16S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 120 | [[maybe_unused]] std::string_view value) { | ||
| 121 | NotImplemented(); | ||
| 122 | } | ||
| 123 | |||
| 124 | void EmitConvertF16S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 125 | [[maybe_unused]] std::string_view value) { | ||
| 126 | NotImplemented(); | ||
| 127 | } | ||
| 128 | |||
| 129 | void EmitConvertF16S32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 130 | [[maybe_unused]] std::string_view value) { | ||
| 131 | NotImplemented(); | ||
| 132 | } | ||
| 133 | |||
| 134 | void EmitConvertF16S64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 135 | [[maybe_unused]] std::string_view value) { | ||
| 136 | NotImplemented(); | ||
| 137 | } | ||
| 138 | |||
| 139 | void EmitConvertF16U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 140 | [[maybe_unused]] std::string_view value) { | ||
| 141 | NotImplemented(); | ||
| 142 | } | ||
| 143 | |||
| 144 | void EmitConvertF16U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 145 | [[maybe_unused]] std::string_view value) { | ||
| 146 | NotImplemented(); | ||
| 147 | } | ||
| 148 | |||
| 149 | void EmitConvertF16U32([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 150 | [[maybe_unused]] std::string_view value) { | ||
| 151 | NotImplemented(); | ||
| 152 | } | ||
| 153 | |||
| 154 | void EmitConvertF16U64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 155 | [[maybe_unused]] std::string_view value) { | ||
| 156 | NotImplemented(); | ||
| 157 | } | ||
| 158 | |||
| 159 | void EmitConvertF32S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 160 | [[maybe_unused]] std::string_view value) { | ||
| 161 | NotImplemented(); | ||
| 162 | } | ||
| 163 | |||
| 164 | void EmitConvertF32S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 165 | [[maybe_unused]] std::string_view value) { | ||
| 166 | NotImplemented(); | ||
| 167 | } | ||
| 168 | |||
| 169 | void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 170 | ctx.AddF32("{}=float(int({}));", inst, value); | ||
| 171 | } | ||
| 172 | |||
| 173 | void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 174 | ctx.AddF32("{}=float(int64_t({}));", inst, value); | ||
| 175 | } | ||
| 176 | |||
| 177 | void EmitConvertF32U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 178 | [[maybe_unused]] std::string_view value) { | ||
| 179 | NotImplemented(); | ||
| 180 | } | ||
| 181 | |||
| 182 | void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 183 | ctx.AddF32("{}=float({}&0xffff);", inst, value); | ||
| 184 | } | ||
| 185 | |||
| 186 | void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 187 | ctx.AddF32("{}=float({});", inst, value); | ||
| 188 | } | ||
| 189 | |||
| 190 | void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 191 | ctx.AddF32("{}=float({});", inst, value); | ||
| 192 | } | ||
| 193 | |||
| 194 | void EmitConvertF64S8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 195 | [[maybe_unused]] std::string_view value) { | ||
| 196 | NotImplemented(); | ||
| 197 | } | ||
| 198 | |||
| 199 | void EmitConvertF64S16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 200 | [[maybe_unused]] std::string_view value) { | ||
| 201 | NotImplemented(); | ||
| 202 | } | ||
| 203 | |||
| 204 | void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 205 | ctx.AddF64("{}=double(int({}));", inst, value); | ||
| 206 | } | ||
| 207 | |||
| 208 | void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 209 | ctx.AddF64("{}=double(int64_t({}));", inst, value); | ||
| 210 | } | ||
| 211 | |||
| 212 | void EmitConvertF64U8([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 213 | [[maybe_unused]] std::string_view value) { | ||
| 214 | NotImplemented(); | ||
| 215 | } | ||
| 216 | |||
| 217 | void EmitConvertF64U16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 218 | [[maybe_unused]] std::string_view value) { | ||
| 219 | NotImplemented(); | ||
| 220 | } | ||
| 221 | |||
| 222 | void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 223 | ctx.AddF64("{}=double({});", inst, value); | ||
| 224 | } | ||
| 225 | |||
| 226 | void 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 | |||
| 12 | namespace Shader::Backend::GLSL { | ||
| 13 | namespace { | ||
| 14 | void 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 | |||
| 22 | bool IsPrecise(const IR::Inst& inst) { | ||
| 23 | return inst.Flags<IR::FpControl>().no_contraction; | ||
| 24 | } | ||
| 25 | } // Anonymous namespace | ||
| 26 | |||
| 27 | void EmitFPAbs16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 28 | [[maybe_unused]] std::string_view value) { | ||
| 29 | NotImplemented(); | ||
| 30 | } | ||
| 31 | |||
| 32 | void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 33 | ctx.AddF32("{}=abs({});", inst, value); | ||
| 34 | } | ||
| 35 | |||
| 36 | void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 37 | ctx.AddF64("{}=abs({});", inst, value); | ||
| 38 | } | ||
| 39 | |||
| 40 | void 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 | |||
| 45 | void 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 | |||
| 53 | void 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 | |||
| 61 | void 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 | |||
| 67 | void 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 | |||
| 76 | void 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 | |||
| 85 | void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 86 | ctx.AddF32("{}=max({},{});", inst, a, b); | ||
| 87 | } | ||
| 88 | |||
| 89 | void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 90 | ctx.AddF64("{}=max({},{});", inst, a, b); | ||
| 91 | } | ||
| 92 | |||
| 93 | void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 94 | ctx.AddF32("{}=min({},{});", inst, a, b); | ||
| 95 | } | ||
| 96 | |||
| 97 | void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 98 | ctx.AddF64("{}=min({},{});", inst, a, b); | ||
| 99 | } | ||
| 100 | |||
| 101 | void 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 | |||
| 106 | void 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 | |||
| 114 | void 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 | |||
| 122 | void EmitFPNeg16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 123 | [[maybe_unused]] std::string_view value) { | ||
| 124 | NotImplemented(); | ||
| 125 | } | ||
| 126 | |||
| 127 | void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 128 | ctx.AddF32("{}=-({});", inst, value); | ||
| 129 | } | ||
| 130 | |||
| 131 | void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 132 | ctx.AddF64("{}=-({});", inst, value); | ||
| 133 | } | ||
| 134 | |||
| 135 | void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 136 | ctx.AddF32("{}=sin({});", inst, value); | ||
| 137 | } | ||
| 138 | |||
| 139 | void EmitFPCos(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 140 | ctx.AddF32("{}=cos({});", inst, value); | ||
| 141 | } | ||
| 142 | |||
| 143 | void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 144 | ctx.AddF32("{}=exp2({});", inst, value); | ||
| 145 | } | ||
| 146 | |||
| 147 | void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 148 | ctx.AddF32("{}=log2({});", inst, value); | ||
| 149 | } | ||
| 150 | |||
| 151 | void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 152 | ctx.AddF32("{}=(1.0f)/{};", inst, value); | ||
| 153 | } | ||
| 154 | |||
| 155 | void EmitFPRecip64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 156 | ctx.AddF64("{}=1.0/{};", inst, value); | ||
| 157 | } | ||
| 158 | |||
| 159 | void 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 | |||
| 164 | void EmitFPRecipSqrt64([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 165 | [[maybe_unused]] std::string_view value) { | ||
| 166 | NotImplemented(); | ||
| 167 | } | ||
| 168 | |||
| 169 | void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 170 | ctx.AddF32("{}=sqrt({});", inst, value); | ||
| 171 | } | ||
| 172 | |||
| 173 | void EmitFPSaturate16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 174 | [[maybe_unused]] std::string_view value) { | ||
| 175 | NotImplemented(); | ||
| 176 | } | ||
| 177 | |||
| 178 | void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 179 | ctx.AddF32("{}=min(max({},0.0),1.0);", inst, value); | ||
| 180 | } | ||
| 181 | |||
| 182 | void EmitFPSaturate64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 183 | ctx.AddF64("{}=min(max({},0.0),1.0);", inst, value); | ||
| 184 | } | ||
| 185 | |||
| 186 | void 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 | |||
| 193 | void 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 | |||
| 199 | void 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 | |||
| 205 | void EmitFPRoundEven16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 206 | [[maybe_unused]] std::string_view value) { | ||
| 207 | NotImplemented(); | ||
| 208 | } | ||
| 209 | |||
| 210 | void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 211 | ctx.AddF32("{}=roundEven({});", inst, value); | ||
| 212 | } | ||
| 213 | |||
| 214 | void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 215 | ctx.AddF64("{}=roundEven({});", inst, value); | ||
| 216 | } | ||
| 217 | |||
| 218 | void EmitFPFloor16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 219 | [[maybe_unused]] std::string_view value) { | ||
| 220 | NotImplemented(); | ||
| 221 | } | ||
| 222 | |||
| 223 | void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 224 | ctx.AddF32("{}=floor({});", inst, value); | ||
| 225 | } | ||
| 226 | |||
| 227 | void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 228 | ctx.AddF64("{}=floor({});", inst, value); | ||
| 229 | } | ||
| 230 | |||
| 231 | void EmitFPCeil16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 232 | [[maybe_unused]] std::string_view value) { | ||
| 233 | NotImplemented(); | ||
| 234 | } | ||
| 235 | |||
| 236 | void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 237 | ctx.AddF32("{}=ceil({});", inst, value); | ||
| 238 | } | ||
| 239 | |||
| 240 | void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 241 | ctx.AddF64("{}=ceil({});", inst, value); | ||
| 242 | } | ||
| 243 | |||
| 244 | void EmitFPTrunc16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 245 | [[maybe_unused]] std::string_view value) { | ||
| 246 | NotImplemented(); | ||
| 247 | } | ||
| 248 | |||
| 249 | void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 250 | ctx.AddF32("{}=trunc({});", inst, value); | ||
| 251 | } | ||
| 252 | |||
| 253 | void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 254 | ctx.AddF64("{}=trunc({});", inst, value); | ||
| 255 | } | ||
| 256 | |||
| 257 | void EmitFPOrdEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs, | ||
| 258 | [[maybe_unused]] std::string_view rhs) { | ||
| 259 | NotImplemented(); | ||
| 260 | } | ||
| 261 | |||
| 262 | void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 263 | std::string_view rhs) { | ||
| 264 | Compare(ctx, inst, lhs, rhs, "==", true); | ||
| 265 | } | ||
| 266 | |||
| 267 | void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 268 | std::string_view rhs) { | ||
| 269 | Compare(ctx, inst, lhs, rhs, "==", true); | ||
| 270 | } | ||
| 271 | |||
| 272 | void EmitFPUnordEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs, | ||
| 273 | [[maybe_unused]] std::string_view rhs) { | ||
| 274 | NotImplemented(); | ||
| 275 | } | ||
| 276 | |||
| 277 | void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 278 | std::string_view rhs) { | ||
| 279 | Compare(ctx, inst, lhs, rhs, "==", false); | ||
| 280 | } | ||
| 281 | |||
| 282 | void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 283 | std::string_view rhs) { | ||
| 284 | Compare(ctx, inst, lhs, rhs, "==", false); | ||
| 285 | } | ||
| 286 | |||
| 287 | void EmitFPOrdNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs, | ||
| 288 | [[maybe_unused]] std::string_view rhs) { | ||
| 289 | NotImplemented(); | ||
| 290 | } | ||
| 291 | |||
| 292 | void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 293 | std::string_view rhs) { | ||
| 294 | Compare(ctx, inst, lhs, rhs, "!=", true); | ||
| 295 | } | ||
| 296 | |||
| 297 | void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 298 | std::string_view rhs) { | ||
| 299 | Compare(ctx, inst, lhs, rhs, "!=", true); | ||
| 300 | } | ||
| 301 | |||
| 302 | void EmitFPUnordNotEqual16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs, | ||
| 303 | [[maybe_unused]] std::string_view rhs) { | ||
| 304 | NotImplemented(); | ||
| 305 | } | ||
| 306 | |||
| 307 | void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 308 | std::string_view rhs) { | ||
| 309 | Compare(ctx, inst, lhs, rhs, "!=", false); | ||
| 310 | } | ||
| 311 | |||
| 312 | void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 313 | std::string_view rhs) { | ||
| 314 | Compare(ctx, inst, lhs, rhs, "!=", false); | ||
| 315 | } | ||
| 316 | |||
| 317 | void EmitFPOrdLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs, | ||
| 318 | [[maybe_unused]] std::string_view rhs) { | ||
| 319 | NotImplemented(); | ||
| 320 | } | ||
| 321 | |||
| 322 | void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 323 | std::string_view rhs) { | ||
| 324 | Compare(ctx, inst, lhs, rhs, "<", true); | ||
| 325 | } | ||
| 326 | |||
| 327 | void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 328 | std::string_view rhs) { | ||
| 329 | Compare(ctx, inst, lhs, rhs, "<", true); | ||
| 330 | } | ||
| 331 | |||
| 332 | void EmitFPUnordLessThan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] std::string_view lhs, | ||
| 333 | [[maybe_unused]] std::string_view rhs) { | ||
| 334 | NotImplemented(); | ||
| 335 | } | ||
| 336 | |||
| 337 | void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 338 | std::string_view rhs) { | ||
| 339 | Compare(ctx, inst, lhs, rhs, "<", false); | ||
| 340 | } | ||
| 341 | |||
| 342 | void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 343 | std::string_view rhs) { | ||
| 344 | Compare(ctx, inst, lhs, rhs, "<", false); | ||
| 345 | } | ||
| 346 | |||
| 347 | void EmitFPOrdGreaterThan16([[maybe_unused]] EmitContext& ctx, | ||
| 348 | [[maybe_unused]] std::string_view lhs, | ||
| 349 | [[maybe_unused]] std::string_view rhs) { | ||
| 350 | NotImplemented(); | ||
| 351 | } | ||
| 352 | |||
| 353 | void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 354 | std::string_view rhs) { | ||
| 355 | Compare(ctx, inst, lhs, rhs, ">", true); | ||
| 356 | } | ||
| 357 | |||
| 358 | void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 359 | std::string_view rhs) { | ||
| 360 | Compare(ctx, inst, lhs, rhs, ">", true); | ||
| 361 | } | ||
| 362 | |||
| 363 | void EmitFPUnordGreaterThan16([[maybe_unused]] EmitContext& ctx, | ||
| 364 | [[maybe_unused]] std::string_view lhs, | ||
| 365 | [[maybe_unused]] std::string_view rhs) { | ||
| 366 | NotImplemented(); | ||
| 367 | } | ||
| 368 | |||
| 369 | void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 370 | std::string_view rhs) { | ||
| 371 | Compare(ctx, inst, lhs, rhs, ">", false); | ||
| 372 | } | ||
| 373 | |||
| 374 | void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 375 | std::string_view rhs) { | ||
| 376 | Compare(ctx, inst, lhs, rhs, ">", false); | ||
| 377 | } | ||
| 378 | |||
| 379 | void EmitFPOrdLessThanEqual16([[maybe_unused]] EmitContext& ctx, | ||
| 380 | [[maybe_unused]] std::string_view lhs, | ||
| 381 | [[maybe_unused]] std::string_view rhs) { | ||
| 382 | NotImplemented(); | ||
| 383 | } | ||
| 384 | |||
| 385 | void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 386 | std::string_view rhs) { | ||
| 387 | Compare(ctx, inst, lhs, rhs, "<=", true); | ||
| 388 | } | ||
| 389 | |||
| 390 | void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 391 | std::string_view rhs) { | ||
| 392 | Compare(ctx, inst, lhs, rhs, "<=", true); | ||
| 393 | } | ||
| 394 | |||
| 395 | void EmitFPUnordLessThanEqual16([[maybe_unused]] EmitContext& ctx, | ||
| 396 | [[maybe_unused]] std::string_view lhs, | ||
| 397 | [[maybe_unused]] std::string_view rhs) { | ||
| 398 | NotImplemented(); | ||
| 399 | } | ||
| 400 | |||
| 401 | void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 402 | std::string_view rhs) { | ||
| 403 | Compare(ctx, inst, lhs, rhs, "<=", false); | ||
| 404 | } | ||
| 405 | |||
| 406 | void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 407 | std::string_view rhs) { | ||
| 408 | Compare(ctx, inst, lhs, rhs, "<=", false); | ||
| 409 | } | ||
| 410 | |||
| 411 | void EmitFPOrdGreaterThanEqual16([[maybe_unused]] EmitContext& ctx, | ||
| 412 | [[maybe_unused]] std::string_view lhs, | ||
| 413 | [[maybe_unused]] std::string_view rhs) { | ||
| 414 | NotImplemented(); | ||
| 415 | } | ||
| 416 | |||
| 417 | void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 418 | std::string_view rhs) { | ||
| 419 | Compare(ctx, inst, lhs, rhs, ">=", true); | ||
| 420 | } | ||
| 421 | |||
| 422 | void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 423 | std::string_view rhs) { | ||
| 424 | Compare(ctx, inst, lhs, rhs, ">=", true); | ||
| 425 | } | ||
| 426 | |||
| 427 | void EmitFPUnordGreaterThanEqual16([[maybe_unused]] EmitContext& ctx, | ||
| 428 | [[maybe_unused]] std::string_view lhs, | ||
| 429 | [[maybe_unused]] std::string_view rhs) { | ||
| 430 | NotImplemented(); | ||
| 431 | } | ||
| 432 | |||
| 433 | void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 434 | std::string_view rhs) { | ||
| 435 | Compare(ctx, inst, lhs, rhs, ">=", false); | ||
| 436 | } | ||
| 437 | |||
| 438 | void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 439 | std::string_view rhs) { | ||
| 440 | Compare(ctx, inst, lhs, rhs, ">=", false); | ||
| 441 | } | ||
| 442 | |||
| 443 | void EmitFPIsNan16([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst, | ||
| 444 | [[maybe_unused]] std::string_view value) { | ||
| 445 | NotImplemented(); | ||
| 446 | } | ||
| 447 | |||
| 448 | void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 449 | ctx.AddU1("{}=isnan({});", inst, value); | ||
| 450 | } | ||
| 451 | |||
| 452 | void 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 | |||
| 13 | namespace Shader::Backend::GLSL { | ||
| 14 | namespace { | ||
| 15 | std::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 | |||
| 22 | std::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 | |||
| 29 | std::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 | |||
| 48 | std::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 | |||
| 67 | bool 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 | |||
| 78 | std::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 | |||
| 116 | std::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 | |||
| 133 | IR::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 | |||
| 142 | void 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 | |||
| 184 | void 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 | |||
| 221 | void 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 | |||
| 276 | void 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 | |||
| 326 | void 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 | |||
| 372 | void 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 | |||
| 416 | void 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 | |||
| 458 | void 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 | |||
| 485 | void 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 | |||
| 492 | void 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 | |||
| 519 | void 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 | |||
| 530 | void 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 | |||
| 537 | void 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 | |||
| 544 | void 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 | |||
| 552 | void 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 | |||
| 560 | void 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 | |||
| 568 | void 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 | |||
| 576 | void EmitImageAtomicInc32(EmitContext&, IR::Inst&, const IR::Value&, std::string_view, | ||
| 577 | std::string_view) { | ||
| 578 | NotImplemented(); | ||
| 579 | } | ||
| 580 | |||
| 581 | void EmitImageAtomicDec32(EmitContext&, IR::Inst&, const IR::Value&, std::string_view, | ||
| 582 | std::string_view) { | ||
| 583 | NotImplemented(); | ||
| 584 | } | ||
| 585 | |||
| 586 | void 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 | |||
| 593 | void 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 | |||
| 600 | void 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 | |||
| 607 | void 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 | |||
| 615 | void EmitBindlessImageSampleImplicitLod(EmitContext&) { | ||
| 616 | NotImplemented(); | ||
| 617 | } | ||
| 618 | |||
| 619 | void EmitBindlessImageSampleExplicitLod(EmitContext&) { | ||
| 620 | NotImplemented(); | ||
| 621 | } | ||
| 622 | |||
| 623 | void EmitBindlessImageSampleDrefImplicitLod(EmitContext&) { | ||
| 624 | NotImplemented(); | ||
| 625 | } | ||
| 626 | |||
| 627 | void EmitBindlessImageSampleDrefExplicitLod(EmitContext&) { | ||
| 628 | NotImplemented(); | ||
| 629 | } | ||
| 630 | |||
| 631 | void EmitBindlessImageGather(EmitContext&) { | ||
| 632 | NotImplemented(); | ||
| 633 | } | ||
| 634 | |||
| 635 | void EmitBindlessImageGatherDref(EmitContext&) { | ||
| 636 | NotImplemented(); | ||
| 637 | } | ||
| 638 | |||
| 639 | void EmitBindlessImageFetch(EmitContext&) { | ||
| 640 | NotImplemented(); | ||
| 641 | } | ||
| 642 | |||
| 643 | void EmitBindlessImageQueryDimensions(EmitContext&) { | ||
| 644 | NotImplemented(); | ||
| 645 | } | ||
| 646 | |||
| 647 | void EmitBindlessImageQueryLod(EmitContext&) { | ||
| 648 | NotImplemented(); | ||
| 649 | } | ||
| 650 | |||
| 651 | void EmitBindlessImageGradient(EmitContext&) { | ||
| 652 | NotImplemented(); | ||
| 653 | } | ||
| 654 | |||
| 655 | void EmitBindlessImageRead(EmitContext&) { | ||
| 656 | NotImplemented(); | ||
| 657 | } | ||
| 658 | |||
| 659 | void EmitBindlessImageWrite(EmitContext&) { | ||
| 660 | NotImplemented(); | ||
| 661 | } | ||
| 662 | |||
| 663 | void EmitBoundImageSampleImplicitLod(EmitContext&) { | ||
| 664 | NotImplemented(); | ||
| 665 | } | ||
| 666 | |||
| 667 | void EmitBoundImageSampleExplicitLod(EmitContext&) { | ||
| 668 | NotImplemented(); | ||
| 669 | } | ||
| 670 | |||
| 671 | void EmitBoundImageSampleDrefImplicitLod(EmitContext&) { | ||
| 672 | NotImplemented(); | ||
| 673 | } | ||
| 674 | |||
| 675 | void EmitBoundImageSampleDrefExplicitLod(EmitContext&) { | ||
| 676 | NotImplemented(); | ||
| 677 | } | ||
| 678 | |||
| 679 | void EmitBoundImageGather(EmitContext&) { | ||
| 680 | NotImplemented(); | ||
| 681 | } | ||
| 682 | |||
| 683 | void EmitBoundImageGatherDref(EmitContext&) { | ||
| 684 | NotImplemented(); | ||
| 685 | } | ||
| 686 | |||
| 687 | void EmitBoundImageFetch(EmitContext&) { | ||
| 688 | NotImplemented(); | ||
| 689 | } | ||
| 690 | |||
| 691 | void EmitBoundImageQueryDimensions(EmitContext&) { | ||
| 692 | NotImplemented(); | ||
| 693 | } | ||
| 694 | |||
| 695 | void EmitBoundImageQueryLod(EmitContext&) { | ||
| 696 | NotImplemented(); | ||
| 697 | } | ||
| 698 | |||
| 699 | void EmitBoundImageGradient(EmitContext&) { | ||
| 700 | NotImplemented(); | ||
| 701 | } | ||
| 702 | |||
| 703 | void EmitBoundImageRead(EmitContext&) { | ||
| 704 | NotImplemented(); | ||
| 705 | } | ||
| 706 | |||
| 707 | void EmitBoundImageWrite(EmitContext&) { | ||
| 708 | NotImplemented(); | ||
| 709 | } | ||
| 710 | |||
| 711 | void EmitBindlessImageAtomicIAdd32(EmitContext&) { | ||
| 712 | NotImplemented(); | ||
| 713 | } | ||
| 714 | |||
| 715 | void EmitBindlessImageAtomicSMin32(EmitContext&) { | ||
| 716 | NotImplemented(); | ||
| 717 | } | ||
| 718 | |||
| 719 | void EmitBindlessImageAtomicUMin32(EmitContext&) { | ||
| 720 | NotImplemented(); | ||
| 721 | } | ||
| 722 | |||
| 723 | void EmitBindlessImageAtomicSMax32(EmitContext&) { | ||
| 724 | NotImplemented(); | ||
| 725 | } | ||
| 726 | |||
| 727 | void EmitBindlessImageAtomicUMax32(EmitContext&) { | ||
| 728 | NotImplemented(); | ||
| 729 | } | ||
| 730 | |||
| 731 | void EmitBindlessImageAtomicInc32(EmitContext&) { | ||
| 732 | NotImplemented(); | ||
| 733 | } | ||
| 734 | |||
| 735 | void EmitBindlessImageAtomicDec32(EmitContext&) { | ||
| 736 | NotImplemented(); | ||
| 737 | } | ||
| 738 | |||
| 739 | void EmitBindlessImageAtomicAnd32(EmitContext&) { | ||
| 740 | NotImplemented(); | ||
| 741 | } | ||
| 742 | |||
| 743 | void EmitBindlessImageAtomicOr32(EmitContext&) { | ||
| 744 | NotImplemented(); | ||
| 745 | } | ||
| 746 | |||
| 747 | void EmitBindlessImageAtomicXor32(EmitContext&) { | ||
| 748 | NotImplemented(); | ||
| 749 | } | ||
| 750 | |||
| 751 | void EmitBindlessImageAtomicExchange32(EmitContext&) { | ||
| 752 | NotImplemented(); | ||
| 753 | } | ||
| 754 | |||
| 755 | void EmitBoundImageAtomicIAdd32(EmitContext&) { | ||
| 756 | NotImplemented(); | ||
| 757 | } | ||
| 758 | |||
| 759 | void EmitBoundImageAtomicSMin32(EmitContext&) { | ||
| 760 | NotImplemented(); | ||
| 761 | } | ||
| 762 | |||
| 763 | void EmitBoundImageAtomicUMin32(EmitContext&) { | ||
| 764 | NotImplemented(); | ||
| 765 | } | ||
| 766 | |||
| 767 | void EmitBoundImageAtomicSMax32(EmitContext&) { | ||
| 768 | NotImplemented(); | ||
| 769 | } | ||
| 770 | |||
| 771 | void EmitBoundImageAtomicUMax32(EmitContext&) { | ||
| 772 | NotImplemented(); | ||
| 773 | } | ||
| 774 | |||
| 775 | void EmitBoundImageAtomicInc32(EmitContext&) { | ||
| 776 | NotImplemented(); | ||
| 777 | } | ||
| 778 | |||
| 779 | void EmitBoundImageAtomicDec32(EmitContext&) { | ||
| 780 | NotImplemented(); | ||
| 781 | } | ||
| 782 | |||
| 783 | void EmitBoundImageAtomicAnd32(EmitContext&) { | ||
| 784 | NotImplemented(); | ||
| 785 | } | ||
| 786 | |||
| 787 | void EmitBoundImageAtomicOr32(EmitContext&) { | ||
| 788 | NotImplemented(); | ||
| 789 | } | ||
| 790 | |||
| 791 | void EmitBoundImageAtomicXor32(EmitContext&) { | ||
| 792 | NotImplemented(); | ||
| 793 | } | ||
| 794 | |||
| 795 | void 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 | |||
| 11 | namespace Shader::IR { | ||
| 12 | enum class Attribute : u64; | ||
| 13 | enum class Patch : u64; | ||
| 14 | class Inst; | ||
| 15 | class Value; | ||
| 16 | } // namespace Shader::IR | ||
| 17 | |||
| 18 | namespace Shader::Backend::GLSL { | ||
| 19 | class EmitContext; | ||
| 20 | |||
| 21 | #define NotImplemented() throw NotImplementedException("GLSL instruction {}", __func__) | ||
| 22 | |||
| 23 | // Microinstruction emitters | ||
| 24 | void EmitPhi(EmitContext& ctx, IR::Inst& inst); | ||
| 25 | void EmitVoid(EmitContext& ctx); | ||
| 26 | void EmitIdentity(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); | ||
| 27 | void EmitConditionRef(EmitContext& ctx, IR::Inst& inst, const IR::Value& value); | ||
| 28 | void EmitReference(EmitContext& ctx, const IR::Value& value); | ||
| 29 | void EmitPhiMove(EmitContext& ctx, const IR::Value& phi, const IR::Value& value); | ||
| 30 | void EmitJoin(EmitContext& ctx); | ||
| 31 | void EmitDemoteToHelperInvocation(EmitContext& ctx); | ||
| 32 | void EmitBarrier(EmitContext& ctx); | ||
| 33 | void EmitWorkgroupMemoryBarrier(EmitContext& ctx); | ||
| 34 | void EmitDeviceMemoryBarrier(EmitContext& ctx); | ||
| 35 | void EmitPrologue(EmitContext& ctx); | ||
| 36 | void EmitEpilogue(EmitContext& ctx); | ||
| 37 | void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream); | ||
| 38 | void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream); | ||
| 39 | void EmitGetRegister(EmitContext& ctx); | ||
| 40 | void EmitSetRegister(EmitContext& ctx); | ||
| 41 | void EmitGetPred(EmitContext& ctx); | ||
| 42 | void EmitSetPred(EmitContext& ctx); | ||
| 43 | void EmitSetGotoVariable(EmitContext& ctx); | ||
| 44 | void EmitGetGotoVariable(EmitContext& ctx); | ||
| 45 | void EmitSetIndirectBranchVariable(EmitContext& ctx); | ||
| 46 | void EmitGetIndirectBranchVariable(EmitContext& ctx); | ||
| 47 | void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 48 | const IR::Value& offset); | ||
| 49 | void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 50 | const IR::Value& offset); | ||
| 51 | void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 52 | const IR::Value& offset); | ||
| 53 | void EmitGetCbufS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 54 | const IR::Value& offset); | ||
| 55 | void EmitGetCbufU32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 56 | const IR::Value& offset); | ||
| 57 | void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 58 | const IR::Value& offset); | ||
| 59 | void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 60 | const IR::Value& offset); | ||
| 61 | void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, | ||
| 62 | std::string_view vertex); | ||
| 63 | void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, std::string_view value, | ||
| 64 | std::string_view vertex); | ||
| 65 | void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, std::string_view offset, | ||
| 66 | std::string_view vertex); | ||
| 67 | void EmitSetAttributeIndexed(EmitContext& ctx, std::string_view offset, std::string_view value, | ||
| 68 | std::string_view vertex); | ||
| 69 | void EmitGetPatch(EmitContext& ctx, IR::Inst& inst, IR::Patch patch); | ||
| 70 | void EmitSetPatch(EmitContext& ctx, IR::Patch patch, std::string_view value); | ||
| 71 | void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, std::string_view value); | ||
| 72 | void EmitSetSampleMask(EmitContext& ctx, std::string_view value); | ||
| 73 | void EmitSetFragDepth(EmitContext& ctx, std::string_view value); | ||
| 74 | void EmitGetZFlag(EmitContext& ctx); | ||
| 75 | void EmitGetSFlag(EmitContext& ctx); | ||
| 76 | void EmitGetCFlag(EmitContext& ctx); | ||
| 77 | void EmitGetOFlag(EmitContext& ctx); | ||
| 78 | void EmitSetZFlag(EmitContext& ctx); | ||
| 79 | void EmitSetSFlag(EmitContext& ctx); | ||
| 80 | void EmitSetCFlag(EmitContext& ctx); | ||
| 81 | void EmitSetOFlag(EmitContext& ctx); | ||
| 82 | void EmitWorkgroupId(EmitContext& ctx, IR::Inst& inst); | ||
| 83 | void EmitLocalInvocationId(EmitContext& ctx, IR::Inst& inst); | ||
| 84 | void EmitInvocationId(EmitContext& ctx, IR::Inst& inst); | ||
| 85 | void EmitSampleId(EmitContext& ctx, IR::Inst& inst); | ||
| 86 | void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); | ||
| 87 | void EmitYDirection(EmitContext& ctx, IR::Inst& inst); | ||
| 88 | void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset); | ||
| 89 | void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value); | ||
| 90 | void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); | ||
| 91 | void EmitUndefU8(EmitContext& ctx, IR::Inst& inst); | ||
| 92 | void EmitUndefU16(EmitContext& ctx, IR::Inst& inst); | ||
| 93 | void EmitUndefU32(EmitContext& ctx, IR::Inst& inst); | ||
| 94 | void EmitUndefU64(EmitContext& ctx, IR::Inst& inst); | ||
| 95 | void EmitLoadGlobalU8(EmitContext& ctx); | ||
| 96 | void EmitLoadGlobalS8(EmitContext& ctx); | ||
| 97 | void EmitLoadGlobalU16(EmitContext& ctx); | ||
| 98 | void EmitLoadGlobalS16(EmitContext& ctx); | ||
| 99 | void EmitLoadGlobal32(EmitContext& ctx, IR::Inst& inst, std::string_view address); | ||
| 100 | void EmitLoadGlobal64(EmitContext& ctx, IR::Inst& inst, std::string_view address); | ||
| 101 | void EmitLoadGlobal128(EmitContext& ctx, IR::Inst& inst, std::string_view address); | ||
| 102 | void EmitWriteGlobalU8(EmitContext& ctx); | ||
| 103 | void EmitWriteGlobalS8(EmitContext& ctx); | ||
| 104 | void EmitWriteGlobalU16(EmitContext& ctx); | ||
| 105 | void EmitWriteGlobalS16(EmitContext& ctx); | ||
| 106 | void EmitWriteGlobal32(EmitContext& ctx, std::string_view address, std::string_view value); | ||
| 107 | void EmitWriteGlobal64(EmitContext& ctx, std::string_view address, std::string_view value); | ||
| 108 | void EmitWriteGlobal128(EmitContext& ctx, std::string_view address, std::string_view value); | ||
| 109 | void EmitLoadStorageU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 110 | const IR::Value& offset); | ||
| 111 | void EmitLoadStorageS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 112 | const IR::Value& offset); | ||
| 113 | void EmitLoadStorageU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 114 | const IR::Value& offset); | ||
| 115 | void EmitLoadStorageS16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 116 | const IR::Value& offset); | ||
| 117 | void EmitLoadStorage32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 118 | const IR::Value& offset); | ||
| 119 | void EmitLoadStorage64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 120 | const IR::Value& offset); | ||
| 121 | void EmitLoadStorage128(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 122 | const IR::Value& offset); | ||
| 123 | void EmitWriteStorageU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 124 | std::string_view value); | ||
| 125 | void EmitWriteStorageS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 126 | std::string_view value); | ||
| 127 | void EmitWriteStorageU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 128 | std::string_view value); | ||
| 129 | void EmitWriteStorageS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 130 | std::string_view value); | ||
| 131 | void EmitWriteStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 132 | std::string_view value); | ||
| 133 | void EmitWriteStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 134 | std::string_view value); | ||
| 135 | void EmitWriteStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, | ||
| 136 | std::string_view value); | ||
| 137 | void EmitLoadSharedU8(EmitContext& ctx, IR::Inst& inst, std::string_view offset); | ||
| 138 | void EmitLoadSharedS8(EmitContext& ctx, IR::Inst& inst, std::string_view offset); | ||
| 139 | void EmitLoadSharedU16(EmitContext& ctx, IR::Inst& inst, std::string_view offset); | ||
| 140 | void EmitLoadSharedS16(EmitContext& ctx, IR::Inst& inst, std::string_view offset); | ||
| 141 | void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, std::string_view offset); | ||
| 142 | void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, std::string_view offset); | ||
| 143 | void EmitLoadSharedU128(EmitContext& ctx, IR::Inst& inst, std::string_view offset); | ||
| 144 | void EmitWriteSharedU8(EmitContext& ctx, std::string_view offset, std::string_view value); | ||
| 145 | void EmitWriteSharedU16(EmitContext& ctx, std::string_view offset, std::string_view value); | ||
| 146 | void EmitWriteSharedU32(EmitContext& ctx, std::string_view offset, std::string_view value); | ||
| 147 | void EmitWriteSharedU64(EmitContext& ctx, std::string_view offset, std::string_view value); | ||
| 148 | void EmitWriteSharedU128(EmitContext& ctx, std::string_view offset, std::string_view value); | ||
| 149 | void EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1, | ||
| 150 | std::string_view e2); | ||
| 151 | void EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1, | ||
| 152 | std::string_view e2, std::string_view e3); | ||
| 153 | void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1, | ||
| 154 | std::string_view e2, std::string_view e3, std::string_view e4); | ||
| 155 | void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 156 | u32 index); | ||
| 157 | void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 158 | u32 index); | ||
| 159 | void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 160 | u32 index); | ||
| 161 | void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 162 | std::string_view object, u32 index); | ||
| 163 | void EmitCompositeInsertU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 164 | std::string_view object, u32 index); | ||
| 165 | void EmitCompositeInsertU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 166 | std::string_view object, u32 index); | ||
| 167 | void EmitCompositeConstructF16x2(EmitContext& ctx, std::string_view e1, std::string_view e2); | ||
| 168 | void EmitCompositeConstructF16x3(EmitContext& ctx, std::string_view e1, std::string_view e2, | ||
| 169 | std::string_view e3); | ||
| 170 | void EmitCompositeConstructF16x4(EmitContext& ctx, std::string_view e1, std::string_view e2, | ||
| 171 | std::string_view e3, std::string_view e4); | ||
| 172 | void EmitCompositeExtractF16x2(EmitContext& ctx, std::string_view composite, u32 index); | ||
| 173 | void EmitCompositeExtractF16x3(EmitContext& ctx, std::string_view composite, u32 index); | ||
| 174 | void EmitCompositeExtractF16x4(EmitContext& ctx, std::string_view composite, u32 index); | ||
| 175 | void EmitCompositeInsertF16x2(EmitContext& ctx, std::string_view composite, std::string_view object, | ||
| 176 | u32 index); | ||
| 177 | void EmitCompositeInsertF16x3(EmitContext& ctx, std::string_view composite, std::string_view object, | ||
| 178 | u32 index); | ||
| 179 | void EmitCompositeInsertF16x4(EmitContext& ctx, std::string_view composite, std::string_view object, | ||
| 180 | u32 index); | ||
| 181 | void EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view e1, | ||
| 182 | std::string_view e2); | ||
| 183 | void EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view e1, | ||
| 184 | std::string_view e2, std::string_view e3); | ||
| 185 | void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view e1, | ||
| 186 | std::string_view e2, std::string_view e3, std::string_view e4); | ||
| 187 | void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 188 | u32 index); | ||
| 189 | void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 190 | u32 index); | ||
| 191 | void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 192 | u32 index); | ||
| 193 | void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 194 | std::string_view object, u32 index); | ||
| 195 | void EmitCompositeInsertF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 196 | std::string_view object, u32 index); | ||
| 197 | void EmitCompositeInsertF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite, | ||
| 198 | std::string_view object, u32 index); | ||
| 199 | void EmitCompositeConstructF64x2(EmitContext& ctx); | ||
| 200 | void EmitCompositeConstructF64x3(EmitContext& ctx); | ||
| 201 | void EmitCompositeConstructF64x4(EmitContext& ctx); | ||
| 202 | void EmitCompositeExtractF64x2(EmitContext& ctx); | ||
| 203 | void EmitCompositeExtractF64x3(EmitContext& ctx); | ||
| 204 | void EmitCompositeExtractF64x4(EmitContext& ctx); | ||
| 205 | void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object, | ||
| 206 | u32 index); | ||
| 207 | void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object, | ||
| 208 | u32 index); | ||
| 209 | void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object, | ||
| 210 | u32 index); | ||
| 211 | void EmitSelectU1(EmitContext& ctx, IR::Inst& inst, std::string_view cond, | ||
| 212 | std::string_view true_value, std::string_view false_value); | ||
| 213 | void EmitSelectU8(EmitContext& ctx, std::string_view cond, std::string_view true_value, | ||
| 214 | std::string_view false_value); | ||
| 215 | void EmitSelectU16(EmitContext& ctx, std::string_view cond, std::string_view true_value, | ||
| 216 | std::string_view false_value); | ||
| 217 | void EmitSelectU32(EmitContext& ctx, IR::Inst& inst, std::string_view cond, | ||
| 218 | std::string_view true_value, std::string_view false_value); | ||
| 219 | void EmitSelectU64(EmitContext& ctx, IR::Inst& inst, std::string_view cond, | ||
| 220 | std::string_view true_value, std::string_view false_value); | ||
| 221 | void EmitSelectF16(EmitContext& ctx, std::string_view cond, std::string_view true_value, | ||
| 222 | std::string_view false_value); | ||
| 223 | void EmitSelectF32(EmitContext& ctx, IR::Inst& inst, std::string_view cond, | ||
| 224 | std::string_view true_value, std::string_view false_value); | ||
| 225 | void EmitSelectF64(EmitContext& ctx, IR::Inst& inst, std::string_view cond, | ||
| 226 | std::string_view true_value, std::string_view false_value); | ||
| 227 | void EmitBitCastU16F16(EmitContext& ctx, IR::Inst& inst); | ||
| 228 | void EmitBitCastU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 229 | void EmitBitCastU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 230 | void EmitBitCastF16U16(EmitContext& ctx, IR::Inst& inst); | ||
| 231 | void EmitBitCastF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 232 | void EmitBitCastF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 233 | void EmitPackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 234 | void EmitUnpackUint2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 235 | void EmitPackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 236 | void EmitUnpackFloat2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 237 | void EmitPackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 238 | void EmitUnpackHalf2x16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 239 | void EmitPackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 240 | void EmitUnpackDouble2x32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 241 | void EmitGetZeroFromOp(EmitContext& ctx); | ||
| 242 | void EmitGetSignFromOp(EmitContext& ctx); | ||
| 243 | void EmitGetCarryFromOp(EmitContext& ctx); | ||
| 244 | void EmitGetOverflowFromOp(EmitContext& ctx); | ||
| 245 | void EmitGetSparseFromOp(EmitContext& ctx); | ||
| 246 | void EmitGetInBoundsFromOp(EmitContext& ctx); | ||
| 247 | void EmitFPAbs16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 248 | void EmitFPAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 249 | void EmitFPAbs64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 250 | void EmitFPAdd16(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 251 | void EmitFPAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 252 | void EmitFPAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 253 | void EmitFPFma16(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b, | ||
| 254 | std::string_view c); | ||
| 255 | void EmitFPFma32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b, | ||
| 256 | std::string_view c); | ||
| 257 | void EmitFPFma64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b, | ||
| 258 | std::string_view c); | ||
| 259 | void EmitFPMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 260 | void EmitFPMax64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 261 | void EmitFPMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 262 | void EmitFPMin64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 263 | void EmitFPMul16(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 264 | void EmitFPMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 265 | void EmitFPMul64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 266 | void EmitFPNeg16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 267 | void EmitFPNeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 268 | void EmitFPNeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 269 | void EmitFPSin(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 270 | void EmitFPCos(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 271 | void EmitFPExp2(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 272 | void EmitFPLog2(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 273 | void EmitFPRecip32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 274 | void EmitFPRecip64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 275 | void EmitFPRecipSqrt32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 276 | void EmitFPRecipSqrt64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 277 | void EmitFPSqrt(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 278 | void EmitFPSaturate16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 279 | void EmitFPSaturate32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 280 | void EmitFPSaturate64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 281 | void EmitFPClamp16(EmitContext& ctx, IR::Inst& inst, std::string_view value, | ||
| 282 | std::string_view min_value, std::string_view max_value); | ||
| 283 | void EmitFPClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, | ||
| 284 | std::string_view min_value, std::string_view max_value); | ||
| 285 | void EmitFPClamp64(EmitContext& ctx, IR::Inst& inst, std::string_view value, | ||
| 286 | std::string_view min_value, std::string_view max_value); | ||
| 287 | void EmitFPRoundEven16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 288 | void EmitFPRoundEven32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 289 | void EmitFPRoundEven64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 290 | void EmitFPFloor16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 291 | void EmitFPFloor32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 292 | void EmitFPFloor64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 293 | void EmitFPCeil16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 294 | void EmitFPCeil32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 295 | void EmitFPCeil64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 296 | void EmitFPTrunc16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 297 | void EmitFPTrunc32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 298 | void EmitFPTrunc64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 299 | void EmitFPOrdEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 300 | void EmitFPOrdEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs); | ||
| 301 | void EmitFPOrdEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs); | ||
| 302 | void EmitFPUnordEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 303 | void EmitFPUnordEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 304 | std::string_view rhs); | ||
| 305 | void EmitFPUnordEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 306 | std::string_view rhs); | ||
| 307 | void EmitFPOrdNotEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 308 | void EmitFPOrdNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 309 | std::string_view rhs); | ||
| 310 | void EmitFPOrdNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 311 | std::string_view rhs); | ||
| 312 | void EmitFPUnordNotEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 313 | void EmitFPUnordNotEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 314 | std::string_view rhs); | ||
| 315 | void EmitFPUnordNotEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 316 | std::string_view rhs); | ||
| 317 | void EmitFPOrdLessThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 318 | void EmitFPOrdLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 319 | std::string_view rhs); | ||
| 320 | void EmitFPOrdLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 321 | std::string_view rhs); | ||
| 322 | void EmitFPUnordLessThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 323 | void EmitFPUnordLessThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 324 | std::string_view rhs); | ||
| 325 | void EmitFPUnordLessThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 326 | std::string_view rhs); | ||
| 327 | void EmitFPOrdGreaterThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 328 | void EmitFPOrdGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 329 | std::string_view rhs); | ||
| 330 | void EmitFPOrdGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 331 | std::string_view rhs); | ||
| 332 | void EmitFPUnordGreaterThan16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 333 | void EmitFPUnordGreaterThan32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 334 | std::string_view rhs); | ||
| 335 | void EmitFPUnordGreaterThan64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 336 | std::string_view rhs); | ||
| 337 | void EmitFPOrdLessThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 338 | void EmitFPOrdLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 339 | std::string_view rhs); | ||
| 340 | void EmitFPOrdLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 341 | std::string_view rhs); | ||
| 342 | void EmitFPUnordLessThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 343 | void EmitFPUnordLessThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 344 | std::string_view rhs); | ||
| 345 | void EmitFPUnordLessThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 346 | std::string_view rhs); | ||
| 347 | void EmitFPOrdGreaterThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 348 | void EmitFPOrdGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 349 | std::string_view rhs); | ||
| 350 | void EmitFPOrdGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 351 | std::string_view rhs); | ||
| 352 | void EmitFPUnordGreaterThanEqual16(EmitContext& ctx, std::string_view lhs, std::string_view rhs); | ||
| 353 | void EmitFPUnordGreaterThanEqual32(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 354 | std::string_view rhs); | ||
| 355 | void EmitFPUnordGreaterThanEqual64(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 356 | std::string_view rhs); | ||
| 357 | void EmitFPIsNan16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 358 | void EmitFPIsNan32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 359 | void EmitFPIsNan64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 360 | void EmitIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 361 | void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 362 | void EmitISub32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 363 | void EmitISub64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 364 | void EmitIMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 365 | void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 366 | void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 367 | void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 368 | void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 369 | std::string_view shift); | ||
| 370 | void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 371 | std::string_view shift); | ||
| 372 | void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 373 | std::string_view shift); | ||
| 374 | void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 375 | std::string_view shift); | ||
| 376 | void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 377 | std::string_view shift); | ||
| 378 | void EmitShiftRightArithmetic64(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 379 | std::string_view shift); | ||
| 380 | void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 381 | void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 382 | void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 383 | void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 384 | std::string_view insert, std::string_view offset, std::string_view count); | ||
| 385 | void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 386 | std::string_view offset, std::string_view count); | ||
| 387 | void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 388 | std::string_view offset, std::string_view count); | ||
| 389 | void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 390 | void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 391 | void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 392 | void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 393 | void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 394 | void EmitSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 395 | void EmitUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 396 | void EmitSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 397 | void EmitUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 398 | void EmitSClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min, | ||
| 399 | std::string_view max); | ||
| 400 | void EmitUClamp32(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view min, | ||
| 401 | std::string_view max); | ||
| 402 | void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs); | ||
| 403 | void EmitULessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs); | ||
| 404 | void EmitIEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs); | ||
| 405 | void EmitSLessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 406 | std::string_view rhs); | ||
| 407 | void EmitULessThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 408 | std::string_view rhs); | ||
| 409 | void EmitSGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs); | ||
| 410 | void EmitUGreaterThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs); | ||
| 411 | void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs); | ||
| 412 | void EmitSGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 413 | std::string_view rhs); | ||
| 414 | void EmitUGreaterThanEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, | ||
| 415 | std::string_view rhs); | ||
| 416 | void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 417 | std::string_view value); | ||
| 418 | void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 419 | std::string_view value); | ||
| 420 | void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 421 | std::string_view value); | ||
| 422 | void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 423 | std::string_view value); | ||
| 424 | void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 425 | std::string_view value); | ||
| 426 | void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 427 | std::string_view value); | ||
| 428 | void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 429 | std::string_view value); | ||
| 430 | void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 431 | std::string_view value); | ||
| 432 | void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 433 | std::string_view value); | ||
| 434 | void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 435 | std::string_view value); | ||
| 436 | void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 437 | std::string_view value); | ||
| 438 | void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset, | ||
| 439 | std::string_view value); | ||
| 440 | void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 441 | const IR::Value& offset, std::string_view value); | ||
| 442 | void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 443 | const IR::Value& offset, std::string_view value); | ||
| 444 | void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 445 | const IR::Value& offset, std::string_view value); | ||
| 446 | void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 447 | const IR::Value& offset, std::string_view value); | ||
| 448 | void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 449 | const IR::Value& offset, std::string_view value); | ||
| 450 | void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 451 | const IR::Value& offset, std::string_view value); | ||
| 452 | void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 453 | const IR::Value& offset, std::string_view value); | ||
| 454 | void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 455 | const IR::Value& offset, std::string_view value); | ||
| 456 | void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 457 | const IR::Value& offset, std::string_view value); | ||
| 458 | void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 459 | const IR::Value& offset, std::string_view value); | ||
| 460 | void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 461 | const IR::Value& offset, std::string_view value); | ||
| 462 | void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 463 | const IR::Value& offset, std::string_view value); | ||
| 464 | void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 465 | const IR::Value& offset, std::string_view value); | ||
| 466 | void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 467 | const IR::Value& offset, std::string_view value); | ||
| 468 | void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 469 | const IR::Value& offset, std::string_view value); | ||
| 470 | void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 471 | const IR::Value& offset, std::string_view value); | ||
| 472 | void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 473 | const IR::Value& offset, std::string_view value); | ||
| 474 | void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 475 | const IR::Value& offset, std::string_view value); | ||
| 476 | void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 477 | const IR::Value& offset, std::string_view value); | ||
| 478 | void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 479 | const IR::Value& offset, std::string_view value); | ||
| 480 | void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 481 | const IR::Value& offset, std::string_view value); | ||
| 482 | void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 483 | const IR::Value& offset, std::string_view value); | ||
| 484 | void EmitStorageAtomicAddF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 485 | const IR::Value& offset, std::string_view value); | ||
| 486 | void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 487 | const IR::Value& offset, std::string_view value); | ||
| 488 | void EmitStorageAtomicMinF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 489 | const IR::Value& offset, std::string_view value); | ||
| 490 | void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 491 | const IR::Value& offset, std::string_view value); | ||
| 492 | void EmitStorageAtomicMaxF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||
| 493 | const IR::Value& offset, std::string_view value); | ||
| 494 | void EmitGlobalAtomicIAdd32(EmitContext& ctx); | ||
| 495 | void EmitGlobalAtomicSMin32(EmitContext& ctx); | ||
| 496 | void EmitGlobalAtomicUMin32(EmitContext& ctx); | ||
| 497 | void EmitGlobalAtomicSMax32(EmitContext& ctx); | ||
| 498 | void EmitGlobalAtomicUMax32(EmitContext& ctx); | ||
| 499 | void EmitGlobalAtomicInc32(EmitContext& ctx); | ||
| 500 | void EmitGlobalAtomicDec32(EmitContext& ctx); | ||
| 501 | void EmitGlobalAtomicAnd32(EmitContext& ctx); | ||
| 502 | void EmitGlobalAtomicOr32(EmitContext& ctx); | ||
| 503 | void EmitGlobalAtomicXor32(EmitContext& ctx); | ||
| 504 | void EmitGlobalAtomicExchange32(EmitContext& ctx); | ||
| 505 | void EmitGlobalAtomicIAdd64(EmitContext& ctx); | ||
| 506 | void EmitGlobalAtomicSMin64(EmitContext& ctx); | ||
| 507 | void EmitGlobalAtomicUMin64(EmitContext& ctx); | ||
| 508 | void EmitGlobalAtomicSMax64(EmitContext& ctx); | ||
| 509 | void EmitGlobalAtomicUMax64(EmitContext& ctx); | ||
| 510 | void EmitGlobalAtomicInc64(EmitContext& ctx); | ||
| 511 | void EmitGlobalAtomicDec64(EmitContext& ctx); | ||
| 512 | void EmitGlobalAtomicAnd64(EmitContext& ctx); | ||
| 513 | void EmitGlobalAtomicOr64(EmitContext& ctx); | ||
| 514 | void EmitGlobalAtomicXor64(EmitContext& ctx); | ||
| 515 | void EmitGlobalAtomicExchange64(EmitContext& ctx); | ||
| 516 | void EmitGlobalAtomicAddF32(EmitContext& ctx); | ||
| 517 | void EmitGlobalAtomicAddF16x2(EmitContext& ctx); | ||
| 518 | void EmitGlobalAtomicAddF32x2(EmitContext& ctx); | ||
| 519 | void EmitGlobalAtomicMinF16x2(EmitContext& ctx); | ||
| 520 | void EmitGlobalAtomicMinF32x2(EmitContext& ctx); | ||
| 521 | void EmitGlobalAtomicMaxF16x2(EmitContext& ctx); | ||
| 522 | void EmitGlobalAtomicMaxF32x2(EmitContext& ctx); | ||
| 523 | void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 524 | void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 525 | void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b); | ||
| 526 | void EmitLogicalNot(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 527 | void EmitConvertS16F16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 528 | void EmitConvertS16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 529 | void EmitConvertS16F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 530 | void EmitConvertS32F16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 531 | void EmitConvertS32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 532 | void EmitConvertS32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 533 | void EmitConvertS64F16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 534 | void EmitConvertS64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 535 | void EmitConvertS64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 536 | void EmitConvertU16F16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 537 | void EmitConvertU16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 538 | void EmitConvertU16F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 539 | void EmitConvertU32F16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 540 | void EmitConvertU32F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 541 | void EmitConvertU32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 542 | void EmitConvertU64F16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 543 | void EmitConvertU64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 544 | void EmitConvertU64F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 545 | void EmitConvertU64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 546 | void EmitConvertU32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 547 | void EmitConvertF16F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 548 | void EmitConvertF32F16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 549 | void EmitConvertF32F64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 550 | void EmitConvertF64F32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 551 | void EmitConvertF16S8(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 552 | void EmitConvertF16S16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 553 | void EmitConvertF16S32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 554 | void EmitConvertF16S64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 555 | void EmitConvertF16U8(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 556 | void EmitConvertF16U16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 557 | void EmitConvertF16U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 558 | void EmitConvertF16U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 559 | void EmitConvertF32S8(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 560 | void EmitConvertF32S16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 561 | void EmitConvertF32S32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 562 | void EmitConvertF32S64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 563 | void EmitConvertF32U8(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 564 | void EmitConvertF32U16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 565 | void EmitConvertF32U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 566 | void EmitConvertF32U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 567 | void EmitConvertF64S8(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 568 | void EmitConvertF64S16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 569 | void EmitConvertF64S32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 570 | void EmitConvertF64S64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 571 | void EmitConvertF64U8(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 572 | void EmitConvertF64U16(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 573 | void EmitConvertF64U32(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 574 | void EmitConvertF64U64(EmitContext& ctx, IR::Inst& inst, std::string_view value); | ||
| 575 | void EmitBindlessImageSampleImplicitLod(EmitContext&); | ||
| 576 | void EmitBindlessImageSampleExplicitLod(EmitContext&); | ||
| 577 | void EmitBindlessImageSampleDrefImplicitLod(EmitContext&); | ||
| 578 | void EmitBindlessImageSampleDrefExplicitLod(EmitContext&); | ||
| 579 | void EmitBindlessImageGather(EmitContext&); | ||
| 580 | void EmitBindlessImageGatherDref(EmitContext&); | ||
| 581 | void EmitBindlessImageFetch(EmitContext&); | ||
| 582 | void EmitBindlessImageQueryDimensions(EmitContext&); | ||
| 583 | void EmitBindlessImageQueryLod(EmitContext&); | ||
| 584 | void EmitBindlessImageGradient(EmitContext&); | ||
| 585 | void EmitBindlessImageRead(EmitContext&); | ||
| 586 | void EmitBindlessImageWrite(EmitContext&); | ||
| 587 | void EmitBoundImageSampleImplicitLod(EmitContext&); | ||
| 588 | void EmitBoundImageSampleExplicitLod(EmitContext&); | ||
| 589 | void EmitBoundImageSampleDrefImplicitLod(EmitContext&); | ||
| 590 | void EmitBoundImageSampleDrefExplicitLod(EmitContext&); | ||
| 591 | void EmitBoundImageGather(EmitContext&); | ||
| 592 | void EmitBoundImageGatherDref(EmitContext&); | ||
| 593 | void EmitBoundImageFetch(EmitContext&); | ||
| 594 | void EmitBoundImageQueryDimensions(EmitContext&); | ||
| 595 | void EmitBoundImageQueryLod(EmitContext&); | ||
| 596 | void EmitBoundImageGradient(EmitContext&); | ||
| 597 | void EmitBoundImageRead(EmitContext&); | ||
| 598 | void EmitBoundImageWrite(EmitContext&); | ||
| 599 | void 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); | ||
| 602 | void 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); | ||
| 605 | void 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); | ||
| 608 | void 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); | ||
| 611 | void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 612 | std::string_view coords, const IR::Value& offset, const IR::Value& offset2); | ||
| 613 | void 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); | ||
| 616 | void 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); | ||
| 619 | void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 620 | std::string_view lod); | ||
| 621 | void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 622 | std::string_view coords); | ||
| 623 | void 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); | ||
| 626 | void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 627 | std::string_view coords); | ||
| 628 | void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 629 | std::string_view coords, std::string_view color); | ||
| 630 | void EmitBindlessImageAtomicIAdd32(EmitContext&); | ||
| 631 | void EmitBindlessImageAtomicSMin32(EmitContext&); | ||
| 632 | void EmitBindlessImageAtomicUMin32(EmitContext&); | ||
| 633 | void EmitBindlessImageAtomicSMax32(EmitContext&); | ||
| 634 | void EmitBindlessImageAtomicUMax32(EmitContext&); | ||
| 635 | void EmitBindlessImageAtomicInc32(EmitContext&); | ||
| 636 | void EmitBindlessImageAtomicDec32(EmitContext&); | ||
| 637 | void EmitBindlessImageAtomicAnd32(EmitContext&); | ||
| 638 | void EmitBindlessImageAtomicOr32(EmitContext&); | ||
| 639 | void EmitBindlessImageAtomicXor32(EmitContext&); | ||
| 640 | void EmitBindlessImageAtomicExchange32(EmitContext&); | ||
| 641 | void EmitBoundImageAtomicIAdd32(EmitContext&); | ||
| 642 | void EmitBoundImageAtomicSMin32(EmitContext&); | ||
| 643 | void EmitBoundImageAtomicUMin32(EmitContext&); | ||
| 644 | void EmitBoundImageAtomicSMax32(EmitContext&); | ||
| 645 | void EmitBoundImageAtomicUMax32(EmitContext&); | ||
| 646 | void EmitBoundImageAtomicInc32(EmitContext&); | ||
| 647 | void EmitBoundImageAtomicDec32(EmitContext&); | ||
| 648 | void EmitBoundImageAtomicAnd32(EmitContext&); | ||
| 649 | void EmitBoundImageAtomicOr32(EmitContext&); | ||
| 650 | void EmitBoundImageAtomicXor32(EmitContext&); | ||
| 651 | void EmitBoundImageAtomicExchange32(EmitContext&); | ||
| 652 | void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 653 | std::string_view coords, std::string_view value); | ||
| 654 | void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 655 | std::string_view coords, std::string_view value); | ||
| 656 | void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 657 | std::string_view coords, std::string_view value); | ||
| 658 | void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 659 | std::string_view coords, std::string_view value); | ||
| 660 | void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 661 | std::string_view coords, std::string_view value); | ||
| 662 | void EmitImageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 663 | std::string_view coords, std::string_view value); | ||
| 664 | void EmitImageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 665 | std::string_view coords, std::string_view value); | ||
| 666 | void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 667 | std::string_view coords, std::string_view value); | ||
| 668 | void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 669 | std::string_view coords, std::string_view value); | ||
| 670 | void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 671 | std::string_view coords, std::string_view value); | ||
| 672 | void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | ||
| 673 | std::string_view coords, std::string_view value); | ||
| 674 | void EmitLaneId(EmitContext& ctx, IR::Inst& inst); | ||
| 675 | void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred); | ||
| 676 | void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred); | ||
| 677 | void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred); | ||
| 678 | void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred); | ||
| 679 | void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst); | ||
| 680 | void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst); | ||
| 681 | void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst); | ||
| 682 | void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst); | ||
| 683 | void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst); | ||
| 684 | void 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); | ||
| 687 | void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index, | ||
| 688 | std::string_view clamp, std::string_view segmentation_mask); | ||
| 689 | void 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); | ||
| 692 | void 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); | ||
| 695 | void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b, | ||
| 696 | std::string_view swizzle); | ||
| 697 | void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a); | ||
| 698 | void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a); | ||
| 699 | void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a); | ||
| 700 | void 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 | |||
| 11 | namespace Shader::Backend::GLSL { | ||
| 12 | namespace { | ||
| 13 | void 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 | |||
| 22 | void 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 | |||
| 31 | void 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 | |||
| 40 | void 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 | |||
| 65 | void EmitIAdd64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 66 | ctx.AddU64("{}={}+{};", inst, a, b); | ||
| 67 | } | ||
| 68 | |||
| 69 | void EmitISub32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 70 | ctx.AddU32("{}={}-{};", inst, a, b); | ||
| 71 | } | ||
| 72 | |||
| 73 | void EmitISub64(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 74 | ctx.AddU64("{}={}-{};", inst, a, b); | ||
| 75 | } | ||
| 76 | |||
| 77 | void EmitIMul32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 78 | ctx.AddU32("{}=uint({}*{});", inst, a, b); | ||
| 79 | } | ||
| 80 | |||
| 81 | void EmitINeg32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 82 | ctx.AddU32("{}=uint(-({}));", inst, value); | ||
| 83 | } | ||
| 84 | |||
| 85 | void EmitINeg64(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 86 | ctx.AddU64("{}=-({});", inst, value); | ||
| 87 | } | ||
| 88 | |||
| 89 | void EmitIAbs32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 90 | ctx.AddU32("{}=abs(int({}));", inst, value); | ||
| 91 | } | ||
| 92 | |||
| 93 | void EmitShiftLeftLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 94 | std::string_view shift) { | ||
| 95 | ctx.AddU32("{}={}<<{};", inst, base, shift); | ||
| 96 | } | ||
| 97 | |||
| 98 | void EmitShiftLeftLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 99 | std::string_view shift) { | ||
| 100 | ctx.AddU64("{}={}<<{};", inst, base, shift); | ||
| 101 | } | ||
| 102 | |||
| 103 | void EmitShiftRightLogical32(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 104 | std::string_view shift) { | ||
| 105 | ctx.AddU32("{}={}>>{};", inst, base, shift); | ||
| 106 | } | ||
| 107 | |||
| 108 | void EmitShiftRightLogical64(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 109 | std::string_view shift) { | ||
| 110 | ctx.AddU64("{}={}>>{};", inst, base, shift); | ||
| 111 | } | ||
| 112 | |||
| 113 | void EmitShiftRightArithmetic32(EmitContext& ctx, IR::Inst& inst, std::string_view base, | ||
| 114 | std::string_view shift) { | ||
| 115 | ctx.AddU32("{}=int({})>>{};", inst, base, shift); | ||
| 116 | } | ||
| 117 | |||
| 118 | void 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 | |||
| 123 | void EmitBitwiseAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 124 | BitwiseLogicalOp(ctx, inst, a, b, '&'); | ||
| 125 | } | ||
| 126 | |||
| 127 | void EmitBitwiseOr32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 128 | BitwiseLogicalOp(ctx, inst, a, b, '|'); | ||
| 129 | } | ||
| 130 | |||
| 131 | void EmitBitwiseXor32(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 132 | BitwiseLogicalOp(ctx, inst, a, b, '^'); | ||
| 133 | } | ||
| 134 | |||
| 135 | void 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 | |||
| 140 | void 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 | |||
| 148 | void 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 | |||
| 156 | void EmitBitReverse32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 157 | ctx.AddU32("{}=bitfieldReverse({});", inst, value); | ||
| 158 | } | ||
| 159 | |||
| 160 | void EmitBitCount32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 161 | ctx.AddU32("{}=bitCount({});", inst, value); | ||
| 162 | } | ||
| 163 | |||
| 164 | void EmitBitwiseNot32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 165 | ctx.AddU32("{}=~{};", inst, value); | ||
| 166 | } | ||
| 167 | |||
| 168 | void EmitFindSMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 169 | ctx.AddU32("{}=findMSB(int({}));", inst, value); | ||
| 170 | } | ||
| 171 | |||
| 172 | void EmitFindUMsb32(EmitContext& ctx, IR::Inst& inst, std::string_view value) { | ||
| 173 | ctx.AddU32("{}=findMSB(uint({}));", inst, value); | ||
| 174 | } | ||
| 175 | |||
| 176 | void 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 | |||
| 180 | void 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 | |||
| 184 | void 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 | |||
| 188 | void 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 | |||
| 192 | void 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 | |||
| 200 | void 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 | |||
| 208 | void EmitSLessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) { | ||
| 209 | ctx.AddU1("{}=int({})<int({});", inst, lhs, rhs); | ||
| 210 | } | ||
| 211 | |||
| 212 | void EmitULessThan(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) { | ||
| 213 | ctx.AddU1("{}=uint({})<uint({});", inst, lhs, rhs); | ||
| 214 | } | ||
| 215 | |||
| 216 | void EmitIEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) { | ||
| 217 | ctx.AddU1("{}={}=={};", inst, lhs, rhs); | ||
| 218 | } | ||
| 219 | |||
| 220 | void 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 | |||
| 225 | void 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 | |||
| 230 | void 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 | |||
| 235 | void 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 | |||
| 240 | void EmitINotEqual(EmitContext& ctx, IR::Inst& inst, std::string_view lhs, std::string_view rhs) { | ||
| 241 | ctx.AddU1("{}={}!={};", inst, lhs, rhs); | ||
| 242 | } | ||
| 243 | |||
| 244 | void 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 | |||
| 249 | void 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 | |||
| 11 | namespace Shader::Backend::GLSL { | ||
| 12 | |||
| 13 | void EmitLogicalOr(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 14 | ctx.AddU1("{}={}||{};", inst, a, b); | ||
| 15 | } | ||
| 16 | |||
| 17 | void EmitLogicalAnd(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 18 | ctx.AddU1("{}={}&&{};", inst, a, b); | ||
| 19 | } | ||
| 20 | |||
| 21 | void EmitLogicalXor(EmitContext& ctx, IR::Inst& inst, std::string_view a, std::string_view b) { | ||
| 22 | ctx.AddU1("{}={}^^{};", inst, a, b); | ||
| 23 | } | ||
| 24 | |||
| 25 | void 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 | |||
| 12 | namespace Shader::Backend::GLSL { | ||
| 13 | namespace { | ||
| 14 | constexpr char cas_loop[]{"for(;;){{uint old_value={};uint " | ||
| 15 | "cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));" | ||
| 16 | "if(cas_result==old_value){{break;}}}}"}; | ||
| 17 | |||
| 18 | void 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 | |||
| 25 | void EmitLoadGlobalU8(EmitContext&) { | ||
| 26 | NotImplemented(); | ||
| 27 | } | ||
| 28 | |||
| 29 | void EmitLoadGlobalS8(EmitContext&) { | ||
| 30 | NotImplemented(); | ||
| 31 | } | ||
| 32 | |||
| 33 | void EmitLoadGlobalU16(EmitContext&) { | ||
| 34 | NotImplemented(); | ||
| 35 | } | ||
| 36 | |||
| 37 | void EmitLoadGlobalS16(EmitContext&) { | ||
| 38 | NotImplemented(); | ||
| 39 | } | ||
| 40 | |||
| 41 | void 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 | |||
| 49 | void 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 | |||
| 57 | void 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 | |||
| 65 | void EmitWriteGlobalU8(EmitContext&) { | ||
| 66 | NotImplemented(); | ||
| 67 | } | ||
| 68 | |||
| 69 | void EmitWriteGlobalS8(EmitContext&) { | ||
| 70 | NotImplemented(); | ||
| 71 | } | ||
| 72 | |||
| 73 | void EmitWriteGlobalU16(EmitContext&) { | ||
| 74 | NotImplemented(); | ||
| 75 | } | ||
| 76 | |||
| 77 | void EmitWriteGlobalS16(EmitContext&) { | ||
| 78 | NotImplemented(); | ||
| 79 | } | ||
| 80 | |||
| 81 | void 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 | |||
| 88 | void 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 | |||
| 95 | void 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 | |||
| 102 | void 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 | |||
| 109 | void 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 | |||
| 116 | void 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 | |||
| 123 | void 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 | |||
| 130 | void 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 | |||
| 136 | void 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 | |||
| 143 | void 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 | |||
| 153 | void 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 | |||
| 160 | void 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 | |||
| 167 | void 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 | |||
| 174 | void 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 | |||
| 181 | void 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 | |||
| 187 | void 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 | |||
| 194 | void 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 | |||
| 15 | namespace Shader::Backend::GLSL { | ||
| 16 | |||
| 17 | void EmitGetRegister(EmitContext& ctx) { | ||
| 18 | NotImplemented(); | ||
| 19 | } | ||
| 20 | |||
| 21 | void EmitSetRegister(EmitContext& ctx) { | ||
| 22 | NotImplemented(); | ||
| 23 | } | ||
| 24 | |||
| 25 | void EmitGetPred(EmitContext& ctx) { | ||
| 26 | NotImplemented(); | ||
| 27 | } | ||
| 28 | |||
| 29 | void EmitSetPred(EmitContext& ctx) { | ||
| 30 | NotImplemented(); | ||
| 31 | } | ||
| 32 | |||
| 33 | void EmitSetGotoVariable(EmitContext& ctx) { | ||
| 34 | NotImplemented(); | ||
| 35 | } | ||
| 36 | |||
| 37 | void EmitGetGotoVariable(EmitContext& ctx) { | ||
| 38 | NotImplemented(); | ||
| 39 | } | ||
| 40 | |||
| 41 | void EmitSetIndirectBranchVariable(EmitContext& ctx) { | ||
| 42 | NotImplemented(); | ||
| 43 | } | ||
| 44 | |||
| 45 | void EmitGetIndirectBranchVariable(EmitContext& ctx) { | ||
| 46 | NotImplemented(); | ||
| 47 | } | ||
| 48 | |||
| 49 | void EmitGetZFlag(EmitContext& ctx) { | ||
| 50 | NotImplemented(); | ||
| 51 | } | ||
| 52 | |||
| 53 | void EmitGetSFlag(EmitContext& ctx) { | ||
| 54 | NotImplemented(); | ||
| 55 | } | ||
| 56 | |||
| 57 | void EmitGetCFlag(EmitContext& ctx) { | ||
| 58 | NotImplemented(); | ||
| 59 | } | ||
| 60 | |||
| 61 | void EmitGetOFlag(EmitContext& ctx) { | ||
| 62 | NotImplemented(); | ||
| 63 | } | ||
| 64 | |||
| 65 | void EmitSetZFlag(EmitContext& ctx) { | ||
| 66 | NotImplemented(); | ||
| 67 | } | ||
| 68 | |||
| 69 | void EmitSetSFlag(EmitContext& ctx) { | ||
| 70 | NotImplemented(); | ||
| 71 | } | ||
| 72 | |||
| 73 | void EmitSetCFlag(EmitContext& ctx) { | ||
| 74 | NotImplemented(); | ||
| 75 | } | ||
| 76 | |||
| 77 | void EmitSetOFlag(EmitContext& ctx) { | ||
| 78 | NotImplemented(); | ||
| 79 | } | ||
| 80 | |||
| 81 | void EmitGetZeroFromOp(EmitContext& ctx) { | ||
| 82 | NotImplemented(); | ||
| 83 | } | ||
| 84 | |||
| 85 | void EmitGetSignFromOp(EmitContext& ctx) { | ||
| 86 | NotImplemented(); | ||
| 87 | } | ||
| 88 | |||
| 89 | void EmitGetCarryFromOp(EmitContext& ctx) { | ||
| 90 | NotImplemented(); | ||
| 91 | } | ||
| 92 | |||
| 93 | void EmitGetOverflowFromOp(EmitContext& ctx) { | ||
| 94 | NotImplemented(); | ||
| 95 | } | ||
| 96 | |||
| 97 | void EmitGetSparseFromOp(EmitContext& ctx) { | ||
| 98 | NotImplemented(); | ||
| 99 | } | ||
| 100 | |||
| 101 | void 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 | |||
| 11 | namespace Shader::Backend::GLSL { | ||
| 12 | void 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 | |||
| 17 | void 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 | |||
| 23 | void 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 | |||
| 29 | void 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 | |||
| 34 | void 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 | |||
| 39 | void 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 | |||
| 45 | void 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 | |||
| 50 | void 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 | |||
| 11 | namespace Shader::Backend::GLSL { | ||
| 12 | namespace { | ||
| 13 | constexpr char cas_loop[]{"for(;;){{uint old_value={};uint " | ||
| 14 | "cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));" | ||
| 15 | "if(cas_result==old_value){{break;}}}}"}; | ||
| 16 | |||
| 17 | void 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 | |||
| 24 | void 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 | |||
| 28 | void 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 | |||
| 32 | void 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 | |||
| 36 | void 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 | |||
| 40 | void EmitLoadSharedU32(EmitContext& ctx, IR::Inst& inst, std::string_view offset) { | ||
| 41 | ctx.AddU32("{}=smem[{}>>2];", inst, offset); | ||
| 42 | } | ||
| 43 | |||
| 44 | void EmitLoadSharedU64(EmitContext& ctx, IR::Inst& inst, std::string_view offset) { | ||
| 45 | ctx.AddU32x2("{}=uvec2(smem[{}>>2],smem[({}+4)>>2]);", inst, offset, offset); | ||
| 46 | } | ||
| 47 | |||
| 48 | void 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 | |||
| 53 | void 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 | |||
| 58 | void 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 | |||
| 63 | void EmitWriteSharedU32(EmitContext& ctx, std::string_view offset, std::string_view value) { | ||
| 64 | ctx.Add("smem[{}>>2]={};", offset, value); | ||
| 65 | } | ||
| 66 | |||
| 67 | void 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 | |||
| 72 | void 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 | |||
| 13 | namespace Shader::Backend::GLSL { | ||
| 14 | namespace { | ||
| 15 | std::string_view OutputVertexIndex(EmitContext& ctx) { | ||
| 16 | return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : ""; | ||
| 17 | } | ||
| 18 | |||
| 19 | void 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 | |||
| 64 | void 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 | |||
| 75 | void EmitVoid(EmitContext&) {} | ||
| 76 | |||
| 77 | void EmitReference(EmitContext& ctx, const IR::Value& value) { | ||
| 78 | ctx.var_alloc.Consume(value); | ||
| 79 | } | ||
| 80 | |||
| 81 | void 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 | |||
| 96 | void EmitPrologue(EmitContext& ctx) { | ||
| 97 | InitializeOutputVaryings(ctx); | ||
| 98 | } | ||
| 99 | |||
| 100 | void EmitEpilogue(EmitContext&) {} | ||
| 101 | |||
| 102 | void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) { | ||
| 103 | ctx.Add("EmitStreamVertex(int({}));", ctx.var_alloc.Consume(stream)); | ||
| 104 | InitializeOutputVaryings(ctx); | ||
| 105 | } | ||
| 106 | |||
| 107 | void 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 | |||
| 10 | namespace Shader::Backend::GLSL { | ||
| 11 | |||
| 12 | void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) { | ||
| 13 | ctx.AddU1("{}=false;", inst); | ||
| 14 | } | ||
| 15 | |||
| 16 | void EmitUndefU8(EmitContext& ctx, IR::Inst& inst) { | ||
| 17 | ctx.AddU32("{}=0u;", inst); | ||
| 18 | } | ||
| 19 | |||
| 20 | void EmitUndefU16(EmitContext& ctx, IR::Inst& inst) { | ||
| 21 | ctx.AddU32("{}=0u;", inst); | ||
| 22 | } | ||
| 23 | |||
| 24 | void EmitUndefU32(EmitContext& ctx, IR::Inst& inst) { | ||
| 25 | ctx.AddU32("{}=0u;", inst); | ||
| 26 | } | ||
| 27 | |||
| 28 | void 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 | |||
| 12 | namespace Shader::Backend::GLSL { | ||
| 13 | namespace { | ||
| 14 | void 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 | |||
| 23 | std::string ComputeMinThreadId(std::string_view thread_id, std::string_view segmentation_mask) { | ||
| 24 | return fmt::format("({}&{})", thread_id, segmentation_mask); | ||
| 25 | } | ||
| 26 | |||
| 27 | std::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 | |||
| 32 | std::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 | |||
| 39 | void 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 | |||
| 48 | void EmitLaneId(EmitContext& ctx, IR::Inst& inst) { | ||
| 49 | ctx.AddU32("{}=gl_SubGroupInvocationARB&31u;", inst); | ||
| 50 | } | ||
| 51 | |||
| 52 | void 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 | |||
| 62 | void 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 | |||
| 72 | void 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 | |||
| 83 | void 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 | |||
| 91 | void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) { | ||
| 92 | ctx.AddU32("{}=uint(gl_SubGroupEqMaskARB.x);", inst); | ||
| 93 | } | ||
| 94 | |||
| 95 | void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) { | ||
| 96 | ctx.AddU32("{}=uint(gl_SubGroupLtMaskARB.x);", inst); | ||
| 97 | } | ||
| 98 | |||
| 99 | void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) { | ||
| 100 | ctx.AddU32("{}=uint(gl_SubGroupLeMaskARB.x);", inst); | ||
| 101 | } | ||
| 102 | |||
| 103 | void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) { | ||
| 104 | ctx.AddU32("{}=uint(gl_SubGroupGtMaskARB.x);", inst); | ||
| 105 | } | ||
| 106 | |||
| 107 | void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) { | ||
| 108 | ctx.AddU32("{}=uint(gl_SubGroupGeMaskARB.x);", inst); | ||
| 109 | } | ||
| 110 | |||
| 111 | void 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 | |||
| 130 | void 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 | |||
| 144 | void 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 | |||
| 159 | void 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 | |||
| 174 | void 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 | |||
| 182 | void 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 | |||
| 191 | void 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 | |||
| 200 | void 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 | |||
| 209 | void 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 | |||
| 14 | namespace Shader::Backend::GLSL { | ||
| 15 | namespace { | ||
| 16 | std::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 | |||
| 53 | std::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 | |||
| 77 | std::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 | |||
| 97 | std::string VarAlloc::Representation(u32 index, GlslVarType type) const { | ||
| 98 | const auto prefix{TypePrefix(type)}; | ||
| 99 | return fmt::format("{}{}", prefix, index); | ||
| 100 | } | ||
| 101 | |||
| 102 | std::string VarAlloc::Representation(Id id) const { | ||
| 103 | return Representation(id.index, id.type); | ||
| 104 | } | ||
| 105 | |||
| 106 | std::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 | |||
| 119 | std::string VarAlloc::Define(IR::Inst& inst, IR::Type type) { | ||
| 120 | return Define(inst, RegType(type)); | ||
| 121 | } | ||
| 122 | |||
| 123 | std::string VarAlloc::PhiDefine(IR::Inst& inst, IR::Type type) { | ||
| 124 | return AddDefine(inst, RegType(type)); | ||
| 125 | } | ||
| 126 | |||
| 127 | std::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 | |||
| 137 | std::string VarAlloc::Consume(const IR::Value& value) { | ||
| 138 | return value.IsImmediate() ? MakeImm(value) : ConsumeInst(*value.InstRecursive()); | ||
| 139 | } | ||
| 140 | |||
| 141 | std::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 | |||
| 149 | std::string VarAlloc::GetGlslType(IR::Type type) const { | ||
| 150 | return GetGlslType(RegType(type)); | ||
| 151 | } | ||
| 152 | |||
| 153 | Id 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 | |||
| 178 | void 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 | |||
| 186 | GlslVarType 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 | |||
| 203 | std::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 | |||
| 238 | VarAlloc::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 | |||
| 273 | const 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 | |||
| 14 | namespace Shader::IR { | ||
| 15 | class Inst; | ||
| 16 | class Value; | ||
| 17 | enum class Type; | ||
| 18 | } // namespace Shader::IR | ||
| 19 | |||
| 20 | namespace Shader::Backend::GLSL { | ||
| 21 | enum 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 | |||
| 39 | struct 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 | }; | ||
| 54 | static_assert(sizeof(Id) == sizeof(u32)); | ||
| 55 | |||
| 56 | class VarAlloc { | ||
| 57 | public: | ||
| 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 | |||
| 82 | private: | ||
| 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 | ||