emit_spirv_image.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <boost/container/static_vector.hpp>
  5. #include "shader_recompiler/backend/spirv/emit_spirv.h"
  6. #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
  7. #include "shader_recompiler/backend/spirv/spirv_emit_context.h"
  8. #include "shader_recompiler/frontend/ir/modifiers.h"
  9. namespace Shader::Backend::SPIRV {
  10. namespace {
  11. class ImageOperands {
  12. public:
  13. explicit ImageOperands(EmitContext& ctx, bool has_bias, bool has_lod, bool has_lod_clamp,
  14. Id lod, const IR::Value& offset) {
  15. if (has_bias) {
  16. const Id bias{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
  17. Add(spv::ImageOperandsMask::Bias, bias);
  18. }
  19. if (has_lod) {
  20. const Id lod_value{has_lod_clamp ? ctx.OpCompositeExtract(ctx.F32[1], lod, 0) : lod};
  21. Add(spv::ImageOperandsMask::Lod, lod_value);
  22. }
  23. AddOffset(ctx, offset);
  24. if (has_lod_clamp) {
  25. const Id lod_clamp{has_bias ? ctx.OpCompositeExtract(ctx.F32[1], lod, 1) : lod};
  26. Add(spv::ImageOperandsMask::MinLod, lod_clamp);
  27. }
  28. }
  29. explicit ImageOperands(EmitContext& ctx, const IR::Value& offset, const IR::Value& offset2) {
  30. if (offset2.IsEmpty()) {
  31. if (offset.IsEmpty()) {
  32. return;
  33. }
  34. Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
  35. return;
  36. }
  37. const std::array values{offset.InstRecursive(), offset2.InstRecursive()};
  38. if (!values[0]->AreAllArgsImmediates() || !values[1]->AreAllArgsImmediates()) {
  39. LOG_WARNING(Shader_SPIRV, "Not all arguments in PTP are immediate, ignoring");
  40. return;
  41. }
  42. const IR::Opcode opcode{values[0]->GetOpcode()};
  43. if (opcode != values[1]->GetOpcode() || opcode != IR::Opcode::CompositeConstructU32x4) {
  44. throw LogicError("Invalid PTP arguments");
  45. }
  46. auto read{[&](unsigned int a, unsigned int b) { return values[a]->Arg(b).U32(); }};
  47. const Id offsets{ctx.ConstantComposite(
  48. ctx.TypeArray(ctx.U32[2], ctx.Const(4U)), ctx.Const(read(0, 0), read(0, 1)),
  49. ctx.Const(read(0, 2), read(0, 3)), ctx.Const(read(1, 0), read(1, 1)),
  50. ctx.Const(read(1, 2), read(1, 3)))};
  51. Add(spv::ImageOperandsMask::ConstOffsets, offsets);
  52. }
  53. explicit ImageOperands(Id offset, Id lod, Id ms) {
  54. if (Sirit::ValidId(lod)) {
  55. Add(spv::ImageOperandsMask::Lod, lod);
  56. }
  57. if (Sirit::ValidId(offset)) {
  58. Add(spv::ImageOperandsMask::Offset, offset);
  59. }
  60. if (Sirit::ValidId(ms)) {
  61. Add(spv::ImageOperandsMask::Sample, ms);
  62. }
  63. }
  64. explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates, u32 num_derivates,
  65. Id offset, Id lod_clamp) {
  66. if (!Sirit::ValidId(derivates)) {
  67. throw LogicError("Derivates must be present");
  68. }
  69. boost::container::static_vector<Id, 3> deriv_x_accum;
  70. boost::container::static_vector<Id, 3> deriv_y_accum;
  71. for (u32 i = 0; i < num_derivates; ++i) {
  72. deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2));
  73. deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2 + 1));
  74. }
  75. const Id derivates_X{ctx.OpCompositeConstruct(
  76. ctx.F32[num_derivates], std::span{deriv_x_accum.data(), deriv_x_accum.size()})};
  77. const Id derivates_Y{ctx.OpCompositeConstruct(
  78. ctx.F32[num_derivates], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
  79. Add(spv::ImageOperandsMask::Grad, derivates_X, derivates_Y);
  80. if (Sirit::ValidId(offset)) {
  81. Add(spv::ImageOperandsMask::Offset, offset);
  82. }
  83. if (has_lod_clamp) {
  84. Add(spv::ImageOperandsMask::MinLod, lod_clamp);
  85. }
  86. }
  87. std::span<const Id> Span() const noexcept {
  88. return std::span{operands.data(), operands.size()};
  89. }
  90. std::optional<spv::ImageOperandsMask> MaskOptional() const noexcept {
  91. return mask != spv::ImageOperandsMask{} ? std::make_optional(mask) : std::nullopt;
  92. }
  93. spv::ImageOperandsMask Mask() const noexcept {
  94. return mask;
  95. }
  96. private:
  97. void AddOffset(EmitContext& ctx, const IR::Value& offset) {
  98. if (offset.IsEmpty()) {
  99. return;
  100. }
  101. if (offset.IsImmediate()) {
  102. Add(spv::ImageOperandsMask::ConstOffset, ctx.SConst(static_cast<s32>(offset.U32())));
  103. return;
  104. }
  105. IR::Inst* const inst{offset.InstRecursive()};
  106. if (inst->AreAllArgsImmediates()) {
  107. switch (inst->GetOpcode()) {
  108. case IR::Opcode::CompositeConstructU32x2:
  109. Add(spv::ImageOperandsMask::ConstOffset,
  110. ctx.SConst(static_cast<s32>(inst->Arg(0).U32()),
  111. static_cast<s32>(inst->Arg(1).U32())));
  112. return;
  113. case IR::Opcode::CompositeConstructU32x3:
  114. Add(spv::ImageOperandsMask::ConstOffset,
  115. ctx.SConst(static_cast<s32>(inst->Arg(0).U32()),
  116. static_cast<s32>(inst->Arg(1).U32()),
  117. static_cast<s32>(inst->Arg(2).U32())));
  118. return;
  119. case IR::Opcode::CompositeConstructU32x4:
  120. Add(spv::ImageOperandsMask::ConstOffset,
  121. ctx.SConst(static_cast<s32>(inst->Arg(0).U32()),
  122. static_cast<s32>(inst->Arg(1).U32()),
  123. static_cast<s32>(inst->Arg(2).U32()),
  124. static_cast<s32>(inst->Arg(3).U32())));
  125. return;
  126. default:
  127. break;
  128. }
  129. }
  130. Add(spv::ImageOperandsMask::Offset, ctx.Def(offset));
  131. }
  132. void Add(spv::ImageOperandsMask new_mask, Id value) {
  133. mask = static_cast<spv::ImageOperandsMask>(static_cast<unsigned>(mask) |
  134. static_cast<unsigned>(new_mask));
  135. operands.push_back(value);
  136. }
  137. void Add(spv::ImageOperandsMask new_mask, Id value_1, Id value_2) {
  138. mask = static_cast<spv::ImageOperandsMask>(static_cast<unsigned>(mask) |
  139. static_cast<unsigned>(new_mask));
  140. operands.push_back(value_1);
  141. operands.push_back(value_2);
  142. }
  143. boost::container::static_vector<Id, 4> operands;
  144. spv::ImageOperandsMask mask{};
  145. };
  146. Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
  147. const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
  148. if (def.count > 1) {
  149. const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, ctx.Def(index))};
  150. return ctx.OpLoad(def.sampled_type, pointer);
  151. } else {
  152. return ctx.OpLoad(def.sampled_type, def.id);
  153. }
  154. }
  155. Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& index) {
  156. if (!index.IsImmediate() || index.U32() != 0) {
  157. throw NotImplementedException("Indirect image indexing");
  158. }
  159. if (info.type == TextureType::Buffer) {
  160. const TextureBufferDefinition& def{ctx.texture_buffers.at(info.descriptor_index)};
  161. if (def.count > 1) {
  162. throw NotImplementedException("Indirect texture sample");
  163. }
  164. const Id sampler_id{def.id};
  165. const Id id{ctx.OpLoad(ctx.sampled_texture_buffer_type, sampler_id)};
  166. return ctx.OpImage(ctx.image_buffer_type, id);
  167. } else {
  168. const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
  169. if (def.count > 1) {
  170. throw NotImplementedException("Indirect texture sample");
  171. }
  172. return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id));
  173. }
  174. }
  175. Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
  176. if (!index.IsImmediate() || index.U32() != 0) {
  177. throw NotImplementedException("Indirect image indexing");
  178. }
  179. if (info.type == TextureType::Buffer) {
  180. const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
  181. return ctx.OpLoad(def.image_type, def.id);
  182. } else {
  183. const ImageDefinition def{ctx.images.at(info.descriptor_index)};
  184. return ctx.OpLoad(def.image_type, def.id);
  185. }
  186. }
  187. Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
  188. const auto info{inst->Flags<IR::TextureInstInfo>()};
  189. if (info.relaxed_precision != 0) {
  190. ctx.Decorate(sample, spv::Decoration::RelaxedPrecision);
  191. }
  192. return sample;
  193. }
  194. template <typename MethodPtrType, typename... Args>
  195. Id Emit(MethodPtrType sparse_ptr, MethodPtrType non_sparse_ptr, EmitContext& ctx, IR::Inst* inst,
  196. Id result_type, Args&&... args) {
  197. IR::Inst* const sparse{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
  198. if (!sparse) {
  199. return Decorate(ctx, inst, (ctx.*non_sparse_ptr)(result_type, std::forward<Args>(args)...));
  200. }
  201. const Id struct_type{ctx.TypeStruct(ctx.U32[1], result_type)};
  202. const Id sample{(ctx.*sparse_ptr)(struct_type, std::forward<Args>(args)...)};
  203. const Id resident_code{ctx.OpCompositeExtract(ctx.U32[1], sample, 0U)};
  204. sparse->SetDefinition(ctx.OpImageSparseTexelsResident(ctx.U1, resident_code));
  205. sparse->Invalidate();
  206. Decorate(ctx, inst, sample);
  207. return ctx.OpCompositeExtract(result_type, sample, 1U);
  208. }
  209. Id IsScaled(EmitContext& ctx, const IR::Value& index, Id member_index, u32 base_index) {
  210. const Id push_constant_u32{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.U32[1])};
  211. Id bit{};
  212. if (index.IsImmediate()) {
  213. // Use BitwiseAnd instead of BitfieldExtract for better codegen on Nvidia OpenGL.
  214. // LOP32I.NZ is used to set the predicate rather than BFE+ISETP.
  215. const u32 index_value{index.U32() + base_index};
  216. const Id word_index{ctx.Const(index_value / 32)};
  217. const Id bit_index_mask{ctx.Const(1u << (index_value % 32))};
  218. const Id pointer{ctx.OpAccessChain(push_constant_u32, ctx.rescaling_push_constants,
  219. member_index, word_index)};
  220. const Id word{ctx.OpLoad(ctx.U32[1], pointer)};
  221. bit = ctx.OpBitwiseAnd(ctx.U32[1], word, bit_index_mask);
  222. } else {
  223. Id index_value{ctx.Def(index)};
  224. if (base_index != 0) {
  225. index_value = ctx.OpIAdd(ctx.U32[1], index_value, ctx.Const(base_index));
  226. }
  227. const Id bit_index{ctx.OpBitwiseAnd(ctx.U32[1], index_value, ctx.Const(31u))};
  228. bit = ctx.OpBitFieldUExtract(ctx.U32[1], index_value, bit_index, ctx.Const(1u));
  229. }
  230. return ctx.OpINotEqual(ctx.U1, bit, ctx.u32_zero_value);
  231. }
  232. Id BitTest(EmitContext& ctx, Id mask, Id bit) {
  233. const Id shifted{ctx.OpShiftRightLogical(ctx.U32[1], mask, bit)};
  234. const Id bit_value{ctx.OpBitwiseAnd(ctx.U32[1], shifted, ctx.Const(1u))};
  235. return ctx.OpINotEqual(ctx.U1, bit_value, ctx.u32_zero_value);
  236. }
  237. } // Anonymous namespace
  238. Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
  239. throw LogicError("Unreachable instruction");
  240. }
  241. Id EmitBindlessImageSampleExplicitLod(EmitContext&) {
  242. throw LogicError("Unreachable instruction");
  243. }
  244. Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
  245. throw LogicError("Unreachable instruction");
  246. }
  247. Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
  248. throw LogicError("Unreachable instruction");
  249. }
  250. Id EmitBindlessImageGather(EmitContext&) {
  251. throw LogicError("Unreachable instruction");
  252. }
  253. Id EmitBindlessImageGatherDref(EmitContext&) {
  254. throw LogicError("Unreachable instruction");
  255. }
  256. Id EmitBindlessImageFetch(EmitContext&) {
  257. throw LogicError("Unreachable instruction");
  258. }
  259. Id EmitBindlessImageQueryDimensions(EmitContext&) {
  260. throw LogicError("Unreachable instruction");
  261. }
  262. Id EmitBindlessImageQueryLod(EmitContext&) {
  263. throw LogicError("Unreachable instruction");
  264. }
  265. Id EmitBindlessImageGradient(EmitContext&) {
  266. throw LogicError("Unreachable instruction");
  267. }
  268. Id EmitBindlessImageRead(EmitContext&) {
  269. throw LogicError("Unreachable instruction");
  270. }
  271. Id EmitBindlessImageWrite(EmitContext&) {
  272. throw LogicError("Unreachable instruction");
  273. }
  274. Id EmitBoundImageSampleImplicitLod(EmitContext&) {
  275. throw LogicError("Unreachable instruction");
  276. }
  277. Id EmitBoundImageSampleExplicitLod(EmitContext&) {
  278. throw LogicError("Unreachable instruction");
  279. }
  280. Id EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
  281. throw LogicError("Unreachable instruction");
  282. }
  283. Id EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
  284. throw LogicError("Unreachable instruction");
  285. }
  286. Id EmitBoundImageGather(EmitContext&) {
  287. throw LogicError("Unreachable instruction");
  288. }
  289. Id EmitBoundImageGatherDref(EmitContext&) {
  290. throw LogicError("Unreachable instruction");
  291. }
  292. Id EmitBoundImageFetch(EmitContext&) {
  293. throw LogicError("Unreachable instruction");
  294. }
  295. Id EmitBoundImageQueryDimensions(EmitContext&) {
  296. throw LogicError("Unreachable instruction");
  297. }
  298. Id EmitBoundImageQueryLod(EmitContext&) {
  299. throw LogicError("Unreachable instruction");
  300. }
  301. Id EmitBoundImageGradient(EmitContext&) {
  302. throw LogicError("Unreachable instruction");
  303. }
  304. Id EmitBoundImageRead(EmitContext&) {
  305. throw LogicError("Unreachable instruction");
  306. }
  307. Id EmitBoundImageWrite(EmitContext&) {
  308. throw LogicError("Unreachable instruction");
  309. }
  310. Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
  311. Id bias_lc, const IR::Value& offset) {
  312. const auto info{inst->Flags<IR::TextureInstInfo>()};
  313. if (ctx.stage == Stage::Fragment) {
  314. const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
  315. bias_lc, offset);
  316. return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
  317. &EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
  318. Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
  319. } else {
  320. // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
  321. // if the lod was explicitly zero. This may change on Turing with implicit compute
  322. // derivatives
  323. const Id lod{ctx.Const(0.0f)};
  324. const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
  325. return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
  326. &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
  327. Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
  328. }
  329. }
  330. Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
  331. Id lod, const IR::Value& offset) {
  332. const auto info{inst->Flags<IR::TextureInstInfo>()};
  333. const ImageOperands operands(ctx, false, true, false, lod, offset);
  334. return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
  335. &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
  336. Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
  337. }
  338. Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
  339. Id coords, Id dref, Id bias_lc, const IR::Value& offset) {
  340. const auto info{inst->Flags<IR::TextureInstInfo>()};
  341. if (ctx.stage == Stage::Fragment) {
  342. const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
  343. bias_lc, offset);
  344. return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod,
  345. &EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1],
  346. Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
  347. operands.Span());
  348. } else {
  349. // Implicit lods in compute behave on hardware as if sampling from LOD 0.
  350. // This check is to ensure all drivers behave this way.
  351. const Id lod{ctx.Const(0.0f)};
  352. const ImageOperands operands(ctx, false, true, false, lod, offset);
  353. return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
  354. &EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
  355. Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
  356. }
  357. }
  358. Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
  359. Id coords, Id dref, Id lod, const IR::Value& offset) {
  360. const auto info{inst->Flags<IR::TextureInstInfo>()};
  361. const ImageOperands operands(ctx, false, true, false, lod, offset);
  362. return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
  363. &EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
  364. Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
  365. }
  366. Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
  367. const IR::Value& offset, const IR::Value& offset2) {
  368. const auto info{inst->Flags<IR::TextureInstInfo>()};
  369. const ImageOperands operands(ctx, offset, offset2);
  370. return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
  371. ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
  372. operands.MaskOptional(), operands.Span());
  373. }
  374. Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
  375. const IR::Value& offset, const IR::Value& offset2, Id dref) {
  376. const auto info{inst->Flags<IR::TextureInstInfo>()};
  377. const ImageOperands operands(ctx, offset, offset2);
  378. return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
  379. ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
  380. operands.Span());
  381. }
  382. Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
  383. Id lod, Id ms) {
  384. const auto info{inst->Flags<IR::TextureInstInfo>()};
  385. if (info.type == TextureType::Buffer) {
  386. lod = Id{};
  387. }
  388. const ImageOperands operands(offset, lod, ms);
  389. return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
  390. TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
  391. }
  392. Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) {
  393. const auto info{inst->Flags<IR::TextureInstInfo>()};
  394. const Id image{TextureImage(ctx, info, index)};
  395. const Id zero{ctx.u32_zero_value};
  396. const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }};
  397. switch (info.type) {
  398. case TextureType::Color1D:
  399. return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod),
  400. zero, zero, mips());
  401. case TextureType::ColorArray1D:
  402. case TextureType::Color2D:
  403. case TextureType::ColorCube:
  404. return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[2], image, lod),
  405. zero, mips());
  406. case TextureType::ColorArray2D:
  407. case TextureType::Color3D:
  408. case TextureType::ColorArrayCube:
  409. return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[3], image, lod),
  410. mips());
  411. case TextureType::Buffer:
  412. return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySize(ctx.U32[1], image), zero,
  413. zero, mips());
  414. }
  415. throw LogicError("Unspecified image type {}", info.type.Value());
  416. }
  417. Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
  418. const auto info{inst->Flags<IR::TextureInstInfo>()};
  419. const Id zero{ctx.f32_zero_value};
  420. const Id sampler{Texture(ctx, info, index)};
  421. return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords),
  422. zero, zero);
  423. }
  424. Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
  425. Id derivates, Id offset, Id lod_clamp) {
  426. const auto info{inst->Flags<IR::TextureInstInfo>()};
  427. const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates,
  428. offset, lod_clamp);
  429. return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
  430. &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
  431. Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
  432. }
  433. Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
  434. const auto info{inst->Flags<IR::TextureInstInfo>()};
  435. if (info.image_format == ImageFormat::Typeless && !ctx.profile.support_typeless_image_loads) {
  436. LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
  437. return ctx.ConstantNull(ctx.U32[4]);
  438. }
  439. return Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, ctx.U32[4],
  440. Image(ctx, index, info), coords, std::nullopt, std::span<const Id>{});
  441. }
  442. void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
  443. const auto info{inst->Flags<IR::TextureInstInfo>()};
  444. ctx.OpImageWrite(Image(ctx, index, info), coords, color);
  445. }
  446. Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index) {
  447. if (ctx.profile.unified_descriptor_binding) {
  448. const Id member_index{ctx.Const(ctx.rescaling_textures_member_index)};
  449. return IsScaled(ctx, index, member_index, ctx.texture_rescaling_index);
  450. } else {
  451. const Id composite{ctx.OpLoad(ctx.F32[4], ctx.rescaling_uniform_constant)};
  452. const Id mask_f32{ctx.OpCompositeExtract(ctx.F32[1], composite, 0u)};
  453. const Id mask{ctx.OpBitcast(ctx.U32[1], mask_f32)};
  454. return BitTest(ctx, mask, ctx.Def(index));
  455. }
  456. }
  457. Id EmitIsImageScaled(EmitContext& ctx, const IR::Value& index) {
  458. if (ctx.profile.unified_descriptor_binding) {
  459. const Id member_index{ctx.Const(ctx.rescaling_images_member_index)};
  460. return IsScaled(ctx, index, member_index, ctx.image_rescaling_index);
  461. } else {
  462. const Id composite{ctx.OpLoad(ctx.F32[4], ctx.rescaling_uniform_constant)};
  463. const Id mask_f32{ctx.OpCompositeExtract(ctx.F32[1], composite, 1u)};
  464. const Id mask{ctx.OpBitcast(ctx.U32[1], mask_f32)};
  465. return BitTest(ctx, mask, ctx.Def(index));
  466. }
  467. }
  468. } // namespace Shader::Backend::SPIRV