node.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. // Copyright 2019 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <array>
  6. #include <cstddef>
  7. #include <memory>
  8. #include <string>
  9. #include <tuple>
  10. #include <utility>
  11. #include <variant>
  12. #include <vector>
  13. #include "common/common_types.h"
  14. #include "video_core/engines/shader_bytecode.h"
  15. namespace VideoCommon::Shader {
  16. enum class OperationCode {
  17. Assign, /// (float& dest, float src) -> void
  18. Select, /// (MetaArithmetic, bool pred, float a, float b) -> float
  19. FAdd, /// (MetaArithmetic, float a, float b) -> float
  20. FMul, /// (MetaArithmetic, float a, float b) -> float
  21. FDiv, /// (MetaArithmetic, float a, float b) -> float
  22. FFma, /// (MetaArithmetic, float a, float b, float c) -> float
  23. FNegate, /// (MetaArithmetic, float a) -> float
  24. FAbsolute, /// (MetaArithmetic, float a) -> float
  25. FClamp, /// (MetaArithmetic, float value, float min, float max) -> float
  26. FCastHalf0, /// (MetaArithmetic, f16vec2 a) -> float
  27. FCastHalf1, /// (MetaArithmetic, f16vec2 a) -> float
  28. FMin, /// (MetaArithmetic, float a, float b) -> float
  29. FMax, /// (MetaArithmetic, float a, float b) -> float
  30. FCos, /// (MetaArithmetic, float a) -> float
  31. FSin, /// (MetaArithmetic, float a) -> float
  32. FExp2, /// (MetaArithmetic, float a) -> float
  33. FLog2, /// (MetaArithmetic, float a) -> float
  34. FInverseSqrt, /// (MetaArithmetic, float a) -> float
  35. FSqrt, /// (MetaArithmetic, float a) -> float
  36. FRoundEven, /// (MetaArithmetic, float a) -> float
  37. FFloor, /// (MetaArithmetic, float a) -> float
  38. FCeil, /// (MetaArithmetic, float a) -> float
  39. FTrunc, /// (MetaArithmetic, float a) -> float
  40. FCastInteger, /// (MetaArithmetic, int a) -> float
  41. FCastUInteger, /// (MetaArithmetic, uint a) -> float
  42. IAdd, /// (MetaArithmetic, int a, int b) -> int
  43. IMul, /// (MetaArithmetic, int a, int b) -> int
  44. IDiv, /// (MetaArithmetic, int a, int b) -> int
  45. INegate, /// (MetaArithmetic, int a) -> int
  46. IAbsolute, /// (MetaArithmetic, int a) -> int
  47. IMin, /// (MetaArithmetic, int a, int b) -> int
  48. IMax, /// (MetaArithmetic, int a, int b) -> int
  49. ICastFloat, /// (MetaArithmetic, float a) -> int
  50. ICastUnsigned, /// (MetaArithmetic, uint a) -> int
  51. ILogicalShiftLeft, /// (MetaArithmetic, int a, uint b) -> int
  52. ILogicalShiftRight, /// (MetaArithmetic, int a, uint b) -> int
  53. IArithmeticShiftRight, /// (MetaArithmetic, int a, uint b) -> int
  54. IBitwiseAnd, /// (MetaArithmetic, int a, int b) -> int
  55. IBitwiseOr, /// (MetaArithmetic, int a, int b) -> int
  56. IBitwiseXor, /// (MetaArithmetic, int a, int b) -> int
  57. IBitwiseNot, /// (MetaArithmetic, int a) -> int
  58. IBitfieldInsert, /// (MetaArithmetic, int base, int insert, int offset, int bits) -> int
  59. IBitfieldExtract, /// (MetaArithmetic, int value, int offset, int offset) -> int
  60. IBitCount, /// (MetaArithmetic, int) -> int
  61. UAdd, /// (MetaArithmetic, uint a, uint b) -> uint
  62. UMul, /// (MetaArithmetic, uint a, uint b) -> uint
  63. UDiv, /// (MetaArithmetic, uint a, uint b) -> uint
  64. UMin, /// (MetaArithmetic, uint a, uint b) -> uint
  65. UMax, /// (MetaArithmetic, uint a, uint b) -> uint
  66. UCastFloat, /// (MetaArithmetic, float a) -> uint
  67. UCastSigned, /// (MetaArithmetic, int a) -> uint
  68. ULogicalShiftLeft, /// (MetaArithmetic, uint a, uint b) -> uint
  69. ULogicalShiftRight, /// (MetaArithmetic, uint a, uint b) -> uint
  70. UArithmeticShiftRight, /// (MetaArithmetic, uint a, uint b) -> uint
  71. UBitwiseAnd, /// (MetaArithmetic, uint a, uint b) -> uint
  72. UBitwiseOr, /// (MetaArithmetic, uint a, uint b) -> uint
  73. UBitwiseXor, /// (MetaArithmetic, uint a, uint b) -> uint
  74. UBitwiseNot, /// (MetaArithmetic, uint a) -> uint
  75. UBitfieldInsert, /// (MetaArithmetic, uint base, uint insert, int offset, int bits) -> uint
  76. UBitfieldExtract, /// (MetaArithmetic, uint value, int offset, int offset) -> uint
  77. UBitCount, /// (MetaArithmetic, uint) -> uint
  78. HAdd, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2
  79. HMul, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2
  80. HFma, /// (MetaArithmetic, f16vec2 a, f16vec2 b, f16vec2 c) -> f16vec2
  81. HAbsolute, /// (f16vec2 a) -> f16vec2
  82. HNegate, /// (f16vec2 a, bool first, bool second) -> f16vec2
  83. HClamp, /// (f16vec2 src, float min, float max) -> f16vec2
  84. HCastFloat, /// (MetaArithmetic, float a) -> f16vec2
  85. HUnpack, /// (Tegra::Shader::HalfType, T value) -> f16vec2
  86. HMergeF32, /// (f16vec2 src) -> float
  87. HMergeH0, /// (f16vec2 dest, f16vec2 src) -> f16vec2
  88. HMergeH1, /// (f16vec2 dest, f16vec2 src) -> f16vec2
  89. HPack2, /// (float a, float b) -> f16vec2
  90. LogicalAssign, /// (bool& dst, bool src) -> void
  91. LogicalAnd, /// (bool a, bool b) -> bool
  92. LogicalOr, /// (bool a, bool b) -> bool
  93. LogicalXor, /// (bool a, bool b) -> bool
  94. LogicalNegate, /// (bool a) -> bool
  95. LogicalPick2, /// (bool2 pair, uint index) -> bool
  96. LogicalAnd2, /// (bool2 a) -> bool
  97. LogicalFLessThan, /// (float a, float b) -> bool
  98. LogicalFEqual, /// (float a, float b) -> bool
  99. LogicalFLessEqual, /// (float a, float b) -> bool
  100. LogicalFGreaterThan, /// (float a, float b) -> bool
  101. LogicalFNotEqual, /// (float a, float b) -> bool
  102. LogicalFGreaterEqual, /// (float a, float b) -> bool
  103. LogicalFIsNan, /// (float a) -> bool
  104. LogicalILessThan, /// (int a, int b) -> bool
  105. LogicalIEqual, /// (int a, int b) -> bool
  106. LogicalILessEqual, /// (int a, int b) -> bool
  107. LogicalIGreaterThan, /// (int a, int b) -> bool
  108. LogicalINotEqual, /// (int a, int b) -> bool
  109. LogicalIGreaterEqual, /// (int a, int b) -> bool
  110. LogicalULessThan, /// (uint a, uint b) -> bool
  111. LogicalUEqual, /// (uint a, uint b) -> bool
  112. LogicalULessEqual, /// (uint a, uint b) -> bool
  113. LogicalUGreaterThan, /// (uint a, uint b) -> bool
  114. LogicalUNotEqual, /// (uint a, uint b) -> bool
  115. LogicalUGreaterEqual, /// (uint a, uint b) -> bool
  116. Logical2HLessThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  117. Logical2HEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  118. Logical2HLessEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  119. Logical2HGreaterThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  120. Logical2HNotEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  121. Logical2HGreaterEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  122. Logical2HLessThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  123. Logical2HEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  124. Logical2HLessEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  125. Logical2HGreaterThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  126. Logical2HNotEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  127. Logical2HGreaterEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
  128. Texture, /// (MetaTexture, float[N] coords) -> float4
  129. TextureLod, /// (MetaTexture, float[N] coords) -> float4
  130. TextureGather, /// (MetaTexture, float[N] coords) -> float4
  131. TextureQueryDimensions, /// (MetaTexture, float a) -> float4
  132. TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4
  133. TexelFetch, /// (MetaTexture, int[N], int) -> float4
  134. ImageStore, /// (MetaImage, float[N] coords) -> void
  135. Branch, /// (uint branch_target) -> void
  136. BranchIndirect, /// (uint branch_target) -> void
  137. PushFlowStack, /// (uint branch_target) -> void
  138. PopFlowStack, /// () -> void
  139. Exit, /// () -> void
  140. Discard, /// () -> void
  141. EmitVertex, /// () -> void
  142. EndPrimitive, /// () -> void
  143. YNegate, /// () -> float
  144. LocalInvocationIdX, /// () -> uint
  145. LocalInvocationIdY, /// () -> uint
  146. LocalInvocationIdZ, /// () -> uint
  147. WorkGroupIdX, /// () -> uint
  148. WorkGroupIdY, /// () -> uint
  149. WorkGroupIdZ, /// () -> uint
  150. BallotThread, /// (bool) -> uint
  151. VoteAll, /// (bool) -> bool
  152. VoteAny, /// (bool) -> bool
  153. VoteEqual, /// (bool) -> bool
  154. Amount,
  155. };
  156. enum class InternalFlag {
  157. Zero = 0,
  158. Sign = 1,
  159. Carry = 2,
  160. Overflow = 3,
  161. Amount = 4,
  162. };
  163. enum class MetaStackClass {
  164. Ssy,
  165. Pbk,
  166. };
  167. class OperationNode;
  168. class ConditionalNode;
  169. class GprNode;
  170. class ImmediateNode;
  171. class InternalFlagNode;
  172. class PredicateNode;
  173. class AbufNode;
  174. class CbufNode;
  175. class LmemNode;
  176. class GmemNode;
  177. class CommentNode;
  178. using NodeData =
  179. std::variant<OperationNode, ConditionalNode, GprNode, ImmediateNode, InternalFlagNode,
  180. PredicateNode, AbufNode, CbufNode, LmemNode, GmemNode, CommentNode>;
  181. using Node = std::shared_ptr<NodeData>;
  182. using Node4 = std::array<Node, 4>;
  183. using NodeBlock = std::vector<Node>;
  184. class Sampler {
  185. public:
  186. /// This constructor is for bound samplers
  187. explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type,
  188. bool is_array, bool is_shadow)
  189. : offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow},
  190. is_bindless{false} {}
  191. /// This constructor is for bindless samplers
  192. explicit Sampler(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
  193. Tegra::Shader::TextureType type, bool is_array, bool is_shadow)
  194. : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
  195. is_array{is_array}, is_shadow{is_shadow}, is_bindless{true} {}
  196. /// This constructor is for serialization/deserialization
  197. explicit Sampler(std::size_t offset, std::size_t index, Tegra::Shader::TextureType type,
  198. bool is_array, bool is_shadow, bool is_bindless)
  199. : offset{offset}, index{index}, type{type}, is_array{is_array}, is_shadow{is_shadow},
  200. is_bindless{is_bindless} {}
  201. std::size_t GetOffset() const {
  202. return offset;
  203. }
  204. std::size_t GetIndex() const {
  205. return index;
  206. }
  207. Tegra::Shader::TextureType GetType() const {
  208. return type;
  209. }
  210. bool IsArray() const {
  211. return is_array;
  212. }
  213. bool IsShadow() const {
  214. return is_shadow;
  215. }
  216. bool IsBindless() const {
  217. return is_bindless;
  218. }
  219. std::pair<u32, u32> GetBindlessCBuf() const {
  220. return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
  221. }
  222. bool operator<(const Sampler& rhs) const {
  223. return std::tie(index, offset, type, is_array, is_shadow, is_bindless) <
  224. std::tie(rhs.index, rhs.offset, rhs.type, rhs.is_array, rhs.is_shadow,
  225. rhs.is_bindless);
  226. }
  227. private:
  228. /// Offset in TSC memory from which to read the sampler object, as specified by the sampling
  229. /// instruction.
  230. std::size_t offset{};
  231. std::size_t index{}; ///< Value used to index into the generated GLSL sampler array.
  232. Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
  233. bool is_array{}; ///< Whether the texture is being sampled as an array texture or not.
  234. bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not.
  235. bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
  236. };
  237. class Image final {
  238. public:
  239. constexpr explicit Image(u64 offset, std::size_t index, Tegra::Shader::ImageType type)
  240. : offset{offset}, index{index}, type{type}, is_bindless{false} {}
  241. constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
  242. Tegra::Shader::ImageType type)
  243. : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
  244. is_bindless{true} {}
  245. constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
  246. bool is_bindless, bool is_written, bool is_read)
  247. : offset{offset}, index{index}, type{type}, is_bindless{is_bindless},
  248. is_written{is_written}, is_read{is_read} {}
  249. void MarkRead() {
  250. is_read = true;
  251. }
  252. void MarkWrite() {
  253. is_written = true;
  254. }
  255. constexpr std::size_t GetOffset() const {
  256. return offset;
  257. }
  258. constexpr std::size_t GetIndex() const {
  259. return index;
  260. }
  261. constexpr Tegra::Shader::ImageType GetType() const {
  262. return type;
  263. }
  264. constexpr bool IsBindless() const {
  265. return is_bindless;
  266. }
  267. constexpr bool IsRead() const {
  268. return is_read;
  269. }
  270. constexpr bool IsWritten() const {
  271. return is_written;
  272. }
  273. constexpr std::pair<u32, u32> GetBindlessCBuf() const {
  274. return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
  275. }
  276. private:
  277. u64 offset{};
  278. std::size_t index{};
  279. Tegra::Shader::ImageType type{};
  280. bool is_bindless{};
  281. bool is_read{};
  282. bool is_written{};
  283. };
  284. struct GlobalMemoryBase {
  285. u32 cbuf_index{};
  286. u32 cbuf_offset{};
  287. bool operator<(const GlobalMemoryBase& rhs) const {
  288. return std::tie(cbuf_index, cbuf_offset) < std::tie(rhs.cbuf_index, rhs.cbuf_offset);
  289. }
  290. };
  291. /// Parameters describing an arithmetic operation
  292. struct MetaArithmetic {
  293. bool precise{}; ///< Whether the operation can be constraint or not
  294. };
  295. /// Parameters describing a texture sampler
  296. struct MetaTexture {
  297. const Sampler& sampler;
  298. Node array;
  299. Node depth_compare;
  300. std::vector<Node> aoffi;
  301. Node bias;
  302. Node lod;
  303. Node component{};
  304. u32 element{};
  305. };
  306. struct MetaImage {
  307. const Image& image;
  308. std::vector<Node> values;
  309. };
  310. /// Parameters that modify an operation but are not part of any particular operand
  311. using Meta =
  312. std::variant<MetaArithmetic, MetaTexture, MetaImage, MetaStackClass, Tegra::Shader::HalfType>;
  313. /// Holds any kind of operation that can be done in the IR
  314. class OperationNode final {
  315. public:
  316. explicit OperationNode(OperationCode code) : OperationNode(code, Meta{}) {}
  317. explicit OperationNode(OperationCode code, Meta meta)
  318. : OperationNode(code, meta, std::vector<Node>{}) {}
  319. explicit OperationNode(OperationCode code, std::vector<Node> operands)
  320. : OperationNode(code, Meta{}, std::move(operands)) {}
  321. explicit OperationNode(OperationCode code, Meta meta, std::vector<Node> operands)
  322. : code{code}, meta{std::move(meta)}, operands{std::move(operands)} {}
  323. template <typename... Args>
  324. explicit OperationNode(OperationCode code, Meta meta, Args&&... operands)
  325. : code{code}, meta{std::move(meta)}, operands{operands...} {}
  326. OperationCode GetCode() const {
  327. return code;
  328. }
  329. const Meta& GetMeta() const {
  330. return meta;
  331. }
  332. std::size_t GetOperandsCount() const {
  333. return operands.size();
  334. }
  335. const Node& operator[](std::size_t operand_index) const {
  336. return operands.at(operand_index);
  337. }
  338. private:
  339. OperationCode code{};
  340. Meta meta{};
  341. std::vector<Node> operands;
  342. };
  343. /// Encloses inside any kind of node that returns a boolean conditionally-executed code
  344. class ConditionalNode final {
  345. public:
  346. explicit ConditionalNode(Node condition, std::vector<Node>&& code)
  347. : condition{std::move(condition)}, code{std::move(code)} {}
  348. const Node& GetCondition() const {
  349. return condition;
  350. }
  351. const std::vector<Node>& GetCode() const {
  352. return code;
  353. }
  354. private:
  355. Node condition; ///< Condition to be satisfied
  356. std::vector<Node> code; ///< Code to execute
  357. };
  358. /// A general purpose register
  359. class GprNode final {
  360. public:
  361. explicit constexpr GprNode(Tegra::Shader::Register index) : index{index} {}
  362. u32 GetIndex() const {
  363. return static_cast<u32>(index);
  364. }
  365. private:
  366. Tegra::Shader::Register index{};
  367. };
  368. /// A 32-bits value that represents an immediate value
  369. class ImmediateNode final {
  370. public:
  371. explicit constexpr ImmediateNode(u32 value) : value{value} {}
  372. u32 GetValue() const {
  373. return value;
  374. }
  375. private:
  376. u32 value{};
  377. };
  378. /// One of Maxwell's internal flags
  379. class InternalFlagNode final {
  380. public:
  381. explicit constexpr InternalFlagNode(InternalFlag flag) : flag{flag} {}
  382. InternalFlag GetFlag() const {
  383. return flag;
  384. }
  385. private:
  386. InternalFlag flag{};
  387. };
  388. /// A predicate register, it can be negated without additional nodes
  389. class PredicateNode final {
  390. public:
  391. explicit constexpr PredicateNode(Tegra::Shader::Pred index, bool negated)
  392. : index{index}, negated{negated} {}
  393. Tegra::Shader::Pred GetIndex() const {
  394. return index;
  395. }
  396. bool IsNegated() const {
  397. return negated;
  398. }
  399. private:
  400. Tegra::Shader::Pred index{};
  401. bool negated{};
  402. };
  403. /// Attribute buffer memory (known as attributes or varyings in GLSL terms)
  404. class AbufNode final {
  405. public:
  406. // Initialize for standard attributes (index is explicit).
  407. explicit AbufNode(Tegra::Shader::Attribute::Index index, u32 element, Node buffer = {})
  408. : buffer{std::move(buffer)}, index{index}, element{element} {}
  409. // Initialize for physical attributes (index is a variable value).
  410. explicit AbufNode(Node physical_address, Node buffer = {})
  411. : physical_address{std::move(physical_address)}, buffer{std::move(buffer)} {}
  412. Tegra::Shader::Attribute::Index GetIndex() const {
  413. return index;
  414. }
  415. u32 GetElement() const {
  416. return element;
  417. }
  418. const Node& GetBuffer() const {
  419. return buffer;
  420. }
  421. bool IsPhysicalBuffer() const {
  422. return static_cast<bool>(physical_address);
  423. }
  424. const Node& GetPhysicalAddress() const {
  425. return physical_address;
  426. }
  427. private:
  428. Node physical_address;
  429. Node buffer;
  430. Tegra::Shader::Attribute::Index index{};
  431. u32 element{};
  432. };
  433. /// Constant buffer node, usually mapped to uniform buffers in GLSL
  434. class CbufNode final {
  435. public:
  436. explicit CbufNode(u32 index, Node offset) : index{index}, offset{std::move(offset)} {}
  437. u32 GetIndex() const {
  438. return index;
  439. }
  440. const Node& GetOffset() const {
  441. return offset;
  442. }
  443. private:
  444. u32 index{};
  445. Node offset;
  446. };
  447. /// Local memory node
  448. class LmemNode final {
  449. public:
  450. explicit LmemNode(Node address) : address{std::move(address)} {}
  451. const Node& GetAddress() const {
  452. return address;
  453. }
  454. private:
  455. Node address;
  456. };
  457. /// Global memory node
  458. class GmemNode final {
  459. public:
  460. explicit GmemNode(Node real_address, Node base_address, const GlobalMemoryBase& descriptor)
  461. : real_address{std::move(real_address)}, base_address{std::move(base_address)},
  462. descriptor{descriptor} {}
  463. const Node& GetRealAddress() const {
  464. return real_address;
  465. }
  466. const Node& GetBaseAddress() const {
  467. return base_address;
  468. }
  469. const GlobalMemoryBase& GetDescriptor() const {
  470. return descriptor;
  471. }
  472. private:
  473. Node real_address;
  474. Node base_address;
  475. GlobalMemoryBase descriptor;
  476. };
  477. /// Commentary, can be dropped
  478. class CommentNode final {
  479. public:
  480. explicit CommentNode(std::string text) : text{std::move(text)} {}
  481. const std::string& GetText() const {
  482. return text;
  483. }
  484. private:
  485. std::string text;
  486. };
  487. } // namespace VideoCommon::Shader