emit_glsl_warp.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <string_view>
  5. #include "shader_recompiler/backend/glsl/emit_context.h"
  6. #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h"
  7. #include "shader_recompiler/frontend/ir/value.h"
  8. #include "shader_recompiler/profile.h"
  9. namespace Shader::Backend::GLSL {
  10. namespace {
  11. constexpr char THREAD_ID[]{"gl_SubGroupInvocationARB"};
  12. void SetInBoundsFlag(EmitContext& ctx, IR::Inst& inst) {
  13. IR::Inst* const in_bounds{inst.GetAssociatedPseudoOperation(IR::Opcode::GetInBoundsFromOp)};
  14. if (!in_bounds) {
  15. return;
  16. }
  17. ctx.AddU1("{}=shfl_in_bounds;", *in_bounds);
  18. in_bounds->Invalidate();
  19. }
  20. std::string ComputeMinThreadId(std::string_view thread_id, std::string_view segmentation_mask) {
  21. return fmt::format("({}&{})", thread_id, segmentation_mask);
  22. }
  23. std::string ComputeMaxThreadId(std::string_view min_thread_id, std::string_view clamp,
  24. std::string_view not_seg_mask) {
  25. return fmt::format("({})|({}&{})", min_thread_id, clamp, not_seg_mask);
  26. }
  27. std::string GetMaxThreadId(std::string_view thread_id, std::string_view clamp,
  28. std::string_view segmentation_mask) {
  29. const auto not_seg_mask{fmt::format("(~{})", segmentation_mask)};
  30. const auto min_thread_id{ComputeMinThreadId(thread_id, segmentation_mask)};
  31. return ComputeMaxThreadId(min_thread_id, clamp, not_seg_mask);
  32. }
  33. void UseShuffleNv(EmitContext& ctx, IR::Inst& inst, std::string_view shfl_op,
  34. std::string_view value, std::string_view index,
  35. [[maybe_unused]] std::string_view clamp, std::string_view segmentation_mask) {
  36. const auto width{fmt::format("32u>>(bitCount({}&31u))", segmentation_mask)};
  37. ctx.AddU32("{}={}({},{},{},shfl_in_bounds);", inst, shfl_op, value, index, width);
  38. SetInBoundsFlag(ctx, inst);
  39. }
  40. std::string_view BallotIndex(EmitContext& ctx) {
  41. if (!ctx.profile.warp_size_potentially_larger_than_guest) {
  42. return ".x";
  43. }
  44. return "[gl_SubGroupInvocationARB>>5]";
  45. }
  46. std::string GetMask(EmitContext& ctx, std::string_view mask) {
  47. const auto ballot_index{BallotIndex(ctx)};
  48. return fmt::format("uint(uvec2({}){})", mask, ballot_index);
  49. }
  50. } // Anonymous namespace
  51. void EmitLaneId(EmitContext& ctx, IR::Inst& inst) {
  52. ctx.AddU32("{}={}&31u;", inst, THREAD_ID);
  53. }
  54. void EmitVoteAll(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
  55. if (!ctx.profile.warp_size_potentially_larger_than_guest) {
  56. ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
  57. return;
  58. }
  59. const auto ballot_index{BallotIndex(ctx)};
  60. const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
  61. const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
  62. ctx.AddU1("{}=({}&{})=={};", inst, ballot, active_mask, active_mask);
  63. }
  64. void EmitVoteAny(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
  65. if (!ctx.profile.warp_size_potentially_larger_than_guest) {
  66. ctx.AddU1("{}=anyInvocationARB({});", inst, pred);
  67. return;
  68. }
  69. const auto ballot_index{BallotIndex(ctx)};
  70. const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
  71. const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
  72. ctx.AddU1("{}=({}&{})!=0u;", inst, ballot, active_mask, active_mask);
  73. }
  74. void EmitVoteEqual(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
  75. if (!ctx.profile.warp_size_potentially_larger_than_guest) {
  76. ctx.AddU1("{}=allInvocationsEqualARB({});", inst, pred);
  77. return;
  78. }
  79. const auto ballot_index{BallotIndex(ctx)};
  80. const auto active_mask{fmt::format("uvec2(ballotARB(true)){}", ballot_index)};
  81. const auto ballot{fmt::format("uvec2(ballotARB({})){}", pred, ballot_index)};
  82. const auto value{fmt::format("({}^{})", ballot, active_mask)};
  83. ctx.AddU1("{}=({}==0)||({}=={});", inst, value, value, active_mask);
  84. }
  85. void EmitSubgroupBallot(EmitContext& ctx, IR::Inst& inst, std::string_view pred) {
  86. const auto ballot_index{BallotIndex(ctx)};
  87. ctx.AddU32("{}=uvec2(ballotARB({})){};", inst, pred, ballot_index);
  88. }
  89. void EmitSubgroupEqMask(EmitContext& ctx, IR::Inst& inst) {
  90. ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupEqMaskARB"));
  91. }
  92. void EmitSubgroupLtMask(EmitContext& ctx, IR::Inst& inst) {
  93. ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLtMaskARB"));
  94. }
  95. void EmitSubgroupLeMask(EmitContext& ctx, IR::Inst& inst) {
  96. ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupLeMaskARB"));
  97. }
  98. void EmitSubgroupGtMask(EmitContext& ctx, IR::Inst& inst) {
  99. ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGtMaskARB"));
  100. }
  101. void EmitSubgroupGeMask(EmitContext& ctx, IR::Inst& inst) {
  102. ctx.AddU32("{}={};", inst, GetMask(ctx, "gl_SubGroupGeMaskARB"));
  103. }
  104. void EmitShuffleIndex(EmitContext& ctx, IR::Inst& inst, std::string_view value,
  105. std::string_view index, std::string_view clamp, std::string_view seg_mask) {
  106. if (ctx.profile.support_gl_warp_intrinsics) {
  107. UseShuffleNv(ctx, inst, "shuffleNV", value, index, clamp, seg_mask);
  108. return;
  109. }
  110. const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
  111. const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
  112. const auto upper_index{fmt::format("{}?{}+32:{}", is_upper_partition, index, index)};
  113. const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
  114. const auto not_seg_mask{fmt::format("(~{})", seg_mask)};
  115. const auto min_thread_id{ComputeMinThreadId(THREAD_ID, seg_mask)};
  116. const auto max_thread_id{
  117. ComputeMaxThreadId(min_thread_id, big_warp ? upper_clamp : clamp, not_seg_mask)};
  118. const auto lhs{fmt::format("({}&{})", big_warp ? upper_index : index, not_seg_mask)};
  119. const auto src_thread_id{fmt::format("({})|({})", lhs, min_thread_id)};
  120. ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
  121. SetInBoundsFlag(ctx, inst);
  122. ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
  123. }
  124. void EmitShuffleUp(EmitContext& ctx, IR::Inst& inst, std::string_view value, std::string_view index,
  125. std::string_view clamp, std::string_view seg_mask) {
  126. if (ctx.profile.support_gl_warp_intrinsics) {
  127. UseShuffleNv(ctx, inst, "shuffleUpNV", value, index, clamp, seg_mask);
  128. return;
  129. }
  130. const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
  131. const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
  132. const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
  133. const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
  134. const auto src_thread_id{fmt::format("({}-{})", THREAD_ID, index)};
  135. ctx.Add("shfl_in_bounds=int({})>=int({});", src_thread_id, max_thread_id);
  136. SetInBoundsFlag(ctx, inst);
  137. ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
  138. }
  139. void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
  140. std::string_view index, std::string_view clamp, std::string_view seg_mask) {
  141. if (ctx.profile.support_gl_warp_intrinsics) {
  142. UseShuffleNv(ctx, inst, "shuffleDownNV", value, index, clamp, seg_mask);
  143. return;
  144. }
  145. const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
  146. const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
  147. const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
  148. const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
  149. const auto src_thread_id{fmt::format("({}+{})", THREAD_ID, index)};
  150. ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
  151. SetInBoundsFlag(ctx, inst);
  152. ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
  153. }
  154. void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
  155. std::string_view index, std::string_view clamp,
  156. std::string_view seg_mask) {
  157. if (ctx.profile.support_gl_warp_intrinsics) {
  158. UseShuffleNv(ctx, inst, "shuffleXorNV", value, index, clamp, seg_mask);
  159. return;
  160. }
  161. const bool big_warp{ctx.profile.warp_size_potentially_larger_than_guest};
  162. const auto is_upper_partition{"int(gl_SubGroupInvocationARB)>=32"};
  163. const auto upper_clamp{fmt::format("{}?{}+32:{}", is_upper_partition, clamp, clamp)};
  164. const auto max_thread_id{GetMaxThreadId(THREAD_ID, big_warp ? upper_clamp : clamp, seg_mask)};
  165. const auto src_thread_id{fmt::format("({}^{})", THREAD_ID, index)};
  166. ctx.Add("shfl_in_bounds=int({})<=int({});", src_thread_id, max_thread_id);
  167. SetInBoundsFlag(ctx, inst);
  168. ctx.AddU32("{}=shfl_in_bounds?readInvocationARB({},{}):{};", inst, value, src_thread_id, value);
  169. }
  170. void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
  171. std::string_view swizzle) {
  172. const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
  173. const std::string modifier_a = fmt::format("FSWZ_A[{}]", mask);
  174. const std::string modifier_b = fmt::format("FSWZ_B[{}]", mask);
  175. ctx.AddF32("{}=({}*{})+({}*{});", inst, op_a, modifier_a, op_b, modifier_b);
  176. }
  177. void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
  178. if (ctx.profile.support_gl_derivative_control) {
  179. ctx.AddF32("{}=dFdxFine({});", inst, op_a);
  180. } else {
  181. LOG_WARNING(Shader_GLSL, "Device does not support dFdxFine, fallback to dFdx");
  182. ctx.AddF32("{}=dFdx({});", inst, op_a);
  183. }
  184. }
  185. void EmitDPdyFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
  186. if (ctx.profile.support_gl_derivative_control) {
  187. ctx.AddF32("{}=dFdyFine({});", inst, op_a);
  188. } else {
  189. LOG_WARNING(Shader_GLSL, "Device does not support dFdyFine, fallback to dFdy");
  190. ctx.AddF32("{}=dFdy({});", inst, op_a);
  191. }
  192. }
  193. void EmitDPdxCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
  194. if (ctx.profile.support_gl_derivative_control) {
  195. ctx.AddF32("{}=dFdxCoarse({});", inst, op_a);
  196. } else {
  197. LOG_WARNING(Shader_GLSL, "Device does not support dFdxCoarse, fallback to dFdx");
  198. ctx.AddF32("{}=dFdx({});", inst, op_a);
  199. }
  200. }
  201. void EmitDPdyCoarse(EmitContext& ctx, IR::Inst& inst, std::string_view op_a) {
  202. if (ctx.profile.support_gl_derivative_control) {
  203. ctx.AddF32("{}=dFdyCoarse({});", inst, op_a);
  204. } else {
  205. LOG_WARNING(Shader_GLSL, "Device does not support dFdyCoarse, fallback to dFdy");
  206. ctx.AddF32("{}=dFdy({});", inst, op_a);
  207. }
  208. }
  209. } // namespace Shader::Backend::GLSL