emit_spirv_image.cpp 27 KB

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