This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/gpu/metal/mtl_shader_generator.hh
Jason Fielder 94855119da Fix: Metal validation error when shader has no uniforms
Metal buffer binding validation would trigger an error
when a given shader had an empty PushConstantBlock.
This patch removes the default uniform code gen if
no uniforms are present, to avoid any possible issues
with buffers being bound to a shader where the destination
data block is size zero.

Authored by Apple: Michael Parkin-White

Pull Request: blender/blender#105796
2023-03-16 08:10:38 +01:00

798 lines
26 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "gpu_shader_create_info.hh"
#include "gpu_shader_private.hh"
/** -- Metal Shader Generator for GLSL -> MSL conversion --
*
* The Metal shader generator class is used as a conversion utility for generating
* a compatible MSL shader from a source GLSL shader. There are several steps
* involved in creating a shader, and structural changes which enable the source
* to function in the same way.
*
* 1) Extraction and conversion of shaders input's and output's to their Metal-compatible
* version. This is a subtle data transformation from GPUShaderCreateInfo, allowing
* for Metal-specific parameters.
*
* 2) Determine usage of shader features such as GL global variable usage, depth write output,
* clip distances, multilayered rendering, barycentric coordinates etc;
*
* 3) Generate MSL shader.
*
* 4) Populate #MTLShaderInterface, describing input/output structure, bind-points, buffer size and
* alignment, shader feature usage etc; Everything required by the Metal back-end to
* successfully enable use of shaders and GPU back-end features.
*
*
*
* For each shading stage, we generate an MSL shader following these steps:
*
* 1) Output custom shader defines describing modes e.g. whether we are using
* sampler bindings or argument buffers; at the top of the shader.
*
* 2) Inject common Metal headers.
* - `mtl_shader_defines.msl` is used to map GLSL functions to MSL.
* - `mtl_shader_common.msl` is added to ALL MSL shaders to provide
* common functionality required by the back-end. This primarily
* contains function-constant hooks, used in PSO generation.
*
* 3) Create a class Scope which wraps the GLSL shader. This is used to
* create a global per-thread scope around the shader source, to allow
* access to common shader members (GLSL globals, shader inputs/outputs etc)
*
* 4) Generate shader interface structs and populate local members where required for:
* - `VertexInputs`
* - `VertexOutputs`
* - `Uniforms`
* - `Uniform Blocks`
* - `textures` ;
* etc;
*
* 5) Inject GLSL source.
*
* 6) Generate MSL shader entry point function. Every Metal shader must have a
* vertex/fragment/kernel entry-point, which contains the function binding table.
* This is where bindings are specified and passed into the shader.
*
* For converted shaders, the MSL entry-point will also instantiate a shader
* class per thread, and pass over bound resource references into the class.
*
* Finally, the shaders "main()" method will be called, and outputs are copied.
*
* NOTE: For position outputs, the default output position will be converted to
* the Metal coordinate space, which involves flipping the Y coordinate and
* re-mapping the depth range between 0 and 1, as with Vulkan.
*
*
* The final shader structure looks as follows:
*
* \code{.cc}
* -- Shader defines --
* #define USE_ARGUMENT_BUFFER_FOR_SAMPLERS 0
* ... etc ...;
*
* class MetalShaderVertexImp {
*
* -- Common shader interface structs --
* struct VertexIn {
* vec4 pos [[attribute(0)]]
* }
* struct VertexOut {...}
* struct PushConstantBlock {...}
* struct drw_Globals {...}
* ...
*
* -- GLSL source code --
* ...
* };
*
* vertex MetalShaderVertexImp::VertexOut vertex_function_entry(
* MetalShaderVertexImp::VertexIn v_in [[stage_in]],
* constant PushConstantBlock& globals [[buffer(MTL_uniform_buffer_base_index)]]) {
*
* MetalShaderVertexImp impl;
* -- Copy input members into impl instance --
* -- Execute GLSL main function --
* impl.main();
*
* -- Copy outputs and return --
* MetalShaderVertexImp::VertexOut out;
* out.pos = impl.pos;
* -- transform position to Metal coordinate system --
* return v_out;
* }
* \endcode
*
* -- SSBO-vertex-fetchmode --
*
* SSBO-vertex-fetchmode is a special option wherein vertex buffers are bound directly
* as buffers in the shader, rather than using the VertexDescriptor and [[stage_in]] vertex
* assembly.
*
* The purpose of this mode is to enable random-access reading of all vertex data. This is
* particularly useful for efficiently converting geometry shaders to Metal shading language,
* as these techniques are not supported natively in Metal.
*
* Geometry shaders can be re-created by firing off a vertex shader with the desired number of
* total output vertices. Each vertex can then read whichever input attributes it needs to
* achieve the output result.
* This manual reading is also used to provide support for GPU_provoking_vertex, wherein the
* output vertex for flat shading needs to change. In these cases, the manual vertex assembly
* can flip which vertices are read within the primitive.
*
* From an efficiency perspective, this is more GPU-friendly than geometry shading, due to improved
* parallelism throughout the whole pipe, and for Apple hardware specifically, there is no
* significant performance loss from manual vertex assembly vs under-the-hood assembly.
*
* This mode works by passing the required vertex descriptor information into the shader
* as uniform data, describing the type, stride, offset, step-mode and buffer index of each
* attribute, such that the shader SSBO-vertex-fetch utility functions know how to extract data.
*
* This also works with indexed rendering,
* by similarly binding the index buffer as a manual buffer.
*
* When this mode is used, the code generation and shader interface generation varies to
* accommodate the required features.
*
* This mode can be enabled in a shader with:
*
* `#pragma USE_SSBO_VERTEX_FETCH(TriangleList/LineList, output_vertex_count_per_input_primitive)`
*
* This mirrors the geometry shader interface `layout(triangle_strip, max_vertices = 3) out;`
*/
/* SSBO vertex fetch attribute uniform parameter names.
* These uniforms are used to pass the information
* required to perform manual vertex assembly within
* the vertex shader.
* Each vertex attribute requires a number of properties
* in order to correctly extract data from the bound vertex
* buffers. */
#ifndef NDEBUG
/* Global. */
# define UNIFORM_SSBO_USES_INDEXED_RENDERING_STR "uniform_ssbo_uses_indexed_rendering"
# define UNIFORM_SSBO_INDEX_MODE_U16_STR "uniform_ssbo_index_mode_u16"
# define UNIFORM_SSBO_INPUT_PRIM_TYPE_STR "uniform_ssbo_input_prim_type"
# define UNIFORM_SSBO_INPUT_VERT_COUNT_STR "uniform_ssbo_input_vert_count"
/* Per-attribute. */
# define UNIFORM_SSBO_OFFSET_STR "uniform_ssbo_offset_"
# define UNIFORM_SSBO_STRIDE_STR "uniform_ssbo_stride_"
# define UNIFORM_SSBO_FETCHMODE_STR "uniform_ssbo_fetchmode_"
# define UNIFORM_SSBO_VBO_ID_STR "uniform_ssbo_vbo_id_"
# define UNIFORM_SSBO_TYPE_STR "uniform_ssbo_type_"
#else
/* Global. */
# define UNIFORM_SSBO_USES_INDEXED_RENDERING_STR "_ir"
# define UNIFORM_SSBO_INDEX_MODE_U16_STR "_mu"
# define UNIFORM_SSBO_INPUT_PRIM_TYPE_STR "_pt"
# define UNIFORM_SSBO_INPUT_VERT_COUNT_STR "_vc"
/* Per-attribute. */
# define UNIFORM_SSBO_OFFSET_STR "_so"
# define UNIFORM_SSBO_STRIDE_STR "_ss"
# define UNIFORM_SSBO_FETCHMODE_STR "_sf"
# define UNIFORM_SSBO_VBO_ID_STR "_sv"
# define UNIFORM_SSBO_TYPE_STR "_st"
#endif
namespace blender::gpu {
struct MSLUniform {
shader::Type type;
std::string name;
bool is_array;
int array_elems;
ShaderStage stage;
MSLUniform(shader::Type uniform_type,
std::string uniform_name,
bool is_array_type,
uint32_t num_elems = 1)
: type(uniform_type), name(uniform_name), is_array(is_array_type), array_elems(num_elems)
{
}
bool operator==(const MSLUniform &right) const
{
return (type == right.type && name == right.name && is_array == right.is_array &&
array_elems == right.array_elems);
}
};
struct MSLUniformBlock {
std::string type_name;
std::string name;
ShaderStage stage;
bool is_array;
uint slot;
bool operator==(const MSLUniformBlock &right) const
{
return (type_name == right.type_name && name == right.name);
}
};
enum MSLTextureSamplerAccess {
TEXTURE_ACCESS_NONE = 0,
TEXTURE_ACCESS_SAMPLE,
TEXTURE_ACCESS_READ,
TEXTURE_ACCESS_WRITE,
TEXTURE_ACCESS_READWRITE,
};
struct MSLTextureSampler {
ShaderStage stage;
shader::ImageType type;
std::string name;
MSLTextureSamplerAccess access;
uint location;
eGPUTextureType get_texture_binding_type() const;
eGPUSamplerFormat get_sampler_format() const;
void resolve_binding_indices();
MSLTextureSampler(ShaderStage in_stage,
shader::ImageType in_sampler_type,
std::string in_sampler_name,
MSLTextureSamplerAccess in_access,
uint in_location)
: stage(in_stage),
type(in_sampler_type),
name(in_sampler_name),
access(in_access),
location(in_location)
{
}
bool operator==(const MSLTextureSampler &right) const
{
/* We do not compare stage as we want to avoid duplication of resources used across multiple
* stages. */
return (type == right.type && name == right.name && access == right.access);
}
std::string get_msl_access_str() const
{
switch (access) {
case TEXTURE_ACCESS_SAMPLE:
return "access::sample";
case TEXTURE_ACCESS_READ:
return "access::read";
case TEXTURE_ACCESS_WRITE:
return "access::write";
case TEXTURE_ACCESS_READWRITE:
return "access::read_write";
default:
BLI_assert(false);
return "";
}
return "";
}
/* Get typestring for wrapped texture class members.
* wrapper struct type contains combined texture and sampler, templated
* against the texture type.
* See `COMBINED_SAMPLER_TYPE` in `mtl_shader_defines.msl`. */
std::string get_msl_typestring_wrapper(bool is_addr) const
{
std::string str;
str = this->get_msl_wrapper_type_str() + "<" + this->get_msl_return_type_str() + "," +
this->get_msl_access_str() + ">" + ((is_addr) ? "* " : " ") + this->name;
return str;
}
/* Get raw texture typestring -- used in entry-point function argument table. */
std::string get_msl_typestring(bool is_addr) const
{
std::string str;
str = this->get_msl_texture_type_str() + "<" + this->get_msl_return_type_str() + "," +
this->get_msl_access_str() + ">" + ((is_addr) ? "* " : " ") + this->name;
return str;
}
std::string get_msl_return_type_str() const;
std::string get_msl_texture_type_str() const;
std::string get_msl_wrapper_type_str() const;
};
struct MSLVertexInputAttribute {
/* layout_location of -1 means unspecified and will
* be populated manually. */
int layout_location;
shader::Type type;
std::string name;
bool operator==(const MSLVertexInputAttribute &right) const
{
return (layout_location == right.layout_location && type == right.type && name == right.name);
}
};
struct MSLVertexOutputAttribute {
std::string type;
std::string name;
/* Instance name specified if attributes belong to a struct. */
std::string instance_name;
/* Interpolation qualifier can be any of smooth (default), flat, no_perspective. */
std::string interpolation_qualifier;
bool is_array;
int array_elems;
bool operator==(const MSLVertexOutputAttribute &right) const
{
return (type == right.type && name == right.name &&
interpolation_qualifier == right.interpolation_qualifier &&
is_array == right.is_array && array_elems == right.array_elems);
}
std::string get_mtl_interpolation_qualifier() const
{
if (interpolation_qualifier == "" || interpolation_qualifier == "smooth") {
return "";
}
else if (interpolation_qualifier == "flat") {
return " [[flat]]";
}
else if (interpolation_qualifier == "noperspective") {
return " [[center_no_perspective]]";
}
return "";
}
};
struct MSLFragmentOutputAttribute {
/* Explicit output binding location N for [[color(N)]] -1 = unspecified. */
int layout_location;
/* Output index for dual source blending. -1 = unspecified. */
int layout_index;
shader::Type type;
std::string name;
bool operator==(const MSLFragmentOutputAttribute &right) const
{
return (layout_location == right.layout_location && type == right.type && name == right.name &&
layout_index == right.layout_index);
}
};
struct MSLSharedMemoryBlock {
/* e.g. shared vec4 color_cache[cache_size][cache_size]; */
std::string type_name;
std::string varname;
bool is_array;
std::string array_decl; /* String containing array declaration. e.g. [cache_size][cache_size]*/
};
class MSLGeneratorInterface {
static char *msl_patch_default;
public:
/** Shader stage input/output binding information.
* Derived from shader source reflection or GPUShaderCreateInfo. */
blender::Vector<MSLUniformBlock> uniform_blocks;
blender::Vector<MSLUniform> uniforms;
blender::Vector<MSLTextureSampler> texture_samplers;
blender::Vector<MSLVertexInputAttribute> vertex_input_attributes;
blender::Vector<MSLVertexOutputAttribute> vertex_output_varyings;
/* Should match vertex outputs, but defined separately as
* some shader permutations will not utilize all inputs/outputs.
* Final shader uses the intersection between the two sets. */
blender::Vector<MSLVertexOutputAttribute> fragment_input_varyings;
blender::Vector<MSLFragmentOutputAttribute> fragment_outputs;
/* Transform feedback interface. */
blender::Vector<MSLVertexOutputAttribute> vertex_output_varyings_tf;
/* Clip Distances. */
blender::Vector<char> clip_distances;
/* Shared Memory Blocks. */
blender::Vector<MSLSharedMemoryBlock> shared_memory_blocks;
/** GL Global usage. */
/* Whether GL position is used, or an alternative vertex output should be the default. */
bool uses_gl_Position;
/* Whether gl_FragColor is used, or whether an alternative fragment output
* should be the default. */
bool uses_gl_FragColor;
/* Whether gl_PointCoord is used in the fragment shader. If so,
* we define float2 gl_PointCoord [[point_coord]]. */
bool uses_gl_PointCoord;
/* Writes out to gl_PointSize in the vertex shader output. */
bool uses_gl_PointSize;
bool uses_gl_VertexID;
bool uses_gl_InstanceID;
bool uses_gl_BaseInstanceARB;
bool uses_gl_FrontFacing;
bool uses_gl_PrimitiveID;
/* Sets the output render target array index when using multilayered rendering. */
bool uses_gl_FragDepth;
bool uses_mtl_array_index_;
bool uses_transform_feedback;
bool uses_barycentrics;
/* Compute shader global variables. */
bool uses_gl_GlobalInvocationID;
bool uses_gl_WorkGroupSize;
bool uses_gl_WorkGroupID;
bool uses_gl_NumWorkGroups;
bool uses_gl_LocalInvocationIndex;
bool uses_gl_LocalInvocationID;
/* Parameters. */
shader::DepthWrite depth_write;
/* Bind index trackers. */
int max_ubo_slot = -1;
/* Shader buffer bind indices for argument buffers per shader stage.
* NOTE: Compute stage will re-use index 0. */
int sampler_argument_buffer_bind_index[3] = {-1, -1, -1};
/*** SSBO Vertex fetch mode. ***/
/* Indicates whether to pass in Vertex Buffer's as a regular buffers instead of using vertex
* assembly in the PSO descriptor. Enabled with special pragma. */
bool uses_ssbo_vertex_fetch_mode;
private:
/* Parent shader instance. */
MTLShader &parent_shader_;
/* If prepared from Create info. */
const shader::ShaderCreateInfo *create_info_;
public:
MSLGeneratorInterface(MTLShader &shader) : parent_shader_(shader){};
/** Prepare MSLGeneratorInterface from create-info. **/
void prepare_from_createinfo(const shader::ShaderCreateInfo *info);
/* When SSBO Vertex Fetch mode is used, uniforms are used to pass on the required information
* about vertex attribute bindings, in order to perform manual vertex assembly and random-access
* vertex lookup throughout the bound VBOs.
*
* Some parameters are global for the shader, others change with the currently bound
* VertexBuffers, and their format, as they do with regular GPUBatch's.
*
* (Where ##attr is the attributes name)
* uniform_ssbo_stride_##attr -- Representing the stride between elements of attribute(attr)
* uniform_ssbo_offset_##attr -- Representing the base offset within the vertex
* uniform_ssbo_fetchmode_##attr -- Whether using per-vertex fetch or per-instance fetch
* (0=vert, 1=inst) uniform_ssbo_vbo_id_##attr -- index of the vertex buffer within which the
* data for this attribute is contained uniform_ssbo_type_##attr - The type of data in the
* currently bound buffer -- Could be a mismatch with the Officially reported type. */
void prepare_ssbo_vertex_fetch_uniforms();
/* Samplers. */
bool use_argument_buffer_for_samplers() const;
uint32_t num_samplers_for_stage(ShaderStage stage) const;
/* Returns the bind index, relative to MTL_uniform_buffer_base_index. */
uint32_t get_sampler_argument_buffer_bind_index(ShaderStage stage);
/* Code generation utility functions. */
std::string generate_msl_uniform_structs(ShaderStage shader_stage);
std::string generate_msl_vertex_in_struct();
std::string generate_msl_vertex_out_struct(ShaderStage shader_stage);
std::string generate_msl_vertex_transform_feedback_out_struct(ShaderStage shader_stage);
std::string generate_msl_fragment_out_struct();
std::string generate_msl_vertex_inputs_string();
std::string generate_msl_fragment_inputs_string();
std::string generate_msl_compute_inputs_string();
std::string generate_msl_vertex_entry_stub();
std::string generate_msl_fragment_entry_stub();
std::string generate_msl_compute_entry_stub();
std::string generate_msl_global_uniform_population(ShaderStage stage);
std::string generate_ubo_block_macro_chain(MSLUniformBlock block);
std::string generate_msl_uniform_block_population(ShaderStage stage);
std::string generate_msl_vertex_attribute_input_population();
std::string generate_msl_vertex_output_population();
std::string generate_msl_vertex_output_tf_population();
std::string generate_msl_fragment_input_population();
std::string generate_msl_fragment_output_population();
std::string generate_msl_uniform_undefs(ShaderStage stage);
std::string generate_ubo_block_undef_chain(ShaderStage stage);
std::string generate_msl_texture_vars(ShaderStage shader_stage);
void generate_msl_textures_input_string(std::stringstream &out,
ShaderStage stage,
bool &is_first_parameter);
void generate_msl_uniforms_input_string(std::stringstream &out,
ShaderStage stage,
bool &is_first_parameter);
/* Location is not always specified, so this will resolve outstanding locations. */
void resolve_input_attribute_locations();
void resolve_fragment_output_locations();
/* Create shader interface for converted GLSL shader. */
MTLShaderInterface *bake_shader_interface(const char *name);
/* Fetch combined shader source header. */
char *msl_patch_default_get();
MEM_CXX_CLASS_ALLOC_FUNCS("MSLGeneratorInterface");
};
inline const char *get_stage_class_name(ShaderStage stage)
{
switch (stage) {
case ShaderStage::VERTEX:
return "MTLShaderVertexImpl";
case ShaderStage::FRAGMENT:
return "MTLShaderFragmentImpl";
case ShaderStage::COMPUTE:
return "MTLShaderComputeImpl";
default:
BLI_assert_unreachable();
return "";
}
return "";
}
inline const char *get_shader_stage_instance_name(ShaderStage stage)
{
switch (stage) {
case ShaderStage::VERTEX:
return "vertex_shader_instance";
case ShaderStage::FRAGMENT:
return "fragment_shader_instance";
case ShaderStage::COMPUTE:
return "compute_shader_instance";
default:
BLI_assert_unreachable();
return "";
}
return "";
}
inline bool is_builtin_type(std::string type)
{
/* Add Types as needed. */
/* TODO(Metal): Consider replacing this with a switch and `constexpr` hash and switch.
* Though most efficient and maintainable approach to be determined. */
static std::map<std::string, eMTLDataType> glsl_builtin_types = {
{"float", MTL_DATATYPE_FLOAT},
{"vec2", MTL_DATATYPE_FLOAT2},
{"vec3", MTL_DATATYPE_FLOAT3},
{"vec4", MTL_DATATYPE_FLOAT4},
{"int", MTL_DATATYPE_INT},
{"ivec2", MTL_DATATYPE_INT2},
{"ivec3", MTL_DATATYPE_INT3},
{"ivec4", MTL_DATATYPE_INT4},
{"uint32_t", MTL_DATATYPE_UINT},
{"uvec2", MTL_DATATYPE_UINT2},
{"uvec3", MTL_DATATYPE_UINT3},
{"uvec4", MTL_DATATYPE_UINT4},
{"mat3", MTL_DATATYPE_FLOAT3x3},
{"mat4", MTL_DATATYPE_FLOAT4x4},
{"bool", MTL_DATATYPE_INT},
{"uchar", MTL_DATATYPE_UCHAR},
{"uchar2", MTL_DATATYPE_UCHAR2},
{"uchar2", MTL_DATATYPE_UCHAR3},
{"uchar4", MTL_DATATYPE_UCHAR4},
{"vec3_1010102_Unorm", MTL_DATATYPE_UINT1010102_NORM},
{"vec3_1010102_Inorm", MTL_DATATYPE_INT1010102_NORM},
};
return (glsl_builtin_types.find(type) != glsl_builtin_types.end());
}
inline bool is_matrix_type(const std::string &type)
{
/* Matrix type support. Add types as necessary. */
return (type == "mat4");
}
inline bool is_matrix_type(const shader::Type &type)
{
/* Matrix type support. Add types as necessary. */
return (type == shader::Type::MAT4 || type == shader::Type::MAT3);
}
inline int get_matrix_location_count(const std::string &type)
{
/* Matrix type support. Add types as necessary. */
if (type == "mat4") {
return 4;
}
if (type == "mat3") {
return 3;
}
return 1;
}
inline int get_matrix_location_count(const shader::Type &type)
{
/* Matrix type support. Add types as necessary. */
if (type == shader::Type::MAT4) {
return 4;
}
else if (type == shader::Type::MAT3) {
return 3;
}
return 1;
}
inline std::string get_matrix_subtype(const std::string &type)
{
if (type == "mat4") {
return "vec4";
}
return type;
}
inline shader::Type get_matrix_subtype(const shader::Type &type)
{
if (type == shader::Type::MAT4) {
return shader::Type::VEC4;
}
if (type == shader::Type::MAT3) {
return shader::Type::VEC3;
}
return type;
}
inline std::string get_attribute_conversion_function(bool *uses_conversion,
const shader::Type &type)
{
/* NOTE(Metal): Add more attribute types as required. */
if (type == shader::Type::FLOAT) {
*uses_conversion = true;
return "internal_vertex_attribute_convert_read_float";
}
else if (type == shader::Type::VEC2) {
*uses_conversion = true;
return "internal_vertex_attribute_convert_read_float2";
}
else if (type == shader::Type::VEC3) {
*uses_conversion = true;
return "internal_vertex_attribute_convert_read_float3";
}
else if (type == shader::Type::VEC4) {
*uses_conversion = true;
return "internal_vertex_attribute_convert_read_float4";
}
*uses_conversion = false;
return "";
}
inline const char *to_string(const shader::PrimitiveOut &layout)
{
switch (layout) {
case shader::PrimitiveOut::POINTS:
return "points";
case shader::PrimitiveOut::LINE_STRIP:
return "line_strip";
case shader::PrimitiveOut::TRIANGLE_STRIP:
return "triangle_strip";
default:
BLI_assert(false);
return "unknown";
}
}
inline const char *to_string(const shader::PrimitiveIn &layout)
{
switch (layout) {
case shader::PrimitiveIn::POINTS:
return "points";
case shader::PrimitiveIn::LINES:
return "lines";
case shader::PrimitiveIn::LINES_ADJACENCY:
return "lines_adjacency";
case shader::PrimitiveIn::TRIANGLES:
return "triangles";
case shader::PrimitiveIn::TRIANGLES_ADJACENCY:
return "triangles_adjacency";
default:
BLI_assert(false);
return "unknown";
}
}
inline const char *to_string(const shader::Interpolation &interp)
{
switch (interp) {
case shader::Interpolation::SMOOTH:
return "smooth";
case shader::Interpolation::FLAT:
return "flat";
case shader::Interpolation::NO_PERSPECTIVE:
return "noperspective";
default:
BLI_assert(false);
return "unkown";
}
}
inline const char *to_string_msl(const shader::Interpolation &interp)
{
switch (interp) {
case shader::Interpolation::SMOOTH:
return "[[center_perspective]]";
case shader::Interpolation::FLAT:
return "[[flat]]";
case shader::Interpolation::NO_PERSPECTIVE:
return "[[center_no_perspective]]";
default:
return "";
}
}
inline const char *to_string(const shader::Type &type)
{
switch (type) {
case shader::Type::FLOAT:
return "float";
case shader::Type::VEC2:
return "vec2";
case shader::Type::VEC3:
return "vec3";
case shader::Type::VEC3_101010I2:
return "vec3_1010102_Inorm";
case shader::Type::VEC4:
return "vec4";
case shader::Type::MAT3:
return "mat3";
case shader::Type::MAT4:
return "mat4";
case shader::Type::UINT:
return "uint32_t";
case shader::Type::UVEC2:
return "uvec2";
case shader::Type::UVEC3:
return "uvec3";
case shader::Type::UVEC4:
return "uvec4";
case shader::Type::INT:
return "int";
case shader::Type::IVEC2:
return "ivec2";
case shader::Type::IVEC3:
return "ivec3";
case shader::Type::IVEC4:
return "ivec4";
case shader::Type::BOOL:
return "bool";
case shader::Type::UCHAR:
return "uchar";
case shader::Type::UCHAR2:
return "uchar2";
case shader::Type::UCHAR3:
return "uchar3";
case shader::Type::UCHAR4:
return "uchar4";
case shader::Type::CHAR:
return "char";
case shader::Type::CHAR2:
return "char2";
case shader::Type::CHAR3:
return "char3";
case shader::Type::CHAR4:
return "char4";
default:
BLI_assert(false);
return "unkown";
}
}
inline char *next_symbol_in_range(char *begin, char *end, char symbol)
{
for (char *a = begin; a < end; a++) {
if (*a == symbol) {
return a;
}
}
return nullptr;
}
inline char *next_word_in_range(char *begin, char *end)
{
for (char *a = begin; a < end; a++) {
char chr = *a;
if ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') ||
(chr == '_')) {
return a;
}
}
return nullptr;
}
} // namespace blender::gpu