This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/gpu/intern/gpu_shader_create_info.hh
Jeroen Bakker 8fb2ff458b GPUShaderCreateInfo for interface abstraction
This is a first part of the Shader Create Info system could be.

A shader create info provides a way to define shader structure, resources
and interfaces. This makes for a quick way to provide backend agnostic
binding informations while also making shader variations easy to declare.

- Clear source input (only one file). Cleans up the GPU api since we can create a
  shader from one descriptor
- Resources and interfaces are generated by the backend (much simpler than parsing).
- Bindings are explicit from position in the array.
- GPUShaderInterface becomes a trivial translation of enums and string copy.
- No external dependency to third party lib.
- Cleaner code, less fragmentation of resources in several libs.
- Easy to modify / extend at runtime.
- no parser involve, very easy to code.
- Does not hold any data, can be static and kept on disc.
- Could hold precompiled bytecode for static shaders.

This also includes a new global dependency system.
GLSL shaders can include other sources by using #pragma BLENDER_REQUIRE(...).

This patch already migrated several builtin shaders. Other shaders should be migrated
one at a time, and could be done inside master.

There is a new compile directive `WITH_GPU_SHADER_BUILDER` this is an optional
directive for linting shaders to increase turn around time.

What is remaining:
- pyGPU API {T94975}
- Migration of other shaders. This could be a community effort.

Reviewed By: jbakker

Maniphest Tasks: T94975

Differential Revision: https://developer.blender.org/D13360
2022-01-17 14:32:28 +01:00

596 lines
15 KiB
C++

/*
* 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<InOut> 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<VertIn> 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<FragOut> 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<Resource> pass_resources_, batch_resources_;
Vector<StageInterfaceInfo *> vertex_out_interfaces_;
Vector<StageInterfaceInfo *> geometry_out_interfaces_;
struct PushConst {
int index;
Type type;
StringRefNull name;
int array_size;
};
Vector<PushConst> push_constants_;
/* Sources for resources type definitions. */
Vector<StringRefNull> typedef_sources_;
StringRefNull vertex_source_, geometry_source_, fragment_source_, compute_source_;
Vector<std::array<StringRefNull, 2>> defines_;
/**
* Name of other infos to recursively merge with this one.
* No data slot must overlap otherwise we throw an error.
*/
Vector<StringRefNull> 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