Blender  V3.3
gpu_shader_create_info.hh
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2021 Blender Foundation. All rights reserved. */
3 
13 #pragma once
14 
15 #include "BLI_string_ref.hh"
16 #include "BLI_vector.hh"
17 #include "GPU_material.h"
18 #include "GPU_texture.h"
19 
20 #include <iostream>
21 
22 namespace blender::gpu::shader {
23 
24 #ifndef GPU_SHADER_CREATE_INFO
25 /* Helps intellisense / auto-completion. */
26 # define GPU_SHADER_INTERFACE_INFO(_interface, _inst_name) \
27  StageInterfaceInfo _interface(#_interface, _inst_name); \
28  _interface
29 # define GPU_SHADER_CREATE_INFO(_info) \
30  ShaderCreateInfo _info(#_info); \
31  _info
32 #endif
33 
34 enum class Type {
35  FLOAT = 0,
36  VEC2,
37  VEC3,
38  VEC4,
39  MAT3,
40  MAT4,
41  UINT,
42  UVEC2,
43  UVEC3,
44  UVEC4,
45  INT,
46  IVEC2,
47  IVEC3,
48  IVEC4,
49  BOOL,
50 };
51 
52 /* All of these functions is a bit out of place */
53 static inline Type to_type(const eGPUType type)
54 {
55  switch (type) {
56  case GPU_FLOAT:
57  return Type::FLOAT;
58  case GPU_VEC2:
59  return Type::VEC2;
60  case GPU_VEC3:
61  return Type::VEC3;
62  case GPU_VEC4:
63  return Type::VEC4;
64  case GPU_MAT3:
65  return Type::MAT3;
66  case GPU_MAT4:
67  return Type::MAT4;
68  default:
69  BLI_assert_msg(0, "Error: Cannot convert eGPUType to shader::Type.");
70  return Type::FLOAT;
71  }
72 }
73 
74 static inline std::ostream &operator<<(std::ostream &stream, const Type type)
75 {
76  switch (type) {
77  case Type::FLOAT:
78  return stream << "float";
79  case Type::VEC2:
80  return stream << "vec2";
81  case Type::VEC3:
82  return stream << "vec3";
83  case Type::VEC4:
84  return stream << "vec4";
85  case Type::MAT3:
86  return stream << "mat3";
87  case Type::MAT4:
88  return stream << "mat4";
89  default:
90  BLI_assert(0);
91  return stream;
92  }
93 }
94 
95 static inline std::ostream &operator<<(std::ostream &stream, const eGPUType type)
96 {
97  switch (type) {
98  case GPU_CLOSURE:
99  return stream << "Closure";
100  default:
101  return stream << to_type(type);
102  }
103 }
104 
105 enum class BuiltinBits {
106  NONE = 0,
111  BARYCENTRIC_COORD = (1 << 0),
112  FRAG_COORD = (1 << 2),
113  FRONT_FACING = (1 << 4),
114  GLOBAL_INVOCATION_ID = (1 << 5),
115  INSTANCE_ID = (1 << 6),
120  LAYER = (1 << 7),
121  LOCAL_INVOCATION_ID = (1 << 8),
122  LOCAL_INVOCATION_INDEX = (1 << 9),
123  NUM_WORK_GROUP = (1 << 10),
124  POINT_COORD = (1 << 11),
125  POINT_SIZE = (1 << 12),
126  PRIMITIVE_ID = (1 << 13),
127  VERTEX_ID = (1 << 14),
128  WORK_GROUP_ID = (1 << 15),
129  WORK_GROUP_SIZE = (1 << 16),
130 };
132 
137 enum class DepthWrite {
138  ANY = 0,
139  GREATER,
140  LESS,
141  UNCHANGED,
142 };
143 
144 /* Samplers & images. */
145 enum class ImageType {
147  FLOAT_BUFFER = 0,
148  FLOAT_1D,
150  FLOAT_2D,
152  FLOAT_3D,
153  FLOAT_CUBE,
155  INT_BUFFER,
156  INT_1D,
157  INT_1D_ARRAY,
158  INT_2D,
159  INT_2D_ARRAY,
160  INT_3D,
161  INT_CUBE,
163  UINT_BUFFER,
164  UINT_1D,
166  UINT_2D,
168  UINT_3D,
169  UINT_CUBE,
172  SHADOW_2D,
174  SHADOW_CUBE,
176  DEPTH_2D,
178  DEPTH_CUBE,
180 };
181 
182 /* Storage qualifiers. */
183 enum class Qualifier {
185  NO_RESTRICT = (1 << 0),
186  READ = (1 << 1),
187  WRITE = (1 << 2),
189  READ_WRITE = READ | WRITE,
190  QUALIFIER_MAX = (WRITE << 1) - 1,
191 };
193 
194 enum class Frequency {
195  BATCH = 0,
196  PASS,
197 };
198 
199 /* Dual Source Blending Index. */
200 enum class DualBlend {
201  NONE = 0,
202  SRC_0,
203  SRC_1,
204 };
205 
206 /* Interpolation qualifiers. */
207 enum class Interpolation {
208  SMOOTH = 0,
209  FLAT,
211 };
212 
214 enum class PrimitiveIn {
215  POINTS = 0,
216  LINES,
218  TRIANGLES,
220 };
221 
223 enum class PrimitiveOut {
224  POINTS = 0,
225  LINE_STRIP,
227 };
228 
230  struct InOut {
234  };
235 
242 
243  StageInterfaceInfo(const char *name_, const char *instance_name_)
244  : name(name_), instance_name(instance_name_){};
246 
248 
250  {
251  inouts.append({Interpolation::SMOOTH, type, _name});
252  return *(Self *)this;
253  }
254 
256  {
257  inouts.append({Interpolation::FLAT, type, _name});
258  return *(Self *)this;
259  }
260 
262  {
263  inouts.append({Interpolation::NO_PERSPECTIVE, type, _name});
264  return *(Self *)this;
265  }
266 };
267 
282  bool finalized_ = false;
286  bool early_fragment_test_ = false;
299  std::string vertex_source_generated = "";
300  std::string fragment_source_generated = "";
301  std::string geometry_source_generated = "";
302  std::string typedef_source_generated = "";
305 
306 #define TEST_EQUAL(a, b, _member) \
307  if (!((a)._member == (b)._member)) { \
308  return false; \
309  }
310 
311 #define TEST_VECTOR_EQUAL(a, b, _vector) \
312  TEST_EQUAL(a, b, _vector.size()); \
313  for (auto i : _vector.index_range()) { \
314  TEST_EQUAL(a, b, _vector[i]); \
315  }
316 
317  struct VertIn {
318  int index;
321 
322  bool operator==(const VertIn &b)
323  {
324  TEST_EQUAL(*this, b, index);
325  TEST_EQUAL(*this, b, type);
326  TEST_EQUAL(*this, b, name);
327  return true;
328  }
329  };
331 
337  int max_vertices = -1;
338 
340  {
341  TEST_EQUAL(*this, b, primitive_in);
342  TEST_EQUAL(*this, b, invocations);
343  TEST_EQUAL(*this, b, primitive_out);
344  TEST_EQUAL(*this, b, max_vertices);
345  return true;
346  }
347  };
349 
351  int local_size_x = -1;
352  int local_size_y = -1;
353  int local_size_z = -1;
354 
356  {
357  TEST_EQUAL(*this, b, local_size_x);
358  TEST_EQUAL(*this, b, local_size_y);
359  TEST_EQUAL(*this, b, local_size_z);
360  return true;
361  }
362  };
364 
365  struct FragOut {
366  int index;
370 
371  bool operator==(const FragOut &b)
372  {
373  TEST_EQUAL(*this, b, index);
374  TEST_EQUAL(*this, b, type);
375  TEST_EQUAL(*this, b, blend);
376  TEST_EQUAL(*this, b, name);
377  return true;
378  }
379  };
381 
382  struct Sampler {
386  };
387 
388  struct Image {
393  };
394 
395  struct UniformBuf {
398  };
399 
400  struct StorageBuf {
404  };
405 
406  struct Resource {
407  enum BindType {
412  };
413 
415  int slot;
416  union {
421  };
422 
423  Resource(BindType type, int _slot) : bind_type(type), slot(_slot){};
424 
425  bool operator==(const Resource &b)
426  {
427  TEST_EQUAL(*this, b, bind_type);
428  TEST_EQUAL(*this, b, slot);
429  switch (bind_type) {
430  case UNIFORM_BUFFER:
431  TEST_EQUAL(*this, b, uniformbuf.type_name);
432  TEST_EQUAL(*this, b, uniformbuf.name);
433  break;
434  case STORAGE_BUFFER:
436  TEST_EQUAL(*this, b, storagebuf.type_name);
437  TEST_EQUAL(*this, b, storagebuf.name);
438  break;
439  case SAMPLER:
440  TEST_EQUAL(*this, b, sampler.type);
441  TEST_EQUAL(*this, b, sampler.sampler);
442  TEST_EQUAL(*this, b, sampler.name);
443  break;
444  case IMAGE:
445  TEST_EQUAL(*this, b, image.format);
446  TEST_EQUAL(*this, b, image.type);
447  TEST_EQUAL(*this, b, image.qualifiers);
448  TEST_EQUAL(*this, b, image.name);
449  break;
450  }
451  return true;
452  }
453  };
461 
464 
465  struct PushConst {
469 
470  bool operator==(const PushConst &b)
471  {
472  TEST_EQUAL(*this, b, type);
473  TEST_EQUAL(*this, b, name);
474  TEST_EQUAL(*this, b, array_size);
475  return true;
476  }
477  };
478 
480 
481  /* Sources for resources type definitions. */
483 
485 
492 
493  public:
494  ShaderCreateInfo(const char *name) : name_(name){};
496 
498 
499  /* -------------------------------------------------------------------- */
504  {
505  vertex_inputs_.append({slot, type, name});
506  interface_names_size_ += name.size() + 1;
507  return *(Self *)this;
508  }
509 
511  {
512  vertex_out_interfaces_.append(&interface);
513  return *(Self *)this;
514  }
515 
523  PrimitiveOut prim_out,
524  int max_vertices,
525  int invocations = -1)
526  {
527  geometry_layout_.primitive_in = prim_in;
528  geometry_layout_.primitive_out = prim_out;
529  geometry_layout_.max_vertices = max_vertices;
530  geometry_layout_.invocations = invocations;
531  return *(Self *)this;
532  }
533 
534  Self &local_group_size(int local_size_x = -1, int local_size_y = -1, int local_size_z = -1)
535  {
536  compute_layout_.local_size_x = local_size_x;
537  compute_layout_.local_size_y = local_size_y;
538  compute_layout_.local_size_z = local_size_z;
539  return *(Self *)this;
540  }
541 
546  Self &early_fragment_test(bool enable)
547  {
548  early_fragment_test_ = enable;
549  return *(Self *)this;
550  }
551 
559  {
560  geometry_out_interfaces_.append(&interface);
561  return *(Self *)this;
562  }
563 
565  {
566  fragment_outputs_.append({slot, type, blend, name});
567  return *(Self *)this;
568  }
569 
572  /* -------------------------------------------------------------------- */
576  Self &uniform_buf(int slot,
577  StringRefNull type_name,
578  StringRefNull name,
579  Frequency freq = Frequency::PASS)
580  {
581  Resource res(Resource::BindType::UNIFORM_BUFFER, slot);
582  res.uniformbuf.name = name;
583  res.uniformbuf.type_name = type_name;
584  ((freq == Frequency::PASS) ? pass_resources_ : batch_resources_).append(res);
585  interface_names_size_ += name.size() + 1;
586  return *(Self *)this;
587  }
588 
589  Self &storage_buf(int slot,
590  Qualifier qualifiers,
591  StringRefNull type_name,
592  StringRefNull name,
593  Frequency freq = Frequency::PASS)
594  {
595  Resource res(Resource::BindType::STORAGE_BUFFER, slot);
596  res.storagebuf.qualifiers = qualifiers;
597  res.storagebuf.type_name = type_name;
598  res.storagebuf.name = name;
599  ((freq == Frequency::PASS) ? pass_resources_ : batch_resources_).append(res);
600  interface_names_size_ += name.size() + 1;
601  return *(Self *)this;
602  }
603 
604  Self &image(int slot,
606  Qualifier qualifiers,
607  ImageType type,
608  StringRefNull name,
609  Frequency freq = Frequency::PASS)
610  {
611  Resource res(Resource::BindType::IMAGE, slot);
612  res.image.format = format;
613  res.image.qualifiers = qualifiers;
614  res.image.type = type;
615  res.image.name = name;
616  ((freq == Frequency::PASS) ? pass_resources_ : batch_resources_).append(res);
617  interface_names_size_ += name.size() + 1;
618  return *(Self *)this;
619  }
620 
621  Self &sampler(int slot,
622  ImageType type,
623  StringRefNull name,
624  Frequency freq = Frequency::PASS,
626  {
627  Resource res(Resource::BindType::SAMPLER, slot);
628  res.sampler.type = type;
629  res.sampler.name = name;
630  /* Produces ASAN errors for the moment. */
631  // res.sampler.sampler = sampler;
633  ((freq == Frequency::PASS) ? pass_resources_ : batch_resources_).append(res);
634  interface_names_size_ += name.size() + 1;
635  return *(Self *)this;
636  }
637 
640  /* -------------------------------------------------------------------- */
645  {
646  vertex_source_ = filename;
647  return *(Self *)this;
648  }
649 
651  {
652  geometry_source_ = filename;
653  return *(Self *)this;
654  }
655 
657  {
658  fragment_source_ = filename;
659  return *(Self *)this;
660  }
661 
663  {
664  compute_source_ = filename;
665  return *(Self *)this;
666  }
667 
670  /* -------------------------------------------------------------------- */
676  Self &push_constant(Type type, StringRefNull name, int array_size = 0)
677  {
678  BLI_assert_msg(name.find("[") == -1,
679  "Array syntax is forbidden for push constants."
680  "Use the array_size parameter instead.");
681  push_constants_.append({type, name, array_size});
682  interface_names_size_ += name.size() + 1;
683  return *(Self *)this;
684  }
685 
688  /* -------------------------------------------------------------------- */
693  {
694  defines_.append({name, value});
695  return *(Self *)this;
696  }
697 
700  /* -------------------------------------------------------------------- */
705  {
706  do_static_compilation_ = value;
707  return *(Self *)this;
708  }
709 
711  {
712  builtins_ |= builtin;
713  return *(Self *)this;
714  }
715 
716  /* Defines how the fragment shader will write to gl_FragDepth. */
718  {
719  depth_write_ = value;
720  return *(Self *)this;
721  }
722 
724  {
725  auto_resource_location_ = value;
726  return *(Self *)this;
727  }
728 
730  {
732  return *(Self *)this;
733  }
734 
737  /* -------------------------------------------------------------------- */
744  StringRefNull info_name1 = "",
745  StringRefNull info_name2 = "",
746  StringRefNull info_name3 = "",
747  StringRefNull info_name4 = "",
748  StringRefNull info_name5 = "",
749  StringRefNull info_name6 = "")
750  {
751  additional_infos_.append(info_name0);
752  if (!info_name1.is_empty()) {
753  additional_infos_.append(info_name1);
754  }
755  if (!info_name2.is_empty()) {
756  additional_infos_.append(info_name2);
757  }
758  if (!info_name3.is_empty()) {
759  additional_infos_.append(info_name3);
760  }
761  if (!info_name4.is_empty()) {
762  additional_infos_.append(info_name4);
763  }
764  if (!info_name5.is_empty()) {
765  additional_infos_.append(info_name5);
766  }
767  if (!info_name6.is_empty()) {
768  additional_infos_.append(info_name6);
769  }
770  return *(Self *)this;
771  }
772 
775  /* -------------------------------------------------------------------- */
784  {
785  typedef_sources_.append(filename);
786  return *(Self *)this;
787  }
788 
791  /* -------------------------------------------------------------------- */
798  /* WARNING: Recursive. */
799  void finalize();
800 
801  std::string check_error() const;
802 
804  void validate_merge(const ShaderCreateInfo &other_info);
805  void validate_vertex_attributes(const ShaderCreateInfo *other_info = nullptr);
806 
809  /* -------------------------------------------------------------------- */
814  /* Comparison operator for GPUPass cache. We only compare if it will create the same shader code.
815  * So we do not compare name and some other internal stuff. */
817  {
818  TEST_EQUAL(*this, b, builtins_);
823  TEST_EQUAL(*this, b, geometry_layout_);
824  TEST_EQUAL(*this, b, compute_layout_);
832  TEST_EQUAL(*this, b, vertex_source_);
833  TEST_EQUAL(*this, b, geometry_source_);
834  TEST_EQUAL(*this, b, fragment_source_);
835  TEST_EQUAL(*this, b, compute_source_);
837  TEST_VECTOR_EQUAL(*this, b, defines_);
838  return true;
839  }
840 
842  friend std::ostream &operator<<(std::ostream &stream, const ShaderCreateInfo &info)
843  {
844  /* TODO(@fclem): Complete print. */
845 
846  auto print_resource = [&](const Resource &res) {
847  switch (res.bind_type) {
848  case Resource::BindType::UNIFORM_BUFFER:
849  stream << "UNIFORM_BUFFER(" << res.slot << ", " << res.uniformbuf.name << ")"
850  << std::endl;
851  break;
852  case Resource::BindType::STORAGE_BUFFER:
853  stream << "STORAGE_BUFFER(" << res.slot << ", " << res.storagebuf.name << ")"
854  << std::endl;
855  break;
856  case Resource::BindType::SAMPLER:
857  stream << "SAMPLER(" << res.slot << ", " << res.sampler.name << ")" << std::endl;
858  break;
859  case Resource::BindType::IMAGE:
860  stream << "IMAGE(" << res.slot << ", " << res.image.name << ")" << std::endl;
861  break;
862  }
863  };
864 
865  /* TODO(@fclem): Order the resources. */
866  for (auto &res : info.batch_resources_) {
867  print_resource(res);
868  }
869  for (auto &res : info.pass_resources_) {
870  print_resource(res);
871  }
872  return stream;
873  }
874 
875  bool has_resource_type(Resource::BindType bind_type) const
876  {
877  for (auto &res : batch_resources_) {
878  if (res.bind_type == bind_type) {
879  return true;
880  }
881  }
882  for (auto &res : pass_resources_) {
883  if (res.bind_type == bind_type) {
884  return true;
885  }
886  }
887  return false;
888  }
889 
890  bool has_resource_image() const
891  {
892  return has_resource_type(Resource::BindType::IMAGE);
893  }
894 
895  bool has_resource_storage() const
896  {
897  return has_resource_type(Resource::BindType::STORAGE_BUFFER);
898  }
899 
902 #undef TEST_EQUAL
903 #undef TEST_VECTOR_EQUAL
904 };
905 
906 } // namespace blender::gpu::shader
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
#define UNUSED_VARS(...)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
eGPUType
Definition: GPU_material.h:45
@ GPU_VEC2
Definition: GPU_material.h:50
@ GPU_MAT4
Definition: GPU_material.h:54
@ GPU_VEC4
Definition: GPU_material.h:52
@ GPU_CLOSURE
Definition: GPU_material.h:64
@ GPU_VEC3
Definition: GPU_material.h:51
@ GPU_MAT3
Definition: GPU_material.h:53
@ GPU_FLOAT
Definition: GPU_material.h:49
eGPUSamplerState
Definition: GPU_texture.h:25
eGPUTextureFormat
Definition: GPU_texture.h:83
constexpr int64_t find(char c, int64_t pos=0) const
constexpr int64_t size() const
static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &res)
Definition: gl_shader.cc:363
#define TEST_VECTOR_EQUAL(a, b, _vector)
#define TEST_EQUAL(a, b, _member)
format
Definition: logImageCore.h:38
static Type to_type(const eGPUType type)
static std::ostream & operator<<(std::ostream &stream, const Type type)
ENUM_OPERATORS(BuiltinBits, BuiltinBits::WORK_GROUP_SIZE)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
Self & typedef_source(StringRefNull filename)
Vector< StageInterfaceInfo * > vertex_out_interfaces_
Self & uniform_buf(int slot, StringRefNull type_name, StringRefNull name, Frequency freq=Frequency::PASS)
Self & sampler(int slot, ImageType type, StringRefNull name, Frequency freq=Frequency::PASS, eGPUSamplerState sampler=(eGPUSamplerState) -1)
Vector< std::array< StringRefNull, 2 > > defines_
Self & geometry_source(StringRefNull filename)
void validate_vertex_attributes(const ShaderCreateInfo *other_info=nullptr)
Self & fragment_source(StringRefNull filename)
Self & geometry_out(StageInterfaceInfo &interface)
Self & storage_buf(int slot, Qualifier qualifiers, StringRefNull type_name, StringRefNull name, Frequency freq=Frequency::PASS)
Self & additional_info(StringRefNull info_name0, StringRefNull info_name1="", StringRefNull info_name2="", StringRefNull info_name3="", StringRefNull info_name4="", StringRefNull info_name5="", StringRefNull info_name6="")
friend std::ostream & operator<<(std::ostream &stream, const ShaderCreateInfo &info)
Self & local_group_size(int local_size_x=-1, int local_size_y=-1, int local_size_z=-1)
Vector< StageInterfaceInfo * > geometry_out_interfaces_
Self & compute_source(StringRefNull filename)
Self & vertex_in(int slot, Type type, StringRefNull name)
bool has_resource_type(Resource::BindType bind_type) const
Self & vertex_out(StageInterfaceInfo &interface)
Self & push_constant(Type type, StringRefNull name, int array_size=0)
Self & define(StringRefNull name, StringRefNull value="")
bool operator==(const ShaderCreateInfo &b)
Self & vertex_source(StringRefNull filename)
Self & geometry_layout(PrimitiveIn prim_in, PrimitiveOut prim_out, int max_vertices, int invocations=-1)
void validate_merge(const ShaderCreateInfo &other_info)
Self & image(int slot, eGPUTextureFormat format, Qualifier qualifiers, ImageType type, StringRefNull name, Frequency freq=Frequency::PASS)
Self & fragment_out(int slot, Type type, StringRefNull name, DualBlend blend=DualBlend::NONE)
Self & no_perspective(Type type, StringRefNull _name)
StageInterfaceInfo(const char *name_, const char *instance_name_)
Self & smooth(Type type, StringRefNull _name)
Self & flat(Type type, StringRefNull _name)
static int blend(const Tex *tex, const float texvec[3], TexResult *texres)