/* * 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 { friend class parser; public: /// /// Virtual destructor to guarantee that memory of the implementations deriving from this interface is properly destroyed. /// virtual ~codegen() {} /// /// Gets the module describing the generated code. /// const effect_module &module() const { return _module; } /// /// Finalizes and returns the generated code for the entire module (all entry points). /// virtual std::basic_string finalize_code() const = 0; /// /// Finalizes and returns the generated code for the specified entry point (and no other entry points). /// /// Name of the entry point function to generate code for. virtual std::basic_string finalize_code_for_entry_point(const std::string &entry_point_name) const = 0; protected: /// /// 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_type &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) = 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 &tex_info, sampler &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 &tex_info, storage &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) = 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) = 0; /// /// Defines a new effect technique. /// /// Source location matching this definition (for debugging). /// Description of the technique. void define_technique(technique &&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 uniquely generated function. virtual void define_entry_point(function &function) = 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; id emit_constant(const type &data_type, uint32_t value) { // Create a constant value of the specified type constant data = {}; // Initialize to zero, so that components not set below still have a defined value for lookup via std::memcmp for (unsigned int i = 0; i < data_type.components(); ++i) { if (data_type.is_integral()) data.as_uint[i] = value; else data.as_float[i] = static_cast(value); } return emit_constant(data_type, data); } /// /// 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. /// bool is_in_function() const { return _current_function != nullptr; } /// /// 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; /// /// Recalculates sampler and storage bindings to take as little binding space as possible for each entry point. /// virtual void optimize_bindings(); /// /// Looks up an existing struct type. /// /// SSA ID of the type to find. /// Reference to the struct description. const struct_type &get_struct(id id) const { return *std::find_if(_structs.begin(), _structs.end(), [id](const struct_type &info) { return info.id == id; }); } /// /// Looks up an existing texture binding. /// /// SSA ID of the texture binding to find. /// Reference to the texture description. texture &get_texture(id id) { return *std::find_if(_module.textures.begin(), _module.textures.end(), [id](const texture &info) { return info.id == id; }); } /// /// Looks up an existing sampler binding. /// /// SSA ID of the sampler binding to find. /// Reference to the sampler description. const sampler &get_sampler(id id) const { return *std::find_if(_module.samplers.begin(), _module.samplers.end(), [id](const sampler &info) { return info.id == id; }); } /// /// Looks up an existing storage binding. /// /// SSA ID of the storage binding to find. /// Reference to the storage description. const storage &get_storage(id id) const { return *std::find_if(_module.storages.begin(), _module.storages.end(), [id](const storage &info) { return info.id == id; }); } /// /// Looks up an existing function definition. /// /// SSA ID of the function variable to find. /// Reference to the function description. function &get_function(id id) { return *std::find_if(_functions.begin(), _functions.end(), [id](const std::unique_ptr &info) { return info->id == id; })->get(); } function &get_function(const std::string &unique_name) { return *std::find_if(_functions.begin(), _functions.end(), [&unique_name](const std::unique_ptr &info) { return info->unique_name == unique_name; })->get(); } id make_id() { return _next_id++; } effect_module _module; std::vector _structs; std::vector> _functions; id _next_id = 1; id _last_block = 0; id _current_block = 0; function *_current_function = nullptr; }; /// /// Creates a back-end implementation for GLSL code generation. /// /// GLSL version to insert at the beginning of the file. /// Generate GLSL ES code instead of core OpenGL. /// 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(unsigned version, bool gles, 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); }