emit_glsl_atomic.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <string_view>
  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. namespace Shader::Backend::GLSL {
  9. namespace {
  10. constexpr char cas_loop[]{
  11. "for (;;){{uint old={};{}=atomicCompSwap({},old,{}({},{}));if({}==old){{break;}}}}"};
  12. void SharedCasFunction(EmitContext& ctx, IR::Inst& inst, std::string_view offset,
  13. std::string_view value, std::string_view function) {
  14. const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
  15. const std::string smem{fmt::format("smem[{}>>2]", offset)};
  16. ctx.Add(cas_loop, smem, ret, smem, function, smem, value, ret);
  17. }
  18. void SsboCasFunction(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  19. const IR::Value& offset, std::string_view value, std::string_view function) {
  20. const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
  21. const std::string ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(),
  22. ctx.var_alloc.Consume(offset))};
  23. ctx.Add(cas_loop, ssbo, ret, ssbo, function, ssbo, value, ret);
  24. }
  25. void SsboCasFunctionF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  26. const IR::Value& offset, std::string_view value,
  27. std::string_view function) {
  28. const std::string ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(),
  29. ctx.var_alloc.Consume(offset))};
  30. const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32)};
  31. ctx.Add(cas_loop, ssbo, ret, ssbo, function, ssbo, value, ret);
  32. ctx.AddF32("{}=utof({});", inst, ret);
  33. }
  34. } // Anonymous namespace
  35. void EmitSharedAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  36. std::string_view value) {
  37. ctx.AddU32("{}=atomicAdd(smem[{}>>2],{});", inst, pointer_offset, value);
  38. }
  39. void EmitSharedAtomicSMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  40. std::string_view value) {
  41. const std::string u32_value{fmt::format("uint({})", value)};
  42. SharedCasFunction(ctx, inst, pointer_offset, u32_value, "CasMinS32");
  43. }
  44. void EmitSharedAtomicUMin32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  45. std::string_view value) {
  46. ctx.AddU32("{}=atomicMin(smem[{}>>2],{});", inst, pointer_offset, value);
  47. }
  48. void EmitSharedAtomicSMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  49. std::string_view value) {
  50. const std::string u32_value{fmt::format("uint({})", value)};
  51. SharedCasFunction(ctx, inst, pointer_offset, u32_value, "CasMaxS32");
  52. }
  53. void EmitSharedAtomicUMax32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  54. std::string_view value) {
  55. ctx.AddU32("{}=atomicMax(smem[{}>>2],{});", inst, pointer_offset, value);
  56. }
  57. void EmitSharedAtomicInc32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  58. std::string_view value) {
  59. SharedCasFunction(ctx, inst, pointer_offset, value, "CasIncrement");
  60. }
  61. void EmitSharedAtomicDec32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  62. std::string_view value) {
  63. SharedCasFunction(ctx, inst, pointer_offset, value, "CasDecrement");
  64. }
  65. void EmitSharedAtomicAnd32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  66. std::string_view value) {
  67. ctx.AddU32("{}=atomicAnd(smem[{}>>2],{});", inst, pointer_offset, value);
  68. }
  69. void EmitSharedAtomicOr32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  70. std::string_view value) {
  71. ctx.AddU32("{}=atomicOr(smem[{}>>2],{});", inst, pointer_offset, value);
  72. }
  73. void EmitSharedAtomicXor32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  74. std::string_view value) {
  75. ctx.AddU32("{}=atomicXor(smem[{}>>2],{});", inst, pointer_offset, value);
  76. }
  77. void EmitSharedAtomicExchange32(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  78. std::string_view value) {
  79. ctx.AddU32("{}=atomicExchange(smem[{}>>2],{});", inst, pointer_offset, value);
  80. }
  81. void EmitSharedAtomicExchange64(EmitContext& ctx, IR::Inst& inst, std::string_view pointer_offset,
  82. std::string_view value) {
  83. LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
  84. ctx.AddU64("{}=packUint2x32(uvec2(smem[{}>>2],smem[({}+4)>>2]));", inst, pointer_offset,
  85. pointer_offset);
  86. ctx.Add("smem[{}>>2]=unpackUint2x32({}).x;smem[({}+4)>>2]=unpackUint2x32({}).y;",
  87. pointer_offset, value, pointer_offset, value);
  88. }
  89. void EmitStorageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  90. const IR::Value& offset, std::string_view value) {
  91. ctx.AddU32("{}=atomicAdd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
  92. ctx.var_alloc.Consume(offset), value);
  93. }
  94. void EmitStorageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  95. const IR::Value& offset, std::string_view value) {
  96. const std::string u32_value{fmt::format("uint({})", value)};
  97. SsboCasFunction(ctx, inst, binding, offset, u32_value, "CasMinS32");
  98. }
  99. void EmitStorageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  100. const IR::Value& offset, std::string_view value) {
  101. ctx.AddU32("{}=atomicMin({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
  102. ctx.var_alloc.Consume(offset), value);
  103. }
  104. void EmitStorageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  105. const IR::Value& offset, std::string_view value) {
  106. const std::string u32_value{fmt::format("uint({})", value)};
  107. SsboCasFunction(ctx, inst, binding, offset, u32_value, "CasMaxS32");
  108. }
  109. void EmitStorageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  110. const IR::Value& offset, std::string_view value) {
  111. ctx.AddU32("{}=atomicMax({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
  112. ctx.var_alloc.Consume(offset), value);
  113. }
  114. void EmitStorageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  115. const IR::Value& offset, std::string_view value) {
  116. SsboCasFunction(ctx, inst, binding, offset, value, "CasIncrement");
  117. }
  118. void EmitStorageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  119. const IR::Value& offset, std::string_view value) {
  120. SsboCasFunction(ctx, inst, binding, offset, value, "CasDecrement");
  121. }
  122. void EmitStorageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  123. const IR::Value& offset, std::string_view value) {
  124. ctx.AddU32("{}=atomicAnd({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
  125. ctx.var_alloc.Consume(offset), value);
  126. }
  127. void EmitStorageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  128. const IR::Value& offset, std::string_view value) {
  129. ctx.AddU32("{}=atomicOr({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
  130. ctx.var_alloc.Consume(offset), value);
  131. }
  132. void EmitStorageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  133. const IR::Value& offset, std::string_view value) {
  134. ctx.AddU32("{}=atomicXor({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
  135. ctx.var_alloc.Consume(offset), value);
  136. }
  137. void EmitStorageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  138. const IR::Value& offset, std::string_view value) {
  139. ctx.AddU32("{}=atomicExchange({}_ssbo{}[{}>>2],{});", inst, ctx.stage_name, binding.U32(),
  140. ctx.var_alloc.Consume(offset), value);
  141. }
  142. void EmitStorageAtomicIAdd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  143. const IR::Value& offset, std::string_view value) {
  144. LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
  145. ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
  146. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
  147. binding.U32(), ctx.var_alloc.Consume(offset));
  148. ctx.Add("{}_ssbo{}[{}>>2]+=unpackUint2x32({}).x;{}_ssbo{}[({}>>2)+1]+=unpackUint2x32({}).y;",
  149. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
  150. binding.U32(), ctx.var_alloc.Consume(offset), value);
  151. }
  152. void EmitStorageAtomicSMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  153. const IR::Value& offset, std::string_view value) {
  154. LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
  155. ctx.AddU64("{}=packInt2x32(ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
  156. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
  157. binding.U32(), ctx.var_alloc.Consume(offset));
  158. ctx.Add("for(int i=0;i<2;++i){{ "
  159. "{}_ssbo{}[({}>>2)+i]=uint(min(int({}_ssbo{}[({}>>2)+i]),unpackInt2x32(int64_t({}))[i])"
  160. ");}}",
  161. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
  162. binding.U32(), ctx.var_alloc.Consume(offset), value);
  163. }
  164. void EmitStorageAtomicUMin64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  165. const IR::Value& offset, std::string_view value) {
  166. LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
  167. ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
  168. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
  169. binding.U32(), ctx.var_alloc.Consume(offset));
  170. ctx.Add("for(int i=0;i<2;++i){{ "
  171. "{}_ssbo{}[({}>>2)+i]=min({}_ssbo{}[({}>>2)+i],unpackUint2x32(uint64_t({}))[i]);}}",
  172. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
  173. binding.U32(), ctx.var_alloc.Consume(offset), value);
  174. }
  175. void EmitStorageAtomicSMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  176. const IR::Value& offset, std::string_view value) {
  177. LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
  178. ctx.AddU64("{}=packInt2x32(ivec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
  179. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
  180. binding.U32(), ctx.var_alloc.Consume(offset));
  181. ctx.Add("for(int i=0;i<2;++i){{ "
  182. "{}_ssbo{}[({}>>2)+i]=uint(max(int({}_ssbo{}[({}>>2)+i]),unpackInt2x32(int64_t({}))[i])"
  183. ");}}",
  184. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
  185. binding.U32(), ctx.var_alloc.Consume(offset), value);
  186. }
  187. void EmitStorageAtomicUMax64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  188. const IR::Value& offset, std::string_view value) {
  189. LOG_WARNING(Shader_GLSL, "Int64 atomics not supported, fallback to non-atomic");
  190. ctx.AddU64("{}=packUint2x32(uvec2({}_ssbo{}[{}>>2],{}_ssbo{}[({}>>2)+1]));", inst,
  191. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
  192. binding.U32(), ctx.var_alloc.Consume(offset));
  193. ctx.Add("for(int "
  194. "i=0;i<2;++i){{{}_ssbo{}[({}>>2)+i]=max({}_ssbo{}[({}>>2)+i],unpackUint2x32(uint64_t({}"
  195. "))[i]);}}",
  196. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), ctx.stage_name,
  197. binding.U32(), ctx.var_alloc.Consume(offset), value);
  198. }
  199. void EmitStorageAtomicAnd64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  200. const IR::Value& offset, std::string_view value) {
  201. ctx.AddU64(
  202. "{}=packUint2x32(uvec2(atomicAnd({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicAnd({}_"
  203. "ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
  204. inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
  205. binding.U32(), ctx.var_alloc.Consume(offset), value);
  206. }
  207. void EmitStorageAtomicOr64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  208. const IR::Value& offset, std::string_view value) {
  209. ctx.AddU64("{}=packUint2x32(uvec2(atomicOr({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicOr({}_"
  210. "ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
  211. inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
  212. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
  213. }
  214. void EmitStorageAtomicXor64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  215. const IR::Value& offset, std::string_view value) {
  216. ctx.AddU64(
  217. "{}=packUint2x32(uvec2(atomicXor({}_ssbo{}[{}>>2],unpackUint2x32({}).x),atomicXor({}_"
  218. "ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
  219. inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value, ctx.stage_name,
  220. binding.U32(), ctx.var_alloc.Consume(offset), value);
  221. }
  222. void EmitStorageAtomicExchange64(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  223. const IR::Value& offset, std::string_view value) {
  224. ctx.AddU64("{}=packUint2x32(uvec2(atomicExchange({}_ssbo{}[{}>>2],unpackUint2x32({}).x),"
  225. "atomicExchange({}_ssbo{}[({}>>2)+1],unpackUint2x32({}).y)));",
  226. inst, ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value,
  227. ctx.stage_name, binding.U32(), ctx.var_alloc.Consume(offset), value);
  228. }
  229. void EmitStorageAtomicAddF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  230. const IR::Value& offset, std::string_view value) {
  231. SsboCasFunctionF32(ctx, inst, binding, offset, value, "CasFloatAdd");
  232. }
  233. void EmitStorageAtomicAddF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  234. const IR::Value& offset, std::string_view value) {
  235. SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatAdd16x2");
  236. }
  237. void EmitStorageAtomicAddF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  238. const IR::Value& offset, std::string_view value) {
  239. SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatAdd32x2");
  240. }
  241. void EmitStorageAtomicMinF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  242. const IR::Value& offset, std::string_view value) {
  243. SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMin16x2");
  244. }
  245. void EmitStorageAtomicMinF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  246. const IR::Value& offset, std::string_view value) {
  247. SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMin32x2");
  248. }
  249. void EmitStorageAtomicMaxF16x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  250. const IR::Value& offset, std::string_view value) {
  251. SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMax16x2");
  252. }
  253. void EmitStorageAtomicMaxF32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding,
  254. const IR::Value& offset, std::string_view value) {
  255. SsboCasFunction(ctx, inst, binding, offset, value, "CasFloatMax32x2");
  256. }
  257. void EmitGlobalAtomicIAdd32(EmitContext&) {
  258. throw NotImplementedException("GLSL Instrucion");
  259. }
  260. void EmitGlobalAtomicSMin32(EmitContext&) {
  261. throw NotImplementedException("GLSL Instrucion");
  262. }
  263. void EmitGlobalAtomicUMin32(EmitContext&) {
  264. throw NotImplementedException("GLSL Instrucion");
  265. }
  266. void EmitGlobalAtomicSMax32(EmitContext&) {
  267. throw NotImplementedException("GLSL Instrucion");
  268. }
  269. void EmitGlobalAtomicUMax32(EmitContext&) {
  270. throw NotImplementedException("GLSL Instrucion");
  271. }
  272. void EmitGlobalAtomicInc32(EmitContext&) {
  273. throw NotImplementedException("GLSL Instrucion");
  274. }
  275. void EmitGlobalAtomicDec32(EmitContext&) {
  276. throw NotImplementedException("GLSL Instrucion");
  277. }
  278. void EmitGlobalAtomicAnd32(EmitContext&) {
  279. throw NotImplementedException("GLSL Instrucion");
  280. }
  281. void EmitGlobalAtomicOr32(EmitContext&) {
  282. throw NotImplementedException("GLSL Instrucion");
  283. }
  284. void EmitGlobalAtomicXor32(EmitContext&) {
  285. throw NotImplementedException("GLSL Instrucion");
  286. }
  287. void EmitGlobalAtomicExchange32(EmitContext&) {
  288. throw NotImplementedException("GLSL Instrucion");
  289. }
  290. void EmitGlobalAtomicIAdd64(EmitContext&) {
  291. throw NotImplementedException("GLSL Instrucion");
  292. }
  293. void EmitGlobalAtomicSMin64(EmitContext&) {
  294. throw NotImplementedException("GLSL Instrucion");
  295. }
  296. void EmitGlobalAtomicUMin64(EmitContext&) {
  297. throw NotImplementedException("GLSL Instrucion");
  298. }
  299. void EmitGlobalAtomicSMax64(EmitContext&) {
  300. throw NotImplementedException("GLSL Instrucion");
  301. }
  302. void EmitGlobalAtomicUMax64(EmitContext&) {
  303. throw NotImplementedException("GLSL Instrucion");
  304. }
  305. void EmitGlobalAtomicInc64(EmitContext&) {
  306. throw NotImplementedException("GLSL Instrucion");
  307. }
  308. void EmitGlobalAtomicDec64(EmitContext&) {
  309. throw NotImplementedException("GLSL Instrucion");
  310. }
  311. void EmitGlobalAtomicAnd64(EmitContext&) {
  312. throw NotImplementedException("GLSL Instrucion");
  313. }
  314. void EmitGlobalAtomicOr64(EmitContext&) {
  315. throw NotImplementedException("GLSL Instrucion");
  316. }
  317. void EmitGlobalAtomicXor64(EmitContext&) {
  318. throw NotImplementedException("GLSL Instrucion");
  319. }
  320. void EmitGlobalAtomicExchange64(EmitContext&) {
  321. throw NotImplementedException("GLSL Instrucion");
  322. }
  323. void EmitGlobalAtomicAddF32(EmitContext&) {
  324. throw NotImplementedException("GLSL Instrucion");
  325. }
  326. void EmitGlobalAtomicAddF16x2(EmitContext&) {
  327. throw NotImplementedException("GLSL Instrucion");
  328. }
  329. void EmitGlobalAtomicAddF32x2(EmitContext&) {
  330. throw NotImplementedException("GLSL Instrucion");
  331. }
  332. void EmitGlobalAtomicMinF16x2(EmitContext&) {
  333. throw NotImplementedException("GLSL Instrucion");
  334. }
  335. void EmitGlobalAtomicMinF32x2(EmitContext&) {
  336. throw NotImplementedException("GLSL Instrucion");
  337. }
  338. void EmitGlobalAtomicMaxF16x2(EmitContext&) {
  339. throw NotImplementedException("GLSL Instrucion");
  340. }
  341. void EmitGlobalAtomicMaxF32x2(EmitContext&) {
  342. throw NotImplementedException("GLSL Instrucion");
  343. }
  344. } // namespace Shader::Backend::GLSL