Blender V4.5
gpu/intern/gpu_material.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "DNA_material_types.h"
16#include "DNA_scene_types.h"
17#include "DNA_world_types.h"
18
19#include "BLI_listbase.h"
20#include "BLI_math_vector.h"
21#include "BLI_string.h"
22#include "BLI_time.h"
23#include "BLI_utildefines.h"
24
25#include "BKE_main.hh"
26#include "BKE_material.hh"
27#include "BKE_node.hh"
28
29#include "NOD_shader.h"
30
31#include "GPU_material.hh"
32#include "GPU_pass.hh"
33#include "GPU_shader.hh"
34#include "GPU_texture.hh"
35#include "GPU_uniform_buffer.hh"
36
37#include "DRW_engine.hh"
38
39#include "gpu_node_graph.hh"
40
41#include "atomic_ops.h"
42
45
46/* Structs */
47#define MAX_COLOR_BAND 128
48#define MAX_GPU_SKIES 8
49
54
59
61 /* Contains #GPUShader and source code for deferred compilation.
62 * Can be shared between materials sharing same node-tree topology. */
63 GPUPass *pass = nullptr;
64 /* Optimized GPUPass, situationally compiled after initial pass for optimal realtime performance.
65 * This shader variant bakes dynamic uniform data as constant. This variant will not use
66 * the ubo, and instead bake constants directly into the shader source. */
68
69 /* UBOs for this material parameters. */
70 GPUUniformBuf *ubo = nullptr;
71 /* Some flags about the nodetree & the needed resources. */
73 /* The engine type this material is compiled for. */
75 /* Identify shader variations (shadow, probe, world background...) */
77 /* Number of generated function. */
79
80 /* Source material, might be null. */
82 /* 1D Texture array containing all color bands. */
83 GPUTexture *coba_tex = nullptr;
84 /* Builder for coba_tex. */
86 /* 2D Texture array containing all sky textures. */
87 GPUTexture *sky_tex = nullptr;
88 /* Builder for sky_tex. */
90 /* Low level node graph(s). Also contains resources needed by the material. */
92
93 bool has_surface_output = false;
94 bool has_volume_output = false;
96
97 std::string name;
98
100 {
101 graph.used_libraries = BLI_gset_new(
102 BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUNodeGraph.used_libraries");
103 };
104
106 {
108
109 if (optimized_pass != nullptr) {
111 }
112 if (pass != nullptr) {
114 }
115 if (ubo != nullptr) {
117 }
118 if (coba_builder != nullptr) {
120 }
121 if (coba_tex != nullptr) {
123 }
124 if (sky_tex != nullptr) {
126 }
127 }
128};
129
130/* Public API */
131
133 bNodeTree *ntree,
134 ListBase *gpumaterials,
135 const char *name,
136 eGPUMaterialEngine engine,
137 uint64_t shader_uuid,
138 bool deferred_compilation,
139 GPUCodegenCallbackFn callback,
140 void *thunk,
141 GPUMaterialPassReplacementCallbackFn pass_replacement_cb)
142{
143 /* Search if this material is not already compiled. */
144 LISTBASE_FOREACH (LinkData *, link, gpumaterials) {
145 GPUMaterial *mat = (GPUMaterial *)link->data;
146 if (mat->uuid == shader_uuid && mat->engine == engine) {
147 if (!deferred_compilation) {
149 }
150 return mat;
151 }
152 }
153
154 GPUMaterial *mat = MEM_new<GPUMaterial>(__func__, engine);
155 mat->source_material = ma;
156 mat->uuid = shader_uuid;
157 mat->name = name;
158
159 /* Localize tree to create links for reroute and mute. */
160 bNodeTree *localtree = blender::bke::node_tree_localize(ntree, nullptr);
161 ntreeGPUMaterialNodes(localtree, mat);
162
165
166 /* Use default material pass when possible. */
167 if (GPUPass *default_pass = pass_replacement_cb ? pass_replacement_cb(thunk, mat) : nullptr) {
168 mat->pass = default_pass;
175 mat->ubo = GPU_uniformbuf_create_ex(256, nullptr, "Dummy UBO");
176 }
177 else {
178 /* Create source code and search pass cache for an already compiled version. */
179 mat->pass = GPU_generate_pass(
180 mat, &mat->graph, mat->name.c_str(), engine, deferred_compilation, callback, thunk, false);
181 }
182
183 /* Determine whether we should generate an optimized variant of the graph.
184 * Heuristic is based on complexity of default material pass and shader node graph. */
185 if (GPU_pass_should_optimize(mat->pass)) {
187 mat, &mat->graph, mat->name.c_str(), engine, true, callback, thunk, true);
188 }
189
191 /* Only free after GPU_pass_shader_get where GPUUniformBuf read data from the local tree. */
193 BLI_assert(!localtree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
194 MEM_freeN(localtree);
195
196 /* Note that even if building the shader fails in some way, we want to keep
197 * it to avoid trying to compile again and again, and simply do not use
198 * the actual shader on drawing. */
199 LinkData *link = MEM_callocN<LinkData>("GPUMaterialLink");
200 link->data = mat;
201 BLI_addtail(gpumaterials, link);
202
203 return mat;
204}
205
207 ConstructGPUMaterialFn construct_function_cb,
208 GPUCodegenCallbackFn generate_code_function_cb,
209 void *thunk)
210{
211 /* Allocate a new material and its material graph. */
212 GPUMaterial *material = MEM_new<GPUMaterial>(__func__, engine);
213
214 /* Construct the material graph by adding and linking the necessary GPU material nodes. */
215 construct_function_cb(thunk, material);
216
217 /* Create and initialize the texture storing color bands used by Ramp and Curve nodes. */
219
220 /* Lookup an existing pass in the cache or generate a new one. */
221 material->pass = GPU_generate_pass(material,
222 &material->graph,
223 __func__,
224 engine,
225 false,
226 generate_code_function_cb,
227 thunk,
228 false);
229
230 /* Determine whether we should generate an optimized variant of the graph.
231 * Heuristic is based on complexity of default material pass and shader node graph. */
232 if (GPU_pass_should_optimize(material->pass)) {
233 material->optimized_pass = GPU_generate_pass(material,
234 &material->graph,
235 __func__,
236 engine,
237 true,
238 generate_code_function_cb,
239 thunk,
240 true);
241 }
242
244
245 return material;
246}
247
249{
250 MEM_delete(material);
251}
252
253void GPU_material_free(ListBase *gpumaterial)
254{
255 LISTBASE_FOREACH (LinkData *, link, gpumaterial) {
256 GPUMaterial *material = static_cast<GPUMaterial *>(link->data);
257 GPU_material_free_single(material);
258 }
259 BLI_freelistN(gpumaterial);
260}
261
263{
264 LISTBASE_FOREACH (Material *, ma, &bmain->materials) {
265 GPU_material_free(&ma->gpumaterial);
266 }
267
268 LISTBASE_FOREACH (World *, wo, &bmain->worlds) {
269 GPU_material_free(&wo->gpumaterial);
270 }
271
273}
274
275const char *GPU_material_get_name(GPUMaterial *material)
276{
277 return material->name.c_str();
278}
279
281{
282 return mat->uuid;
283}
284
286{
287 return material->source_material;
288}
289
291{
292 /* If an optimized pass variant is available, and optimization is
293 * flagged as complete, we use this one instead. */
295 material->optimized_pass :
296 material->pass;
297}
298
300{
302}
303
305{
306 switch (GPU_pass_status(mat->pass)) {
307 case GPU_PASS_SUCCESS:
308 return GPU_MAT_SUCCESS;
309 case GPU_PASS_QUEUED:
310 return GPU_MAT_QUEUED;
311 default:
312 return GPU_MAT_FAILED;
313 }
314}
315
332
337
342
347
352
354{
355 return (mat->flag & flag) != 0;
356}
357
359{
360 return mat->flag;
361}
362
364{
365 if ((flag & GPU_MATFLAG_GLOSSY) && (mat->flag & GPU_MATFLAG_GLOSSY)) {
366 /* Tag material using multiple glossy BSDF as using clear coat. */
367 mat->flag |= GPU_MATFLAG_COAT;
368 }
369 mat->flag |= flag;
370}
371
373{
374 material->ubo = GPU_uniformbuf_create_from_list(inputs, material->name.c_str());
375}
376
378{
379 return material->ubo;
380}
381
383{
384 return material->graph.attributes;
385}
386
388{
389 return material->graph.textures;
390}
391
393{
394 const GPUUniformAttrList *attrs = &material->graph.uniform_attrs;
395 return attrs->count > 0 ? attrs : nullptr;
396}
397
399{
400 const ListBase *attrs = &material->graph.layer_attrs;
401 return !BLI_listbase_is_empty(attrs) ? attrs : nullptr;
402}
403
405{
406 return &material->graph;
407}
408
409/* Resources */
410
412 GPUMaterial *mat, int width, int height, const float *pixels, float *row)
413{
414 /* In order to put all sky textures into one 2D array texture,
415 * we need them to be the same size. */
416 BLI_assert(width == GPU_SKY_WIDTH);
417 BLI_assert(height == GPU_SKY_HEIGHT);
418 UNUSED_VARS_NDEBUG(width, height);
419
420 if (mat->sky_builder == nullptr) {
421 mat->sky_builder = MEM_mallocN<GPUSkyBuilder>("GPUSkyBuilder");
422 mat->sky_builder->current_layer = 0;
423 }
424
425 int layer = mat->sky_builder->current_layer;
426 *row = float(layer);
427
428 if (*row == MAX_GPU_SKIES) {
429 printf("Too many sky textures in shader!\n");
430 }
431 else {
432 float *dst = (float *)mat->sky_builder->pixels[layer];
433 memcpy(dst, pixels, sizeof(float) * GPU_SKY_WIDTH * GPU_SKY_HEIGHT * 4);
434 mat->sky_builder->current_layer += 1;
435 }
436
437 return &mat->sky_tex;
438}
439
441 int size,
442 const float *pixels,
443 float *r_row)
444{
445 /* In order to put all the color-bands into one 1D array texture,
446 * we need them to be the same size. */
447 BLI_assert(size == CM_TABLE + 1);
449
450 if (mat->coba_builder == nullptr) {
451 mat->coba_builder = MEM_mallocN<GPUColorBandBuilder>("GPUColorBandBuilder");
452 mat->coba_builder->current_layer = 0;
453 }
454
455 int layer = mat->coba_builder->current_layer;
456 *r_row = float(layer);
457
458 if (*r_row == MAX_COLOR_BAND) {
459 printf("Too many color band in shader! Remove some Curve, Black Body or Color Ramp Node.\n");
460 }
461 else {
462 float *dst = (float *)mat->coba_builder->pixels[layer];
463 memcpy(dst, pixels, sizeof(float) * (CM_TABLE + 1) * 4);
464 mat->coba_builder->current_layer += 1;
465 }
466
467 return &mat->coba_tex;
468}
469
471{
472 if (mat->coba_builder == nullptr) {
473 return;
474 }
475
476 GPUColorBandBuilder *builder = mat->coba_builder;
477
478 mat->coba_tex = GPU_texture_create_1d_array("mat_ramp",
479 CM_TABLE + 1,
480 builder->current_layer,
481 1,
484 (float *)builder->pixels);
485
486 MEM_freeN(builder);
487 mat->coba_builder = nullptr;
488}
489
491{
492 if (mat->sky_builder == nullptr) {
493 return;
494 }
495
496 mat->sky_tex = GPU_texture_create_2d_array("mat_sky",
500 1,
503 (float *)mat->sky_builder->pixels);
504
506 mat->sky_builder = nullptr;
507}
508
509/* Code generation */
510
512{
513 if (!material->graph.outlink_surface) {
514 material->graph.outlink_surface = link;
515 material->has_surface_output = true;
516 }
517}
518
520{
521 if (!material->graph.outlink_volume) {
522 material->graph.outlink_volume = link;
523 material->has_volume_output = true;
524 }
525}
526
528{
529 if (!material->graph.outlink_displacement) {
530 material->graph.outlink_displacement = link;
531 material->has_displacement_output = true;
532 }
533}
534
536{
537 if (!material->graph.outlink_thickness) {
538 material->graph.outlink_thickness = link;
539 }
540}
541
543{
545 aov_link->outlink = link;
546 aov_link->hash = hash;
547 BLI_addtail(&material->graph.outlink_aovs, aov_link);
548}
549
551{
553 compositor_link->outlink = link;
554 BLI_addtail(&material->graph.outlink_compositor, compositor_link);
555}
556
558 eGPUType return_type,
559 GPUNodeLink **link)
560{
561 /* Force cast to return type. */
562 switch (return_type) {
563 case GPU_FLOAT:
564 GPU_link(material, "set_value", *link, link);
565 break;
566 case GPU_VEC3:
567 GPU_link(material, "set_rgb", *link, link);
568 break;
569 case GPU_VEC4:
570 GPU_link(material, "set_rgba", *link, link);
571 break;
572 default:
573 BLI_assert(0);
574 break;
575 }
576
578 func_link->outlink = *link;
579 SNPRINTF(func_link->name, "ntree_fn%d", material->generated_function_len++);
580 BLI_addtail(&material->graph.material_functions, func_link);
581
582 return func_link->name;
583}
General operations, lookup, etc. for materials.
void BKE_material_defaults_free_gpu()
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
unsigned int BLI_ghashutil_ptrhash(const void *key)
GSet * BLI_gset_new(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:944
bool BLI_ghashutil_ptrcmp(const void *a, const void *b)
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
Platform independent time functions.
#define UNUSED_VARS_NDEBUG(...)
void(*)(void *thunk, GPUMaterial *material) ConstructGPUMaterialFn
void(*)(void *thunk, GPUMaterial *mat, struct GPUCodegenOutput *codegen) GPUCodegenCallbackFn
eGPUMaterialEngine
eGPUMaterialFlag
@ GPU_MATFLAG_UPDATED
@ GPU_MATFLAG_GLOSSY
@ GPU_MATFLAG_COAT
eGPUMaterialStatus
@ GPU_MAT_QUEUED
@ GPU_MAT_FAILED
@ GPU_MAT_SUCCESS
eGPUMaterialOptimizationStatus
@ GPU_MAT_OPTIMIZATION_QUEUED
@ GPU_MAT_OPTIMIZATION_SUCCESS
@ GPU_MAT_OPTIMIZATION_SKIP
GPUPass *(*)(void *thunk, GPUMaterial *mat) GPUMaterialPassReplacementCallbackFn
eGPUType
@ GPU_VEC4
@ GPU_VEC3
@ GPU_FLOAT
bool GPU_link(GPUMaterial *mat, const char *name,...)
@ GPU_PASS_QUEUED
Definition GPU_pass.hh:22
@ GPU_PASS_SUCCESS
Definition GPU_pass.hh:23
eGPUPassStatus GPU_pass_status(GPUPass *pass)
Definition gpu_pass.cc:154
void GPU_pass_ensure_its_ready(GPUPass *pass)
Definition gpu_pass.cc:311
void GPU_pass_release(GPUPass *pass)
Definition gpu_pass.cc:185
void GPU_pass_acquire(GPUPass *pass)
Definition gpu_pass.cc:178
GPUPass * GPU_generate_pass(GPUMaterial *material, GPUNodeGraph *graph, const char *debug_name, eGPUMaterialEngine engine, bool deferred_compilation, GPUCodegenCallbackFn finalize_source_cb, void *thunk, bool optimize_graph)
Definition gpu_pass.cc:365
uint64_t GPU_pass_compilation_timestamp(GPUPass *pass)
Definition gpu_pass.cc:197
bool GPU_pass_should_optimize(GPUPass *pass)
Definition gpu_pass.cc:159
GPUShader * GPU_pass_shader_get(GPUPass *pass)
Definition gpu_pass.cc:173
void GPU_texture_free(GPUTexture *texture)
@ GPU_TEXTURE_USAGE_SHADER_READ
GPUTexture * GPU_texture_create_2d_array(const char *name, int width, int height, int layer_len, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const float *data)
@ GPU_RGBA32F
@ GPU_RGBA16F
GPUTexture * GPU_texture_create_1d_array(const char *name, int width, int layer_len, int mip_len, eGPUTextureFormat format, eGPUTextureUsage usage, const float *data)
GPUUniformBuf * GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name)
void GPU_uniformbuf_free(GPUUniformBuf *ubo)
GPUUniformBuf * GPU_uniformbuf_create_from_list(ListBase *inputs, const char *name)
Read Guarded memory(de)allocation.
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
char * GPU_material_split_sub_function(GPUMaterial *material, eGPUType return_type, GPUNodeLink **link)
uint64_t GPU_material_uuid_get(GPUMaterial *mat)
static void gpu_material_ramp_texture_build(GPUMaterial *mat)
ListBase GPU_material_attributes(const GPUMaterial *material)
void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link)
GPUPass * GPU_material_get_pass(GPUMaterial *material)
bool GPU_material_has_surface_output(GPUMaterial *mat)
void GPU_material_free_single(GPUMaterial *material)
GPUTexture ** gpu_material_ramp_texture_row_set(GPUMaterial *mat, int size, const float *pixels, float *r_row)
void GPU_materials_free(Main *bmain)
void GPU_material_free(ListBase *gpumaterial)
void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link)
const ListBase * GPU_material_layer_attributes(const GPUMaterial *material)
void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link)
GPUShader * GPU_material_get_shader(GPUMaterial *material)
void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash)
GPUMaterial * GPU_material_from_nodetree(Material *ma, bNodeTree *ntree, ListBase *gpumaterials, const char *name, eGPUMaterialEngine engine, uint64_t shader_uuid, bool deferred_compilation, GPUCodegenCallbackFn callback, void *thunk, GPUMaterialPassReplacementCallbackFn pass_replacement_cb)
#define MAX_GPU_SKIES
bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag)
ListBase GPU_material_textures(GPUMaterial *material)
void GPU_material_add_output_link_composite(GPUMaterial *material, GPUNodeLink *link)
GPUMaterial * GPU_material_from_callbacks(eGPUMaterialEngine engine, ConstructGPUMaterialFn construct_function_cb, GPUCodegenCallbackFn generate_code_function_cb, void *thunk)
GPUTexture ** gpu_material_sky_texture_layer_set(GPUMaterial *mat, int width, int height, const float *pixels, float *row)
eGPUMaterialStatus GPU_material_status(GPUMaterial *mat)
void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag)
Material * GPU_material_get_material(GPUMaterial *material)
void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link)
eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat)
uint64_t GPU_material_compilation_timestamp(GPUMaterial *mat)
static void gpu_material_sky_texture_build(GPUMaterial *mat)
const char * GPU_material_get_name(GPUMaterial *material)
const GPUUniformAttrList * GPU_material_uniform_attributes(const GPUMaterial *material)
bool GPU_material_has_displacement_output(GPUMaterial *mat)
GPUNodeGraph * gpu_material_node_graph(GPUMaterial *material)
void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs)
#define MAX_COLOR_BAND
GPUUniformBuf * GPU_material_uniform_buffer_get(GPUMaterial *material)
bool GPU_material_has_volume_output(GPUMaterial *mat)
eGPUMaterialOptimizationStatus GPU_material_optimization_status(GPUMaterial *mat)
#define printf(...)
void gpu_node_graph_free(GPUNodeGraph *graph)
void gpu_node_graph_free_nodes(GPUNodeGraph *graph)
#define GPU_SKY_WIDTH
#define GPU_SKY_HEIGHT
#define CM_TABLE
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
void node_tree_free_local_tree(bNodeTree *ntree)
Definition node.cc:4731
bNodeTree * node_tree_localize(bNodeTree *ntree, std::optional< ID * > new_owner_id)
Definition node.cc:4858
static blender::bke::bNodeSocketTemplate inputs[]
#define hash
Definition noise_c.cc:154
float pixels[MAX_COLOR_BAND][CM_TABLE+1][4]
GPUSkyBuilder * sky_builder
GPUUniformBuf * ubo
GPUColorBandBuilder * coba_builder
GPUMaterial(eGPUMaterialEngine engine)
eGPUMaterialFlag flag
eGPUMaterialEngine engine
ListBase outlink_compositor
GPUNodeLink * outlink_displacement
ListBase layer_attrs
ListBase outlink_aovs
GPUNodeLink * outlink_thickness
GPUNodeLink * outlink_volume
ListBase attributes
GPUNodeLink * outlink_surface
GPUUniformAttrList uniform_attrs
ListBase material_functions
float pixels[MAX_GPU_SKIES][GPU_SKY_WIDTH *GPU_SKY_HEIGHT][4]
void * py_instance
Definition DNA_ID.h:483
void * data
ListBase materials
Definition BKE_main.hh:251
ListBase worlds
Definition BKE_main.hh:260
void * ntreeGPUMaterialNodes
Definition stubs.c:42
uint8_t flag
Definition wm_window.cc:139