emit_glasm_image.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <utility>
  5. #include "shader_recompiler/backend/glasm/emit_context.h"
  6. #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
  7. #include "shader_recompiler/frontend/ir/modifiers.h"
  8. #include "shader_recompiler/frontend/ir/value.h"
  9. namespace Shader::Backend::GLASM {
  10. namespace {
  11. struct ScopedRegister {
  12. ScopedRegister() = default;
  13. ScopedRegister(RegAlloc& reg_alloc_) : reg_alloc{&reg_alloc_}, reg{reg_alloc->AllocReg()} {}
  14. ~ScopedRegister() {
  15. if (reg_alloc) {
  16. reg_alloc->FreeReg(reg);
  17. }
  18. }
  19. ScopedRegister& operator=(ScopedRegister&& rhs) noexcept {
  20. if (reg_alloc) {
  21. reg_alloc->FreeReg(reg);
  22. }
  23. reg_alloc = std::exchange(rhs.reg_alloc, nullptr);
  24. reg = rhs.reg;
  25. return *this;
  26. }
  27. ScopedRegister(ScopedRegister&& rhs) noexcept
  28. : reg_alloc{std::exchange(rhs.reg_alloc, nullptr)}, reg{rhs.reg} {}
  29. ScopedRegister& operator=(const ScopedRegister&) = delete;
  30. ScopedRegister(const ScopedRegister&) = delete;
  31. RegAlloc* reg_alloc{};
  32. Register reg;
  33. };
  34. std::string Texture(EmitContext& ctx, IR::TextureInstInfo info,
  35. [[maybe_unused]] const IR::Value& index) {
  36. // FIXME: indexed reads
  37. return fmt::format("texture[{}]", ctx.texture_bindings.at(info.descriptor_index));
  38. }
  39. std::string_view TextureType(IR::TextureInstInfo info) {
  40. if (info.is_depth) {
  41. switch (info.type) {
  42. case TextureType::Color1D:
  43. return "SHADOW1D";
  44. case TextureType::ColorArray1D:
  45. return "SHADOWARRAY1D";
  46. case TextureType::Color2D:
  47. return "SHADOW2D";
  48. case TextureType::ColorArray2D:
  49. return "SHADOWARRAY2D";
  50. case TextureType::Color3D:
  51. return "SHADOW3D";
  52. case TextureType::ColorCube:
  53. return "SHADOWCUBE";
  54. case TextureType::ColorArrayCube:
  55. return "SHADOWARRAYCUBE";
  56. case TextureType::Buffer:
  57. return "SHADOWBUFFER";
  58. }
  59. } else {
  60. switch (info.type) {
  61. case TextureType::Color1D:
  62. return "1D";
  63. case TextureType::ColorArray1D:
  64. return "ARRAY1D";
  65. case TextureType::Color2D:
  66. return "2D";
  67. case TextureType::ColorArray2D:
  68. return "ARRAY2D";
  69. case TextureType::Color3D:
  70. return "3D";
  71. case TextureType::ColorCube:
  72. return "CUBE";
  73. case TextureType::ColorArrayCube:
  74. return "ARRAYCUBE";
  75. case TextureType::Buffer:
  76. return "BUFFER";
  77. }
  78. }
  79. throw InvalidArgument("Invalid texture type {}", info.type.Value());
  80. }
  81. std::string Offset(EmitContext& ctx, const IR::Value& offset) {
  82. if (offset.IsEmpty()) {
  83. return "";
  84. }
  85. return fmt::format(",offset({})", Register{ctx.reg_alloc.Consume(offset)});
  86. }
  87. std::pair<ScopedRegister, ScopedRegister> AllocOffsetsRegs(EmitContext& ctx,
  88. const IR::Value& offset2) {
  89. if (offset2.IsEmpty()) {
  90. return {};
  91. } else {
  92. return {ctx.reg_alloc, ctx.reg_alloc};
  93. }
  94. }
  95. void SwizzleOffsets(EmitContext& ctx, Register off_x, Register off_y, const IR::Value& offset1,
  96. const IR::Value& offset2) {
  97. const Register offsets_a{ctx.reg_alloc.Consume(offset1)};
  98. const Register offsets_b{ctx.reg_alloc.Consume(offset2)};
  99. // Input swizzle: [XYXY] [XYXY]
  100. // Output swizzle: [XXXX] [YYYY]
  101. ctx.Add("MOV {}.x,{}.x;"
  102. "MOV {}.y,{}.z;"
  103. "MOV {}.z,{}.x;"
  104. "MOV {}.w,{}.z;"
  105. "MOV {}.x,{}.y;"
  106. "MOV {}.y,{}.w;"
  107. "MOV {}.z,{}.y;"
  108. "MOV {}.w,{}.w;",
  109. off_x, offsets_a, off_x, offsets_a, off_x, offsets_b, off_x, offsets_b, off_y,
  110. offsets_a, off_y, offsets_a, off_y, offsets_b, off_y, offsets_b);
  111. }
  112. std::pair<std::string, ScopedRegister> Coord(EmitContext& ctx, const IR::Value& coord) {
  113. if (coord.IsImmediate()) {
  114. ScopedRegister scoped_reg(ctx.reg_alloc);
  115. return {fmt::to_string(scoped_reg.reg), std::move(scoped_reg)};
  116. }
  117. std::string coord_vec{fmt::to_string(Register{ctx.reg_alloc.Consume(coord)})};
  118. if (coord.InstRecursive()->HasUses()) {
  119. // Move non-dead coords to a separate register, although this should never happen because
  120. // vectors are only assembled for immediate texture instructions
  121. ctx.Add("MOV.F RC,{};", coord_vec);
  122. coord_vec = "RC";
  123. }
  124. return {std::move(coord_vec), ScopedRegister{}};
  125. }
  126. void StoreSparse(EmitContext& ctx, IR::Inst* sparse_inst) {
  127. if (!sparse_inst) {
  128. return;
  129. }
  130. const Register sparse_ret{ctx.reg_alloc.Define(*sparse_inst)};
  131. ctx.Add("MOV.S {},-1;"
  132. "MOV.S {}(NONRESIDENT),0;",
  133. sparse_ret, sparse_ret);
  134. sparse_inst->Invalidate();
  135. }
  136. } // Anonymous namespace
  137. void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  138. const IR::Value& coord, Register bias_lc, const IR::Value& offset) {
  139. const auto info{inst.Flags<IR::TextureInstInfo>()};
  140. const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
  141. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  142. const std::string_view lod_clamp_mod{info.has_lod_clamp ? ".LODCLAMP" : ""};
  143. const std::string_view type{TextureType(info)};
  144. const std::string texture{Texture(ctx, info, index)};
  145. const std::string offset_vec{Offset(ctx, offset)};
  146. const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
  147. const Register ret{ctx.reg_alloc.Define(inst)};
  148. if (info.has_bias) {
  149. if (info.type == TextureType::ColorArrayCube) {
  150. ctx.Add("TXB.F{}{} {},{},{},{},ARRAYCUBE{};", lod_clamp_mod, sparse_mod, ret, coord_vec,
  151. bias_lc, texture, offset_vec);
  152. } else {
  153. if (info.has_lod_clamp) {
  154. ctx.Add("MOV.F {}.w,{}.x;"
  155. "TXB.F.LODCLAMP{} {},{},{}.y,{},{}{};",
  156. coord_vec, bias_lc, sparse_mod, ret, coord_vec, bias_lc, texture, type,
  157. offset_vec);
  158. } else {
  159. ctx.Add("MOV.F {}.w,{}.x;"
  160. "TXB.F{} {},{},{},{}{};",
  161. coord_vec, bias_lc, sparse_mod, ret, coord_vec, texture, type, offset_vec);
  162. }
  163. }
  164. } else {
  165. if (info.has_lod_clamp && info.type == TextureType::ColorArrayCube) {
  166. ctx.Add("TEX.F.LODCLAMP{} {},{},{},{},ARRAYCUBE{};", sparse_mod, ret, coord_vec,
  167. bias_lc, texture, offset_vec);
  168. } else {
  169. ctx.Add("TEX.F{}{} {},{},{},{}{};", lod_clamp_mod, sparse_mod, ret, coord_vec, texture,
  170. type, offset_vec);
  171. }
  172. }
  173. StoreSparse(ctx, sparse_inst);
  174. }
  175. void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  176. const IR::Value& coord, ScalarF32 lod, const IR::Value& offset) {
  177. const auto info{inst.Flags<IR::TextureInstInfo>()};
  178. const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
  179. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  180. const std::string_view type{TextureType(info)};
  181. const std::string texture{Texture(ctx, info, index)};
  182. const std::string offset_vec{Offset(ctx, offset)};
  183. const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
  184. const Register ret{ctx.reg_alloc.Define(inst)};
  185. if (info.type == TextureType::ColorArrayCube) {
  186. ctx.Add("TXL.F{} {},{},{},{},ARRAYCUBE{};", sparse_mod, ret, coord_vec, lod, texture,
  187. offset_vec);
  188. } else {
  189. ctx.Add("MOV.F {}.w,{};"
  190. "TXL.F{} {},{},{},{}{};",
  191. coord_vec, lod, sparse_mod, ret, coord_vec, texture, type, offset_vec);
  192. }
  193. StoreSparse(ctx, sparse_inst);
  194. }
  195. void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  196. const IR::Value& coord, ScalarF32 dref, Register bias_lc,
  197. const IR::Value& offset) {
  198. const auto info{inst.Flags<IR::TextureInstInfo>()};
  199. const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
  200. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  201. const std::string_view type{TextureType(info)};
  202. const std::string texture{Texture(ctx, info, index)};
  203. const std::string offset_vec{Offset(ctx, offset)};
  204. const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
  205. const Register ret{ctx.reg_alloc.Define(inst)};
  206. if (info.has_bias) {
  207. if (info.has_lod_clamp) {
  208. switch (info.type) {
  209. case TextureType::Color1D:
  210. case TextureType::ColorArray1D:
  211. case TextureType::Color2D:
  212. ctx.Add("MOV.F {}.z,{};"
  213. "MOV.F {}.w,{}.x;"
  214. "TXB.F.LODCLAMP{} {},{},{}.y,{},{}{};",
  215. coord_vec, dref, coord_vec, bias_lc, sparse_mod, ret, coord_vec, bias_lc,
  216. texture, type, offset_vec);
  217. break;
  218. case TextureType::ColorArray2D:
  219. case TextureType::ColorCube:
  220. ctx.Add("MOV.F {}.w,{};"
  221. "TXB.F.LODCLAMP{} {},{},{},{},{}{};",
  222. coord_vec, dref, sparse_mod, ret, coord_vec, bias_lc, texture, type,
  223. offset_vec);
  224. break;
  225. default:
  226. throw NotImplementedException("Invalid type {} with bias and lod clamp",
  227. info.type.Value());
  228. }
  229. } else {
  230. switch (info.type) {
  231. case TextureType::Color1D:
  232. case TextureType::ColorArray1D:
  233. case TextureType::Color2D:
  234. ctx.Add("MOV.F {}.z,{};"
  235. "MOV.F {}.w,{}.x;"
  236. "TXB.F{} {},{},{},{}{};",
  237. coord_vec, dref, coord_vec, bias_lc, sparse_mod, ret, coord_vec, texture,
  238. type, offset_vec);
  239. break;
  240. case TextureType::ColorArray2D:
  241. case TextureType::ColorCube:
  242. ctx.Add("MOV.F {}.w,{};"
  243. "TXB.F{} {},{},{},{},{}{};",
  244. coord_vec, dref, sparse_mod, ret, coord_vec, bias_lc, texture, type,
  245. offset_vec);
  246. break;
  247. case TextureType::ColorArrayCube: {
  248. const ScopedRegister pair{ctx.reg_alloc};
  249. ctx.Add("MOV.F {}.x,{};"
  250. "MOV.F {}.y,{}.x;"
  251. "TXB.F{} {},{},{},{},{}{};",
  252. pair.reg, dref, pair.reg, bias_lc, sparse_mod, ret, coord_vec, pair.reg,
  253. texture, type, offset_vec);
  254. break;
  255. }
  256. default:
  257. throw NotImplementedException("Invalid type {}", info.type.Value());
  258. }
  259. }
  260. } else {
  261. if (info.has_lod_clamp) {
  262. if (info.type != TextureType::ColorArrayCube) {
  263. const bool w_swizzle{info.type == TextureType::ColorArray2D ||
  264. info.type == TextureType::ColorCube};
  265. const char dref_swizzle{w_swizzle ? 'w' : 'z'};
  266. ctx.Add("MOV.F {}.{},{};"
  267. "TEX.F.LODCLAMP{} {},{},{},{},{}{};",
  268. coord_vec, dref_swizzle, dref, sparse_mod, ret, coord_vec, bias_lc, texture,
  269. type, offset_vec);
  270. } else {
  271. const ScopedRegister pair{ctx.reg_alloc};
  272. ctx.Add("MOV.F {}.x,{};"
  273. "MOV.F {}.y,{};"
  274. "TEX.F.LODCLAMP{} {},{},{},{},{}{};",
  275. pair.reg, dref, pair.reg, bias_lc, sparse_mod, ret, coord_vec, pair.reg,
  276. texture, type, offset_vec);
  277. }
  278. } else {
  279. if (info.type != TextureType::ColorArrayCube) {
  280. const bool w_swizzle{info.type == TextureType::ColorArray2D ||
  281. info.type == TextureType::ColorCube};
  282. const char dref_swizzle{w_swizzle ? 'w' : 'z'};
  283. ctx.Add("MOV.F {}.{},{};"
  284. "TEX.F{} {},{},{},{}{};",
  285. coord_vec, dref_swizzle, dref, sparse_mod, ret, coord_vec, texture, type,
  286. offset_vec);
  287. } else {
  288. const ScopedRegister pair{ctx.reg_alloc};
  289. ctx.Add("TEX.F{} {},{},{},{},{}{};", sparse_mod, ret, coord_vec, dref, texture,
  290. type, offset_vec);
  291. }
  292. }
  293. }
  294. StoreSparse(ctx, sparse_inst);
  295. }
  296. void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  297. const IR::Value& coord, ScalarF32 dref, ScalarF32 lod,
  298. const IR::Value& offset) {
  299. const auto info{inst.Flags<IR::TextureInstInfo>()};
  300. const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
  301. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  302. const std::string_view type{TextureType(info)};
  303. const std::string texture{Texture(ctx, info, index)};
  304. const std::string offset_vec{Offset(ctx, offset)};
  305. const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
  306. const Register ret{ctx.reg_alloc.Define(inst)};
  307. switch (info.type) {
  308. case TextureType::Color1D:
  309. case TextureType::ColorArray1D:
  310. case TextureType::Color2D:
  311. ctx.Add("MOV.F {}.z,{};"
  312. "MOV.F {}.w,{};"
  313. "TXL.F{} {},{},{},{}{};",
  314. coord_vec, dref, coord_vec, lod, sparse_mod, ret, coord_vec, texture, type,
  315. offset_vec);
  316. break;
  317. case TextureType::ColorArray2D:
  318. case TextureType::ColorCube:
  319. ctx.Add("MOV.F {}.w,{};"
  320. "TXL.F{} {},{},{},{},{}{};",
  321. coord_vec, dref, sparse_mod, ret, coord_vec, lod, texture, type, offset_vec);
  322. break;
  323. case TextureType::ColorArrayCube: {
  324. const ScopedRegister pair{ctx.reg_alloc};
  325. ctx.Add("MOV.F {}.x,{};"
  326. "MOV.F {}.y,{};"
  327. "TXL.F{} {},{},{},{},{}{};",
  328. pair.reg, dref, pair.reg, lod, sparse_mod, ret, coord_vec, pair.reg, texture, type,
  329. offset_vec);
  330. break;
  331. }
  332. default:
  333. throw NotImplementedException("Invalid type {}", info.type.Value());
  334. }
  335. StoreSparse(ctx, sparse_inst);
  336. }
  337. void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  338. const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2) {
  339. // Allocate offsets early so they don't overwrite any consumed register
  340. const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
  341. const auto info{inst.Flags<IR::TextureInstInfo>()};
  342. const char comp{"xyzw"[info.gather_component]};
  343. const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
  344. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  345. const std::string_view type{TextureType(info)};
  346. const std::string texture{Texture(ctx, info, index)};
  347. const Register coord_vec{ctx.reg_alloc.Consume(coord)};
  348. const Register ret{ctx.reg_alloc.Define(inst)};
  349. if (offset2.IsEmpty()) {
  350. const std::string offset_vec{Offset(ctx, offset)};
  351. ctx.Add("TXG.F{} {},{},{}.{},{}{};", sparse_mod, ret, coord_vec, texture, comp, type,
  352. offset_vec);
  353. } else {
  354. SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
  355. ctx.Add("TXGO.F{} {},{},{},{},{}.{},{};", sparse_mod, ret, coord_vec, off_x.reg, off_y.reg,
  356. texture, comp, type);
  357. }
  358. StoreSparse(ctx, sparse_inst);
  359. }
  360. void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  361. const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2,
  362. const IR::Value& dref) {
  363. // FIXME: This instruction is not working as expected
  364. // Allocate offsets early so they don't overwrite any consumed register
  365. const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
  366. const auto info{inst.Flags<IR::TextureInstInfo>()};
  367. const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
  368. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  369. const std::string_view type{TextureType(info)};
  370. const std::string texture{Texture(ctx, info, index)};
  371. const Register coord_vec{ctx.reg_alloc.Consume(coord)};
  372. const ScalarF32 dref_value{ctx.reg_alloc.Consume(dref)};
  373. const Register ret{ctx.reg_alloc.Define(inst)};
  374. std::string args;
  375. switch (info.type) {
  376. case TextureType::Color2D:
  377. ctx.Add("MOV.F {}.z,{};", coord_vec, dref_value);
  378. args = fmt::to_string(coord_vec);
  379. break;
  380. case TextureType::ColorArray2D:
  381. case TextureType::ColorCube:
  382. ctx.Add("MOV.F {}.w,{};", coord_vec, dref_value);
  383. args = fmt::to_string(coord_vec);
  384. break;
  385. case TextureType::ColorArrayCube:
  386. args = fmt::format("{},{}", coord_vec, dref_value);
  387. break;
  388. default:
  389. throw NotImplementedException("Invalid type {}", info.type.Value());
  390. }
  391. if (offset2.IsEmpty()) {
  392. const std::string offset_vec{Offset(ctx, offset)};
  393. ctx.Add("TXG.F{} {},{},{},{}{};", sparse_mod, ret, args, texture, type, offset_vec);
  394. } else {
  395. SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
  396. ctx.Add("TXGO.F{} {},{},{},{},{},{};", sparse_mod, ret, args, off_x.reg, off_y.reg, texture,
  397. type);
  398. }
  399. StoreSparse(ctx, sparse_inst);
  400. }
  401. void EmitImageFetch([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
  402. [[maybe_unused]] const IR::Value& index, [[maybe_unused]] Register coord,
  403. [[maybe_unused]] Register offset, [[maybe_unused]] Register lod,
  404. [[maybe_unused]] Register ms) {
  405. throw NotImplementedException("GLASM instruction");
  406. }
  407. void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  408. ScalarF32 lod) {
  409. const auto info{inst.Flags<IR::TextureInstInfo>()};
  410. const std::string texture{Texture(ctx, info, index)};
  411. const std::string_view type{TextureType(info)};
  412. ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type);
  413. }
  414. void EmitImageQueryLod([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
  415. [[maybe_unused]] const IR::Value& index, [[maybe_unused]] Register coord) {
  416. throw NotImplementedException("GLASM instruction");
  417. }
  418. void EmitImageGradient([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
  419. [[maybe_unused]] const IR::Value& index, [[maybe_unused]] Register coord,
  420. [[maybe_unused]] Register derivates, [[maybe_unused]] Register offset,
  421. [[maybe_unused]] Register lod_clamp) {
  422. throw NotImplementedException("GLASM instruction");
  423. }
  424. void EmitImageRead([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
  425. [[maybe_unused]] const IR::Value& index, [[maybe_unused]] Register coord) {
  426. throw NotImplementedException("GLASM instruction");
  427. }
  428. void EmitImageWrite([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
  429. [[maybe_unused]] const IR::Value& index, [[maybe_unused]] Register coord,
  430. [[maybe_unused]] Register color) {
  431. throw NotImplementedException("GLASM instruction");
  432. }
  433. void EmitBindlessImageSampleImplicitLod(EmitContext&) {
  434. throw LogicError("Unreachable instruction");
  435. }
  436. void EmitBindlessImageSampleExplicitLod(EmitContext&) {
  437. throw LogicError("Unreachable instruction");
  438. }
  439. void EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
  440. throw LogicError("Unreachable instruction");
  441. }
  442. void EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
  443. throw LogicError("Unreachable instruction");
  444. }
  445. void EmitBindlessImageGather(EmitContext&) {
  446. throw LogicError("Unreachable instruction");
  447. }
  448. void EmitBindlessImageGatherDref(EmitContext&) {
  449. throw LogicError("Unreachable instruction");
  450. }
  451. void EmitBindlessImageFetch(EmitContext&) {
  452. throw LogicError("Unreachable instruction");
  453. }
  454. void EmitBindlessImageQueryDimensions(EmitContext&) {
  455. throw LogicError("Unreachable instruction");
  456. }
  457. void EmitBindlessImageQueryLod(EmitContext&) {
  458. throw LogicError("Unreachable instruction");
  459. }
  460. void EmitBindlessImageGradient(EmitContext&) {
  461. throw LogicError("Unreachable instruction");
  462. }
  463. void EmitBindlessImageRead(EmitContext&) {
  464. throw LogicError("Unreachable instruction");
  465. }
  466. void EmitBindlessImageWrite(EmitContext&) {
  467. throw LogicError("Unreachable instruction");
  468. }
  469. void EmitBoundImageSampleImplicitLod(EmitContext&) {
  470. throw LogicError("Unreachable instruction");
  471. }
  472. void EmitBoundImageSampleExplicitLod(EmitContext&) {
  473. throw LogicError("Unreachable instruction");
  474. }
  475. void EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
  476. throw LogicError("Unreachable instruction");
  477. }
  478. void EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
  479. throw LogicError("Unreachable instruction");
  480. }
  481. void EmitBoundImageGather(EmitContext&) {
  482. throw LogicError("Unreachable instruction");
  483. }
  484. void EmitBoundImageGatherDref(EmitContext&) {
  485. throw LogicError("Unreachable instruction");
  486. }
  487. void EmitBoundImageFetch(EmitContext&) {
  488. throw LogicError("Unreachable instruction");
  489. }
  490. void EmitBoundImageQueryDimensions(EmitContext&) {
  491. throw LogicError("Unreachable instruction");
  492. }
  493. void EmitBoundImageQueryLod(EmitContext&) {
  494. throw LogicError("Unreachable instruction");
  495. }
  496. void EmitBoundImageGradient(EmitContext&) {
  497. throw LogicError("Unreachable instruction");
  498. }
  499. void EmitBoundImageRead(EmitContext&) {
  500. throw LogicError("Unreachable instruction");
  501. }
  502. void EmitBoundImageWrite(EmitContext&) {
  503. throw LogicError("Unreachable instruction");
  504. }
  505. } // namespace Shader::Backend::GLASM