dep/reshadefx: Update to 7bdfb03

This commit is contained in:
Stenzek
2024-09-08 19:19:58 +10:00
parent 3078339f64
commit 86bff869bc
25 changed files with 6041 additions and 3244 deletions

View File

@ -16,6 +16,8 @@ namespace reshadefx
/// </summary>
class codegen
{
friend class parser;
public:
/// <summary>
/// Virtual destructor to guarantee that memory of the implementations deriving from this interface is properly destroyed.
@ -23,12 +25,21 @@ namespace reshadefx
virtual ~codegen() {}
/// <summary>
/// Writes result of the code generation to the specified <paramref name="module"/>.
/// Gets the module describing the generated code.
/// </summary>
/// <param name="module">Target module to fill.</param>
virtual void write_result(module &module) = 0;
const effect_module &module() const { return _module; }
public:
/// <summary>
/// Finalizes and returns the generated code for the entire module (all entry points).
/// </summary>
virtual std::basic_string<char> finalize_code() const = 0;
/// <summary>
/// Finalizes and returns the generated code for the specified entry point (and no other entry points).
/// </summary>
/// <param name="entry_point_name">Name of the entry point function to generate code for.</param>
virtual std::basic_string<char> finalize_code_for_entry_point(const std::string &entry_point_name) const = 0;
protected:
/// <summary>
/// An opaque ID referring to a SSA value or basic block.
/// </summary>
@ -40,14 +51,14 @@ namespace reshadefx
/// <param name="loc">Source location matching this definition (for debugging).</param>
/// <param name="info">Description of the type.</param>
/// <returns>New SSA ID of the type.</returns>
virtual id define_struct(const location &loc, struct_info &info) = 0;
virtual id define_struct(const location &loc, struct_type &info) = 0;
/// <summary>
/// Defines a new texture binding.
/// </summary>
/// <param name="loc">Source location matching this definition (for debugging).</param>
/// <param name="info">Description of the texture object.</param>
/// <returns>New SSA ID of the binding.</returns>
virtual id define_texture(const location &loc, texture_info &info) = 0;
virtual id define_texture(const location &loc, texture &info) = 0;
/// <summary>
/// Defines a new sampler binding.
/// </summary>
@ -55,7 +66,7 @@ namespace reshadefx
/// <param name="tex_info">Description of the texture this sampler object references.</param>
/// <param name="info">Description of the sampler object.</param>
/// <returns>New SSA ID of the binding.</returns>
virtual id define_sampler(const location &loc, const texture_info &tex_info, sampler_info &info) = 0;
virtual id define_sampler(const location &loc, const texture &tex_info, sampler &info) = 0;
/// <summary>
/// Defines a new storage binding.
/// </summary>
@ -63,14 +74,14 @@ namespace reshadefx
/// <param name="tex_info">Description of the texture this storage object references.</param>
/// <param name="info">Description of the storage object.</param>
/// <returns>New SSA ID of the binding.</returns>
virtual id define_storage(const location &loc, const texture_info &tex_info, storage_info &info) = 0;
virtual id define_storage(const location &loc, const texture &tex_info, storage &info) = 0;
/// <summary>
/// Defines a new uniform variable.
/// </summary>
/// <param name="loc">Source location matching this definition (for debugging).</param>
/// <param name="info">Description of the uniform variable.</param>
/// <returns>New SSA ID of the variable.</returns>
virtual id define_uniform(const location &loc, uniform_info &info) = 0;
virtual id define_uniform(const location &loc, uniform &info) = 0;
/// <summary>
/// Defines a new variable.
/// </summary>
@ -82,26 +93,25 @@ namespace reshadefx
/// <returns>New SSA ID of the variable.</returns>
virtual id define_variable(const location &loc, const type &type, std::string name = std::string(), bool global = false, id initializer_value = 0) = 0;
/// <summary>
/// Defines a new function and its function parameters and make it current. Any code added after this call is added to this function.
/// Defines a new function and its function parameters and make it current.
/// Any code added after this call is added to this function.
/// </summary>
/// <param name="loc">Source location matching this definition (for debugging).</param>
/// <param name="info">Description of the function.</param>
/// <returns>New SSA ID of the function.</returns>
virtual id define_function(const location &loc, function_info &info) = 0;
virtual id define_function(const location &loc, function &info) = 0;
/// <summary>
/// Defines a new effect technique.
/// </summary>
/// <param name="loc">Source location matching this definition (for debugging).</param>
/// <param name="info">Description of the technique.</param>
void define_technique(technique_info &&info) { _module.techniques.push_back(std::move(info)); }
void define_technique(technique &&info) { _module.techniques.push_back(std::move(info)); }
/// <summary>
/// Makes a function a shader entry point.
/// </summary>
/// <param name="function">Function to use as entry point. May be overwritten to point to a new unique function for this entry point.</param>
/// <param name="type">Shader type (vertex, pixel or compute shader).</param>
/// <param name="num_threads">Number of local threads it this is a compute entry point.</param>
virtual void define_entry_point(function_info &function, shader_type type, int num_threads[3] = nullptr) = 0;
/// <param name="function">Function to use as entry point. May be overwritten to point to a new uniquely generated function.</param>
virtual void define_entry_point(function &function) = 0;
/// <summary>
/// Resolves the access chain and add a load operation to the output.
@ -131,6 +141,19 @@ namespace reshadefx
/// <param name="data">Actual constant data to convert into a SSA ID.</param>
/// <returns>New SSA ID with the constant value.</returns>
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<float>(value);
}
return emit_constant(data_type, data);
}
/// <summary>
/// Adds an unary operation to the output (built-in operation with one argument).
@ -222,7 +245,7 @@ namespace reshadefx
/// <summary>
/// Returns <see langword="true"/> if code is currently added to a function.
/// </summary>
virtual bool is_in_function() const { return is_in_block(); }
bool is_in_function() const { return _current_function != nullptr; }
/// <summary>
/// Creates a new basic block.
@ -272,93 +295,96 @@ namespace reshadefx
/// <param name="false_target">ID of the basic block to jump to when the condition is false.</param>
/// <returns>ID of the current basic block.</returns>
virtual id leave_block_and_branch_conditional(id condition, id true_target, id false_target) = 0;
/// <summary>
/// Leaves the current function. Any code added after this call is added in the global scope.
/// </summary>
virtual void leave_function() = 0;
/// <summary>
/// Recalculates sampler and storage bindings to take as little binding space as possible for each entry point.
/// </summary>
virtual void optimize_bindings();
/// <summary>
/// Looks up an existing struct type.
/// </summary>
/// <param name="id">SSA ID of the type to find.</param>
/// <returns>Reference to the struct description.</returns>
const struct_info &get_struct(id id) const
const struct_type &get_struct(id id) const
{
return *std::find_if(_structs.begin(), _structs.end(),
[id](const auto &it) { return it.definition == id; });
[id](const struct_type &info) { return info.id == id; });
}
/// <summary>
/// Looks up an existing texture binding.
/// </summary>
/// <param name="id">SSA ID of the texture binding to find.</param>
/// <returns>Reference to the texture description.</returns>
texture_info &get_texture(id id)
texture &get_texture(id id)
{
return *std::find_if(_module.textures.begin(), _module.textures.end(),
[id](const auto &it) { return it.id == id; });
[id](const texture &info) { return info.id == id; });
}
/// <summary>
/// Looks up an existing sampler binding.
/// </summary>
/// <param name="id">SSA ID of the sampler binding to find.</param>
/// <returns>Reference to the sampler description.</returns>
const sampler_info &get_sampler(id id) const
const sampler &get_sampler(id id) const
{
return *std::find_if(_module.samplers.begin(), _module.samplers.end(),
[id](const auto &it) { return it.id == id; });
[id](const sampler &info) { return info.id == id; });
}
/// <summary>
/// Looks up an existing storage binding.
/// </summary>
/// <param name="id">SSA ID of the storage binding to find.</param>
/// <returns>Reference to the storage description.</returns>
const storage_info &get_storage(id id) const
const storage &get_storage(id id) const
{
return *std::find_if(_module.storages.begin(), _module.storages.end(),
[id](const auto &it) { return it.id == id; });
[id](const storage &info) { return info.id == id; });
}
/// <summary>
/// Looks up an existing function definition.
/// </summary>
/// <param name="id">SSA ID of the function variable to find.</param>
/// <returns>Reference to the function description.</returns>
function_info &get_function(id id)
function &get_function(id id)
{
return *std::find_if(_functions.begin(), _functions.end(),
[id](const auto &it) { return it->definition == id; })->get();
[id](const std::unique_ptr<function> &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<function> &info) { return info->unique_name == unique_name; })->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;
}
effect_module _module;
std::vector<struct_type> _structs;
std::vector<std::unique_ptr<function>> _functions;
reshadefx::module _module;
std::vector<struct_info> _structs;
std::vector<std::unique_ptr<function_info>> _functions;
id _next_id = 1;
id _last_block = 0;
id _current_block = 0;
function *_current_function = nullptr;
};
/// <summary>
/// Creates a back-end implementation for GLSL code generation.
/// </summary>
/// <param name="version">GLSL version to insert at the beginning of the file.</param>
/// <param name="gles">Generate GLSL ES code instead of core OpenGL.</param>
/// <param name="vulkan_semantics">Generate GLSL for OpenGL or for Vulkan.</param>
/// <param name="debug_info">Whether to append debug information like line directives to the generated code.</param>
/// <param name="uniforms_to_spec_constants">Whether to convert uniform variables to specialization constants.</param>
/// <param name="enable_16bit_types">Use real 16-bit types for the minimum precision types "min16int", "min16uint" and "min16float".</param>
/// <param name="flip_vert_y">Insert code to flip the Y component of the output position in vertex shaders.</param>
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);
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);
/// <summary>
/// Creates a back-end implementation for HLSL code generation.
/// </summary>

View File

@ -15,7 +15,7 @@ namespace reshadefx
/// </summary>
struct type
{
enum datatype : uint8_t
enum datatype : uint32_t
{
t_void,
t_bool,
@ -101,6 +101,8 @@ namespace reshadefx
bool is_function() const { return base == t_function; }
bool is_array() const { return array_length != 0; }
bool is_bounded_array() const { return is_array() && array_length != 0xFFFFFFFF; }
bool is_unbounded_array() const { return array_length == 0xFFFFFFFF; }
bool is_scalar() const { return is_numeric() && !is_matrix() && !is_vector() && !is_array(); }
bool is_vector() const { return is_numeric() && rows > 1 && cols == 1; }
bool is_matrix() const { return is_numeric() && rows >= 1 && cols > 1; }
@ -109,27 +111,27 @@ namespace reshadefx
unsigned int components() const { return rows * cols; }
unsigned int texture_dimension() const { return base >= t_texture1d && base <= t_storage3d_float ? ((base - t_texture1d) % 3) + 1 : 0; }
friend inline bool operator==(const type &lhs, const type &rhs)
friend bool operator==(const type &lhs, const type &rhs)
{
return lhs.base == rhs.base && lhs.rows == rhs.rows && lhs.cols == rhs.cols && lhs.array_length == rhs.array_length && lhs.definition == rhs.definition;
return lhs.base == rhs.base && lhs.rows == rhs.rows && lhs.cols == rhs.cols && lhs.array_length == rhs.array_length && lhs.struct_definition == rhs.struct_definition;
}
friend inline bool operator!=(const type &lhs, const type &rhs)
friend bool operator!=(const type &lhs, const type &rhs)
{
return !operator==(lhs, rhs);
}
// Underlying base type ('int', 'float', ...)
datatype base = t_void;
datatype base : 8;
// Number of rows if this is a vector type
unsigned int rows = 0;
uint32_t rows : 4;
// Number of columns if this is a matrix type
unsigned int cols = 0;
uint32_t cols : 4;
// Bit mask of all the qualifiers decorating the type
unsigned int qualifiers = 0;
// Negative if an unsized array, otherwise the number of elements if this is an array type
int array_length = 0;
uint32_t qualifiers : 16;
// Number of elements if this is an array type, 0xFFFFFFFF if it is an unsized array
uint32_t array_length;
// ID of the matching struct if this is a struct type
uint32_t definition = 0;
uint32_t struct_definition;
};
/// <summary>
@ -168,8 +170,8 @@ namespace reshadefx
op_type op;
reshadefx::type from, to;
uint32_t index = 0;
signed char swizzle[4] = {};
uint32_t index;
signed char swizzle[4];
};
uint32_t base = 0;

View File

@ -7,14 +7,48 @@
#include "effect_expression.hpp"
#include <cstdint>
#include <unordered_set>
namespace reshadefx
{
/// <summary>
/// A list of supported texture types.
/// Describes an annotation attached to a variable.
/// </summary>
enum class texture_type
struct annotation
{
reshadefx::type type = {};
std::string name;
reshadefx::constant value = {};
};
/// <summary>
/// Describes a struct member or parameter.
/// </summary>
struct member_type
{
reshadefx::type type = {};
uint32_t id = 0;
std::string name;
std::string semantic;
reshadefx::location location;
bool has_default_value = false;
reshadefx::constant default_value = {};
};
/// <summary>
/// Describes a struct type defined in effect code.
/// </summary>
struct struct_type
{
uint32_t id = 0;
std::string name;
std::string unique_name;
std::vector<member_type> member_list;
};
/// <summary>
/// Available texture types.
/// </summary>
enum class texture_type : uint8_t
{
texture_1d = 1,
texture_2d = 2,
@ -22,9 +56,9 @@ namespace reshadefx
};
/// <summary>
/// A list of supported texture formats.
/// Available texture formats.
/// </summary>
enum class texture_format
enum class texture_format : uint8_t
{
unknown,
@ -46,9 +80,46 @@ namespace reshadefx
};
/// <summary>
/// A filtering type used for texture lookups.
/// Describes the properties of a <see cref="texture"/> object.
/// </summary>
enum class filter_mode
struct texture_desc
{
uint32_t width = 1;
uint32_t height = 1;
uint16_t depth = 1;
uint16_t levels = 1;
texture_type type = texture_type::texture_2d;
texture_format format = texture_format::rgba8;
};
/// <summary>
/// Describes a texture object defined in effect code.
/// </summary>
struct texture : texture_desc
{
uint32_t id = 0;
std::string name;
std::string unique_name;
std::string semantic;
std::vector<annotation> annotations;
bool render_target = false;
bool storage_access = false;
};
/// <summary>
/// Describes the binding of a <see cref="texture"/> object.
/// </summary>
struct texture_binding
{
uint32_t binding = 0;
std::string texture_name;
bool srgb = false;
};
/// <summary>
/// Texture filtering modes available for texture sampling operations.
/// </summary>
enum class filter_mode : uint8_t
{
min_mag_mip_point = 0,
min_mag_point_mip_linear = 0x1,
@ -57,13 +128,14 @@ namespace reshadefx
min_linear_mag_mip_point = 0x10,
min_linear_mag_point_mip_linear = 0x11,
min_mag_linear_mip_point = 0x14,
min_mag_mip_linear = 0x15
min_mag_mip_linear = 0x15,
anisotropic = 0x55
};
/// <summary>
/// Specifies behavior of sampling with texture coordinates outside an image.
/// Sampling behavior at texture coordinates outside the bounds of a texture resource.
/// </summary>
enum class texture_address_mode
enum class texture_address_mode : uint8_t
{
wrap = 1,
mirror = 2,
@ -72,9 +144,117 @@ namespace reshadefx
};
/// <summary>
/// Specifies RGB or alpha blending operations.
/// Describes the properties of a <see cref="sampler"/> object.
/// </summary>
enum class pass_blend_op : uint8_t
struct sampler_desc
{
filter_mode filter = filter_mode::min_mag_mip_linear;
texture_address_mode address_u = texture_address_mode::clamp;
texture_address_mode address_v = texture_address_mode::clamp;
texture_address_mode address_w = texture_address_mode::clamp;
float min_lod = -3.402823466e+38f;
float max_lod = +3.402823466e+38f; // FLT_MAX
float lod_bias = 0.0f;
};
/// <summary>
/// Describes a texture sampler object defined in effect code.
/// </summary>
struct sampler : sampler_desc
{
reshadefx::type type = {};
uint32_t id = 0;
std::string name;
std::string unique_name;
std::string texture_name;
std::vector<annotation> annotations;
bool srgb = false;
};
/// <summary>
/// Describes the binding of a <see cref="sampler"/> object.
/// </summary>
struct sampler_binding : sampler_desc
{
uint32_t binding = 0;
};
/// <summary>
/// Describes the properties of a <see cref="storage"/> object.
/// </summary>
struct storage_desc
{
uint16_t level = 0;
};
/// <summary>
/// Describes a texture storage object defined in effect code.
/// </summary>
struct storage : storage_desc
{
reshadefx::type type = {};
uint32_t id = 0;
std::string name;
std::string unique_name;
std::string texture_name;
};
/// <summary>
/// Describes the binding of a <see cref="storage"/> object.
/// </summary>
struct storage_binding : storage_desc
{
uint32_t binding = 0;
std::string texture_name;
};
/// <summary>
/// Describes a uniform variable defined in effect code.
/// </summary>
struct uniform
{
reshadefx::type type = {};
std::string name;
uint32_t size = 0;
uint32_t offset = 0;
std::vector<annotation> annotations;
bool has_initializer_value = false;
reshadefx::constant initializer_value = {};
};
/// <summary>
/// Type of a shader entry point.
/// </summary>
enum class shader_type
{
unknown,
vertex,
pixel,
compute
};
/// <summary>
/// Describes a function defined in effect code.
/// </summary>
struct function
{
reshadefx::type return_type = {};
uint32_t id = 0;
std::string name;
std::string unique_name;
std::string return_semantic;
std::vector<member_type> parameter_list;
shader_type type = shader_type::unknown;
int num_threads[3] = {};
std::vector<uint32_t> referenced_samplers;
std::vector<uint32_t> referenced_storages;
std::vector<uint32_t> referenced_functions;
};
/// <summary>
/// Color or alpha blending operations.
/// </summary>
enum class blend_op : uint8_t
{
add = 1,
subtract,
@ -84,9 +264,9 @@ namespace reshadefx
};
/// <summary>
/// Specifies blend factors, which modulate values between the pixel shader output and render target.
/// Blend factors in color or alpha blending operations, which modulate values between the pixel shader output and render target.
/// </summary>
enum class pass_blend_factor : uint8_t
enum class blend_factor : uint8_t
{
zero = 0,
one = 1,
@ -101,9 +281,9 @@ namespace reshadefx
};
/// <summary>
/// Specifies the stencil operations that can be performed during depth-stencil testing.
/// Stencil operations that can be performed during depth-stencil testing.
/// </summary>
enum class pass_stencil_op : uint8_t
enum class stencil_op : uint8_t
{
zero = 0,
keep,
@ -116,9 +296,9 @@ namespace reshadefx
};
/// <summary>
/// Specifies comparison options for depth-stencil testing.
/// Comparison operations for depth-stencil testing.
/// </summary>
enum class pass_stencil_func : uint8_t
enum class stencil_func : uint8_t
{
never,
less,
@ -143,205 +323,70 @@ namespace reshadefx
};
/// <summary>
/// A struct type defined in the effect code.
/// Describes a render pass with all its state info.
/// </summary>
struct struct_info
{
std::string name;
std::string unique_name;
std::vector<struct struct_member_info> member_list;
uint32_t definition = 0;
};
/// <summary>
/// A struct field defined in the effect code.
/// </summary>
struct struct_member_info
{
reshadefx::type type;
std::string name;
std::string semantic;
reshadefx::location location;
uint32_t definition = 0;
};
/// <summary>
/// An annotation attached to a variable.
/// </summary>
struct annotation
{
reshadefx::type type;
std::string name;
reshadefx::constant value;
};
/// <summary>
/// A texture defined in the effect code.
/// </summary>
struct texture_info
{
uint32_t id = 0;
uint32_t binding = 0;
std::string name;
std::string semantic;
std::string unique_name;
std::vector<annotation> annotations;
texture_type type = texture_type::texture_2d;
uint32_t width = 1;
uint32_t height = 1;
uint16_t depth = 1;
uint16_t levels = 1;
texture_format format = texture_format::rgba8;
bool render_target = false;
bool storage_access = false;
};
/// <summary>
/// A texture sampler defined in the effect code.
/// </summary>
struct sampler_info
{
uint32_t id = 0;
uint32_t binding = 0;
uint32_t texture_binding = 0;
std::string name;
reshadefx::type type;
std::string unique_name;
std::string texture_name;
std::vector<annotation> annotations;
filter_mode filter = filter_mode::min_mag_mip_linear;
texture_address_mode address_u = texture_address_mode::clamp;
texture_address_mode address_v = texture_address_mode::clamp;
texture_address_mode address_w = texture_address_mode::clamp;
float min_lod = -3.402823466e+38f;
float max_lod = +3.402823466e+38f; // FLT_MAX
float lod_bias = 0.0f;
uint8_t srgb = false;
};
/// <summary>
/// A texture storage object defined in the effect code.
/// </summary>
struct storage_info
{
uint32_t id = 0;
uint32_t binding = 0;
std::string name;
reshadefx::type type;
std::string unique_name;
std::string texture_name;
uint16_t level = 0;
};
/// <summary>
/// An uniform variable defined in the effect code.
/// </summary>
struct uniform_info
{
std::string name;
reshadefx::type type;
uint32_t size = 0;
uint32_t offset = 0;
std::vector<annotation> annotations;
bool has_initializer_value = false;
reshadefx::constant initializer_value;
};
/// <summary>
/// Type of a shader entry point.
/// </summary>
enum class shader_type
{
vs,
ps,
cs,
};
/// <summary>
/// A shader entry point function.
/// </summary>
struct entry_point
{
std::string name;
shader_type type;
};
/// <summary>
/// A function defined in the effect code.
/// </summary>
struct function_info
{
uint32_t definition;
std::string name;
std::string unique_name;
reshadefx::type return_type;
std::string return_semantic;
std::vector<struct_member_info> parameter_list;
std::unordered_set<uint32_t> referenced_samplers;
std::unordered_set<uint32_t> referenced_storages;
};
/// <summary>
/// A render pass with all its state info.
/// </summary>
struct pass_info
struct pass
{
std::string name;
std::string render_target_names[8] = {};
std::string vs_entry_point;
std::string ps_entry_point;
std::string cs_entry_point;
uint8_t generate_mipmaps = true;
uint8_t clear_render_targets = false;
uint8_t srgb_write_enable = false;
uint8_t blend_enable[8] = { false, false, false, false, false, false, false, false };
uint8_t stencil_enable = false;
uint8_t color_write_mask[8] = { 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF };
bool generate_mipmaps = true;
bool clear_render_targets = false;
bool blend_enable[8] = { false, false, false, false, false, false, false, false };
blend_factor source_color_blend_factor[8] = { blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one };
blend_factor dest_color_blend_factor[8] = { blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero };
blend_op color_blend_op[8] = { blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add };
blend_factor source_alpha_blend_factor[8] = { blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one, blend_factor::one };
blend_factor dest_alpha_blend_factor[8] = { blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero, blend_factor::zero };
blend_op alpha_blend_op[8] = { blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add, blend_op::add };
bool srgb_write_enable = false;
uint8_t render_target_write_mask[8] = { 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF };
bool stencil_enable = false;
uint8_t stencil_read_mask = 0xFF;
uint8_t stencil_write_mask = 0xFF;
pass_blend_op blend_op[8] = { pass_blend_op::add, pass_blend_op::add, pass_blend_op::add, pass_blend_op::add, pass_blend_op::add, pass_blend_op::add, pass_blend_op::add, pass_blend_op::add };
pass_blend_op blend_op_alpha[8] = { pass_blend_op::add, pass_blend_op::add, pass_blend_op::add, pass_blend_op::add, pass_blend_op::add, pass_blend_op::add, pass_blend_op::add, pass_blend_op::add };
pass_blend_factor src_blend[8] = { pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one };
pass_blend_factor dest_blend[8] = { pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero };
pass_blend_factor src_blend_alpha[8] = { pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one, pass_blend_factor::one };
pass_blend_factor dest_blend_alpha[8] = { pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero, pass_blend_factor::zero };
pass_stencil_func stencil_comparison_func = pass_stencil_func::always;
uint32_t stencil_reference_value = 0;
pass_stencil_op stencil_op_pass = pass_stencil_op::keep;
pass_stencil_op stencil_op_fail = pass_stencil_op::keep;
pass_stencil_op stencil_op_depth_fail = pass_stencil_op::keep;
uint32_t num_vertices = 3;
stencil_func stencil_comparison_func = stencil_func::always;
stencil_op stencil_pass_op = stencil_op::keep;
stencil_op stencil_fail_op = stencil_op::keep;
stencil_op stencil_depth_fail_op = stencil_op::keep;
primitive_topology topology = primitive_topology::triangle_list;
uint32_t stencil_reference_value = 0;
uint32_t num_vertices = 3;
uint32_t viewport_width = 0;
uint32_t viewport_height = 0;
uint32_t viewport_dispatch_z = 1;
std::vector<sampler_info> samplers;
std::vector<storage_info> storages;
// Bindings specific for the code generation target (in case of combined texture and sampler, 'texture_bindings' and 'sampler_bindings' will be the same size and point to the same bindings, otherwise they are independent)
std::vector<texture_binding> texture_bindings;
std::vector<sampler_binding> sampler_bindings;
std::vector<storage_binding> storage_bindings;
};
/// <summary>
/// A collection of passes that make up an effect.
/// </summary>
struct technique_info
struct technique
{
std::string name;
std::vector<pass_info> passes;
std::vector<pass> passes;
std::vector<annotation> annotations;
};
/// <summary>
/// In-memory representation of an effect file.
/// </summary>
struct module
struct effect_module
{
std::vector<char> code;
std::vector<std::pair<std::string, shader_type>> entry_points;
std::vector<entry_point> entry_points;
std::vector<texture_info> textures;
std::vector<sampler_info> samplers;
std::vector<storage_info> storages;
std::vector<uniform_info> uniforms, spec_constants;
std::vector<technique_info> techniques;
std::vector<texture> textures;
std::vector<sampler> samplers;
std::vector<storage> storages;
std::vector<uniform> uniforms;
std::vector<uniform> spec_constants;
std::vector<technique> techniques;
uint32_t total_uniform_size = 0;
uint32_t num_texture_bindings = 0;

View File

@ -58,14 +58,14 @@ namespace reshadefx
bool peek_multary_op(unsigned int &precedence) const;
bool accept_assignment_op();
void parse_top(bool &parse_success);
bool parse_top(bool &parse_success);
bool parse_struct();
bool parse_function(type type, std::string name);
bool parse_function(type type, std::string name, shader_type stype, int num_threads[3]);
bool parse_variable(type type, std::string name, bool global = false);
bool parse_technique();
bool parse_technique_pass(pass_info &info);
bool parse_technique_pass(pass &info);
bool parse_type(type &type);
bool parse_array_size(type &type);
bool parse_array_length(type &type);
bool parse_expression(expression &expression);
bool parse_expression_unary(expression &expression);
bool parse_expression_multary(expression &expression, unsigned int precedence = 0);
@ -74,15 +74,16 @@ namespace reshadefx
bool parse_statement(bool scoped);
bool parse_statement_block(bool scoped);
codegen *_codegen = nullptr;
std::string _errors;
token _token, _token_next, _token_backup;
std::unique_ptr<class lexer> _lexer;
size_t _lexer_backup_offset = 0;
class codegen *_codegen = nullptr;
token _token;
token _token_next;
token _token_backup;
std::vector<uint32_t> _loop_break_target_stack;
std::vector<uint32_t> _loop_continue_target_stack;
reshadefx::function_info *_current_function = nullptr;
};
}

View File

@ -154,17 +154,16 @@ namespace reshadefx
void expand_macro(const std::string &name, const macro &macro, const std::vector<std::string> &arguments);
void create_macro_replacement_list(macro &macro);
bool _success = true;
include_file_exists_callback _file_exists_cb;
include_read_file_callback _read_file_cb;
std::string _output, _errors;
std::string _current_token_raw_data;
reshadefx::token _token;
location _output_location;
std::vector<input_level> _input_stack;
size_t _next_input_index = 0;
size_t _current_input_index = 0;
reshadefx::token _token;
std::string _current_token_raw_data;
reshadefx::location _output_location;
std::vector<if_level> _if_stack;

View File

@ -42,7 +42,7 @@ namespace reshadefx
uint32_t id = 0;
reshadefx::type type = {};
reshadefx::constant constant = {};
const reshadefx::function_info *function = nullptr;
const reshadefx::function *function = nullptr;
};
struct scoped_symbol : symbol
{

View File

@ -8,6 +8,7 @@
#include <cstdint>
#include <string>
#include <vector>
#include <cstdint>
namespace reshadefx
{
@ -246,7 +247,7 @@ namespace reshadefx
};
std::string literal_as_string;
inline operator tokenid() const { return id; }
operator tokenid() const { return id; }
static std::string id_to_name(tokenid id);
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,16 +3,16 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "effect_lexer.hpp"
#include "effect_codegen.hpp"
#include <cmath> // fmod
#include "effect_expression.hpp"
#include <cmath> // std::fmod
#include <cassert>
#include <cstring> // memcpy, memset
#include <algorithm> // std::min, std::max
#include <cstring> // std::memcpy, std::memset
#include <algorithm> // std::max, std::min
reshadefx::type reshadefx::type::merge(const type &lhs, const type &rhs)
{
type result = { std::max(lhs.base, rhs.base) };
type result;
result.base = std::max(lhs.base, rhs.base);
// Non-numeric types cannot be vectors or matrices
if (!result.is_numeric())
@ -35,11 +35,14 @@ reshadefx::type reshadefx::type::merge(const type &lhs, const type &rhs)
// Some qualifiers propagate to the result
result.qualifiers = (lhs.qualifiers & type::q_precise) | (rhs.qualifiers & type::q_precise);
// In case this is a structure, assume they are the same
result.definition = rhs.definition;
assert(lhs.definition == rhs.definition || lhs.definition == 0);
// Cannot merge array types, assume no arrays
result.array_length = 0;
assert(lhs.array_length == 0 && rhs.array_length == 0);
// In case this is a structure, assume they are the same
result.struct_definition = rhs.struct_definition;
assert(lhs.struct_definition == rhs.struct_definition || lhs.struct_definition == 0);
return result;
}
@ -48,101 +51,101 @@ std::string reshadefx::type::description() const
std::string result;
switch (base)
{
case reshadefx::type::t_void:
case t_void:
result = "void";
break;
case reshadefx::type::t_bool:
case t_bool:
result = "bool";
break;
case reshadefx::type::t_min16int:
case t_min16int:
result = "min16int";
break;
case reshadefx::type::t_int:
case t_int:
result = "int";
break;
case reshadefx::type::t_min16uint:
case t_min16uint:
result = "min16uint";
break;
case reshadefx::type::t_uint:
case t_uint:
result = "uint";
break;
case reshadefx::type::t_min16float:
case t_min16float:
result = "min16float";
break;
case reshadefx::type::t_float:
case t_float:
result = "float";
break;
case reshadefx::type::t_string:
case t_string:
result = "string";
break;
case reshadefx::type::t_struct:
case t_struct:
result = "struct";
break;
case reshadefx::type::t_texture1d:
case t_texture1d:
result = "texture1D";
break;
case reshadefx::type::t_texture2d:
case t_texture2d:
result = "texture2D";
break;
case reshadefx::type::t_texture3d:
case t_texture3d:
result = "texture3D";
break;
case reshadefx::type::t_sampler1d_int:
case t_sampler1d_int:
result = "sampler1D<int" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_sampler2d_int:
case t_sampler2d_int:
result = "sampler2D<int" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_sampler3d_int:
case t_sampler3d_int:
result = "sampler3D<int" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_sampler1d_uint:
case t_sampler1d_uint:
result = "sampler1D<uint" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_sampler2d_uint:
case t_sampler2d_uint:
result = "sampler2D<uint" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_sampler3d_uint:
case t_sampler3d_uint:
result = "sampler3D<uint" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_sampler1d_float:
case t_sampler1d_float:
result = "sampler1D<float" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_sampler2d_float:
case t_sampler2d_float:
result = "sampler2D<float" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_sampler3d_float:
case t_sampler3d_float:
result = "sampler3D<float" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_storage1d_int:
case t_storage1d_int:
result = "storage1D<int" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_storage2d_int:
case t_storage2d_int:
result = "storage2D<int" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_storage3d_int:
case t_storage3d_int:
result = "storage3D<int" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_storage1d_uint:
case t_storage1d_uint:
result = "storage1D<uint" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_storage2d_uint:
case t_storage2d_uint:
result = "storage2D<uint" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_storage3d_uint:
case t_storage3d_uint:
result = "storage3D<uint" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_storage1d_float:
case t_storage1d_float:
result = "storage1D<float" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_storage2d_float:
case t_storage2d_float:
result = "storage2D<float" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_storage3d_float:
case t_storage3d_float:
result = "storage3D<float" + std::to_string(rows) + '>';
break;
case reshadefx::type::t_function:
result = "function";
case t_function:
assert(false);
break;
}
@ -157,7 +160,7 @@ std::string reshadefx::type::description() const
if (is_array())
{
result += '[';
if (array_length > 0)
if (is_bounded_array())
result += std::to_string(array_length);
result += ']';
}
@ -179,7 +182,7 @@ void reshadefx::expression::reset_to_lvalue(const reshadefx::location &loc, uint
type.qualifiers |= type::q_const;
// Strip away global variable qualifiers
type.qualifiers &= ~(reshadefx::type::q_extern | reshadefx::type::q_static | reshadefx::type::q_uniform | reshadefx::type::q_groupshared);
type.qualifiers &= ~(type::q_extern | type::q_static | type::q_uniform | type::q_groupshared);
}
void reshadefx::expression::reset_to_rvalue(const reshadefx::location &loc, uint32_t in_base, const reshadefx::type &in_type)
{
@ -192,7 +195,7 @@ void reshadefx::expression::reset_to_rvalue(const reshadefx::location &loc, uint
chain.clear();
// Strip away global variable qualifiers
type.qualifiers &= ~(reshadefx::type::q_extern | reshadefx::type::q_static | reshadefx::type::q_uniform | reshadefx::type::q_groupshared);
type.qualifiers &= ~(type::q_extern | type::q_static | type::q_uniform | type::q_groupshared);
}
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, bool data)
@ -290,7 +293,7 @@ void reshadefx::expression::add_cast_operation(const reshadefx::type &cast_type)
constant.as_float[i] = static_cast<float>(constant.as_int[i]);
};
for (auto &element : constant.array_data)
for (struct constant &element : constant.array_data)
cast_constant(element, type, cast_type);
cast_constant(constant, type, cast_type);
@ -320,7 +323,7 @@ void reshadefx::expression::add_dynamic_index_access(uint32_t index_expression)
assert(!is_constant); // Cannot have dynamic indexing into constant in SPIR-V
assert(type.is_array() || (type.is_numeric() && !type.is_scalar()));
auto prev_type = type;
struct type prev_type = type;
if (type.is_array())
{
@ -342,11 +345,11 @@ void reshadefx::expression::add_constant_index_access(unsigned int index)
{
assert(type.is_array() || (type.is_numeric() && !type.is_scalar()));
auto prev_type = type;
struct type prev_type = type;
if (type.is_array())
{
assert(type.array_length < 0 || index < static_cast<unsigned int>(type.array_length));
assert(index < type.array_length);
type.array_length = 0;
}
@ -389,7 +392,7 @@ void reshadefx::expression::add_swizzle_access(const signed char swizzle[4], uns
{
assert(type.is_numeric() && !type.is_array());
const auto prev_type = type;
const struct type prev_type = type;
type.rows = length;
type.cols = 1;

View File

@ -18,7 +18,7 @@ enum token_type
};
// Lookup table which translates a given char to a token type
static const unsigned type_lookup[256] = {
static const unsigned int s_type_lookup[256] = {
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, SPACE,
'\n', SPACE, SPACE, SPACE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -35,7 +35,7 @@ static const unsigned type_lookup[256] = {
};
// Lookup tables which translate a given string literal to a token and backwards
static const std::unordered_map<tokenid, std::string_view> token_lookup = {
static const std::unordered_map<tokenid, std::string_view> s_token_lookup = {
{ tokenid::end_of_file, "end of file" },
{ tokenid::exclaim, "!" },
{ tokenid::hash, "#" },
@ -205,7 +205,7 @@ static const std::unordered_map<tokenid, std::string_view> token_lookup = {
{ tokenid::storage2d, "storage2D" },
{ tokenid::storage3d, "storage3D" },
};
static const std::unordered_map<std::string_view, tokenid> keyword_lookup = {
static const std::unordered_map<std::string_view, tokenid> s_keyword_lookup = {
{ "asm", tokenid::reserved },
{ "asm_fragment", tokenid::reserved },
{ "auto", tokenid::reserved },
@ -439,7 +439,7 @@ static const std::unordered_map<std::string_view, tokenid> keyword_lookup = {
{ "volatile", tokenid::volatile_ },
{ "while", tokenid::while_ }
};
static const std::unordered_map<std::string_view, tokenid> pp_directive_lookup = {
static const std::unordered_map<std::string_view, tokenid> s_pp_directive_lookup = {
{ "define", tokenid::hash_def },
{ "undef", tokenid::hash_undef },
{ "if", tokenid::hash_if },
@ -454,15 +454,15 @@ static const std::unordered_map<std::string_view, tokenid> pp_directive_lookup =
{ "include", tokenid::hash_include },
};
static inline bool is_octal_digit(char c)
static bool is_octal_digit(char c)
{
return static_cast<unsigned>(c - '0') < 8;
}
static inline bool is_decimal_digit(char c)
static bool is_decimal_digit(char c)
{
return static_cast<unsigned>(c - '0') < 10;
}
static inline bool is_hexadecimal_digit(char c)
static bool is_hexadecimal_digit(char c)
{
return is_decimal_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
@ -504,8 +504,8 @@ static long long octal_to_decimal(long long n)
std::string reshadefx::token::id_to_name(tokenid id)
{
const auto it = token_lookup.find(id);
if (it != token_lookup.end())
const auto it = s_token_lookup.find(id);
if (it != s_token_lookup.end())
return std::string(it->second);
return "unknown";
}
@ -526,7 +526,7 @@ next_token:
assert(_cur <= _end);
// Do a character type lookup for the current character
switch (type_lookup[uint8_t(*_cur)])
switch (s_type_lookup[uint8_t(*_cur)])
{
case 0xFF: // EOF
tok.id = tokenid::end_of_file;
@ -635,7 +635,7 @@ next_token:
tok.id = tokenid::minus;
break;
case '.':
if (type_lookup[uint8_t(_cur[1])] == DIGIT)
if (s_type_lookup[uint8_t(_cur[1])] == DIGIT)
parse_numeric_literal(tok);
else if (_cur[1] == '.' && _cur[2] == '.')
tok.id = tokenid::ellipsis,
@ -805,7 +805,7 @@ void reshadefx::lexer::skip_space()
continue;
}
if (type_lookup[uint8_t(*_cur)] == SPACE)
if (s_type_lookup[uint8_t(*_cur)] == SPACE)
skip(1);
else
break;
@ -841,7 +841,7 @@ void reshadefx::lexer::parse_identifier(token &tok) const
auto *const begin = _cur, *end = begin;
// Skip to the end of the identifier sequence
while (type_lookup[uint8_t(*end)] == IDENT || type_lookup[uint8_t(*end)] == DIGIT)
while (s_type_lookup[uint8_t(*end)] == IDENT || s_type_lookup[uint8_t(*end)] == DIGIT)
end++;
tok.id = tokenid::identifier;
@ -852,8 +852,8 @@ void reshadefx::lexer::parse_identifier(token &tok) const
if (_ignore_keywords)
return;
if (const auto it = keyword_lookup.find(tok.literal_as_string);
it != keyword_lookup.end())
if (const auto it = s_keyword_lookup.find(tok.literal_as_string);
it != s_keyword_lookup.end())
tok.id = it->second;
}
bool reshadefx::lexer::parse_pp_directive(token &tok)
@ -862,8 +862,8 @@ bool reshadefx::lexer::parse_pp_directive(token &tok)
skip_space(); // Skip any space between the '#' and directive
parse_identifier(tok);
if (const auto it = pp_directive_lookup.find(tok.literal_as_string);
it != pp_directive_lookup.end())
if (const auto it = s_pp_directive_lookup.find(tok.literal_as_string);
it != s_pp_directive_lookup.end())
{
tok.id = it->second;
return true;
@ -999,6 +999,9 @@ void reshadefx::lexer::parse_string_literal(token &tok, bool escape)
tok.id = tokenid::string_literal;
tok.length = end - begin + 1;
// Free up unused memory
tok.literal_as_string.shrink_to_fit();
}
void reshadefx::lexer::parse_numeric_literal(token &tok) const
{

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,8 +5,8 @@
#include "effect_lexer.hpp"
#include "effect_preprocessor.hpp"
#include <cstdio> // fclose, fopen, fread, fseek
#include <cassert>
#include <fstream>
#include <algorithm> // std::find_if
#ifndef _WIN32
@ -52,7 +52,7 @@ enum macro_replacement
macro_replacement_stringize = '\xFE',
};
static const int precedence_lookup[] = {
static const int s_precedence_lookup[] = {
0, 1, 2, 3, 4, // bitwise operators
5, 6, 7, 7, 7, 7, // logical operators
8, 8, // left shift, right shift
@ -61,9 +61,8 @@ static const int precedence_lookup[] = {
11, 11, 11, 11 // unary operators
};
static bool read_file(const std::string &path, std::string &data, reshadefx::preprocessor::include_read_file_callback &cb)
static bool read_file(const std::string &path, std::string &file_data, reshadefx::preprocessor::include_read_file_callback &cb)
{
std::string file_data;
if (!cb(path, file_data))
return false;
@ -77,29 +76,33 @@ static bool read_file(const std::string &path, std::string &data, reshadefx::pre
static_cast<unsigned char>(file_data[2]) == 0xbf)
file_data.erase(0, 3);
data = std::move(file_data);
return true;
}
bool reshadefx::preprocessor::stdfs_read_file_callback(const std::string &path, std::string &data)
{
std::ifstream file(std::filesystem::path(path), std::ios::binary);
if (!file)
return false;
// Read file contents into memory
const std::filesystem::path fspath(path);
#ifndef _WIN32
FILE *const file = fopen(fspath.c_str(), "rb");
#else
FILE *const file = _wfsopen(fspath.generic_wstring().c_str(), L"rb", SH_DENYWR);
#endif
if (file == nullptr)
return false;
// Read file contents into memory
std::error_code ec;
const uintmax_t file_size = std::filesystem::file_size(path, ec);
if (ec)
return false;
fseek(file, 0, SEEK_END);
const size_t file_size = ftell(file);
fseek(file, 0, SEEK_SET);
data.reserve(file_size + 1);
data.resize(static_cast<size_t>(file_size), '\0');
if (!file.read(data.data(), file_size))
return false;
const size_t file_size_read = fread(data.data(), 1, file_size, file);
// No longer need to have a handle open to the file, since all data was read, so can safely close it
fclose(file);
if (file_size_read != file_size)
return false;
// No longer need to have a handle open to the file, since all data was read, so can safely close it
file.close();
return true;
}
@ -156,7 +159,8 @@ bool reshadefx::preprocessor::append_string(std::string source_code, const std::
// Enforce all input strings to end with a line feed
assert(!source_code.empty() && source_code.back() == '\n');
_success = true; // Clear success flag before parsing a new string
// Only consider new errors added below for the success of this call
const size_t errors_offset = _errors.length();
// Give this push a name, so that lexer location starts at a new line
// This is necessary in case this string starts with a preprocessor directive, since the lexer only reports those as such if they appear at the beginning of a new line
@ -164,15 +168,15 @@ bool reshadefx::preprocessor::append_string(std::string source_code, const std::
push(std::move(source_code), path.empty() ? "unknown" : path);
parse();
return _success;
return _errors.find(": preprocessor error: ", errors_offset) == std::string::npos;
}
std::vector<std::filesystem::path> reshadefx::preprocessor::included_files() const
{
std::vector<std::filesystem::path> files;
files.reserve(_file_cache.size());
for (const auto &it : _file_cache)
files.push_back(std::filesystem::u8path(it.first));
for (const std::pair<std::string, std::string> &cache_entry : _file_cache)
files.push_back(std::filesystem::u8path(cache_entry.first));
return files;
}
std::vector<std::pair<std::string, std::string>> reshadefx::preprocessor::used_macro_definitions() const
@ -189,12 +193,19 @@ std::vector<std::pair<std::string, std::string>> reshadefx::preprocessor::used_m
void reshadefx::preprocessor::error(const location &location, const std::string &message)
{
_errors += location.source + '(' + std::to_string(location.line) + ", " + std::to_string(location.column) + ')' + ": preprocessor error: " + message + '\n';
_success = false; // Unset success flag
_errors += location.source;
_errors += '(' + std::to_string(location.line) + ", " + std::to_string(location.column) + ')';
_errors += ": preprocessor error: ";
_errors += message;
_errors += '\n';
}
void reshadefx::preprocessor::warning(const location &location, const std::string &message)
{
_errors += location.source + '(' + std::to_string(location.line) + ", " + std::to_string(location.column) + ')' + ": preprocessor warning: " + message + '\n';
_errors += location.source;
_errors += '(' + std::to_string(location.line) + ", " + std::to_string(location.column) + ')';
_errors += ": preprocessor warning: ";
_errors += message;
_errors += '\n';
}
void reshadefx::preprocessor::push(std::string input, const std::string &name)
@ -357,32 +368,32 @@ void reshadefx::preprocessor::parse()
{
case tokenid::hash_if:
parse_if();
if (!expect(tokenid::end_of_line))
if (!skip && !expect(tokenid::end_of_line))
consume_until(tokenid::end_of_line);
continue;
case tokenid::hash_ifdef:
parse_ifdef();
if (!expect(tokenid::end_of_line))
if (!skip && !expect(tokenid::end_of_line))
consume_until(tokenid::end_of_line);
continue;
case tokenid::hash_ifndef:
parse_ifndef();
if (!expect(tokenid::end_of_line))
if (!skip && !expect(tokenid::end_of_line))
consume_until(tokenid::end_of_line);
continue;
case tokenid::hash_else:
parse_else();
if (!expect(tokenid::end_of_line))
if (!skip && !expect(tokenid::end_of_line))
consume_until(tokenid::end_of_line);
continue;
case tokenid::hash_elif:
parse_elif();
if (!expect(tokenid::end_of_line))
if (!skip && !expect(tokenid::end_of_line))
consume_until(tokenid::end_of_line);
continue;
case tokenid::hash_endif:
parse_endif();
if (!expect(tokenid::end_of_line))
if (!skip && !expect(tokenid::end_of_line))
consume_until(tokenid::end_of_line);
continue;
default:
@ -511,11 +522,18 @@ void reshadefx::preprocessor::parse_if()
level.pp_token = _token;
level.input_index = _current_input_index;
// Evaluate expression after updating 'pp_token', so that it points at the beginning # token
level.value = evaluate_expression();
const bool parent_skipping = !_if_stack.empty() && _if_stack.back().skipping;
level.skipping = parent_skipping || !level.value;
if (parent_skipping)
{
level.value = false;
level.skipping = true;
}
else
{
// Evaluate expression after updating 'pp_token', so that it points at the beginning # token
level.value = evaluate_expression();
level.skipping = !level.value;
}
_if_stack.push_back(std::move(level));
}
@ -528,16 +546,23 @@ void reshadefx::preprocessor::parse_ifdef()
if (!expect(tokenid::identifier))
return;
level.value = is_defined(_token.literal_as_string);
const bool parent_skipping = !_if_stack.empty() && _if_stack.back().skipping;
level.skipping = parent_skipping || !level.value;
if (parent_skipping)
{
level.value = false;
level.skipping = true;
}
else
{
level.value = is_defined(_token.literal_as_string);
level.skipping = !level.value;
_if_stack.push_back(std::move(level));
// Only add to used macro list if this #ifdef is active and the macro was not defined before
if (!parent_skipping)
// Only add to used macro list if this #ifdef is active and the macro was not defined before
if (const auto it = _macros.find(_token.literal_as_string); it == _macros.end() || it->second.is_predefined)
_used_macros.emplace(_token.literal_as_string);
}
_if_stack.push_back(std::move(level));
}
void reshadefx::preprocessor::parse_ifndef()
{
@ -548,16 +573,23 @@ void reshadefx::preprocessor::parse_ifndef()
if (!expect(tokenid::identifier))
return;
level.value = !is_defined(_token.literal_as_string);
const bool parent_skipping = !_if_stack.empty() && _if_stack.back().skipping;
level.skipping = parent_skipping || !level.value;
if (parent_skipping)
{
level.value = false;
level.skipping = true;
}
else
{
level.value = !is_defined(_token.literal_as_string);
level.skipping = !level.value;
_if_stack.push_back(std::move(level));
// Only add to used macro list if this #ifndef is active and the macro was not defined before
if (!parent_skipping)
// Only add to used macro list if this #ifndef is active and the macro was not defined before
if (const auto it = _macros.find(_token.literal_as_string); it == _macros.end() || it->second.is_predefined)
_used_macros.emplace(_token.literal_as_string);
}
_if_stack.push_back(std::move(level));
}
void reshadefx::preprocessor::parse_elif()
{
@ -573,10 +605,19 @@ void reshadefx::preprocessor::parse_elif()
level.input_index = _current_input_index;
const bool parent_skipping = _if_stack.size() > 1 && _if_stack[_if_stack.size() - 2].skipping;
const bool condition_result = evaluate_expression();
level.skipping = parent_skipping || level.value || !condition_result;
if (parent_skipping)
{
level.value = false;
level.skipping = true;
}
else
{
const bool condition_result = evaluate_expression();
level.skipping = level.value || !condition_result;
if (!level.value) level.value = condition_result;
if (!level.value)
level.value = condition_result;
}
}
void reshadefx::preprocessor::parse_else()
{
@ -591,16 +632,25 @@ void reshadefx::preprocessor::parse_else()
level.input_index = _current_input_index;
const bool parent_skipping = _if_stack.size() > 1 && _if_stack[_if_stack.size() - 2].skipping;
level.skipping = parent_skipping || level.value;
if (parent_skipping)
{
level.value = false;
level.skipping = true;
}
else
{
level.skipping = parent_skipping || level.value;
if (!level.value) level.value = true;
if (!level.value)
level.value = true;
}
}
void reshadefx::preprocessor::parse_endif()
{
if (_if_stack.empty())
error(_token.location, "missing #if for #endif");
else
_if_stack.pop_back();
return error(_token.location, "missing #if for #endif");
_if_stack.pop_back();
}
void reshadefx::preprocessor::parse_error()
@ -927,8 +977,8 @@ bool reshadefx::preprocessor::evaluate_expression()
break;
if (left_associative ?
(precedence_lookup[op] > precedence_lookup[prev_op]) :
(precedence_lookup[op] >= precedence_lookup[prev_op]))
(s_precedence_lookup[op] > s_precedence_lookup[prev_op]) :
(s_precedence_lookup[op] >= s_precedence_lookup[prev_op]))
break;
stack_index--;
@ -1016,9 +1066,13 @@ bool reshadefx::preprocessor::evaluate_expression()
BINARY_OPERATION(-);
break;
case op_modulo:
if (stack[stack_index - 1] == 0)
return error(_token.location, "right operand of '%' is zero"), 0;
BINARY_OPERATION(%);
break;
case op_divide:
if (stack[stack_index - 1] == 0)
return error(_token.location, "division by zero"), 0;
BINARY_OPERATION(/);
break;
case op_multiply:
@ -1068,12 +1122,24 @@ bool reshadefx::preprocessor::evaluate_identifier_as_macro()
push(escape_string(file_stem.u8string()));
return true;
}
if (_token.literal_as_string == "__FILE_STEM_HASH__")
{
const std::filesystem::path file_stem = std::filesystem::u8path(_token.location.source).stem();
push(std::to_string(std::hash<std::string>()(file_stem.u8string()) & 0xFFFFFFFF));
return true;
}
if (_token.literal_as_string == "__FILE_NAME__")
{
const std::filesystem::path file_name = std::filesystem::u8path(_token.location.source).filename();
push(escape_string(file_name.u8string()));
return true;
}
if (_token.literal_as_string == "__FILE_NAME_HASH__")
{
const std::filesystem::path file_name = std::filesystem::u8path(_token.location.source).filename();
push(std::to_string(std::hash<std::string>()(file_name.u8string()) & 0xFFFFFFFF));
return true;
}
const auto it = _macros.find(_token.literal_as_string);
if (it == _macros.end())

View File

@ -13,25 +13,23 @@
#include <algorithm> // std::upper_bound, std::sort
#include <functional> // std::greater
enum class intrinsic_id : uint32_t
enum class intrinsic_id
{
#define IMPLEMENT_INTRINSIC_SPIRV(name, i, code) name##i,
#include "effect_symbol_table_intrinsics.inl"
};
struct intrinsic
struct intrinsic : reshadefx::function
{
intrinsic(const char *name, intrinsic_id id, const reshadefx::type &ret_type, std::initializer_list<reshadefx::type> arg_types) : id(id)
intrinsic(const char *name, intrinsic_id id, const reshadefx::type &ret_type, std::initializer_list<reshadefx::type> arg_types)
{
function.name = name;
function.return_type = ret_type;
function.parameter_list.reserve(arg_types.size());
function::return_type = ret_type;
function::id = static_cast<uint32_t>(id);
function::name = name;
function::parameter_list.reserve(arg_types.size());
for (const reshadefx::type &arg_type : arg_types)
function.parameter_list.push_back({ arg_type });
function::parameter_list.push_back({ arg_type });
}
intrinsic_id id;
reshadefx::function_info function;
};
#define void { reshadefx::type::t_void }
@ -130,48 +128,17 @@ static const intrinsic s_intrinsics[] =
#undef uint2
#undef uint3
#undef uint4
#undef float1
#undef float
#undef float2
#undef float3
#undef float4
#undef float2x2
#undef float3x3
#undef float4x4
#undef out_float
#undef out_float2
#undef out_float3
#undef out_float4
#undef sampler1d_int
#undef sampler2d_int
#undef sampler3d_int
#undef sampler1d_uint
#undef sampler2d_uint
#undef sampler3d_uint
#undef sampler1d_float4
#undef sampler2d_float4
#undef sampler3d_float4
#undef storage1d_int
#undef storage2d_int
#undef storage3d_int
#undef storage1d_uint
#undef storage2d_uint
#undef storage3d_uint
#undef storage1d_float4
#undef storage2d_float4
#undef storage3d_float4
#undef inout_storage1d_int
#undef inout_storage2d_int
#undef inout_storage3d_int
#undef inout_storage1d_uint
#undef inout_storage2d_uint
#undef inout_storage3d_uint
unsigned int reshadefx::type::rank(const type &src, const type &dst)
{
if (src.is_array() != dst.is_array() || (src.array_length != dst.array_length && src.array_length > 0 && dst.array_length > 0))
if (src.is_array() != dst.is_array() || (src.array_length != dst.array_length && src.is_bounded_array() && dst.is_bounded_array()))
return 0; // Arrays of different sizes are not compatible
if (src.is_struct() || dst.is_struct())
return src.definition == dst.definition ? 32 : 0; // Structs are only compatible if they are the same type
return src.struct_definition == dst.struct_definition ? 32 : 0; // Structs are only compatible if they are the same type
if (!src.is_numeric() || !dst.is_numeric())
return src.base == dst.base && src.rows == dst.rows && src.cols == dst.cols ? 32 : 0; // Numeric values are not compatible with other types
if (src.is_matrix() && (!dst.is_matrix() || src.rows != dst.rows || src.cols != dst.cols))
@ -181,7 +148,7 @@ unsigned int reshadefx::type::rank(const type &src, const type &dst)
// - Floating point has a higher rank than integer types
// - Integer to floating point promotion has a higher rank than floating point to integer conversion
// - Signed to unsigned integer conversion has a higher rank than unsigned to signed integer conversion
static const int ranks[7][7] = {
static const unsigned int ranks[7][7] = {
{ 5, 4, 4, 4, 4, 4, 4 }, // bool
{ 3, 5, 5, 2, 2, 4, 4 }, // min16int
{ 3, 5, 5, 2, 2, 4, 4 }, // int
@ -194,7 +161,7 @@ unsigned int reshadefx::type::rank(const type &src, const type &dst)
assert(src.base > 0 && src.base <= 7); // bool - float
assert(dst.base > 0 && dst.base <= 7);
const int rank = ranks[src.base - 1][dst.base - 1] << 2;
const unsigned int rank = ranks[src.base - 1][dst.base - 1] << 2;
if ((src.is_scalar() && dst.is_vector()))
return rank >> 1; // Scalar to vector promotion has a lower rank
@ -284,7 +251,7 @@ bool reshadefx::symbol_table::insert_symbol(const std::string &name, const symbo
{
// Extract scope name
scope.name = _current_scope.name.substr(0, pos += 2);
const auto previous_scope_name = _current_scope.name.substr(pos);
const std::string previous_scope_name = _current_scope.name.substr(pos);
// Insert symbol into this scope
insert_sorted(_symbol_stack[previous_scope_name + name], scoped_symbol { symbol, scope });
@ -335,7 +302,7 @@ reshadefx::scoped_symbol reshadefx::symbol_table::find_symbol(const std::string
return result;
}
static int compare_functions(const std::vector<reshadefx::expression> &arguments, const reshadefx::function_info *function1, const reshadefx::function_info *function2)
static int compare_functions(const std::vector<reshadefx::expression> &arguments, const reshadefx::function *function1, const reshadefx::function *function2)
{
const size_t num_arguments = arguments.size();
@ -388,7 +355,7 @@ bool reshadefx::symbol_table::resolve_function_call(const std::string &name, con
{
out_data.op = symbol_type::function;
const function_info *result = nullptr;
const function *result = nullptr;
unsigned int num_overloads = 0;
unsigned int overload_namespace = scope.namespace_level;
@ -405,7 +372,7 @@ bool reshadefx::symbol_table::resolve_function_call(const std::string &name, con
it->scope.namespace_level > scope.namespace_level || (it->scope.namespace_level == scope.namespace_level && it->scope.name != scope.name))
continue;
const function_info *const function = it->function;
const function *const function = it->function;
if (function == nullptr)
continue;
@ -425,7 +392,7 @@ bool reshadefx::symbol_table::resolve_function_call(const std::string &name, con
continue;
}
}
else if (arguments.size() != function->parameter_list.size())
else if (arguments.size() > function->parameter_list.size() || (arguments.size() < function->parameter_list.size() && !function->parameter_list[arguments.size()].has_default_value))
{
continue;
}
@ -453,18 +420,18 @@ bool reshadefx::symbol_table::resolve_function_call(const std::string &name, con
{
for (const intrinsic &intrinsic : s_intrinsics)
{
if (intrinsic.function.name != name || intrinsic.function.parameter_list.size() != arguments.size())
if (intrinsic.name != name || intrinsic.parameter_list.size() != arguments.size())
continue;
// A new possibly-matching intrinsic function was found, compare it against the current result
const int comparison = compare_functions(arguments, &intrinsic.function, result);
const int comparison = compare_functions(arguments, &intrinsic, result);
if (comparison < 0) // The new function is a better match
{
out_data.op = symbol_type::intrinsic;
out_data.id = static_cast<uint32_t>(intrinsic.id);
out_data.type = intrinsic.function.return_type;
out_data.function = &intrinsic.function;
out_data.id = intrinsic.id;
out_data.type = intrinsic.return_type;
out_data.function = &intrinsic;
result = out_data.function;
num_overloads = 1;
}

File diff suppressed because it is too large Load Diff