emit_glasm.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <algorithm>
  4. #include <string>
  5. #include <tuple>
  6. #include "common/div_ceil.h"
  7. #include "common/settings.h"
  8. #include "shader_recompiler/backend/bindings.h"
  9. #include "shader_recompiler/backend/glasm/emit_glasm.h"
  10. #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
  11. #include "shader_recompiler/backend/glasm/glasm_emit_context.h"
  12. #include "shader_recompiler/frontend/ir/ir_emitter.h"
  13. #include "shader_recompiler/frontend/ir/program.h"
  14. #include "shader_recompiler/profile.h"
  15. #include "shader_recompiler/runtime_info.h"
  16. namespace Shader::Backend::GLASM {
  17. namespace {
  18. template <class Func>
  19. struct FuncTraits {};
  20. template <class ReturnType_, class... Args>
  21. struct FuncTraits<ReturnType_ (*)(Args...)> {
  22. using ReturnType = ReturnType_;
  23. static constexpr size_t NUM_ARGS = sizeof...(Args);
  24. template <size_t I>
  25. using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
  26. };
  27. template <typename T>
  28. struct Identity {
  29. Identity(T data_) : data{data_} {}
  30. T Extract() {
  31. return data;
  32. }
  33. T data;
  34. };
  35. template <bool scalar>
  36. class RegWrapper {
  37. public:
  38. RegWrapper(EmitContext& ctx, const IR::Value& ir_value) : reg_alloc{ctx.reg_alloc} {
  39. const Value value{reg_alloc.Peek(ir_value)};
  40. if (value.type == Type::Register) {
  41. inst = ir_value.InstRecursive();
  42. reg = Register{value};
  43. } else {
  44. reg = value.type == Type::U64 ? reg_alloc.AllocLongReg() : reg_alloc.AllocReg();
  45. }
  46. switch (value.type) {
  47. case Type::Register:
  48. case Type::Void:
  49. break;
  50. case Type::U32:
  51. ctx.Add("MOV.U {}.x,{};", reg, value.imm_u32);
  52. break;
  53. case Type::U64:
  54. ctx.Add("MOV.U64 {}.x,{};", reg, value.imm_u64);
  55. break;
  56. }
  57. }
  58. auto Extract() {
  59. if (inst) {
  60. reg_alloc.Unref(*inst);
  61. } else {
  62. reg_alloc.FreeReg(reg);
  63. }
  64. return std::conditional_t<scalar, ScalarRegister, Register>{Value{reg}};
  65. }
  66. private:
  67. RegAlloc& reg_alloc;
  68. IR::Inst* inst{};
  69. Register reg{};
  70. };
  71. template <typename ArgType>
  72. class ValueWrapper {
  73. public:
  74. ValueWrapper(EmitContext& ctx, const IR::Value& ir_value_)
  75. : reg_alloc{ctx.reg_alloc}, ir_value{ir_value_}, value{reg_alloc.Peek(ir_value)} {}
  76. ArgType Extract() {
  77. if (!ir_value.IsImmediate()) {
  78. reg_alloc.Unref(*ir_value.InstRecursive());
  79. }
  80. return value;
  81. }
  82. private:
  83. RegAlloc& reg_alloc;
  84. const IR::Value& ir_value;
  85. ArgType value;
  86. };
  87. template <typename ArgType>
  88. auto Arg(EmitContext& ctx, const IR::Value& arg) {
  89. if constexpr (std::is_same_v<ArgType, Register>) {
  90. return RegWrapper<false>{ctx, arg};
  91. } else if constexpr (std::is_same_v<ArgType, ScalarRegister>) {
  92. return RegWrapper<true>{ctx, arg};
  93. } else if constexpr (std::is_base_of_v<Value, ArgType>) {
  94. return ValueWrapper<ArgType>{ctx, arg};
  95. } else if constexpr (std::is_same_v<ArgType, const IR::Value&>) {
  96. return Identity<const IR::Value&>{arg};
  97. } else if constexpr (std::is_same_v<ArgType, u32>) {
  98. return Identity{arg.U32()};
  99. } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
  100. return Identity{arg.Attribute()};
  101. } else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
  102. return Identity{arg.Patch()};
  103. } else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
  104. return Identity{arg.Reg()};
  105. }
  106. }
  107. template <auto func, bool is_first_arg_inst>
  108. struct InvokeCall {
  109. template <typename... Args>
  110. InvokeCall(EmitContext& ctx, IR::Inst* inst, Args&&... args) {
  111. if constexpr (is_first_arg_inst) {
  112. func(ctx, *inst, args.Extract()...);
  113. } else {
  114. func(ctx, args.Extract()...);
  115. }
  116. }
  117. };
  118. template <auto func, bool is_first_arg_inst, size_t... I>
  119. void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
  120. using Traits = FuncTraits<decltype(func)>;
  121. if constexpr (is_first_arg_inst) {
  122. InvokeCall<func, is_first_arg_inst>{
  123. ctx, inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...};
  124. } else {
  125. InvokeCall<func, is_first_arg_inst>{
  126. ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...};
  127. }
  128. }
  129. template <auto func>
  130. void Invoke(EmitContext& ctx, IR::Inst* inst) {
  131. using Traits = FuncTraits<decltype(func)>;
  132. static_assert(Traits::NUM_ARGS >= 1, "Insufficient arguments");
  133. if constexpr (Traits::NUM_ARGS == 1) {
  134. Invoke<func, false>(ctx, inst, std::make_index_sequence<0>{});
  135. } else {
  136. using FirstArgType = typename Traits::template ArgType<1>;
  137. static constexpr bool is_first_arg_inst = std::is_same_v<FirstArgType, IR::Inst&>;
  138. using Indices = std::make_index_sequence<Traits::NUM_ARGS - (is_first_arg_inst ? 2 : 1)>;
  139. Invoke<func, is_first_arg_inst>(ctx, inst, Indices{});
  140. }
  141. }
  142. void EmitInst(EmitContext& ctx, IR::Inst* inst) {
  143. switch (inst->GetOpcode()) {
  144. #define OPCODE(name, result_type, ...) \
  145. case IR::Opcode::name: \
  146. return Invoke<&Emit##name>(ctx, inst);
  147. #include "shader_recompiler/frontend/ir/opcodes.inc"
  148. #undef OPCODE
  149. }
  150. throw LogicError("Invalid opcode {}", inst->GetOpcode());
  151. }
  152. bool IsReference(IR::Inst& inst) {
  153. return inst.GetOpcode() == IR::Opcode::Reference;
  154. }
  155. void PrecolorInst(IR::Inst& phi) {
  156. // Insert phi moves before references to avoid overwriting other phis
  157. const size_t num_args{phi.NumArgs()};
  158. for (size_t i = 0; i < num_args; ++i) {
  159. IR::Block& phi_block{*phi.PhiBlock(i)};
  160. auto it{std::find_if_not(phi_block.rbegin(), phi_block.rend(), IsReference).base()};
  161. IR::IREmitter ir{phi_block, it};
  162. const IR::Value arg{phi.Arg(i)};
  163. if (arg.IsImmediate()) {
  164. ir.PhiMove(phi, arg);
  165. } else {
  166. ir.PhiMove(phi, IR::Value{&RegAlloc::AliasInst(*arg.Inst())});
  167. }
  168. }
  169. for (size_t i = 0; i < num_args; ++i) {
  170. IR::IREmitter{*phi.PhiBlock(i)}.Reference(IR::Value{&phi});
  171. }
  172. }
  173. void Precolor(const IR::Program& program) {
  174. for (IR::Block* const block : program.blocks) {
  175. for (IR::Inst& phi : block->Instructions()) {
  176. if (!IR::IsPhi(phi)) {
  177. break;
  178. }
  179. PrecolorInst(phi);
  180. }
  181. }
  182. }
  183. void EmitCode(EmitContext& ctx, const IR::Program& program) {
  184. const auto eval{
  185. [&](const IR::U1& cond) { return ScalarS32{ctx.reg_alloc.Consume(IR::Value{cond})}; }};
  186. for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
  187. switch (node.type) {
  188. case IR::AbstractSyntaxNode::Type::Block:
  189. for (IR::Inst& inst : node.data.block->Instructions()) {
  190. EmitInst(ctx, &inst);
  191. }
  192. break;
  193. case IR::AbstractSyntaxNode::Type::If:
  194. ctx.Add("MOV.S.CC RC,{};"
  195. "IF NE.x;",
  196. eval(node.data.if_node.cond));
  197. break;
  198. case IR::AbstractSyntaxNode::Type::EndIf:
  199. ctx.Add("ENDIF;");
  200. break;
  201. case IR::AbstractSyntaxNode::Type::Loop:
  202. ctx.Add("REP;");
  203. break;
  204. case IR::AbstractSyntaxNode::Type::Repeat:
  205. if (!Settings::values.disable_shader_loop_safety_checks) {
  206. const u32 loop_index{ctx.num_safety_loop_vars++};
  207. const u32 vector_index{loop_index / 4};
  208. const char component{"xyzw"[loop_index % 4]};
  209. ctx.Add("SUB.S.CC loop{}.{},loop{}.{},1;"
  210. "BRK(LT.{});",
  211. vector_index, component, vector_index, component, component);
  212. }
  213. if (node.data.repeat.cond.IsImmediate()) {
  214. if (node.data.repeat.cond.U1()) {
  215. ctx.Add("ENDREP;");
  216. } else {
  217. ctx.Add("BRK;"
  218. "ENDREP;");
  219. }
  220. } else {
  221. ctx.Add("MOV.S.CC RC,{};"
  222. "BRK(EQ.x);"
  223. "ENDREP;",
  224. eval(node.data.repeat.cond));
  225. }
  226. break;
  227. case IR::AbstractSyntaxNode::Type::Break:
  228. if (node.data.break_node.cond.IsImmediate()) {
  229. if (node.data.break_node.cond.U1()) {
  230. ctx.Add("BRK;");
  231. }
  232. } else {
  233. ctx.Add("MOV.S.CC RC,{};"
  234. "BRK (NE.x);",
  235. eval(node.data.break_node.cond));
  236. }
  237. break;
  238. case IR::AbstractSyntaxNode::Type::Return:
  239. case IR::AbstractSyntaxNode::Type::Unreachable:
  240. ctx.Add("RET;");
  241. break;
  242. }
  243. }
  244. if (!ctx.reg_alloc.IsEmpty()) {
  245. LOG_WARNING(Shader_GLASM, "Register leak after generating code");
  246. }
  247. }
  248. void SetupOptions(const IR::Program& program, const Profile& profile,
  249. const RuntimeInfo& runtime_info, std::string& header) {
  250. const Info& info{program.info};
  251. const Stage stage{program.stage};
  252. // TODO: Track the shared atomic ops
  253. header += "OPTION NV_internal;"
  254. "OPTION NV_shader_storage_buffer;"
  255. "OPTION NV_gpu_program_fp64;";
  256. if (info.uses_int64_bit_atomics) {
  257. header += "OPTION NV_shader_atomic_int64;";
  258. }
  259. if (info.uses_atomic_f32_add) {
  260. header += "OPTION NV_shader_atomic_float;";
  261. }
  262. if (info.uses_atomic_f16x2_add || info.uses_atomic_f16x2_min || info.uses_atomic_f16x2_max) {
  263. header += "OPTION NV_shader_atomic_fp16_vector;";
  264. }
  265. if (info.uses_subgroup_invocation_id || info.uses_subgroup_mask || info.uses_subgroup_vote ||
  266. info.uses_fswzadd) {
  267. header += "OPTION NV_shader_thread_group;";
  268. }
  269. if (info.uses_subgroup_shuffles) {
  270. header += "OPTION NV_shader_thread_shuffle;";
  271. }
  272. if (info.uses_sparse_residency) {
  273. header += "OPTION EXT_sparse_texture2;";
  274. }
  275. const bool stores_viewport_layer{info.stores[IR::Attribute::ViewportIndex] ||
  276. info.stores[IR::Attribute::Layer]};
  277. if ((stage != Stage::Geometry && stores_viewport_layer) ||
  278. info.stores[IR::Attribute::ViewportMask]) {
  279. if (profile.support_viewport_index_layer_non_geometry) {
  280. header += "OPTION NV_viewport_array2;";
  281. }
  282. }
  283. if (program.is_geometry_passthrough && profile.support_geometry_shader_passthrough) {
  284. header += "OPTION NV_geometry_shader_passthrough;";
  285. }
  286. if (info.uses_typeless_image_reads && profile.support_typeless_image_loads) {
  287. header += "OPTION EXT_shader_image_load_formatted;";
  288. }
  289. if (profile.support_derivative_control) {
  290. header += "OPTION ARB_derivative_control;";
  291. }
  292. if (stage == Stage::Fragment && runtime_info.force_early_z != 0) {
  293. header += "OPTION NV_early_fragment_tests;";
  294. }
  295. if (stage == Stage::Fragment) {
  296. header += "OPTION ARB_draw_buffers;";
  297. header += "OPTION ARB_fragment_layer_viewport;";
  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. if (program.info.uses_rescaling_uniform) {
  420. header += "PARAM scaling[1]={program.local[0..0]};";
  421. }
  422. if (program.info.uses_render_area) {
  423. header += "PARAM render_area[1]={program.local[1..1]};";
  424. }
  425. header += "TEMP ";
  426. for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) {
  427. header += fmt::format("R{},", index);
  428. }
  429. if (program.local_memory_size > 0) {
  430. header += fmt::format("lmem[{}],", program.local_memory_size);
  431. }
  432. if (program.info.uses_fswzadd) {
  433. header += "FSWZA[4],FSWZB[4],";
  434. }
  435. const u32 num_safety_loop_vectors{Common::DivCeil(ctx.num_safety_loop_vars, 4u)};
  436. for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
  437. header += fmt::format("loop{},", index);
  438. }
  439. header += "RC;"
  440. "LONG TEMP ";
  441. for (size_t index = 0; index < ctx.reg_alloc.NumUsedLongRegisters(); ++index) {
  442. header += fmt::format("D{},", index);
  443. }
  444. header += "DC;";
  445. if (program.info.uses_fswzadd) {
  446. header += "MOV.F FSWZA[0],-1;"
  447. "MOV.F FSWZA[1],1;"
  448. "MOV.F FSWZA[2],-1;"
  449. "MOV.F FSWZA[3],0;"
  450. "MOV.F FSWZB[0],-1;"
  451. "MOV.F FSWZB[1],-1;"
  452. "MOV.F FSWZB[2],1;"
  453. "MOV.F FSWZB[3],-1;";
  454. }
  455. for (u32 index = 0; index < num_safety_loop_vectors; ++index) {
  456. header += fmt::format("MOV.S loop{},{{0x2000,0x2000,0x2000,0x2000}};", index);
  457. }
  458. if (ctx.uses_y_direction) {
  459. header += "PARAM y_direction[1]={state.material.front.ambient};";
  460. }
  461. ctx.code.insert(0, header);
  462. ctx.code += "END";
  463. return ctx.code;
  464. }
  465. } // namespace Shader::Backend::GLASM