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 }