emit_glasm.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <string>
  6. #include <tuple>
  7. #include "common/div_ceil.h"
  8. #include "common/settings.h"
  9. #include "shader_recompiler/backend/bindings.h"
  10. #include "shader_recompiler/backend/glasm/emit_context.h"
  11. #include "shader_recompiler/backend/glasm/emit_glasm.h"
  12. #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
  13. #include "shader_recompiler/frontend/ir/ir_emitter.h"
  14. #include "shader_recompiler/frontend/ir/program.h"
  15. #include "shader_recompiler/profile.h"
  16. #include "shader_recompiler/runtime_info.h"
  17. namespace Shader::Backend::GLASM {
  18. namespace {
  19. template <class Func>
  20. struct FuncTraits {};
  21. template <class ReturnType_, class... Args>
  22. struct FuncTraits<ReturnType_ (*)(Args...)> {
  23. using ReturnType = ReturnType_;
  24. static constexpr size_t NUM_ARGS = sizeof...(Args);
  25. template <size_t I>
  26. using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
  27. };
  28. template <typename T>
  29. struct Identity {
  30. Identity(T data_) : data{data_} {}
  31. T Extract() {
  32. return data;
  33. }
  34. T data;
  35. };
  36. template <bool scalar>
  37. class RegWrapper {
  38. public:
  39. RegWrapper(EmitContext& ctx, const IR::Value& ir_value) : reg_alloc{ctx.reg_alloc} {
  40. const Value value{reg_alloc.Peek(ir_value)};
  41. if (value.type == Type::Register) {
  42. inst = ir_value.InstRecursive();
  43. reg = Register{value};
  44. } else {
  45. reg = value.type == Type::U64 ? reg_alloc.AllocLongReg() : reg_alloc.AllocReg();
  46. }
  47. switch (value.type) {
  48. case Type::Register:
  49. case Type::Void:
  50. break;
  51. case Type::U32:
  52. ctx.Add("MOV.U {}.x,{};", reg, value.imm_u32);
  53. break;
  54. case Type::U64:
  55. ctx.Add("MOV.U64 {}.x,{};", reg, value.imm_u64);
  56. break;
  57. }
  58. }
  59. auto Extract() {
  60. if (inst) {
  61. reg_alloc.Unref(*inst);
  62. } else {
  63. reg_alloc.FreeReg(reg);
  64. }
  65. return std::conditional_t<scalar, ScalarRegister, Register>{Value{reg}};
  66. }
  67. private:
  68. RegAlloc& reg_alloc;
  69. IR::Inst* inst{};
  70. Register reg{};
  71. };
  72. template <typename ArgType>
  73. class ValueWrapper {
  74. public:
  75. ValueWrapper(EmitContext& ctx, const IR::Value& ir_value_)
  76. : reg_alloc{ctx.reg_alloc}, ir_value{ir_value_}, value{reg_alloc.Peek(ir_value)} {}
  77. ArgType Extract() {
  78. if (!ir_value.IsImmediate()) {
  79. reg_alloc.Unref(*ir_value.InstRecursive());
  80. }
  81. return value;
  82. }
  83. private:
  84. RegAlloc& reg_alloc;
  85. const IR::Value& ir_value;
  86. ArgType value;
  87. };
  88. template <typename ArgType>
  89. auto Arg(EmitContext& ctx, const IR::Value& arg) {
  90. if constexpr (std::is_same_v<ArgType, Register>) {
  91. return RegWrapper<false>{ctx, arg};
  92. } else if constexpr (std::is_same_v<ArgType, ScalarRegister>) {
  93. return RegWrapper<true>{ctx, arg};
  94. } else if constexpr (std::is_base_of_v<Value, ArgType>) {
  95. return ValueWrapper<ArgType>{ctx, arg};
  96. } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
  97. return Identity<const IR::Value&>{arg};
  98. } else if constexpr (std::is_same_v<ArgType, u32>) {
  99. return Identity{arg.U32()};
  100. } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
  101. return Identity{arg.Attribute()};
  102. } else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
  103. return Identity{arg.Patch()};
  104. } else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
  105. return Identity{arg.Reg()};
  106. }
  107. }
  108. template <auto func, bool is_first_arg_inst>
  109. struct InvokeCall {
  110. template <typename... Args>
  111. InvokeCall(EmitContext& ctx, IR::Inst* inst, Args&&... args) {
  112. if constexpr (is_first_arg_inst) {
  113. func(ctx, *inst, args.Extract()...);
  114. } else {
  115. func(ctx, args.Extract()...);
  116. }
  117. }
  118. };
  119. template <auto func, bool is_first_arg_inst, size_t... I>
  120. void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
  121. using Traits = FuncTraits<decltype(func)>;
  122. if constexpr (is_first_arg_inst) {
  123. InvokeCall<func, is_first_arg_inst>{
  124. ctx, inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...};
  125. } else {
  126. InvokeCall<func, is_first_arg_inst>{
  127. ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...};
  128. }
  129. }
  130. template <auto func>
  131. void Invoke(EmitContext& ctx, IR::Inst* inst) {
  132. using Traits = FuncTraits<decltype(func)>;
  133. static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
  134. if constexpr (Traits::NUM_ARGS == 1) {
  135. Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
  136. } else {
  137. using FirstArgType = typename Traits::template ArgType<1>;
  138. static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
  139. using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
  140. Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
  141. }
  142. }
  143. void EmitInst(EmitContext& ctx, IR::Inst* inst) {
  144. switch (inst->GetOpcode()) {
  145. #define OPCODE(name, result_type, ...) \
  146. case IR::Opcode::name: \
  147. return Invoke<&Emit##name>(ctx, inst);
  148. #include "shader_recompiler/frontend/ir/opcodes.inc"
  149. #undef OPCODE
  150. }
  151. throw LogicError("Invalid opcode {}", inst->GetOpcode());
  152. }
  153. bool IsReference(IR::Inst& inst) {
  154. return inst.GetOpcode() == IR::Opcode::Reference;
  155. }
  156. void PrecolorInst(IR::Inst& phi) {
  157. // Insert phi moves before references to avoid overwritting other phis
  158. const size_t num_args{phi.NumArgs()};
  159. for (size_t i = 0; i < num_args; ++i) {
  160. IR::Block& phi_block{*phi.PhiBlock(i)};
  161. auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
  162. IR::IREmitter ir{phi_block, it};
  163. const IR::Value arg{phi.Arg(i)};
  164. if (arg.IsImmediate()) {
  165. ir.PhiMove(phi, arg);
  166. } else {
  167. ir.PhiMove(phi, IR::Value{&RegAlloc::AliasInst(*arg.Inst())});
  168. }
  169. }
  170. for (size_t i = 0; i < num_args; ++i) {
  171. IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
  172. }
  173. }
  174. void Precolor(const IR::Program& program) {
  175. for (IR::Block* const block : program.blocks) {
  176. for (IR::Inst& phi : block->Instructions()) {
  177. if (!IR::IsPhi(phi)) {
  178. break;
  179. }
  180. PrecolorInst(phi);
  181. }
  182. }
  183. }
  184. void EmitCode(EmitContext& ctx, const IR::Program& program) {
  185. const auto eval{
  186. [&](const IR::U1& cond) { return ScalarS32{ctx.reg_alloc.Consume(IR::Value{cond})}; }};
  187. for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
  188. switch (node.type) {
  189. case IR::AbstractSyntaxNode::Type::Block:
  190. for (IR::Inst& inst : node.data.block->Instructions()) {
  191. EmitInst(ctx, &inst);
  192. }
  193. break;
  194. case IR::AbstractSyntaxNode::Type::If:
  195. ctx.Add("MOV.S.CC RC,{};"
  196. "IF NE.x;",
  197. eval(node.data.if_node.cond));
  198. break;
  199. case IR::AbstractSyntaxNode::Type::EndIf:
  200. ctx.Add("ENDIF;");
  201. break;
  202. case IR::AbstractSyntaxNode::Type::Loop:
  203. ctx.Add("REP;");
  204. break;
  205. case IR::AbstractSyntaxNode::Type::Repeat:
  206. if (!Settings::values.disable_shader_loop_safety_checks) {
  207. const u32 loop_index{ctx.num_safety_loop_vars++};
  208. const u32 vector_index{loop_index / 4};
  209. const char component{"xyzw"[loop_index % 4]};
  210. ctx.Add("SUB.S.CC loop{}.{},loop{}.{},1;"
  211. "BRK(LT.{});",
  212. vector_index, component, vector_index, component, component);
  213. }
  214. if (node.data.repeat.cond.IsImmediate()) {
  215. if (node.data.repeat.cond.U1()) {
  216. ctx.Add("ENDREP;");
  217. } else {
  218. ctx.Add("BRK;"
  219. "ENDREP;");
  220. }
  221. } else {
  222. ctx.Add("MOV.S.CC RC,{};"
  223. "BRK(EQ.x);"
  224. "ENDREP;",
  225. eval(node.data.repeat.cond));
  226. }
  227. break;
  228. case IR::AbstractSyntaxNode::Type::Break:
  229. if (node.data.break_node.cond.IsImmediate()) {
  230. if (node.data.break_node.cond.U1()) {
  231. ctx.Add("BRK;");
  232. }
  233. } else {
  234. ctx.Add("MOV.S.CC RC,{};"
  235. "BRK (NE.x);",
  236. eval(node.data.break_node.cond));
  237. }
  238. break;
  239. case IR::AbstractSyntaxNode::Type::Return:
  240. case IR::AbstractSyntaxNode::Type::Unreachable:
  241. ctx.Add("RET;");
  242. break;
  243. }
  244. }
  245. if (!ctx.reg_alloc.IsEmpty()) {
  246. LOG_WARNING(Shader_GLASM, "Register leak after generating code");
  247. }
  248. }
  249. void SetupOptions(const IR::Program& program, const Profile& profile,
  250. const RuntimeInfo& runtime_info, std::string& header) {
  251. const Info& info{program.info};
  252. const Stage stage{program.stage};
  253. // TODO: Track the shared atomic ops
  254. header += "OPTION NV_internal;"
  255. "OPTION NV_shader_storage_buffer;"
  256. "OPTION NV_gpu_program_fp64;";
  257. if (info.uses_int64_bit_atomics) {
  258. header += "OPTION NV_shader_atomic_int64;";
  259. }
  260. if (info.uses_atomic_f32_add) {
  261. header += "OPTION NV_shader_atomic_float;";
  262. }
  263. if (info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) {
  264. header += "OPTION NV_shader_atomic_fp16_vector;";
  265. }
  266. if (info.uses_subgroup_invocation_id || info.uses_subgroup_mask || info.uses_subgroup_vote ||
  267. info.uses_fswzadd) {
  268. header += "OPTION NV_shader_thread_group;";
  269. }
  270. if (info.uses_subgroup_shuffles) {
  271. header += "OPTION NV_shader_thread_shuffle;";
  272. }
  273. if (info.uses_sparse_residency) {
  274. header += "OPTION EXT_sparse_texture2;";
  275. }
  276. const bool stores_viewport_layer{info.stores[IR::Attribute::ViewportIndex] ||
  277. info.stores[IR::Attribute::Layer]};
  278. if ((stage != Stage::Geometry && stores_viewport_layer) ||
  279. info.stores[IR::Attribute::ViewportMask]) {
  280. if (profile.support_viewport_index_layer_non_geometry) {
  281. header += "OPTION NV_viewport_array2;";
  282. }
  283. }
  284. if (program.is_geometry_passthrough && profile.support_geometry_shader_passthrough) {
  285. header += "OPTION NV_geometry_shader_passthrough;";
  286. }
  287. if (info.uses_typeless_image_reads && profile.support_typeless_image_loads) {
  288. header += "OPTION EXT_shader_image_load_formatted;";
  289. }
  290. if (profile.support_derivative_control) {
  291. header += "OPTION ARB_derivative_control;";
  292. }
  293. if (stage == Stage::Fragment && runtime_info.force_early_z != 0) {
  294. header += "OPTION NV_early_fragment_tests;";
  295. }
  296. if (stage == Stage::Fragment) {
  297. header += "OPTION ARB_draw_buffers;";
  298. }
  299. }
  300. std::string_view StageHeader(Stage stage) {
  301. switch (stage) {
  302. case Stage::VertexA:
  303. case Stage::VertexB:
  304. return "!!NVvp5.0\n";
  305. case Stage::TessellationControl:
  306. return "!!NVtcp5.0\n";
  307. case Stage::TessellationEval:
  308. return "!!NVtep5.0\n";
  309. case Stage::Geometry:
  310. return "!!NVgp5.0\n";
  311. case Stage::Fragment:
  312. return "!!NVfp5.0\n";
  313. case Stage::Compute:
  314. return "!!NVcp5.0\n";
  315. }
  316. throw InvalidArgument("Invalid stage {}", stage);
  317. }
  318. std::string_view InputPrimitive(InputTopology topology) {
  319. switch (topology) {
  320. case InputTopology::Points:
  321. return "POINTS";
  322. case InputTopology::Lines:
  323. return "LINES";
  324. case InputTopology::LinesAdjacency:
  325. return "LINES_ADJACENCY";
  326. case InputTopology::Triangles:
  327. return "TRIANGLES";
  328. case InputTopology::TrianglesAdjacency:
  329. return "TRIANGLES_ADJACENCY";
  330. }
  331. throw InvalidArgument("Invalid input topology {}", topology);
  332. }
  333. std::string_view OutputPrimitive(OutputTopology topology) {
  334. switch (topology) {
  335. case OutputTopology::PointList:
  336. return "POINTS";
  337. case OutputTopology::LineStrip:
  338. return "LINE_STRIP";
  339. case OutputTopology::TriangleStrip:
  340. return "TRIANGLE_STRIP";
  341. }
  342. throw InvalidArgument("Invalid output topology {}", topology);
  343. }
  344. std::string_view GetTessMode(TessPrimitive primitive) {
  345. switch (primitive) {
  346. case TessPrimitive::Triangles:
  347. return "TRIANGLES";
  348. case TessPrimitive::Quads:
  349. return "QUADS";
  350. case TessPrimitive::Isolines:
  351. return "ISOLINES";
  352. }
  353. throw InvalidArgument("Invalid tessellation primitive {}", primitive);
  354. }
  355. std::string_view GetTessSpacing(TessSpacing spacing) {
  356. switch (spacing) {
  357. case TessSpacing::Equal:
  358. return "EQUAL";
  359. case TessSpacing::FractionalOdd:
  360. return "FRACTIONAL_ODD";
  361. case TessSpacing::FractionalEven:
  362. return "FRACTIONAL_EVEN";
  363. }
  364. throw InvalidArgument("Invalid tessellation spacing {}", spacing);
  365. }
  366. } // Anonymous namespace
  367. std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
  368. Bindings& bindings) {
  369. EmitContext ctx{program, bindings, profile, runtime_info};
  370. Precolor(program);
  371. EmitCode(ctx, program);
  372. std::string header{StageHeader(program.stage)};
  373. SetupOptions(program, profile, runtime_info, header);
  374. switch (program.stage) {
  375. case Stage::TessellationControl:
  376. header += fmt::format("VERTICES_OUT {};", program.invocations);
  377. break;
  378. case Stage::TessellationEval:
  379. header += fmt::format("TESS_MODE {};"
  380. "TESS_SPACING {};"
  381. "TESS_VERTEX_ORDER {};",
  382. GetTessMode(runtime_info.tess_primitive),
  383. GetTessSpacing(runtime_info.tess_spacing),
  384. runtime_info.tess_clockwise ? "CW" : "CCW");
  385. break;
  386. case Stage::Geometry:
  387. header += fmt::format("PRIMITIVE_IN {};", InputPrimitive(runtime_info.input_topology));
  388. if (program.is_geometry_passthrough) {
  389. if (profile.support_geometry_shader_passthrough) {
  390. for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
  391. if (program.info.passthrough.Generic(index)) {
  392. header += fmt::format("PASSTHROUGH result.attrib[{}];", index);
  393. }
  394. }
  395. if (program.info.passthrough.AnyComponent(IR::Attribute::PositionX)) {
  396. header += "PASSTHROUGH result.position;";
  397. }
  398. } else {
  399. LOG_WARNING(Shader_GLASM, "Passthrough geometry program used but not supported");
  400. }
  401. } else {
  402. header +=
  403. fmt::format("VERTICES_OUT {};"
  404. "PRIMITIVE_OUT {};",
  405. program.output_vertices, OutputPrimitive(program.output_topology));
  406. }
  407. break;
  408. case Stage::Compute:
  409. header += fmt::format("GROUP_SIZE {} {} {};", program.workgroup_size[0],
  410. program.workgroup_size[1], program.workgroup_size[2]);
  411. break;
  412. default:
  413. break;
  414. }
  415. if (program.shared_memory_size > 0) {
  416. header += fmt::format("SHARED_MEMORY {};", program.shared_memory_size);
  417. header += fmt::format("SHARED shared_mem[]={{program.sharedmem}};");
  418. }
  419. header += "TEMP ";
  420. for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) {
  421. header += fmt::format("R{},", index);
  422. }
  423. if (program.local_memory_size > 0) {
  424. header += fmt::format("lmem[{}],", program.local_memory_size);
  425. }
  426. if (program.info.uses_fswzadd) {
  427. header += "FSWZA[4],FSWZB[4],";
  428. }
  429. const u32 num_safety_loop_vectors{Common::DivCeil(ctx.num_safety_loop_vars, 4u)};
  430. for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
  431. header += fmt::format("loop{},", index);
  432. }
  433. header += "RC;"
  434. "LONG TEMP ";
  435. for (size_t index = 0; index < ctx.reg_alloc.NumUsedLongRegisters(); ++index) {
  436. header += fmt::format("D{},", index);
  437. }
  438. header += "DC;";
  439. if (program.info.uses_fswzadd) {
  440. header += "MOV.F FSWZA[0],-1;"
  441. "MOV.F FSWZA[1],1;"
  442. "MOV.F FSWZA[2],-1;"
  443. "MOV.F FSWZA[3],0;"
  444. "MOV.F FSWZB[0],-1;"
  445. "MOV.F FSWZB[1],-1;"
  446. "MOV.F FSWZB[2],1;"
  447. "MOV.F FSWZB[3],-1;";
  448. }
  449. for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
  450. header += fmt::format("MOV.S loop{},{{0x2000,0x2000,0x2000,0x2000}};", index);
  451. }
  452. if (ctx.uses_y_direction) {
  453. header += "PARAM y_direction[1]={state.material.front.ambient};";
  454. }
  455. ctx.code.insert(0, header);
  456. ctx.code += "END";
  457. return ctx.code;
  458. }
  459. } // namespace Shader::Backend::GLASM