duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

effect_codegen.hpp (19931B)


      1 /*
      2  * Copyright (C) 2014 Patrick Mours
      3  * SPDX-License-Identifier: BSD-3-Clause
      4  */
      5 
      6 #pragma once
      7 
      8 #include "effect_module.hpp"
      9 #include <memory> // std::unique_ptr
     10 #include <algorithm> // std::find_if
     11 
     12 namespace reshadefx
     13 {
     14 	/// <summary>
     15 	/// A SSA code generation back-end interface for the parser to call into.
     16 	/// </summary>
     17 	class codegen
     18 	{
     19 	public:
     20 		/// <summary>
     21 		/// Virtual destructor to guarantee that memory of the implementations deriving from this interface is properly destroyed.
     22 		/// </summary>
     23 		virtual ~codegen() {}
     24 
     25 		/// <summary>
     26 		/// Writes result of the code generation to the specified <paramref name="module"/>.
     27 		/// </summary>
     28 		/// <param name="module">Target module to fill.</param>
     29 		virtual void write_result(module &module) = 0;
     30 
     31 	public:
     32 		/// <summary>
     33 		/// An opaque ID referring to a SSA value or basic block.
     34 		/// </summary>
     35 		using id = uint32_t;
     36 
     37 		/// <summary>
     38 		/// Defines a new struct type.
     39 		/// </summary>
     40 		/// <param name="loc">Source location matching this definition (for debugging).</param>
     41 		/// <param name="info">Description of the type.</param>
     42 		/// <returns>New SSA ID of the type.</returns>
     43 		virtual id define_struct(const location &loc, struct_info &info) = 0;
     44 		/// <summary>
     45 		/// Defines a new texture binding.
     46 		/// </summary>
     47 		/// <param name="loc">Source location matching this definition (for debugging).</param>
     48 		/// <param name="info">Description of the texture object.</param>
     49 		/// <returns>New SSA ID of the binding.</returns>
     50 		virtual id define_texture(const location &loc, texture_info &info) = 0;
     51 		/// <summary>
     52 		/// Defines a new sampler binding.
     53 		/// </summary>
     54 		/// <param name="loc">Source location matching this definition (for debugging).</param>
     55 		/// <param name="tex_info">Description of the texture this sampler object references.</param>
     56 		/// <param name="info">Description of the sampler object.</param>
     57 		/// <returns>New SSA ID of the binding.</returns>
     58 		virtual id define_sampler(const location &loc, const texture_info &tex_info, sampler_info &info) = 0;
     59 		/// <summary>
     60 		/// Defines a new storage binding.
     61 		/// </summary>
     62 		/// <param name="loc">Source location matching this definition (for debugging).</param>
     63 		/// <param name="tex_info">Description of the texture this storage object references.</param>
     64 		/// <param name="info">Description of the storage object.</param>
     65 		/// <returns>New SSA ID of the binding.</returns>
     66 		virtual id define_storage(const location &loc, const texture_info &tex_info, storage_info &info) = 0;
     67 		/// <summary>
     68 		/// Defines a new uniform variable.
     69 		/// </summary>
     70 		/// <param name="loc">Source location matching this definition (for debugging).</param>
     71 		/// <param name="info">Description of the uniform variable.</param>
     72 		/// <returns>New SSA ID of the variable.</returns>
     73 		virtual id define_uniform(const location &loc, uniform_info &info) = 0;
     74 		/// <summary>
     75 		/// Defines a new variable.
     76 		/// </summary>
     77 		/// <param name="loc">Source location matching this definition (for debugging).</param>
     78 		/// <param name="type">Data type of the variable.</param>
     79 		/// <param name="name">Name of the variable.</param>
     80 		/// <param name="global"><c>true</c> if this variable is in global scope, <c>false</c> otherwise.</param>
     81 		/// <param name="initializer_value">SSA ID of an optional initializer value.</param>
     82 		/// <returns>New SSA ID of the variable.</returns>
     83 		virtual id define_variable(const location &loc, const type &type, std::string name = std::string(), bool global = false, id initializer_value = 0) = 0;
     84 		/// <summary>
     85 		/// Defines a new function and its function parameters and make it current. Any code added after this call is added to this function.
     86 		/// </summary>
     87 		/// <param name="loc">Source location matching this definition (for debugging).</param>
     88 		/// <param name="info">Description of the function.</param>
     89 		/// <returns>New SSA ID of the function.</returns>
     90 		virtual id define_function(const location &loc, function_info &info) = 0;
     91 
     92 		/// <summary>
     93 		/// Defines a new effect technique.
     94 		/// </summary>
     95 		/// <param name="loc">Source location matching this definition (for debugging).</param>
     96 		/// <param name="info">Description of the technique.</param>
     97 		void define_technique(technique_info &&info) { _module.techniques.push_back(std::move(info)); }
     98 		/// <summary>
     99 		/// Makes a function a shader entry point.
    100 		/// </summary>
    101 		/// <param name="function">Function to use as entry point. May be overwritten to point to a new unique function for this entry point.</param>
    102 		/// <param name="type">Shader type (vertex, pixel or compute shader).</param>
    103 		/// <param name="num_threads">Number of local threads it this is a compute entry point.</param>
    104 		virtual void define_entry_point(function_info &function, shader_type type, int num_threads[3] = nullptr) = 0;
    105 
    106 		/// <summary>
    107 		/// Resolves the access chain and add a load operation to the output.
    108 		/// </summary>
    109 		/// <param name="chain">Access chain pointing to the variable to load from.</param>
    110 		/// <param name="force_new_id">Set to <see langword="true"/> to force this to return a new SSA ID for l-value loads.</param>
    111 		/// <returns>New SSA ID with the loaded value.</returns>
    112 		virtual id emit_load(const expression &chain, bool force_new_id = false) = 0;
    113 		/// <summary>
    114 		/// Resolves the access chain and add a store operation to the output.
    115 		/// </summary>
    116 		/// <param name="chain">Access chain pointing to the variable to store to.</param>
    117 		/// <param name="value">SSA ID of the value to store.</param>
    118 		virtual void emit_store(const expression &chain, id value) = 0;
    119 		/// <summary>
    120 		/// Resolves the access chain, but do not add a load operation. This returns a pointer instead.
    121 		/// </summary>
    122 		/// <param name="chain">Access chain pointing to the variable to resolve.</param>
    123 		/// <param name="chain_index">Output value which is set to the index in the access chain up to which the access chain went.</param>
    124 		/// <returns>New SSA ID with a pointer to the value.</returns>
    125 		virtual id emit_access_chain(const expression &chain, size_t &chain_index) { chain_index = chain.chain.size(); return emit_load(chain); }
    126 
    127 		/// <summary>
    128 		/// Creates a SSA constant value.
    129 		/// </summary>
    130 		/// <param name="type">Data type of the constant.</param>
    131 		/// <param name="data">Actual constant data to convert into a SSA ID.</param>
    132 		/// <returns>New SSA ID with the constant value.</returns>
    133 		virtual id emit_constant(const type &type, const constant &data) = 0;
    134 
    135 		/// <summary>
    136 		/// Adds an unary operation to the output (built-in operation with one argument).
    137 		/// </summary>
    138 		/// <param name="loc">Source location matching this operation (for debugging).</param>
    139 		/// <param name="op">Unary operator to use.</param>
    140 		/// <param name="type">Data type of the input value.</param>
    141 		/// <param name="val">SSA ID of value to perform the operation on.</param>
    142 		/// <returns>New SSA ID with the result of the operation.</returns>
    143 		virtual id emit_unary_op(const location &loc, tokenid op, const type &type, id val) = 0;
    144 		/// <summary>
    145 		/// Adds a binary operation to the output (built-in operation with two arguments).
    146 		/// </summary>
    147 		/// <param name="loc">Source location matching this operation (for debugging).</param>
    148 		/// <param name="op">Binary operator to use.</param>
    149 		/// <param name="res_type">Data type of the result.</param>
    150 		/// <param name="type">Data type of the input values.</param>
    151 		/// <param name="lhs">SSA ID of the value on the left-hand side of the binary operation.</param>
    152 		/// <param name="rhs">SSA ID of the value on the right-hand side of the binary operation.</param>
    153 		/// <returns>New SSA ID with the result of the operation.</returns>
    154 		virtual id emit_binary_op(const location &loc, tokenid op, const type &res_type, const type &type, id lhs, id rhs) = 0;
    155 		id emit_binary_op(const location &loc, tokenid op, const type &type, id lhs, id rhs) { return emit_binary_op(loc, op, type, type, lhs, rhs); }
    156 		/// <summary>
    157 		/// Adds a ternary operation to the output (built-in operation with three arguments).
    158 		/// </summary>
    159 		/// <param name="loc">Source location matching this operation (for debugging).</param>
    160 		/// <param name="op">Ternary operator to use.</param>
    161 		/// <param name="type">Data type of the input values.</param>
    162 		/// <param name="condition">SSA ID of the condition value of the ternary operation.</param>
    163 		/// <param name="true_value">SSA ID of the first value of the ternary operation.</param>
    164 		/// <param name="false_value">SSA ID of the second value of the ternary operation.</param>
    165 		/// <returns>New SSA ID with the result of the operation.</returns>
    166 		virtual id emit_ternary_op(const location &loc, tokenid op, const type &type, id condition, id true_value, id false_value) = 0;
    167 		/// <summary>
    168 		/// Adds a function call to the output.
    169 		/// </summary>
    170 		/// <param name="loc">Source location matching this operation (for debugging).</param>
    171 		/// <param name="function">SSA ID of the function to call.</param>
    172 		/// <param name="res_type">Data type of the call result.</param>
    173 		/// <param name="args">List of SSA IDs representing the call arguments.</param>
    174 		/// <returns>New SSA ID with the result of the function call.</returns>
    175 		virtual id emit_call(const location &loc, id function, const type &res_type, const std::vector<expression> &args) = 0;
    176 		/// <summary>
    177 		/// Adds an intrinsic function call to the output.
    178 		/// </summary>
    179 		/// <param name="loc">Source location matching this operation (for debugging).</param>
    180 		/// <param name="function">Intrinsic to call.</param>
    181 		/// <param name="res_type">Data type of the call result.</param>
    182 		/// <param name="args">List of SSA IDs representing the call arguments.</param>
    183 		/// <returns>New SSA ID with the result of the function call.</returns>
    184 		virtual id emit_call_intrinsic(const location &loc, id function, const type &res_type, const std::vector<expression> &args) = 0;
    185 		/// <summary>
    186 		/// Adds a type constructor call to the output.
    187 		/// </summary>
    188 		/// <param name="type">Data type to construct.</param>
    189 		/// <param name="args">List of SSA IDs representing the scalar constructor arguments.</param>
    190 		/// <returns>New SSA ID with the constructed value.</returns>
    191 		virtual id emit_construct(const location &loc, const type &type, const std::vector<expression> &args) = 0;
    192 
    193 		/// <summary>
    194 		/// Adds a structured branch control flow to the output.
    195 		/// </summary>
    196 		/// <param name="loc">Source location matching this branch (for debugging).</param>
    197 		/// <param name="flags">0 - default, 1 - flatten, 2 - do not flatten</param>
    198 		virtual void emit_if(const location &loc, id condition_value, id condition_block, id true_statement_block, id false_statement_block, unsigned int flags) = 0;
    199 		/// <summary>
    200 		/// Adds a branch control flow with a SSA phi operation to the output.
    201 		/// </summary>
    202 		/// <param name="loc">Source location matching this branch (for debugging).</param>
    203 		/// <returns>New SSA ID with the result of the phi operation.</returns>
    204 		virtual id   emit_phi(const location &loc, id condition_value, id condition_block, id true_value, id true_statement_block, id false_value, id false_statement_block, const type &type) = 0;
    205 		/// <summary>
    206 		/// Adds a structured loop control flow to the output.
    207 		/// </summary>
    208 		/// <param name="loc">Source location matching this loop (for debugging).</param>
    209 		/// <param name="flags">0 - default, 1 - unroll, 2 - do not unroll</param>
    210 		virtual void emit_loop(const location &loc, id condition_value, id prev_block, id header_block, id condition_block, id loop_block, id continue_block, unsigned int flags) = 0;
    211 		/// <summary>
    212 		/// Adds a structured switch control flow to the output.
    213 		/// </summary>
    214 		/// <param name="loc">Source location matching this switch (for debugging).</param>
    215 		/// <param name="flags">0 - default, 1 - flatten, 2 - do not flatten</param>
    216 		virtual void emit_switch(const location &loc, id selector_value, id selector_block, id default_label, id default_block, const std::vector<id> &case_literal_and_labels, const std::vector<id> &case_blocks, unsigned int flags) = 0;
    217 
    218 		/// <summary>
    219 		/// Returns <see langword="true"/> if code is currently added to a basic block.
    220 		/// </summary>
    221 		bool is_in_block() const { return _current_block != 0; }
    222 		/// <summary>
    223 		/// Returns <see langword="true"/> if code is currently added to a function.
    224 		/// </summary>
    225 		virtual bool is_in_function() const { return is_in_block(); }
    226 
    227 		/// <summary>
    228 		/// Creates a new basic block.
    229 		/// </summary>
    230 		/// <returns>New ID of the basic block.</returns>
    231 		virtual id create_block() { return make_id(); }
    232 		/// <summary>
    233 		/// Overwrites the current block ID.
    234 		/// </summary>
    235 		/// <param name="id">ID of the block to make current.</param>
    236 		/// <returns>ID of the previous basic block.</returns>
    237 		virtual id set_block(id id) = 0;
    238 		/// <summary>
    239 		/// Creates a new basic block and make it current.
    240 		/// </summary>
    241 		/// <param name="id">ID of the basic block to create and make current.</param>
    242 		virtual void enter_block(id id) = 0;
    243 		/// <summary>
    244 		/// Returns from the current basic block and kill the shader invocation.
    245 		/// </summary>
    246 		/// <returns>ID of the current basic block.</returns>
    247 		virtual id leave_block_and_kill() = 0;
    248 		/// <summary>
    249 		/// Returns from the current basic block and hand control flow over to the function call side.
    250 		/// </summary>
    251 		/// <param name="value">Optional SSA ID of a return value.</param>
    252 		/// <returns>ID of the current basic block.</returns>
    253 		virtual id leave_block_and_return(id value = 0) = 0;
    254 		/// <summary>
    255 		/// Diverges the current control flow and enter a switch.
    256 		/// </summary>
    257 		/// <param name="value">SSA ID of the selector value to decide the switch path.</param>
    258 		/// <returns>ID of the current basic block.</returns>
    259 		virtual id leave_block_and_switch(id value, id default_target) = 0;
    260 		/// <summary>
    261 		/// Diverges the current control flow and jump to the specified target block.
    262 		/// </summary>
    263 		/// <param name="target">ID of the basic block to jump to.</param>
    264 		/// <param name="is_continue">Set to <see langword="true"/> if this corresponds to a loop continue statement.</param>
    265 		/// <returns>ID of the current basic block.</returns>
    266 		virtual id leave_block_and_branch(id target, unsigned int loop_flow = 0) = 0;
    267 		/// <summary>
    268 		/// Diverges the current control flow and jump to one of the specified target blocks, depending on the condition.
    269 		/// </summary>
    270 		/// <param name="condition">SSA ID of a value used to choose which path to take.</param>
    271 		/// <param name="true_target">ID of the basic block to jump to when the condition is true.</param>
    272 		/// <param name="false_target">ID of the basic block to jump to when the condition is false.</param>
    273 		/// <returns>ID of the current basic block.</returns>
    274 		virtual id leave_block_and_branch_conditional(id condition, id true_target, id false_target) = 0;
    275 		/// <summary>
    276 		/// Leaves the current function. Any code added after this call is added in the global scope.
    277 		/// </summary>
    278 		virtual void leave_function() = 0;
    279 
    280 		/// <summary>
    281 		/// Looks up an existing struct type.
    282 		/// </summary>
    283 		/// <param name="id">SSA ID of the type to find.</param>
    284 		/// <returns>Reference to the struct description.</returns>
    285 		const struct_info &get_struct(id id) const
    286 		{
    287 			return *std::find_if(_structs.begin(), _structs.end(),
    288 				[id](const auto &it) { return it.definition == id; });
    289 		}
    290 		/// <summary>
    291 		/// Looks up an existing texture binding.
    292 		/// </summary>
    293 		/// <param name="id">SSA ID of the texture binding to find.</param>
    294 		/// <returns>Reference to the texture description.</returns>
    295 		texture_info &get_texture(id id)
    296 		{
    297 			return *std::find_if(_module.textures.begin(), _module.textures.end(),
    298 				[id](const auto &it) { return it.id == id; });
    299 		}
    300 		/// <summary>
    301 		/// Looks up an existing sampler binding.
    302 		/// </summary>
    303 		/// <param name="id">SSA ID of the sampler binding to find.</param>
    304 		/// <returns>Reference to the sampler description.</returns>
    305 		const sampler_info &get_sampler(id id) const
    306 		{
    307 			return *std::find_if(_module.samplers.begin(), _module.samplers.end(),
    308 				[id](const auto &it) { return it.id == id; });
    309 		}
    310 		/// <summary>
    311 		/// Looks up an existing storage binding.
    312 		/// </summary>
    313 		/// <param name="id">SSA ID of the storage binding to find.</param>
    314 		/// <returns>Reference to the storage description.</returns>
    315 		const storage_info &get_storage(id id) const
    316 		{
    317 			return *std::find_if(_module.storages.begin(), _module.storages.end(),
    318 				[id](const auto &it) { return it.id == id; });
    319 		}
    320 		/// <summary>
    321 		/// Looks up an existing function definition.
    322 		/// </summary>
    323 		/// <param name="id">SSA ID of the function variable to find.</param>
    324 		/// <returns>Reference to the function description.</returns>
    325 		function_info &get_function(id id)
    326 		{
    327 			return *std::find_if(_functions.begin(), _functions.end(),
    328 				[id](const auto &it) { return it->definition == id; })->get();
    329 		}
    330 
    331 	protected:
    332 		id make_id() { return _next_id++; }
    333 
    334 		static uint32_t align_up(uint32_t size, uint32_t alignment)
    335 		{
    336 			alignment -= 1;
    337 			return ((size + alignment) & ~alignment);
    338 		}
    339 		static uint32_t align_up(uint32_t size, uint32_t alignment, uint32_t elements)
    340 		{
    341 			return align_up(size, alignment) * (elements - 1) + size;
    342 		}
    343 
    344 		reshadefx::module _module;
    345 		std::vector<struct_info> _structs;
    346 		std::vector<std::unique_ptr<function_info>> _functions;
    347 		id _next_id = 1;
    348 		id _last_block = 0;
    349 		id _current_block = 0;
    350 	};
    351 
    352 	/// <summary>
    353 	/// Creates a back-end implementation for GLSL code generation.
    354 	/// </summary>
    355 	/// <param name="gles">Generate GLSL ES code instead of core OpenGL.</param>
    356 	/// <param name="vulkan_semantics">Generate GLSL for OpenGL or for Vulkan.</param>
    357 	/// <param name="debug_info">Whether to append debug information like line directives to the generated code.</param>
    358 	/// <param name="uniforms_to_spec_constants">Whether to convert uniform variables to specialization constants.</param>
    359 	/// <param name="enable_16bit_types">Use real 16-bit types for the minimum precision types "min16int", "min16uint" and "min16float".</param>
    360 	/// <param name="flip_vert_y">Insert code to flip the Y component of the output position in vertex shaders.</param>
    361 	codegen *create_codegen_glsl(bool gles, bool vulkan_semantics, bool debug_info, bool uniforms_to_spec_constants, bool enable_16bit_types = false, bool flip_vert_y = false);
    362 	/// <summary>
    363 	/// Creates a back-end implementation for HLSL code generation.
    364 	/// </summary>
    365 	/// <param name="shader_model">The HLSL shader model version (e.g. 30, 41, 50, 60, ...)</param>
    366 	/// <param name="debug_info">Whether to append debug information like line directives to the generated code.</param>
    367 	/// <param name="uniforms_to_spec_constants">Whether to convert uniform variables to specialization constants.</param>
    368 	codegen *create_codegen_hlsl(unsigned int shader_model, bool debug_info, bool uniforms_to_spec_constants);
    369 	/// <summary>
    370 	/// Creates a back-end implementation for SPIR-V code generation.
    371 	/// </summary>
    372 	/// <param name="vulkan_semantics">Generate SPIR-V for OpenGL or for Vulkan.</param>
    373 	/// <param name="debug_info">Whether to append debug information like line directives to the generated code.</param>
    374 	/// <param name="uniforms_to_spec_constants">Whether to convert uniform variables to specialization constants.</param>
    375 	/// <param name="enable_16bit_types">Use real 16-bit types for the minimum precision types "min16int", "min16uint" and "min16float".</param>
    376 	/// <param name="flip_vert_y">Insert code to flip the Y component of the output position in vertex shaders.</param>
    377 	codegen *create_codegen_spirv(bool vulkan_semantics, bool debug_info, bool uniforms_to_spec_constants, bool enable_16bit_types = false, bool flip_vert_y = false);
    378 }