emit_glasm_image.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  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. if (info.type == TextureType::Buffer) {
  38. return fmt::format("texture[{}]", ctx.texture_buffer_bindings.at(info.descriptor_index));
  39. } else {
  40. return fmt::format("texture[{}]", ctx.texture_bindings.at(info.descriptor_index));
  41. }
  42. }
  43. std::string Image(EmitContext& ctx, IR::TextureInstInfo info,
  44. [[maybe_unused]] const IR::Value& index) {
  45. // FIXME: indexed reads
  46. if (info.type == TextureType::Buffer) {
  47. return fmt::format("image[{}]", ctx.image_buffer_bindings.at(info.descriptor_index));
  48. } else {
  49. return fmt::format("image[{}]", ctx.image_bindings.at(info.descriptor_index));
  50. }
  51. }
  52. std::string_view TextureType(IR::TextureInstInfo info) {
  53. if (info.is_depth) {
  54. switch (info.type) {
  55. case TextureType::Color1D:
  56. return "SHADOW1D";
  57. case TextureType::ColorArray1D:
  58. return "SHADOWARRAY1D";
  59. case TextureType::Color2D:
  60. return "SHADOW2D";
  61. case TextureType::ColorArray2D:
  62. return "SHADOWARRAY2D";
  63. case TextureType::Color3D:
  64. return "SHADOW3D";
  65. case TextureType::ColorCube:
  66. return "SHADOWCUBE";
  67. case TextureType::ColorArrayCube:
  68. return "SHADOWARRAYCUBE";
  69. case TextureType::Buffer:
  70. return "SHADOWBUFFER";
  71. }
  72. } else {
  73. switch (info.type) {
  74. case TextureType::Color1D:
  75. return "1D";
  76. case TextureType::ColorArray1D:
  77. return "ARRAY1D";
  78. case TextureType::Color2D:
  79. return "2D";
  80. case TextureType::ColorArray2D:
  81. return "ARRAY2D";
  82. case TextureType::Color3D:
  83. return "3D";
  84. case TextureType::ColorCube:
  85. return "CUBE";
  86. case TextureType::ColorArrayCube:
  87. return "ARRAYCUBE";
  88. case TextureType::Buffer:
  89. return "BUFFER";
  90. }
  91. }
  92. throw InvalidArgument("Invalid texture type {}", info.type.Value());
  93. }
  94. std::string Offset(EmitContext& ctx, const IR::Value& offset) {
  95. if (offset.IsEmpty()) {
  96. return "";
  97. }
  98. return fmt::format(",offset({})", Register{ctx.reg_alloc.Consume(offset)});
  99. }
  100. std::pair<ScopedRegister, ScopedRegister> AllocOffsetsRegs(EmitContext& ctx,
  101. const IR::Value& offset2) {
  102. if (offset2.IsEmpty()) {
  103. return {};
  104. } else {
  105. return {ctx.reg_alloc, ctx.reg_alloc};
  106. }
  107. }
  108. void SwizzleOffsets(EmitContext& ctx, Register off_x, Register off_y, const IR::Value& offset1,
  109. const IR::Value& offset2) {
  110. const Register offsets_a{ctx.reg_alloc.Consume(offset1)};
  111. const Register offsets_b{ctx.reg_alloc.Consume(offset2)};
  112. // Input swizzle: [XYXY] [XYXY]
  113. // Output swizzle: [XXXX] [YYYY]
  114. ctx.Add("MOV {}.x,{}.x;"
  115. "MOV {}.y,{}.z;"
  116. "MOV {}.z,{}.x;"
  117. "MOV {}.w,{}.z;"
  118. "MOV {}.x,{}.y;"
  119. "MOV {}.y,{}.w;"
  120. "MOV {}.z,{}.y;"
  121. "MOV {}.w,{}.w;",
  122. off_x, offsets_a, off_x, offsets_a, off_x, offsets_b, off_x, offsets_b, off_y,
  123. offsets_a, off_y, offsets_a, off_y, offsets_b, off_y, offsets_b);
  124. }
  125. std::string GradOffset(const IR::Value& offset) {
  126. if (offset.IsImmediate()) {
  127. // LOG_WARNING immediate
  128. return "";
  129. }
  130. IR::Inst* const vector{offset.InstRecursive()};
  131. if (!vector->AreAllArgsImmediates()) {
  132. // LOG_WARNING elements not immediate
  133. return "";
  134. }
  135. switch (vector->NumArgs()) {
  136. case 1:
  137. return fmt::format(",({})", static_cast<s32>(vector->Arg(0).U32()));
  138. case 2:
  139. return fmt::format(",({},{})", static_cast<s32>(vector->Arg(0).U32()),
  140. static_cast<s32>(vector->Arg(1).U32()));
  141. default:
  142. throw LogicError("Invalid number of gradient offsets {}", vector->NumArgs());
  143. }
  144. }
  145. std::pair<std::string, ScopedRegister> Coord(EmitContext& ctx, const IR::Value& coord) {
  146. if (coord.IsImmediate()) {
  147. ScopedRegister scoped_reg(ctx.reg_alloc);
  148. ctx.Add("MOV.U {}.x,{};", scoped_reg.reg, ScalarU32{ctx.reg_alloc.Consume(coord)});
  149. return {fmt::to_string(scoped_reg.reg), std::move(scoped_reg)};
  150. }
  151. std::string coord_vec{fmt::to_string(Register{ctx.reg_alloc.Consume(coord)})};
  152. if (coord.InstRecursive()->HasUses()) {
  153. // Move non-dead coords to a separate register, although this should never happen because
  154. // vectors are only assembled for immediate texture instructions
  155. ctx.Add("MOV.F RC,{};", coord_vec);
  156. coord_vec = "RC";
  157. }
  158. return {std::move(coord_vec), ScopedRegister{}};
  159. }
  160. void StoreSparse(EmitContext& ctx, IR::Inst* sparse_inst) {
  161. if (!sparse_inst) {
  162. return;
  163. }
  164. const Register sparse_ret{ctx.reg_alloc.Define(*sparse_inst)};
  165. ctx.Add("MOV.S {},-1;"
  166. "MOV.S {}(NONRESIDENT),0;",
  167. sparse_ret, sparse_ret);
  168. }
  169. std::string_view FormatStorage(ImageFormat format) {
  170. switch (format) {
  171. case ImageFormat::Typeless:
  172. return "U";
  173. case ImageFormat::R8_UINT:
  174. return "U8";
  175. case ImageFormat::R8_SINT:
  176. return "S8";
  177. case ImageFormat::R16_UINT:
  178. return "U16";
  179. case ImageFormat::R16_SINT:
  180. return "S16";
  181. case ImageFormat::R32_UINT:
  182. return "U32";
  183. case ImageFormat::R32G32_UINT:
  184. return "U32X2";
  185. case ImageFormat::R32G32B32A32_UINT:
  186. return "U32X4";
  187. }
  188. throw InvalidArgument("Invalid image format {}", format);
  189. }
  190. template <typename T>
  191. void ImageAtomic(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord, T value,
  192. std::string_view op) {
  193. const auto info{inst.Flags<IR::TextureInstInfo>()};
  194. const std::string_view type{TextureType(info)};
  195. const std::string image{Image(ctx, info, index)};
  196. const Register ret{ctx.reg_alloc.Define(inst)};
  197. ctx.Add("ATOMIM.{} {},{},{},{},{};", op, ret, value, coord, image, type);
  198. }
  199. IR::Inst* PrepareSparse(IR::Inst& inst) {
  200. const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
  201. if (sparse_inst) {
  202. sparse_inst->Invalidate();
  203. }
  204. return sparse_inst;
  205. }
  206. } // Anonymous namespace
  207. void EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  208. const IR::Value& coord, Register bias_lc, const IR::Value& offset) {
  209. const auto info{inst.Flags<IR::TextureInstInfo>()};
  210. const auto sparse_inst{PrepareSparse(inst)};
  211. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  212. const std::string_view lod_clamp_mod{info.has_lod_clamp ? ".LODCLAMP" : ""};
  213. const std::string_view type{TextureType(info)};
  214. const std::string texture{Texture(ctx, info, index)};
  215. const std::string offset_vec{Offset(ctx, offset)};
  216. const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
  217. const Register ret{ctx.reg_alloc.Define(inst)};
  218. if (info.has_bias) {
  219. if (info.type == TextureType::ColorArrayCube) {
  220. ctx.Add("TXB.F{}{} {},{},{},{},ARRAYCUBE{};", lod_clamp_mod, sparse_mod, ret, coord_vec,
  221. bias_lc, texture, offset_vec);
  222. } else {
  223. if (info.has_lod_clamp) {
  224. ctx.Add("MOV.F {}.w,{}.x;"
  225. "TXB.F.LODCLAMP{} {},{},{}.y,{},{}{};",
  226. coord_vec, bias_lc, sparse_mod, ret, coord_vec, bias_lc, texture, type,
  227. offset_vec);
  228. } else {
  229. ctx.Add("MOV.F {}.w,{}.x;"
  230. "TXB.F{} {},{},{},{}{};",
  231. coord_vec, bias_lc, sparse_mod, ret, coord_vec, texture, type, offset_vec);
  232. }
  233. }
  234. } else {
  235. if (info.has_lod_clamp && info.type == TextureType::ColorArrayCube) {
  236. ctx.Add("TEX.F.LODCLAMP{} {},{},{},{},ARRAYCUBE{};", sparse_mod, ret, coord_vec,
  237. bias_lc, texture, offset_vec);
  238. } else {
  239. ctx.Add("TEX.F{}{} {},{},{},{}{};", lod_clamp_mod, sparse_mod, ret, coord_vec, texture,
  240. type, offset_vec);
  241. }
  242. }
  243. StoreSparse(ctx, sparse_inst);
  244. }
  245. void EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  246. const IR::Value& coord, ScalarF32 lod, const IR::Value& offset) {
  247. const auto info{inst.Flags<IR::TextureInstInfo>()};
  248. const auto sparse_inst{PrepareSparse(inst)};
  249. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  250. const std::string_view type{TextureType(info)};
  251. const std::string texture{Texture(ctx, info, index)};
  252. const std::string offset_vec{Offset(ctx, offset)};
  253. const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
  254. const Register ret{ctx.reg_alloc.Define(inst)};
  255. if (info.type == TextureType::ColorArrayCube) {
  256. ctx.Add("TXL.F{} {},{},{},{},ARRAYCUBE{};", sparse_mod, ret, coord_vec, lod, texture,
  257. offset_vec);
  258. } else {
  259. ctx.Add("MOV.F {}.w,{};"
  260. "TXL.F{} {},{},{},{}{};",
  261. coord_vec, lod, sparse_mod, ret, coord_vec, texture, type, offset_vec);
  262. }
  263. StoreSparse(ctx, sparse_inst);
  264. }
  265. void EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  266. const IR::Value& coord, const IR::Value& dref,
  267. const IR::Value& bias_lc, const IR::Value& offset) {
  268. // Allocate early to avoid aliases
  269. const auto info{inst.Flags<IR::TextureInstInfo>()};
  270. ScopedRegister staging;
  271. if (info.type == TextureType::ColorArrayCube) {
  272. staging = ScopedRegister{ctx.reg_alloc};
  273. }
  274. const ScalarF32 dref_val{ctx.reg_alloc.Consume(dref)};
  275. const Register bias_lc_vec{ctx.reg_alloc.Consume(bias_lc)};
  276. const auto sparse_inst{PrepareSparse(inst)};
  277. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  278. const std::string_view type{TextureType(info)};
  279. const std::string texture{Texture(ctx, info, index)};
  280. const std::string offset_vec{Offset(ctx, offset)};
  281. const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
  282. const Register ret{ctx.reg_alloc.Define(inst)};
  283. if (info.has_bias) {
  284. if (info.has_lod_clamp) {
  285. switch (info.type) {
  286. case TextureType::Color1D:
  287. case TextureType::ColorArray1D:
  288. case TextureType::Color2D:
  289. ctx.Add("MOV.F {}.z,{};"
  290. "MOV.F {}.w,{}.x;"
  291. "TXB.F.LODCLAMP{} {},{},{}.y,{},{}{};",
  292. coord_vec, dref_val, coord_vec, bias_lc_vec, sparse_mod, ret, coord_vec,
  293. bias_lc_vec, texture, type, offset_vec);
  294. break;
  295. case TextureType::ColorArray2D:
  296. case TextureType::ColorCube:
  297. ctx.Add("MOV.F {}.w,{};"
  298. "TXB.F.LODCLAMP{} {},{},{},{},{}{};",
  299. coord_vec, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec, texture, type,
  300. offset_vec);
  301. break;
  302. default:
  303. throw NotImplementedException("Invalid type {} with bias and lod clamp",
  304. info.type.Value());
  305. }
  306. } else {
  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,{}.x;"
  313. "TXB.F{} {},{},{},{}{};",
  314. coord_vec, dref_val, coord_vec, bias_lc_vec, sparse_mod, ret, coord_vec,
  315. texture, type, offset_vec);
  316. break;
  317. case TextureType::ColorArray2D:
  318. case TextureType::ColorCube:
  319. ctx.Add("MOV.F {}.w,{};"
  320. "TXB.F{} {},{},{},{},{}{};",
  321. coord_vec, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec, texture, type,
  322. offset_vec);
  323. break;
  324. case TextureType::ColorArrayCube:
  325. ctx.Add("MOV.F {}.x,{};"
  326. "MOV.F {}.y,{}.x;"
  327. "TXB.F{} {},{},{},{},{}{};",
  328. staging.reg, dref_val, staging.reg, bias_lc_vec, sparse_mod, ret, coord_vec,
  329. staging.reg, texture, type, offset_vec);
  330. break;
  331. default:
  332. throw NotImplementedException("Invalid type {}", info.type.Value());
  333. }
  334. }
  335. } else {
  336. if (info.has_lod_clamp) {
  337. if (info.type != TextureType::ColorArrayCube) {
  338. const bool w_swizzle{info.type == TextureType::ColorArray2D ||
  339. info.type == TextureType::ColorCube};
  340. const char dref_swizzle{w_swizzle ? 'w' : 'z'};
  341. ctx.Add("MOV.F {}.{},{};"
  342. "TEX.F.LODCLAMP{} {},{},{},{},{}{};",
  343. coord_vec, dref_swizzle, dref_val, sparse_mod, ret, coord_vec, bias_lc_vec,
  344. texture, type, offset_vec);
  345. } else {
  346. ctx.Add("MOV.F {}.x,{};"
  347. "MOV.F {}.y,{};"
  348. "TEX.F.LODCLAMP{} {},{},{},{},{}{};",
  349. staging.reg, dref_val, staging.reg, bias_lc_vec, sparse_mod, ret, coord_vec,
  350. staging.reg, texture, type, offset_vec);
  351. }
  352. } else {
  353. if (info.type != TextureType::ColorArrayCube) {
  354. const bool w_swizzle{info.type == TextureType::ColorArray2D ||
  355. info.type == TextureType::ColorCube};
  356. const char dref_swizzle{w_swizzle ? 'w' : 'z'};
  357. ctx.Add("MOV.F {}.{},{};"
  358. "TEX.F{} {},{},{},{}{};",
  359. coord_vec, dref_swizzle, dref_val, sparse_mod, ret, coord_vec, texture,
  360. type, offset_vec);
  361. } else {
  362. ctx.Add("TEX.F{} {},{},{},{},{}{};", sparse_mod, ret, coord_vec, dref_val, texture,
  363. type, offset_vec);
  364. }
  365. }
  366. }
  367. StoreSparse(ctx, sparse_inst);
  368. }
  369. void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  370. const IR::Value& coord, const IR::Value& dref,
  371. const IR::Value& lod, const IR::Value& offset) {
  372. // Allocate early to avoid aliases
  373. const auto info{inst.Flags<IR::TextureInstInfo>()};
  374. ScopedRegister staging;
  375. if (info.type == TextureType::ColorArrayCube) {
  376. staging = ScopedRegister{ctx.reg_alloc};
  377. }
  378. const ScalarF32 dref_val{ctx.reg_alloc.Consume(dref)};
  379. const ScalarF32 lod_val{ctx.reg_alloc.Consume(lod)};
  380. const auto sparse_inst{PrepareSparse(inst)};
  381. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  382. const std::string_view type{TextureType(info)};
  383. const std::string texture{Texture(ctx, info, index)};
  384. const std::string offset_vec{Offset(ctx, offset)};
  385. const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
  386. const Register ret{ctx.reg_alloc.Define(inst)};
  387. switch (info.type) {
  388. case TextureType::Color1D:
  389. case TextureType::ColorArray1D:
  390. case TextureType::Color2D:
  391. ctx.Add("MOV.F {}.z,{};"
  392. "MOV.F {}.w,{};"
  393. "TXL.F{} {},{},{},{}{};",
  394. coord_vec, dref_val, coord_vec, lod_val, sparse_mod, ret, coord_vec, texture, type,
  395. offset_vec);
  396. break;
  397. case TextureType::ColorArray2D:
  398. case TextureType::ColorCube:
  399. ctx.Add("MOV.F {}.w,{};"
  400. "TXL.F{} {},{},{},{},{}{};",
  401. coord_vec, dref_val, sparse_mod, ret, coord_vec, lod_val, texture, type,
  402. offset_vec);
  403. break;
  404. case TextureType::ColorArrayCube:
  405. ctx.Add("MOV.F {}.x,{};"
  406. "MOV.F {}.y,{};"
  407. "TXL.F{} {},{},{},{},{}{};",
  408. staging.reg, dref_val, staging.reg, lod_val, sparse_mod, ret, coord_vec,
  409. staging.reg, texture, type, offset_vec);
  410. break;
  411. default:
  412. throw NotImplementedException("Invalid type {}", info.type.Value());
  413. }
  414. StoreSparse(ctx, sparse_inst);
  415. }
  416. void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  417. const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2) {
  418. // Allocate offsets early so they don't overwrite any consumed register
  419. const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
  420. const auto info{inst.Flags<IR::TextureInstInfo>()};
  421. const char comp{"xyzw"[info.gather_component]};
  422. const auto sparse_inst{PrepareSparse(inst)};
  423. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  424. const std::string_view type{TextureType(info)};
  425. const std::string texture{Texture(ctx, info, index)};
  426. const Register coord_vec{ctx.reg_alloc.Consume(coord)};
  427. const Register ret{ctx.reg_alloc.Define(inst)};
  428. if (offset2.IsEmpty()) {
  429. const std::string offset_vec{Offset(ctx, offset)};
  430. ctx.Add("TXG.F{} {},{},{}.{},{}{};", sparse_mod, ret, coord_vec, texture, comp, type,
  431. offset_vec);
  432. } else {
  433. SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
  434. ctx.Add("TXGO.F{} {},{},{},{},{}.{},{};", sparse_mod, ret, coord_vec, off_x.reg, off_y.reg,
  435. texture, comp, type);
  436. }
  437. StoreSparse(ctx, sparse_inst);
  438. }
  439. void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  440. const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2,
  441. const IR::Value& dref) {
  442. // FIXME: This instruction is not working as expected
  443. // Allocate offsets early so they don't overwrite any consumed register
  444. const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
  445. const auto info{inst.Flags<IR::TextureInstInfo>()};
  446. const auto sparse_inst{PrepareSparse(inst)};
  447. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  448. const std::string_view type{TextureType(info)};
  449. const std::string texture{Texture(ctx, info, index)};
  450. const Register coord_vec{ctx.reg_alloc.Consume(coord)};
  451. const ScalarF32 dref_value{ctx.reg_alloc.Consume(dref)};
  452. const Register ret{ctx.reg_alloc.Define(inst)};
  453. std::string args;
  454. switch (info.type) {
  455. case TextureType::Color2D:
  456. ctx.Add("MOV.F {}.z,{};", coord_vec, dref_value);
  457. args = fmt::to_string(coord_vec);
  458. break;
  459. case TextureType::ColorArray2D:
  460. case TextureType::ColorCube:
  461. ctx.Add("MOV.F {}.w,{};", coord_vec, dref_value);
  462. args = fmt::to_string(coord_vec);
  463. break;
  464. case TextureType::ColorArrayCube:
  465. args = fmt::format("{},{}", coord_vec, dref_value);
  466. break;
  467. default:
  468. throw NotImplementedException("Invalid type {}", info.type.Value());
  469. }
  470. if (offset2.IsEmpty()) {
  471. const std::string offset_vec{Offset(ctx, offset)};
  472. ctx.Add("TXG.F{} {},{},{},{}{};", sparse_mod, ret, args, texture, type, offset_vec);
  473. } else {
  474. SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
  475. ctx.Add("TXGO.F{} {},{},{},{},{},{};", sparse_mod, ret, args, off_x.reg, off_y.reg, texture,
  476. type);
  477. }
  478. StoreSparse(ctx, sparse_inst);
  479. }
  480. void EmitImageFetch(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  481. const IR::Value& coord, const IR::Value& offset, ScalarS32 lod, ScalarS32 ms) {
  482. const auto info{inst.Flags<IR::TextureInstInfo>()};
  483. const auto sparse_inst{PrepareSparse(inst)};
  484. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  485. const std::string_view type{TextureType(info)};
  486. const std::string texture{Texture(ctx, info, index)};
  487. const std::string offset_vec{Offset(ctx, offset)};
  488. const auto [coord_vec, coord_alloc]{Coord(ctx, coord)};
  489. const Register ret{ctx.reg_alloc.Define(inst)};
  490. if (info.type == TextureType::Buffer) {
  491. ctx.Add("TXF.F{} {},{},{},{}{};", sparse_mod, ret, coord_vec, texture, type, offset_vec);
  492. } else if (ms.type != Type::Void) {
  493. ctx.Add("MOV.S {}.w,{};"
  494. "TXFMS.F{} {},{},{},{}{};",
  495. coord_vec, ms, sparse_mod, ret, coord_vec, texture, type, offset_vec);
  496. } else {
  497. ctx.Add("MOV.S {}.w,{};"
  498. "TXF.F{} {},{},{},{}{};",
  499. coord_vec, lod, sparse_mod, ret, coord_vec, texture, type, offset_vec);
  500. }
  501. StoreSparse(ctx, sparse_inst);
  502. }
  503. void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  504. ScalarS32 lod) {
  505. const auto info{inst.Flags<IR::TextureInstInfo>()};
  506. const std::string texture{Texture(ctx, info, index)};
  507. const std::string_view type{TextureType(info)};
  508. ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type);
  509. }
  510. void EmitImageQueryLod(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord) {
  511. const auto info{inst.Flags<IR::TextureInstInfo>()};
  512. const std::string texture{Texture(ctx, info, index)};
  513. const std::string_view type{TextureType(info)};
  514. ctx.Add("LOD.F {},{},{},{};", inst, coord, texture, type);
  515. }
  516. void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  517. const IR::Value& coord, const IR::Value& derivatives,
  518. const IR::Value& offset, const IR::Value& lod_clamp) {
  519. const auto info{inst.Flags<IR::TextureInstInfo>()};
  520. ScopedRegister dpdx, dpdy;
  521. const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp};
  522. if (multi_component) {
  523. // Allocate this early to avoid aliasing other registers
  524. dpdx = ScopedRegister{ctx.reg_alloc};
  525. dpdy = ScopedRegister{ctx.reg_alloc};
  526. }
  527. const auto sparse_inst{PrepareSparse(inst)};
  528. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  529. const std::string_view type{TextureType(info)};
  530. const std::string texture{Texture(ctx, info, index)};
  531. const std::string offset_vec{GradOffset(offset)};
  532. const Register coord_vec{ctx.reg_alloc.Consume(coord)};
  533. const Register derivatives_vec{ctx.reg_alloc.Consume(derivatives)};
  534. const Register ret{ctx.reg_alloc.Define(inst)};
  535. if (multi_component) {
  536. ctx.Add("MOV.F {}.x,{}.x;"
  537. "MOV.F {}.y,{}.z;"
  538. "MOV.F {}.x,{}.y;"
  539. "MOV.F {}.y,{}.w;",
  540. dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec,
  541. dpdy.reg, derivatives_vec);
  542. if (info.has_lod_clamp) {
  543. const ScalarF32 lod_clamp_value{ctx.reg_alloc.Consume(lod_clamp)};
  544. ctx.Add("MOV.F {}.w,{};"
  545. "TXD.F.LODCLAMP{} {},{},{},{},{},{}{};",
  546. dpdy.reg, lod_clamp_value, sparse_mod, ret, coord_vec, dpdx.reg, dpdy.reg,
  547. texture, type, offset_vec);
  548. } else {
  549. ctx.Add("TXD.F{} {},{},{},{},{},{}{};", sparse_mod, ret, coord_vec, dpdx.reg, dpdy.reg,
  550. texture, type, offset_vec);
  551. }
  552. } else {
  553. ctx.Add("TXD.F{} {},{},{}.x,{}.y,{},{}{};", sparse_mod, ret, coord_vec, derivatives_vec,
  554. derivatives_vec, texture, type, offset_vec);
  555. }
  556. StoreSparse(ctx, sparse_inst);
  557. }
  558. void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord) {
  559. const auto info{inst.Flags<IR::TextureInstInfo>()};
  560. const auto sparse_inst{PrepareSparse(inst)};
  561. const std::string_view format{FormatStorage(info.image_format)};
  562. const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
  563. const std::string_view type{TextureType(info)};
  564. const std::string image{Image(ctx, info, index)};
  565. const Register ret{ctx.reg_alloc.Define(inst)};
  566. ctx.Add("LOADIM.{}{} {},{},{},{};", format, sparse_mod, ret, coord, image, type);
  567. StoreSparse(ctx, sparse_inst);
  568. }
  569. void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
  570. Register color) {
  571. const auto info{inst.Flags<IR::TextureInstInfo>()};
  572. const std::string_view format{FormatStorage(info.image_format)};
  573. const std::string_view type{TextureType(info)};
  574. const std::string image{Image(ctx, info, index)};
  575. ctx.Add("STOREIM.{} {},{},{},{};", format, image, color, coord, type);
  576. }
  577. void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
  578. ScalarU32 value) {
  579. ImageAtomic(ctx, inst, index, coord, value, "ADD.U32");
  580. }
  581. void EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
  582. ScalarS32 value) {
  583. ImageAtomic(ctx, inst, index, coord, value, "MIN.S32");
  584. }
  585. void EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
  586. ScalarU32 value) {
  587. ImageAtomic(ctx, inst, index, coord, value, "MIN.U32");
  588. }
  589. void EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
  590. ScalarS32 value) {
  591. ImageAtomic(ctx, inst, index, coord, value, "MAX.S32");
  592. }
  593. void EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
  594. ScalarU32 value) {
  595. ImageAtomic(ctx, inst, index, coord, value, "MAX.U32");
  596. }
  597. void EmitImageAtomicInc32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
  598. ScalarU32 value) {
  599. ImageAtomic(ctx, inst, index, coord, value, "IWRAP.U32");
  600. }
  601. void EmitImageAtomicDec32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
  602. ScalarU32 value) {
  603. ImageAtomic(ctx, inst, index, coord, value, "DWRAP.U32");
  604. }
  605. void EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
  606. ScalarU32 value) {
  607. ImageAtomic(ctx, inst, index, coord, value, "AND.U32");
  608. }
  609. void EmitImageAtomicOr32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
  610. ScalarU32 value) {
  611. ImageAtomic(ctx, inst, index, coord, value, "OR.U32");
  612. }
  613. void EmitImageAtomicXor32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
  614. ScalarU32 value) {
  615. ImageAtomic(ctx, inst, index, coord, value, "XOR.U32");
  616. }
  617. void EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
  618. Register coord, ScalarU32 value) {
  619. ImageAtomic(ctx, inst, index, coord, value, "EXCH.U32");
  620. }
  621. void EmitBindlessImageSampleImplicitLod(EmitContext&) {
  622. throw LogicError("Unreachable instruction");
  623. }
  624. void EmitBindlessImageSampleExplicitLod(EmitContext&) {
  625. throw LogicError("Unreachable instruction");
  626. }
  627. void EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
  628. throw LogicError("Unreachable instruction");
  629. }
  630. void EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
  631. throw LogicError("Unreachable instruction");
  632. }
  633. void EmitBindlessImageGather(EmitContext&) {
  634. throw LogicError("Unreachable instruction");
  635. }
  636. void EmitBindlessImageGatherDref(EmitContext&) {
  637. throw LogicError("Unreachable instruction");
  638. }
  639. void EmitBindlessImageFetch(EmitContext&) {
  640. throw LogicError("Unreachable instruction");
  641. }
  642. void EmitBindlessImageQueryDimensions(EmitContext&) {
  643. throw LogicError("Unreachable instruction");
  644. }
  645. void EmitBindlessImageQueryLod(EmitContext&) {
  646. throw LogicError("Unreachable instruction");
  647. }
  648. void EmitBindlessImageGradient(EmitContext&) {
  649. throw LogicError("Unreachable instruction");
  650. }
  651. void EmitBindlessImageRead(EmitContext&) {
  652. throw LogicError("Unreachable instruction");
  653. }
  654. void EmitBindlessImageWrite(EmitContext&) {
  655. throw LogicError("Unreachable instruction");
  656. }
  657. void EmitBoundImageSampleImplicitLod(EmitContext&) {
  658. throw LogicError("Unreachable instruction");
  659. }
  660. void EmitBoundImageSampleExplicitLod(EmitContext&) {
  661. throw LogicError("Unreachable instruction");
  662. }
  663. void EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
  664. throw LogicError("Unreachable instruction");
  665. }
  666. void EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
  667. throw LogicError("Unreachable instruction");
  668. }
  669. void EmitBoundImageGather(EmitContext&) {
  670. throw LogicError("Unreachable instruction");
  671. }
  672. void EmitBoundImageGatherDref(EmitContext&) {
  673. throw LogicError("Unreachable instruction");
  674. }
  675. void EmitBoundImageFetch(EmitContext&) {
  676. throw LogicError("Unreachable instruction");
  677. }
  678. void EmitBoundImageQueryDimensions(EmitContext&) {
  679. throw LogicError("Unreachable instruction");
  680. }
  681. void EmitBoundImageQueryLod(EmitContext&) {
  682. throw LogicError("Unreachable instruction");
  683. }
  684. void EmitBoundImageGradient(EmitContext&) {
  685. throw LogicError("Unreachable instruction");
  686. }
  687. void EmitBoundImageRead(EmitContext&) {
  688. throw LogicError("Unreachable instruction");
  689. }
  690. void EmitBoundImageWrite(EmitContext&) {
  691. throw LogicError("Unreachable instruction");
  692. }
  693. void EmitBindlessImageAtomicIAdd32(EmitContext&) {
  694. throw LogicError("Unreachable instruction");
  695. }
  696. void EmitBindlessImageAtomicSMin32(EmitContext&) {
  697. throw LogicError("Unreachable instruction");
  698. }
  699. void EmitBindlessImageAtomicUMin32(EmitContext&) {
  700. throw LogicError("Unreachable instruction");
  701. }
  702. void EmitBindlessImageAtomicSMax32(EmitContext&) {
  703. throw LogicError("Unreachable instruction");
  704. }
  705. void EmitBindlessImageAtomicUMax32(EmitContext&) {
  706. throw LogicError("Unreachable instruction");
  707. }
  708. void EmitBindlessImageAtomicInc32(EmitContext&) {
  709. throw LogicError("Unreachable instruction");
  710. }
  711. void EmitBindlessImageAtomicDec32(EmitContext&) {
  712. throw LogicError("Unreachable instruction");
  713. }
  714. void EmitBindlessImageAtomicAnd32(EmitContext&) {
  715. throw LogicError("Unreachable instruction");
  716. }
  717. void EmitBindlessImageAtomicOr32(EmitContext&) {
  718. throw LogicError("Unreachable instruction");
  719. }
  720. void EmitBindlessImageAtomicXor32(EmitContext&) {
  721. throw LogicError("Unreachable instruction");
  722. }
  723. void EmitBindlessImageAtomicExchange32(EmitContext&) {
  724. throw LogicError("Unreachable instruction");
  725. }
  726. void EmitBoundImageAtomicIAdd32(EmitContext&) {
  727. throw LogicError("Unreachable instruction");
  728. }
  729. void EmitBoundImageAtomicSMin32(EmitContext&) {
  730. throw LogicError("Unreachable instruction");
  731. }
  732. void EmitBoundImageAtomicUMin32(EmitContext&) {
  733. throw LogicError("Unreachable instruction");
  734. }
  735. void EmitBoundImageAtomicSMax32(EmitContext&) {
  736. throw LogicError("Unreachable instruction");
  737. }
  738. void EmitBoundImageAtomicUMax32(EmitContext&) {
  739. throw LogicError("Unreachable instruction");
  740. }
  741. void EmitBoundImageAtomicInc32(EmitContext&) {
  742. throw LogicError("Unreachable instruction");
  743. }
  744. void EmitBoundImageAtomicDec32(EmitContext&) {
  745. throw LogicError("Unreachable instruction");
  746. }
  747. void EmitBoundImageAtomicAnd32(EmitContext&) {
  748. throw LogicError("Unreachable instruction");
  749. }
  750. void EmitBoundImageAtomicOr32(EmitContext&) {
  751. throw LogicError("Unreachable instruction");
  752. }
  753. void EmitBoundImageAtomicXor32(EmitContext&) {
  754. throw LogicError("Unreachable instruction");
  755. }
  756. void EmitBoundImageAtomicExchange32(EmitContext&) {
  757. throw LogicError("Unreachable instruction");
  758. }
  759. } // namespace Shader::Backend::GLASM