/* * Copyright (C) 2014 Patrick Mours * SPDX-License-Identifier: BSD-3-Clause */ #pragma once #include "effect_module.hpp" #include // std::unique_ptr #include // std::find_if namespace reshadefx { /// /// A SSA code generation back-end interface for the parser to call into. /// class codegen { public: /// /// Virtual destructor to guarantee that memory of the implementations deriving from this interface is properly destroyed. /// virtual ~codegen() {} /// /// Writes result of the code generation to the specified . /// /// Target module to fill. virtual void write_result(module &module) = 0; public: /// /// An opaque ID referring to a SSA value or basic block. /// using id = uint32_t; /// /// Defines a new struct type. /// /// Source location matching this definition (for debugging). /// Description of the type. /// New SSA ID of the type. virtual id define_struct(const location &loc, struct_info &info) = 0; /// /// Defines a new texture binding. /// /// Source location matching this definition (for debugging). /// Description of the texture object. /// New SSA ID of the binding. virtual id define_texture(const location &loc, texture_info &info) = 0; /// /// Defines a new sampler binding. /// /// Source location matching this definition (for debugging). /// Description of the texture this sampler object references. /// Description of the sampler object. /// New SSA ID of the binding. virtual id define_sampler(const location &loc, const texture_info &tex_info, sampler_info &info) = 0; /// /// Defines a new storage binding. /// /// Source location matching this definition (for debugging). /// Description of the texture this storage object references. /// Description of the storage object. /// New SSA ID of the binding. virtual id define_storage(const location &loc, const texture_info &tex_info, storage_info &info) = 0; /// /// Defines a new uniform variable. /// /// Source location matching this definition (for debugging). /// Description of the uniform variable. /// New SSA ID of the variable. virtual id define_uniform(const location &loc, uniform_info &info) = 0; /// /// Defines a new variable. /// /// Source location matching this definition (for debugging). /// Data type of the variable. /// Name of the variable. /// true if this variable is in global scope, false otherwise. /// SSA ID of an optional initializer value. /// New SSA ID of the variable. virtual id define_variable(const location &loc, const type &type, std::string name = std::string(), bool global = false, id initializer_value = 0) = 0; /// /// Defines a new function and its function parameters and make it current. Any code added after this call is added to this function. /// /// Source location matching this definition (for debugging). /// Description of the function. /// New SSA ID of the function. virtual id define_function(const location &loc, function_info &info) = 0; /// /// Defines a new effect technique. /// /// Source location matching this definition (for debugging). /// Description of the technique. void define_technique(technique_info &&info) { _module.techniques.push_back(std::move(info)); } /// /// Makes a function a shader entry point. /// /// Function to use as entry point. May be overwritten to point to a new unique function for this entry point. /// Shader type (vertex, pixel or compute shader). /// Number of local threads it this is a compute entry point. virtual void define_entry_point(function_info &function, shader_type type, int num_threads[3] = nullptr) = 0; /// /// Resolves the access chain and add a load operation to the output. /// /// Access chain pointing to the variable to load from. /// Set to to force this to return a new SSA ID for l-value loads. /// New SSA ID with the loaded value. virtual id emit_load(const expression &chain, bool force_new_id = false) = 0; /// /// Resolves the access chain and add a store operation to the output. /// /// Access chain pointing to the variable to store to. /// SSA ID of the value to store. virtual void emit_store(const expression &chain, id value) = 0; /// /// Resolves the access chain, but do not add a load operation. This returns a pointer instead. /// /// Access chain pointing to the variable to resolve. /// Output value which is set to the index in the access chain up to which the access chain went. /// New SSA ID with a pointer to the value. virtual id emit_access_chain(const expression &chain, size_t &chain_index) { chain_index = chain.chain.size(); return emit_load(chain); } /// /// Creates a SSA constant value. /// /// Data type of the constant. /// Actual constant data to convert into a SSA ID. /// New SSA ID with the constant value. virtual id emit_constant(const type &type, const constant &data) = 0; /// /// Adds an unary operation to the output (built-in operation with one argument). /// /// Source location matching this operation (for debugging). /// Unary operator to use. /// Data type of the input value. /// SSA ID of value to perform the operation on. /// New SSA ID with the result of the operation. virtual id emit_unary_op(const location &loc, tokenid op, const type &type, id val) = 0; /// /// Adds a binary operation to the output (built-in operation with two arguments). /// /// Source location matching this operation (for debugging). /// Binary operator to use. /// Data type of the result. /// Data type of the input values. /// SSA ID of the value on the left-hand side of the binary operation. /// SSA ID of the value on the right-hand side of the binary operation. /// New SSA ID with the result of the operation. virtual id emit_binary_op(const location &loc, tokenid op, const type &res_type, const type &type, id lhs, id rhs) = 0; 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); } /// /// Adds a ternary operation to the output (built-in operation with three arguments). /// /// Source location matching this operation (for debugging). /// Ternary operator to use. /// Data type of the input values. /// SSA ID of the condition value of the ternary operation. /// SSA ID of the first value of the ternary operation. /// SSA ID of the second value of the ternary operation. /// New SSA ID with the result of the operation. virtual id emit_ternary_op(const location &loc, tokenid op, const type &type, id condition, id true_value, id false_value) = 0; /// /// Adds a function call to the output. /// /// Source location matching this operation (for debugging). /// SSA ID of the function to call. /// Data type of the call result. /// List of SSA IDs representing the call arguments. /// New SSA ID with the result of the function call. virtual id emit_call(const location &loc, id function, const type &res_type, const std::vector &args) = 0; /// /// Adds an intrinsic function call to the output. /// /// Source location matching this operation (for debugging). /// Intrinsic to call. /// Data type of the call result. /// List of SSA IDs representing the call arguments. /// New SSA ID with the result of the function call. virtual id emit_call_intrinsic(const location &loc, id function, const type &res_type, const std::vector &args) = 0; /// /// Adds a type constructor call to the output. /// /// Data type to construct. /// List of SSA IDs representing the scalar constructor arguments. /// New SSA ID with the constructed value. virtual id emit_construct(const location &loc, const type &type, const std::vector &args) = 0; /// /// Adds a structured branch control flow to the output. /// /// Source location matching this branch (for debugging). /// 0 - default, 1 - flatten, 2 - do not flatten 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; /// /// Adds a branch control flow with a SSA phi operation to the output. /// /// Source location matching this branch (for debugging). /// New SSA ID with the result of the phi operation. 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; /// /// Adds a structured loop control flow to the output. /// /// Source location matching this loop (for debugging). /// 0 - default, 1 - unroll, 2 - do not unroll 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; /// /// Adds a structured switch control flow to the output. /// /// Source location matching this switch (for debugging). /// 0 - default, 1 - flatten, 2 - do not flatten virtual void emit_switch(const location &loc, id selector_value, id selector_block, id default_label, id default_block, const std::vector &case_literal_and_labels, const std::vector &case_blocks, unsigned int flags) = 0; /// /// Returns if code is currently added to a basic block. /// bool is_in_block() const { return _current_block != 0; } /// /// Returns if code is currently added to a function. /// virtual bool is_in_function() const { return is_in_block(); } /// /// Creates a new basic block. /// /// New ID of the basic block. virtual id create_block() { return make_id(); } /// /// Overwrites the current block ID. /// /// ID of the block to make current. /// ID of the previous basic block. virtual id set_block(id id) = 0; /// /// Creates a new basic block and make it current. /// /// ID of the basic block to create and make current. virtual void enter_block(id id) = 0; /// /// Returns from the current basic block and kill the shader invocation. /// /// ID of the current basic block. virtual id leave_block_and_kill() = 0; /// /// Returns from the current basic block and hand control flow over to the function call side. /// /// Optional SSA ID of a return value. /// ID of the current basic block. virtual id leave_block_and_return(id value = 0) = 0; /// /// Diverges the current control flow and enter a switch. /// /// SSA ID of the selector value to decide the switch path. /// ID of the current basic block. virtual id leave_block_and_switch(id value, id default_target) = 0; /// /// Diverges the current control flow and jump to the specified target block. /// /// ID of the basic block to jump to. /// Set to if this corresponds to a loop continue statement. /// ID of the current basic block. virtual id leave_block_and_branch(id target, unsigned int loop_flow = 0) = 0; /// /// Diverges the current control flow and jump to one of the specified target blocks, depending on the condition. /// /// SSA ID of a value used to choose which path to take. /// ID of the basic block to jump to when the condition is true. /// ID of the basic block to jump to when the condition is false. /// ID of the current basic block. virtual id leave_block_and_branch_conditional(id condition, id true_target, id false_target) = 0; /// /// Leaves the current function. Any code added after this call is added in the global scope. /// virtual void leave_function() = 0; /// /// Looks up an existing struct type. /// /// SSA ID of the type to find. /// Reference to the struct description. const struct_info &get_struct(id id) const { return *std::find_if(_structs.begin(), _structs.end(), [id](const auto &it) { return it.definition == id; }); } /// /// Looks up an existing texture binding. /// /// SSA ID of the texture binding to find. /// Reference to the texture description. texture_info &get_texture(id id) { return *std::find_if(_module.textures.begin(), _module.textures.end(), [id](const auto &it) { return it.id == id; }); } /// /// Looks up an existing sampler binding. /// /// SSA ID of the sampler binding to find. /// Reference to the sampler description. const sampler_info &get_sampler(id id) const { return *std::find_if(_module.samplers.begin(), _module.samplers.end(), [id](const auto &it) { return it.id == id; }); } /// /// Looks up an existing storage binding. /// /// SSA ID of the storage binding to find. /// Reference to the storage description. const storage_info &get_storage(id id) const { return *std::find_if(_module.storages.begin(), _module.storages.end(), [id](const auto &it) { return it.id == id; }); } /// /// Looks up an existing function definition. /// /// SSA ID of the function variable to find. /// Reference to the function description. function_info &get_function(id id) { return *std::find_if(_functions.begin(), _functions.end(), [id](const auto &it) { return it->definition == id; })->get(); } protected: id make_id() { return _next_id++; } static uint32_t align_up(uint32_t size, uint32_t alignment) { alignment -= 1; return ((size + alignment) & ~alignment); } static uint32_t align_up(uint32_t size, uint32_t alignment, uint32_t elements) { return align_up(size, alignment) * (elements - 1) + size; } reshadefx::module _module; std::vector _structs; std::vector> _functions; id _next_id = 1; id _last_block = 0; id _current_block = 0; }; /// /// Creates a back-end implementation for GLSL code generation. /// /// Generate GLSL for OpenGL or for Vulkan. /// Whether to append debug information like line directives to the generated code. /// Whether to convert uniform variables to specialization constants. /// Use real 16-bit types for the minimum precision types "min16int", "min16uint" and "min16float". /// Insert code to flip the Y component of the output position in vertex shaders. codegen *create_codegen_glsl(bool vulkan_semantics, bool debug_info, bool uniforms_to_spec_constants, bool enable_16bit_types = false, bool flip_vert_y = false); /// /// Creates a back-end implementation for HLSL code generation. /// /// The HLSL shader model version (e.g. 30, 41, 50, 60, ...) /// Whether to append debug information like line directives to the generated code. /// Whether to convert uniform variables to specialization constants. codegen *create_codegen_hlsl(unsigned int shader_model, bool debug_info, bool uniforms_to_spec_constants); /// /// Creates a back-end implementation for SPIR-V code generation. /// /// Generate SPIR-V for OpenGL or for Vulkan. /// Whether to append debug information like line directives to the generated code. /// Whether to convert uniform variables to specialization constants. /// Use real 16-bit types for the minimum precision types "min16int", "min16uint" and "min16float". /// Insert code to flip the Y component of the output position in vertex shaders. 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); }