emit_spirv_image.cpp 23 KB

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