emit_spirv_image.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  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. bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
  187. if (info.type == TextureType::Buffer) {
  188. return false;
  189. }
  190. return ctx.textures.at(info.descriptor_index).is_multisample;
  191. }
  192. Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
  193. const auto info{inst->Flags<IR::TextureInstInfo>()};
  194. if (info.relaxed_precision != 0) {
  195. ctx.Decorate(sample, spv::Decoration::RelaxedPrecision);
  196. }
  197. return sample;
  198. }
  199. template <typename MethodPtrType, typename... Args>
  200. Id Emit(MethodPtrType sparse_ptr, MethodPtrType non_sparse_ptr, EmitContext& ctx, IR::Inst* inst,
  201. Id result_type, Args&&... args) {
  202. IR::Inst* const sparse{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
  203. if (!sparse) {
  204. return Decorate(ctx, inst, (ctx.*non_sparse_ptr)(result_type, std::forward<Args>(args)...));
  205. }
  206. const Id struct_type{ctx.TypeStruct(ctx.U32[1], result_type)};
  207. const Id sample{(ctx.*sparse_ptr)(struct_type, std::forward<Args>(args)...)};
  208. const Id resident_code{ctx.OpCompositeExtract(ctx.U32[1], sample, 0U)};
  209. sparse->SetDefinition(ctx.OpImageSparseTexelsResident(ctx.U1, resident_code));
  210. sparse->Invalidate();
  211. Decorate(ctx, inst, sample);
  212. return ctx.OpCompositeExtract(result_type, sample, 1U);
  213. }
  214. Id IsScaled(EmitContext& ctx, const IR::Value& index, Id member_index, u32 base_index) {
  215. const Id push_constant_u32{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.U32[1])};
  216. Id bit{};
  217. if (index.IsImmediate()) {
  218. // Use BitwiseAnd instead of BitfieldExtract for better codegen on Nvidia OpenGL.
  219. // LOP32I.NZ is used to set the predicate rather than BFE+ISETP.
  220. const u32 index_value{index.U32() + base_index};
  221. const Id word_index{ctx.Const(index_value / 32)};
  222. const Id bit_index_mask{ctx.Const(1u << (index_value % 32))};
  223. const Id pointer{ctx.OpAccessChain(push_constant_u32, ctx.rescaling_push_constants,
  224. member_index, word_index)};
  225. const Id word{ctx.OpLoad(ctx.U32[1], pointer)};
  226. bit = ctx.OpBitwiseAnd(ctx.U32[1], word, bit_index_mask);
  227. } else {
  228. Id index_value{ctx.Def(index)};
  229. if (base_index != 0) {
  230. index_value = ctx.OpIAdd(ctx.U32[1], index_value, ctx.Const(base_index));
  231. }
  232. const Id bit_index{ctx.OpBitwiseAnd(ctx.U32[1], index_value, ctx.Const(31u))};
  233. bit = ctx.OpBitFieldUExtract(ctx.U32[1], index_value, bit_index, ctx.Const(1u));
  234. }
  235. return ctx.OpINotEqual(ctx.U1, bit, ctx.u32_zero_value);
  236. }
  237. Id BitTest(EmitContext& ctx, Id mask, Id bit) {
  238. const Id shifted{ctx.OpShiftRightLogical(ctx.U32[1], mask, bit)};
  239. const Id bit_value{ctx.OpBitwiseAnd(ctx.U32[1], shifted, ctx.Const(1u))};
  240. return ctx.OpINotEqual(ctx.U1, bit_value, ctx.u32_zero_value);
  241. }
  242. Id ImageGatherSubpixelOffset(EmitContext& ctx, const IR::TextureInstInfo& info, Id texture,
  243. Id coords) {
  244. // Apply a subpixel offset of 1/512 the texel size of the texture to ensure same rounding on
  245. // AMD hardware as on Maxwell or other Nvidia architectures.
  246. const auto calculate_coords{[&](size_t dim) {
  247. const Id nudge{ctx.Const(0x1p-9f)};
  248. const Id image_size{ctx.OpImageQuerySizeLod(ctx.U32[dim], texture, ctx.u32_zero_value)};
  249. Id offset{dim == 2 ? ctx.ConstantComposite(ctx.F32[dim], nudge, nudge)
  250. : ctx.ConstantComposite(ctx.F32[dim], nudge, nudge, ctx.f32_zero_value)};
  251. offset = ctx.OpFDiv(ctx.F32[dim], offset, ctx.OpConvertUToF(ctx.F32[dim], image_size));
  252. return ctx.OpFAdd(ctx.F32[dim], coords, offset);
  253. }};
  254. switch (info.type) {
  255. case TextureType::Color2D:
  256. case TextureType::Color2DRect:
  257. return calculate_coords(2);
  258. case TextureType::ColorArray2D:
  259. case TextureType::ColorCube:
  260. return calculate_coords(3);
  261. default:
  262. return coords;
  263. }
  264. }
  265. } // Anonymous namespace
  266. Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
  267. throw LogicError("Unreachable instruction");
  268. }
  269. Id EmitBindlessImageSampleExplicitLod(EmitContext&) {
  270. throw LogicError("Unreachable instruction");
  271. }
  272. Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&) {
  273. throw LogicError("Unreachable instruction");
  274. }
  275. Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&) {
  276. throw LogicError("Unreachable instruction");
  277. }
  278. Id EmitBindlessImageGather(EmitContext&) {
  279. throw LogicError("Unreachable instruction");
  280. }
  281. Id EmitBindlessImageGatherDref(EmitContext&) {
  282. throw LogicError("Unreachable instruction");
  283. }
  284. Id EmitBindlessImageFetch(EmitContext&) {
  285. throw LogicError("Unreachable instruction");
  286. }
  287. Id EmitBindlessImageQueryDimensions(EmitContext&) {
  288. throw LogicError("Unreachable instruction");
  289. }
  290. Id EmitBindlessImageQueryLod(EmitContext&) {
  291. throw LogicError("Unreachable instruction");
  292. }
  293. Id EmitBindlessImageGradient(EmitContext&) {
  294. throw LogicError("Unreachable instruction");
  295. }
  296. Id EmitBindlessImageRead(EmitContext&) {
  297. throw LogicError("Unreachable instruction");
  298. }
  299. Id EmitBindlessImageWrite(EmitContext&) {
  300. throw LogicError("Unreachable instruction");
  301. }
  302. Id EmitBoundImageSampleImplicitLod(EmitContext&) {
  303. throw LogicError("Unreachable instruction");
  304. }
  305. Id EmitBoundImageSampleExplicitLod(EmitContext&) {
  306. throw LogicError("Unreachable instruction");
  307. }
  308. Id EmitBoundImageSampleDrefImplicitLod(EmitContext&) {
  309. throw LogicError("Unreachable instruction");
  310. }
  311. Id EmitBoundImageSampleDrefExplicitLod(EmitContext&) {
  312. throw LogicError("Unreachable instruction");
  313. }
  314. Id EmitBoundImageGather(EmitContext&) {
  315. throw LogicError("Unreachable instruction");
  316. }
  317. Id EmitBoundImageGatherDref(EmitContext&) {
  318. throw LogicError("Unreachable instruction");
  319. }
  320. Id EmitBoundImageFetch(EmitContext&) {
  321. throw LogicError("Unreachable instruction");
  322. }
  323. Id EmitBoundImageQueryDimensions(EmitContext&) {
  324. throw LogicError("Unreachable instruction");
  325. }
  326. Id EmitBoundImageQueryLod(EmitContext&) {
  327. throw LogicError("Unreachable instruction");
  328. }
  329. Id EmitBoundImageGradient(EmitContext&) {
  330. throw LogicError("Unreachable instruction");
  331. }
  332. Id EmitBoundImageRead(EmitContext&) {
  333. throw LogicError("Unreachable instruction");
  334. }
  335. Id EmitBoundImageWrite(EmitContext&) {
  336. throw LogicError("Unreachable instruction");
  337. }
  338. Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
  339. 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::OpImageSparseSampleImplicitLod,
  345. &EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
  346. Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
  347. } else {
  348. // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
  349. // if the lod was explicitly zero. This may change on Turing with implicit compute
  350. // derivatives
  351. const Id lod{ctx.Const(0.0f)};
  352. const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
  353. return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
  354. &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
  355. Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
  356. }
  357. }
  358. Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
  359. 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::OpImageSparseSampleExplicitLod,
  363. &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
  364. Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
  365. }
  366. Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
  367. Id coords, Id dref, Id bias_lc, const IR::Value& offset) {
  368. const auto info{inst->Flags<IR::TextureInstInfo>()};
  369. if (ctx.stage == Stage::Fragment) {
  370. const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
  371. bias_lc, offset);
  372. return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod,
  373. &EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1],
  374. Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
  375. operands.Span());
  376. } else {
  377. // Implicit lods in compute behave on hardware as if sampling from LOD 0.
  378. // This check is to ensure all drivers behave this way.
  379. const Id lod{ctx.Const(0.0f)};
  380. const ImageOperands operands(ctx, false, true, false, lod, offset);
  381. return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
  382. &EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
  383. Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
  384. }
  385. }
  386. Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
  387. Id coords, Id dref, Id lod, const IR::Value& offset) {
  388. const auto info{inst->Flags<IR::TextureInstInfo>()};
  389. const ImageOperands operands(ctx, false, true, false, lod, offset);
  390. return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
  391. &EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
  392. Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
  393. }
  394. Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
  395. const IR::Value& offset, const IR::Value& offset2) {
  396. const auto info{inst->Flags<IR::TextureInstInfo>()};
  397. const ImageOperands operands(ctx, offset, offset2);
  398. if (ctx.profile.need_gather_subpixel_offset) {
  399. coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
  400. }
  401. return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
  402. ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
  403. operands.MaskOptional(), operands.Span());
  404. }
  405. Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
  406. const IR::Value& offset, const IR::Value& offset2, Id dref) {
  407. const auto info{inst->Flags<IR::TextureInstInfo>()};
  408. const ImageOperands operands(ctx, offset, offset2);
  409. if (ctx.profile.need_gather_subpixel_offset) {
  410. coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
  411. }
  412. return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
  413. ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
  414. operands.Span());
  415. }
  416. Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
  417. Id lod, Id ms) {
  418. const auto info{inst->Flags<IR::TextureInstInfo>()};
  419. if (info.type == TextureType::Buffer) {
  420. lod = Id{};
  421. }
  422. if (Sirit::ValidId(ms)) {
  423. // This image is multisampled, lod must be implicit
  424. lod = Id{};
  425. }
  426. const ImageOperands operands(offset, lod, ms);
  427. return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
  428. TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
  429. }
  430. Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
  431. const IR::Value& skip_mips_val) {
  432. const auto info{inst->Flags<IR::TextureInstInfo>()};
  433. const Id image{TextureImage(ctx, info, index)};
  434. const Id zero{ctx.u32_zero_value};
  435. const bool skip_mips{skip_mips_val.U1()};
  436. const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }};
  437. const bool is_msaa{IsTextureMsaa(ctx, info)};
  438. const bool uses_lod{!is_msaa && info.type != TextureType::Buffer};
  439. const auto query{[&](Id type) {
  440. return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod)
  441. : ctx.OpImageQuerySize(type, image);
  442. }};
  443. switch (info.type) {
  444. case TextureType::Color1D:
  445. return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
  446. case TextureType::ColorArray1D:
  447. case TextureType::Color2D:
  448. case TextureType::ColorCube:
  449. case TextureType::Color2DRect:
  450. return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[2]), zero, mips());
  451. case TextureType::ColorArray2D:
  452. case TextureType::Color3D:
  453. case TextureType::ColorArrayCube:
  454. return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[3]), mips());
  455. case TextureType::Buffer:
  456. return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
  457. }
  458. throw LogicError("Unspecified image type {}", info.type.Value());
  459. }
  460. Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
  461. const auto info{inst->Flags<IR::TextureInstInfo>()};
  462. const Id zero{ctx.f32_zero_value};
  463. const Id sampler{Texture(ctx, info, index)};
  464. return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords),
  465. zero, zero);
  466. }
  467. Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
  468. Id derivates, Id offset, Id lod_clamp) {
  469. const auto info{inst->Flags<IR::TextureInstInfo>()};
  470. const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates,
  471. offset, lod_clamp);
  472. return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
  473. &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
  474. Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
  475. }
  476. Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
  477. const auto info{inst->Flags<IR::TextureInstInfo>()};
  478. if (info.image_format == ImageFormat::Typeless && !ctx.profile.support_typeless_image_loads) {
  479. LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
  480. return ctx.ConstantNull(ctx.U32[4]);
  481. }
  482. return Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, ctx.U32[4],
  483. Image(ctx, index, info), coords, std::nullopt, std::span<const Id>{});
  484. }
  485. void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
  486. const auto info{inst->Flags<IR::TextureInstInfo>()};
  487. ctx.OpImageWrite(Image(ctx, index, info), coords, color);
  488. }
  489. Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index) {
  490. if (ctx.profile.unified_descriptor_binding) {
  491. const Id member_index{ctx.Const(ctx.rescaling_textures_member_index)};
  492. return IsScaled(ctx, index, member_index, ctx.texture_rescaling_index);
  493. } else {
  494. const Id composite{ctx.OpLoad(ctx.F32[4], ctx.rescaling_uniform_constant)};
  495. const Id mask_f32{ctx.OpCompositeExtract(ctx.F32[1], composite, 0u)};
  496. const Id mask{ctx.OpBitcast(ctx.U32[1], mask_f32)};
  497. return BitTest(ctx, mask, ctx.Def(index));
  498. }
  499. }
  500. Id EmitIsImageScaled(EmitContext& ctx, const IR::Value& index) {
  501. if (ctx.profile.unified_descriptor_binding) {
  502. const Id member_index{ctx.Const(ctx.rescaling_images_member_index)};
  503. return IsScaled(ctx, index, member_index, ctx.image_rescaling_index);
  504. } else {
  505. const Id composite{ctx.OpLoad(ctx.F32[4], ctx.rescaling_uniform_constant)};
  506. const Id mask_f32{ctx.OpCompositeExtract(ctx.F32[1], composite, 1u)};
  507. const Id mask{ctx.OpBitcast(ctx.U32[1], mask_f32)};
  508. return BitTest(ctx, mask, ctx.Def(index));
  509. }
  510. }
  511. } // namespace Shader::Backend::SPIRV