Blender  V3.3
gpu_shader_create_info.cc
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 
10 #include "BLI_map.hh"
11 #include "BLI_set.hh"
12 #include "BLI_string_ref.hh"
13 
14 #include "GPU_capabilities.h"
15 #include "GPU_platform.h"
16 #include "GPU_shader.h"
17 #include "GPU_texture.h"
18 
22 
23 #undef GPU_SHADER_INTERFACE_INFO
24 #undef GPU_SHADER_CREATE_INFO
25 
27 
30 
33 
35 {
36  if (finalized_) {
37  return;
38  }
39  finalized_ = true;
40 
41  Set<StringRefNull> deps_merged;
42 
44 
45  for (auto &info_name : additional_infos_) {
46  const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(
47  gpu_shader_create_info_get(info_name.c_str()));
48 
49  /* Recursive. */
50  const_cast<ShaderCreateInfo &>(info).finalize();
51 
53 
54  vertex_inputs_.extend(info.vertex_inputs_);
58 
60 
61  push_constants_.extend(info.push_constants_);
62  defines_.extend(info.defines_);
63 
65  pass_resources_.extend(info.pass_resources_);
66  typedef_sources_.extend_non_duplicates(info.typedef_sources_);
67 
68  if (info.early_fragment_test_) {
69  early_fragment_test_ = true;
70  }
71  if (info.depth_write_ != DepthWrite::ANY) {
73  }
74 
75  validate_merge(info);
76 
77  auto assert_no_overlap = [&](const bool test, const StringRefNull error) {
78  if (!test) {
79  std::cout << name_ << ": Validation failed while merging " << info.name_ << " : ";
80  std::cout << error << std::endl;
81  BLI_assert(0);
82  }
83  };
84 
85  if (!deps_merged.add(info.name_)) {
86  assert_no_overlap(false, "additional info already merged via another info");
87  }
88 
89  if (info.compute_layout_.local_size_x != -1) {
90  assert_no_overlap(compute_layout_.local_size_x == -1, "Compute layout already defined");
92  }
93 
94  if (!info.vertex_source_.is_empty()) {
95  assert_no_overlap(vertex_source_.is_empty(), "Vertex source already existing");
97  }
98  if (!info.geometry_source_.is_empty()) {
99  assert_no_overlap(geometry_source_.is_empty(), "Geometry source already existing");
102  }
103  if (!info.fragment_source_.is_empty()) {
104  assert_no_overlap(fragment_source_.is_empty(), "Fragment source already existing");
106  }
107  if (!info.compute_source_.is_empty()) {
108  assert_no_overlap(compute_source_.is_empty(), "Compute source already existing");
110  }
111  }
112 
114  int images = 0, samplers = 0, ubos = 0, ssbos = 0;
115 
116  auto set_resource_slot = [&](Resource &res) {
117  switch (res.bind_type) {
118  case Resource::BindType::UNIFORM_BUFFER:
119  res.slot = ubos++;
120  break;
121  case Resource::BindType::STORAGE_BUFFER:
122  res.slot = ssbos++;
123  break;
124  case Resource::BindType::SAMPLER:
125  res.slot = samplers++;
126  break;
127  case Resource::BindType::IMAGE:
128  res.slot = images++;
129  break;
130  }
131  };
132 
133  for (auto &res : batch_resources_) {
134  set_resource_slot(res);
135  }
136  for (auto &res : pass_resources_) {
137  set_resource_slot(res);
138  }
139  }
140 }
141 
142 std::string ShaderCreateInfo::check_error() const
143 {
144  std::string error;
145 
146  /* At least a vertex shader and a fragment shader are required, or only a compute shader. */
147  if (this->compute_source_.is_empty()) {
148  if (this->vertex_source_.is_empty()) {
149  error += "Missing vertex shader in " + this->name_ + ".\n";
150  }
151  if (this->fragment_source_.is_empty()) {
152  error += "Missing fragment shader in " + this->name_ + ".\n";
153  }
154  }
155  else {
156  if (!this->vertex_source_.is_empty()) {
157  error += "Compute shader has vertex_source_ shader attached in " + this->name_ + ".\n";
158  }
159  if (!this->geometry_source_.is_empty()) {
160  error += "Compute shader has geometry_source_ shader attached in " + this->name_ + ".\n";
161  }
162  if (!this->fragment_source_.is_empty()) {
163  error += "Compute shader has fragment_source_ shader attached in " + this->name_ + ".\n";
164  }
165  }
166 
167  return error;
168 }
169 
171 {
173  /* Check same bind-points usage in OGL. */
174  Set<int> images, samplers, ubos, ssbos;
175 
176  auto register_resource = [&](const Resource &res) -> bool {
177  switch (res.bind_type) {
178  case Resource::BindType::UNIFORM_BUFFER:
179  return images.add(res.slot);
180  case Resource::BindType::STORAGE_BUFFER:
181  return samplers.add(res.slot);
182  case Resource::BindType::SAMPLER:
183  return ubos.add(res.slot);
184  case Resource::BindType::IMAGE:
185  return ssbos.add(res.slot);
186  default:
187  return false;
188  }
189  };
190 
191  auto print_error_msg = [&](const Resource &res) {
192  std::cout << name_ << ": Validation failed : Overlapping ";
193 
194  switch (res.bind_type) {
195  case Resource::BindType::UNIFORM_BUFFER:
196  std::cout << "Uniform Buffer " << res.uniformbuf.name;
197  break;
198  case Resource::BindType::STORAGE_BUFFER:
199  std::cout << "Storage Buffer " << res.storagebuf.name;
200  break;
201  case Resource::BindType::SAMPLER:
202  std::cout << "Sampler " << res.sampler.name;
203  break;
204  case Resource::BindType::IMAGE:
205  std::cout << "Image " << res.image.name;
206  break;
207  default:
208  std::cout << "Unknown Type";
209  break;
210  }
211  std::cout << " (" << res.slot << ") while merging " << other_info.name_ << std::endl;
212  };
213 
214  for (auto &res : batch_resources_) {
215  if (register_resource(res) == false) {
216  print_error_msg(res);
217  }
218  }
219 
220  for (auto &res : pass_resources_) {
221  if (register_resource(res) == false) {
222  print_error_msg(res);
223  }
224  }
225  }
226 }
227 
229 {
230  uint32_t attr_bits = 0;
231  for (auto &attr : vertex_inputs_) {
232  if (attr.index >= 16 || attr.index < 0) {
233  std::cout << name_ << ": \"" << attr.name
234  << "\" : Type::MAT3 unsupported as vertex attribute." << std::endl;
235  BLI_assert(0);
236  }
237  if (attr.index >= 16 || attr.index < 0) {
238  std::cout << name_ << ": Invalid index for attribute \"" << attr.name << "\"" << std::endl;
239  BLI_assert(0);
240  }
241  uint32_t attr_new = 0;
242  if (attr.type == Type::MAT4) {
243  for (int i = 0; i < 4; i++) {
244  attr_new |= 1 << (attr.index + i);
245  }
246  }
247  else {
248  attr_new |= 1 << attr.index;
249  }
250 
251  if ((attr_bits & attr_new) != 0) {
252  std::cout << name_ << ": Attribute \"" << attr.name
253  << "\" overlap one or more index from another attribute."
254  " Note that mat4 takes up 4 indices.";
255  if (other_info) {
256  std::cout << " While merging " << other_info->name_ << std::endl;
257  }
258  std::cout << std::endl;
259  BLI_assert(0);
260  }
261  attr_bits |= attr_new;
262  }
263 }
264 
265 } // namespace blender::gpu::shader
266 
267 using namespace blender::gpu::shader;
268 
270 {
273 
274 #define GPU_SHADER_INTERFACE_INFO(_interface, _inst_name) \
275  auto *ptr_##_interface = new StageInterfaceInfo(#_interface, _inst_name); \
276  auto &_interface = *ptr_##_interface; \
277  g_interfaces->add_new(#_interface, ptr_##_interface); \
278  _interface
279 
280 #define GPU_SHADER_CREATE_INFO(_info) \
281  auto *ptr_##_info = new ShaderCreateInfo(#_info); \
282  auto &_info = *ptr_##_info; \
283  g_create_infos->add_new(#_info, ptr_##_info); \
284  _info
285 
286 /* Declare, register and construct the infos. */
287 #include "gpu_shader_create_info_list.hh"
288 
289 /* Baked shader data appended to create infos. */
290 /* TODO(jbakker): should call a function with a callback. so we could switch implementations.
291  * We cannot compile bf_gpu twice. */
292 #ifdef GPU_RUNTIME
293 # include "gpu_shader_baked.hh"
294 #endif
295 
296  /* WORKAROUND: Replace draw_mesh info with the legacy one for systems that have problems with UBO
297  * indexing. */
300  draw_modelmat = draw_modelmat_legacy;
301  }
302 
303  for (ShaderCreateInfo *info : g_create_infos->values()) {
304  if (info->do_static_compilation_) {
305  info->builtins_ |= gpu_shader_dependency_get_builtins(info->vertex_source_);
306  info->builtins_ |= gpu_shader_dependency_get_builtins(info->fragment_source_);
307  info->builtins_ |= gpu_shader_dependency_get_builtins(info->geometry_source_);
308  info->builtins_ |= gpu_shader_dependency_get_builtins(info->compute_source_);
309  }
310  }
311 
312  /* TEST */
313  // gpu_shader_create_info_compile_all();
314 }
315 
317 {
318  for (auto *value : g_create_infos->values()) {
319  delete value;
320  }
321  delete g_create_infos;
322 
323  for (auto *value : g_interfaces->values()) {
324  delete value;
325  }
326  delete g_interfaces;
327 }
328 
330 {
331  using namespace blender::gpu;
332  int success = 0;
333  int skipped = 0;
334  int total = 0;
335  for (ShaderCreateInfo *info : g_create_infos->values()) {
336  info->finalize();
337  if (info->do_static_compilation_) {
338  if ((GPU_compute_shader_support() == false && info->compute_source_ != nullptr) ||
339  (GPU_shader_image_load_store_support() == false && info->has_resource_image()) ||
340  (GPU_shader_storage_buffer_objects_support() == false && info->has_resource_storage())) {
341  skipped++;
342  continue;
343  }
344  total++;
346  reinterpret_cast<const GPUShaderCreateInfo *>(info));
347  if (shader == nullptr) {
348  printf("Compilation %s Failed\n", info->name_.c_str());
349  }
350  else {
351  success++;
352 
353 #if 0 /* TODO(fclem): This is too verbose for now. Make it a cmake option. */
354  /* Test if any resource is optimized out and print a warning if that's the case. */
355  /* TODO(fclem): Limit this to OpenGL backend. */
356  const ShaderInterface *interface = unwrap(shader)->interface;
357 
359  all_resources.extend(info->pass_resources_);
360  all_resources.extend(info->batch_resources_);
361 
362  for (ShaderCreateInfo::Resource &res : all_resources) {
363  blender::StringRefNull name = "";
364  const ShaderInput *input = nullptr;
365 
366  switch (res.bind_type) {
367  case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
368  input = interface->ubo_get(res.slot);
369  name = res.uniformbuf.name;
370  break;
371  case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
372  input = interface->ssbo_get(res.slot);
373  name = res.storagebuf.name;
374  break;
375  case ShaderCreateInfo::Resource::BindType::SAMPLER:
376  input = interface->texture_get(res.slot);
377  name = res.sampler.name;
378  break;
379  case ShaderCreateInfo::Resource::BindType::IMAGE:
380  input = interface->texture_get(res.slot);
381  name = res.image.name;
382  break;
383  }
384 
385  if (input == nullptr) {
386  std::cout << "Error: " << info->name_;
387  std::cout << ": Resource « " << name << " » not found in the shader interface\n";
388  }
389  else if (input->location == -1) {
390  std::cout << "Warning: " << info->name_;
391  std::cout << ": Resource « " << name << " » is optimized out\n";
392  }
393  }
394 #endif
395  }
396  GPU_shader_free(shader);
397  }
398  }
399  printf("Shader Test compilation result: %d / %d passed", success, total);
400  if (skipped > 0) {
401  printf(" (skipped %d for compatibility reasons)", skipped);
402  }
403  printf("\n");
404  return success == total;
405 }
406 
407 /* Runtime create infos are not registered in the dictionary and cannot be searched. */
408 const GPUShaderCreateInfo *gpu_shader_create_info_get(const char *info_name)
409 {
410  if (g_create_infos->contains(info_name) == false) {
411  printf("Error: Cannot find shader create info named \"%s\"\n", info_name);
412  return nullptr;
413  }
414  ShaderCreateInfo *info = g_create_infos->lookup(info_name);
415  return reinterpret_cast<const GPUShaderCreateInfo *>(info);
416 }
#define BLI_assert(a)
Definition: BLI_assert.h:46
bool GPU_shader_image_load_store_support(void)
bool GPU_compute_shader_support(void)
bool GPU_crappy_amd_driver(void)
bool GPU_shader_storage_buffer_objects_support(void)
@ GPU_DRIVER_ANY
Definition: GPU_platform.h:47
@ GPU_OS_ANY
Definition: GPU_platform.h:40
@ GPU_OS_MAC
Definition: GPU_platform.h:38
@ GPU_DEVICE_INTEL_UHD
Definition: GPU_platform.h:27
@ GPU_DEVICE_ANY
Definition: GPU_platform.h:31
@ GPU_DEVICE_INTEL
Definition: GPU_platform.h:26
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
struct GPUShader GPUShader
Definition: GPU_shader.h:20
struct GPUShaderCreateInfo GPUShaderCreateInfo
Definition: GPU_shader.h:18
void GPU_shader_free(GPUShader *shader)
Definition: gpu_shader.cc:200
GPUShader * GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
Definition: gpu_shader.cc:277
ValueIterator values() const
Definition: BLI_map.hh:840
const Value & lookup(const Key &key) const
Definition: BLI_map.hh:485
bool contains(const Key &key) const
Definition: BLI_map.hh:308
bool add(const Key &key)
Definition: BLI_set.hh:253
constexpr bool is_empty() const
void extend(Span< T > array)
Definition: BLI_vector.hh:530
void gpu_shader_create_info_exit()
const GPUShaderCreateInfo * gpu_shader_create_info_get(const char *info_name)
void gpu_shader_create_info_init()
bool gpu_shader_create_info_compile_all()
ccl_global KernelShaderEvalInput * input
static void error(const char *str)
Definition: meshlaplacian.c:51
Map< StringRef, StageInterfaceInfo * > InterfaceDictionnary
BuiltinBits gpu_shader_dependency_get_builtins(const StringRefNull shader_source_name)
static InterfaceDictionnary * g_interfaces
Map< StringRef, ShaderCreateInfo * > CreateInfoDictionnary
static CreateInfoDictionnary * g_create_infos
pos fragColor draw_modelmat
unsigned int uint32_t
Definition: stdint.h:80
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
Vector< StageInterfaceInfo * > vertex_out_interfaces_
Vector< std::array< StringRefNull, 2 > > defines_
void validate_vertex_attributes(const ShaderCreateInfo *other_info=nullptr)
Vector< StageInterfaceInfo * > geometry_out_interfaces_
void validate_merge(const ShaderCreateInfo &other_info)