From 9d3f35a0bf1bf5776363bfd61d53a7c85b5827a4 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 17 Jan 2022 14:45:22 +0100 Subject: Revert "Revert "GPUShaderCreateInfo for interface abstraction"" This reverts commit edee5a947b7ea3e1324aa334a22c7c9bbf47f5f7. Fixes compilation error (Missing file BLI_float2.hh) --- .../blender/gpu/intern/gpu_shader_create_info.hh | 595 +++++++++++++++++++++ 1 file changed, 595 insertions(+) create mode 100644 source/blender/gpu/intern/gpu_shader_create_info.hh (limited to 'source/blender/gpu/intern/gpu_shader_create_info.hh') diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh new file mode 100644 index 00000000000..ced7f7039e7 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -0,0 +1,595 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Descriptior type used to define shader structure, resources and interfaces. + * + * Some rule of thumb: + * - Do not include anything else than this file in each info file. + */ + +#pragma once + +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" +#include "GPU_texture.h" + +namespace blender::gpu::shader { + +#ifndef GPU_SHADER_CREATE_INFO +/* Helps intelisense / auto-completion. */ +# define GPU_SHADER_INTERFACE_INFO(_interface, _inst_name) \ + StageInterfaceInfo _interface(#_interface, _inst_name); \ + _interface +# define GPU_SHADER_CREATE_INFO(_info) \ + ShaderCreateInfo _info(#_info); \ + _info +#endif + +enum class Type { + FLOAT = 0, + VEC2, + VEC3, + VEC4, + MAT3, + MAT4, + UINT, + UVEC2, + UVEC3, + UVEC4, + INT, + IVEC2, + IVEC3, + IVEC4, + BOOL, +}; + +enum class BuiltinBits { + /** Allow getting barycentic coordinates inside the fragment shader. NOTE: emulated on OpenGL. */ + BARYCENTRIC_COORD = (1 << 0), + FRAG_COORD = (1 << 2), + FRONT_FACING = (1 << 4), + GLOBAL_INVOCATION_ID = (1 << 5), + INSTANCE_ID = (1 << 6), + LAYER = (1 << 7), + LOCAL_INVOCATION_ID = (1 << 8), + LOCAL_INVOCATION_INDEX = (1 << 9), + NUM_WORK_GROUP = (1 << 10), + POINT_COORD = (1 << 11), + POINT_SIZE = (1 << 12), + PRIMITIVE_ID = (1 << 13), + VERTEX_ID = (1 << 14), + WORK_GROUP_ID = (1 << 15), + WORK_GROUP_SIZE = (1 << 16), +}; +ENUM_OPERATORS(BuiltinBits, BuiltinBits::WORK_GROUP_SIZE); + +/* Samplers & images. */ +enum class ImageType { + /** Color samplers/image. */ + FLOAT_BUFFER = 0, + FLOAT_1D, + FLOAT_1D_ARRAY, + FLOAT_2D, + FLOAT_2D_ARRAY, + FLOAT_3D, + FLOAT_CUBE, + FLOAT_CUBE_ARRAY, + INT_BUFFER, + INT_1D, + INT_1D_ARRAY, + INT_2D, + INT_2D_ARRAY, + INT_3D, + INT_CUBE, + INT_CUBE_ARRAY, + UINT_BUFFER, + UINT_1D, + UINT_1D_ARRAY, + UINT_2D, + UINT_2D_ARRAY, + UINT_3D, + UINT_CUBE, + UINT_CUBE_ARRAY, + /** Depth samplers (not supported as image). */ + SHADOW_2D, + SHADOW_2D_ARRAY, + SHADOW_CUBE, + SHADOW_CUBE_ARRAY, + DEPTH_2D, + DEPTH_2D_ARRAY, + DEPTH_CUBE, + DEPTH_CUBE_ARRAY, +}; + +/* Storage qualifiers. */ +enum class Qualifier { + RESTRICT = (1 << 0), + READ_ONLY = (1 << 1), + WRITE_ONLY = (1 << 2), + QUALIFIER_MAX = (WRITE_ONLY << 1) - 1, +}; +ENUM_OPERATORS(Qualifier, Qualifier::QUALIFIER_MAX); + +enum class Frequency { + BATCH = 0, + PASS, +}; + +/* Dual Source Blending Index. */ +enum class DualBlend { + NONE = 0, + SRC_0, + SRC_1, +}; + +/* Interpolation qualifiers. */ +enum class Interpolation { + SMOOTH = 0, + FLAT, + NO_PERSPECTIVE, +}; + +/** Input layout for geometry shader. */ +enum class InputLayout { + POINTS = 0, + LINES, + LINES_ADJACENCY, + TRIANGLES, + TRIANGLES_ADJACENCY, +}; + +/** Output layout for geometry shader. */ +enum class OutputLayout { + POINTS = 0, + LINE_STRIP, + TRIANGLE_STRIP, +}; + +struct StageInterfaceInfo { + struct InOut { + Interpolation interp; + Type type; + StringRefNull name; + }; + + StringRefNull name; + /** Name of the instance of the block (used to access). + * Can be empty string (i.e: "") only if not using geometry shader. */ + StringRefNull instance_name; + /** List of all members of the interface. */ + Vector inouts; + + StageInterfaceInfo(const char *name_, const char *instance_name_) + : name(name_), instance_name(instance_name_){}; + ~StageInterfaceInfo(){}; + + using Self = StageInterfaceInfo; + + Self &smooth(Type type, StringRefNull _name) + { + inouts.append({Interpolation::SMOOTH, type, _name}); + return *(Self *)this; + } + + Self &flat(Type type, StringRefNull _name) + { + inouts.append({Interpolation::FLAT, type, _name}); + return *(Self *)this; + } + + Self &no_perspective(Type type, StringRefNull _name) + { + inouts.append({Interpolation::NO_PERSPECTIVE, type, _name}); + return *(Self *)this; + } +}; + +/** + * @brief Describe inputs & outputs, stage interfaces, resources and sources of a shader. + * If all data is correctly provided, this is all that is needed to create and compile + * a GPUShader. + * + * IMPORTANT: All strings are references only. Make sure all the strings used by a + * ShaderCreateInfo are not freed until it is consumed or deleted. + */ +struct ShaderCreateInfo { + /** Shader name for debugging. */ + StringRefNull name_; + /** True if the shader is static and can be precompiled at compile time. */ + bool do_static_compilation_ = false; + /** If true, all additionaly linked create info will be merged into this one. */ + bool finalized_ = false; + /** + * Maximum length of all the resource names including each null terminator. + * Only for names used by gpu::ShaderInterface. + */ + size_t interface_names_size_ = 0; + /** Only for compute shaders. */ + int local_group_size_[3] = {0, 0, 0}; + + struct VertIn { + int index; + Type type; + StringRefNull name; + }; + Vector vertex_inputs_; + + struct GeomIn { + InputLayout layout; + }; + GeomIn geom_in_; + + struct GeomOut { + OutputLayout layout; + int max_vertices; + }; + GeomOut geom_out_; + + struct FragOut { + int index; + Type type; + DualBlend blend; + StringRefNull name; + }; + Vector fragment_outputs_; + + struct Sampler { + ImageType type; + eGPUSamplerState sampler; + StringRefNull name; + }; + + struct Image { + eGPUTextureFormat format; + ImageType type; + Qualifier qualifiers; + StringRefNull name; + }; + + struct UniformBuf { + StringRefNull type_name; + StringRefNull name; + }; + + struct StorageBuf { + Qualifier qualifiers; + StringRefNull type_name; + StringRefNull name; + }; + + struct Resource { + enum BindType { + UNIFORM_BUFFER = 0, + STORAGE_BUFFER, + SAMPLER, + IMAGE, + }; + + BindType bind_type; + int slot; + union { + Sampler sampler; + Image image; + UniformBuf uniformbuf; + StorageBuf storagebuf; + }; + + Resource(BindType type, int _slot) : bind_type(type), slot(_slot){}; + }; + /** + * Resources are grouped by frequency of change. + * Pass resources are meants to be valid for the whole pass. + * Batch resources can be changed in a more granular manner (per object/material). + * Mis-usage will only produce suboptimal performance. + */ + Vector pass_resources_, batch_resources_; + + Vector vertex_out_interfaces_; + Vector geometry_out_interfaces_; + + struct PushConst { + int index; + Type type; + StringRefNull name; + int array_size; + }; + + Vector push_constants_; + + /* Sources for resources type definitions. */ + Vector typedef_sources_; + + StringRefNull vertex_source_, geometry_source_, fragment_source_, compute_source_; + + Vector> defines_; + /** + * Name of other infos to recursively merge with this one. + * No data slot must overlap otherwise we throw an error. + */ + Vector additional_infos_; + + public: + ShaderCreateInfo(const char *name) : name_(name){}; + ~ShaderCreateInfo(){}; + + using Self = ShaderCreateInfo; + + /* -------------------------------------------------------------------- */ + /** \name Shaders in/outs (fixed function pipeline config) + * \{ */ + + Self &vertex_in(int slot, Type type, StringRefNull name) + { + vertex_inputs_.append({slot, type, name}); + interface_names_size_ += name.size() + 1; + return *(Self *)this; + } + + Self &vertex_out(StageInterfaceInfo &interface) + { + vertex_out_interfaces_.append(&interface); + return *(Self *)this; + } + + Self &geometry_layout(InputLayout layout_in, OutputLayout layout_out, int max_vertices) + { + geom_in_.layout = layout_in; + geom_out_.layout = layout_out; + geom_out_.max_vertices = max_vertices; + return *(Self *)this; + } + + /* Only needed if geometry shader is enabled. */ + Self &geometry_out(StageInterfaceInfo &interface) + { + geometry_out_interfaces_.append(&interface); + return *(Self *)this; + } + + Self &fragment_out(int slot, Type type, StringRefNull name, DualBlend blend = DualBlend::NONE) + { + fragment_outputs_.append({slot, type, blend, name}); + return *(Self *)this; + } + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Resources bindings points + * \{ */ + + Self &uniform_buf(int slot, + StringRefNull type_name, + StringRefNull name, + Frequency freq = Frequency::PASS) + { + Resource res(Resource::BindType::UNIFORM_BUFFER, slot); + res.uniformbuf.name = name; + res.uniformbuf.type_name = type_name; + ((freq == Frequency::PASS) ? pass_resources_ : batch_resources_).append(res); + interface_names_size_ += name.size() + 1; + return *(Self *)this; + } + + Self &storage_buf(int slot, + Qualifier qualifiers, + StringRefNull type_name, + StringRefNull name, + Frequency freq = Frequency::PASS) + { + Resource res(Resource::BindType::STORAGE_BUFFER, slot); + res.storagebuf.qualifiers = qualifiers; + res.storagebuf.type_name = type_name; + res.storagebuf.name = name; + ((freq == Frequency::PASS) ? pass_resources_ : batch_resources_).append(res); + interface_names_size_ += name.size() + 1; + return *(Self *)this; + } + + Self &image(int slot, + eGPUTextureFormat format, + Qualifier qualifiers, + ImageType type, + StringRefNull name, + Frequency freq = Frequency::PASS) + { + Resource res(Resource::BindType::IMAGE, slot); + res.image.format = format; + res.image.qualifiers = qualifiers; + res.image.type = type; + res.image.name = name; + ((freq == Frequency::PASS) ? pass_resources_ : batch_resources_).append(res); + interface_names_size_ += name.size() + 1; + return *(Self *)this; + } + + Self &sampler(int slot, + ImageType type, + StringRefNull name, + Frequency freq = Frequency::PASS, + eGPUSamplerState sampler = (eGPUSamplerState)-1) + { + Resource res(Resource::BindType::SAMPLER, slot); + res.sampler.type = type; + res.sampler.name = name; + res.sampler.sampler = sampler; + ((freq == Frequency::PASS) ? pass_resources_ : batch_resources_).append(res); + interface_names_size_ += name.size() + 1; + return *(Self *)this; + } + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Shader Source + * \{ */ + + Self &vertex_source(StringRefNull filename) + { + vertex_source_ = filename; + return *(Self *)this; + } + + Self &geometry_source(StringRefNull filename) + { + geometry_source_ = filename; + return *(Self *)this; + } + + Self &fragment_source(StringRefNull filename) + { + fragment_source_ = filename; + return *(Self *)this; + } + + Self &compute_source(StringRefNull filename) + { + compute_source_ = filename; + return *(Self *)this; + } + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Push constants + * + * Data managed by GPUShader. Can be set through uniform functions. Must be less than 128bytes. + * One slot represents 4bytes. Each element needs to have enough empty space left after it. + * example: + * [0] = PUSH_CONSTANT(MAT4, "ModelMatrix"), + * ---- 16 slots occupied by ModelMatrix ---- + * [16] = PUSH_CONSTANT(VEC4, "color"), + * ---- 4 slots occupied by color ---- + * [20] = PUSH_CONSTANT(BOOL, "srgbToggle"), + * The maximum slot is 31. + * \{ */ + + Self &push_constant(int slot, Type type, StringRefNull name, int array_size = 0) + { + BLI_assert_msg(name.find("[") == -1, + "Array syntax is forbidden for push constants." + "Use the array_size parameter instead."); + push_constants_.append({slot, type, name, array_size}); + interface_names_size_ += name.size() + 1; + return *(Self *)this; + } + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Compute shaders Local Group Size + * \{ */ + + Self &local_group_size(int x, int y = 1, int z = 1) + { + local_group_size_[0] = x; + local_group_size_[1] = y; + local_group_size_[2] = z; + return *(Self *)this; + } + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Defines + * \{ */ + + Self &define(StringRefNull name, StringRefNull value = "") + { + defines_.append({name, value}); + return *(Self *)this; + } + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Defines + * \{ */ + + Self &do_static_compilation(bool value) + { + do_static_compilation_ = value; + return *(Self *)this; + } + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Additional Create Info + * + * Used to share parts of the infos that are common to many shaders. + * \{ */ + + Self &additional_info(StringRefNull info_name0, + StringRefNull info_name1 = "", + StringRefNull info_name2 = "", + StringRefNull info_name3 = "", + StringRefNull info_name4 = "") + { + additional_infos_.append(info_name0); + if (!info_name1.is_empty()) { + additional_infos_.append(info_name1); + } + if (!info_name2.is_empty()) { + additional_infos_.append(info_name2); + } + if (!info_name3.is_empty()) { + additional_infos_.append(info_name3); + } + if (!info_name4.is_empty()) { + additional_infos_.append(info_name4); + } + return *(Self *)this; + } + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Typedef Sources + * + * Some resource declarations might need some special structure defined. + * Adding a file using typedef_source will include it before the resource + * and interface definitions. + * \{ */ + + Self &typedef_source(StringRefNull filename) + { + typedef_sources_.append(filename); + return *(Self *)this; + } + + /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Recursive evaluation. + * + * Flatten all dependency so that this descriptor contains all the data from the additional + * descriptors. This avoids tedious traversal in shader source creation. + * \{ */ + + /* WARNING: Recursive. */ + void finalize(); + + /** \} */ +}; + +} // namespace blender::gpu::shader -- cgit v1.2.3