Blender V5.0
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 "BKE_lib_id.hh"
14#include "MEM_guardedalloc.h"
15
16#include "DNA_material_types.h"
17#include "DNA_scene_types.h"
18#include "DNA_world_types.h"
19
20#include "BLI_listbase.h"
21#include "BLI_math_vector.h"
22#include "BLI_string.h"
23#include "BLI_time.h"
24#include "BLI_utildefines.h"
25
26#include "BKE_main.hh"
27#include "BKE_material.hh"
28#include "BKE_node.hh"
29#include "BKE_node_runtime.hh"
30
31#include "NOD_shader.h"
33
34#include "GPU_material.hh"
35#include "GPU_pass.hh"
36#include "GPU_shader.hh"
37#include "GPU_texture.hh"
38#include "GPU_uniform_buffer.hh"
39
40#include "DRW_engine.hh"
41
42#include "gpu_node_graph.hh"
43
44#include "atomic_ops.h"
45
48
49/* Structs */
50#define MAX_COLOR_BAND 128
51#define MAX_GPU_SKIES 8
52
57
62
64 /* Contains #blender::gpu::Shader and source code for deferred compilation.
65 * Can be shared between materials sharing same node-tree topology. */
66 GPUPass *pass = nullptr;
67 /* Optimized GPUPass, situationally compiled after initial pass for optimal realtime performance.
68 * This shader variant bakes dynamic uniform data as constant. This variant will not use
69 * the ubo, and instead bake constants directly into the shader source. */
71
72 /* UBOs for this material parameters. */
74 /* Some flags about the nodetree & the needed resources. */
76 /* The engine type this material is compiled for. */
78 /* Identify shader variations (shadow, probe, world background...) */
80 /* Number of generated function. */
82
83 /* Source material, might be null. */
85 /* 1D Texture array containing all color bands. */
87 /* Builder for coba_tex. */
89 /* 2D Texture array containing all sky textures. */
91 /* Builder for sky_tex. */
93 /* Low level node graph(s). Also contains resources needed by the material. */
95
96 bool has_surface_output = false;
97 bool has_volume_output = false;
99
100 std::string name;
101
103
105 {
107
108 if (optimized_pass != nullptr) {
110 }
111 if (pass != nullptr) {
113 }
114 if (ubo != nullptr) {
116 }
117 if (coba_builder != nullptr) {
119 }
120 if (coba_tex != nullptr) {
122 }
123 if (sky_tex != nullptr) {
125 }
126 }
127};
128
129/* Public API */
130
132 Material *ma,
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
155
156 GPUMaterial *mat = MEM_new<GPUMaterial>(__func__, engine);
157 mat->source_material = ma;
158 mat->uuid = shader_uuid;
159 mat->name = name;
160 result.material = mat;
161
162 /* Localize tree to create links for reroute and mute. */
164 nullptr, (blender::StringRef(ntree->id.name) + " Inlined").c_str(), ntree->idname);
166 inline_params.allow_preserving_repeat_zones = true;
167 blender::nodes::inline_shader_node_tree(*ntree, *localtree, inline_params);
168
170 inline_params.r_error_messages)
171 {
172 result.errors.append({error.node, std::move(error.message)});
173 }
174
175 ntreeGPUMaterialNodes(localtree, mat);
176
179
180 /* Use default material pass when possible. */
181 if (GPUPass *default_pass = pass_replacement_cb ? pass_replacement_cb(thunk, mat) : nullptr) {
182 mat->pass = default_pass;
189 mat->ubo = GPU_uniformbuf_create_ex(256, nullptr, "Dummy UBO");
190 }
191 else {
192 /* Create source code and search pass cache for an already compiled version. */
193 mat->pass = GPU_generate_pass(
194 mat, &mat->graph, mat->name.c_str(), engine, deferred_compilation, callback, thunk, false);
195 }
196
197 /* Determine whether we should generate an optimized variant of the graph.
198 * Heuristic is based on complexity of default material pass and shader node graph. */
199 if (GPU_pass_should_optimize(mat->pass)) {
201 mat, &mat->graph, mat->name.c_str(), engine, true, callback, thunk, true);
202 }
203
205 /* Only free after GPU_pass_shader_get where blender::gpu::UniformBuf read data from the local
206 * tree. */
207 BKE_id_free(nullptr, &localtree->id);
208
209 /* Note that even if building the shader fails in some way, we want to keep
210 * it to avoid trying to compile again and again, and simply do not use
211 * the actual shader on drawing. */
212 LinkData *link = MEM_callocN<LinkData>("GPUMaterialLink");
213 link->data = mat;
214 BLI_addtail(gpumaterials, link);
215
216 return result;
217}
218
220 ConstructGPUMaterialFn construct_function_cb,
221 GPUCodegenCallbackFn generate_code_function_cb,
222 void *thunk)
223{
224 /* Allocate a new material and its material graph. */
225 GPUMaterial *material = MEM_new<GPUMaterial>(__func__, engine);
226
227 /* Construct the material graph by adding and linking the necessary GPU material nodes. */
228 construct_function_cb(thunk, material);
229
230 /* Create and initialize the texture storing color bands used by Ramp and Curve nodes. */
232
233 /* Lookup an existing pass in the cache or generate a new one. */
234 material->pass = GPU_generate_pass(material,
235 &material->graph,
236 __func__,
237 engine,
238 false,
239 generate_code_function_cb,
240 thunk,
241 false);
242
243 /* Determine whether we should generate an optimized variant of the graph.
244 * Heuristic is based on complexity of default material pass and shader node graph. */
245 if (GPU_pass_should_optimize(material->pass)) {
246 material->optimized_pass = GPU_generate_pass(material,
247 &material->graph,
248 __func__,
249 engine,
250 true,
251 generate_code_function_cb,
252 thunk,
253 true);
254 }
255
257
258 return material;
259}
260
262{
263 MEM_delete(material);
264}
265
266void GPU_material_free(ListBase *gpumaterial)
267{
268 LISTBASE_FOREACH (LinkData *, link, gpumaterial) {
269 GPUMaterial *material = static_cast<GPUMaterial *>(link->data);
270 GPU_material_free_single(material);
271 }
272 BLI_freelistN(gpumaterial);
273}
274
276{
277 LISTBASE_FOREACH (Material *, ma, &bmain->materials) {
278 GPU_material_free(&ma->gpumaterial);
279 }
280
281 LISTBASE_FOREACH (World *, wo, &bmain->worlds) {
282 GPU_material_free(&wo->gpumaterial);
283 }
284
286}
287
288const char *GPU_material_get_name(GPUMaterial *material)
289{
290 return material->name.c_str();
291}
292
294{
295 return mat->uuid;
296}
297
299{
300 return material->source_material;
301}
302
304{
305 /* If an optimized pass variant is available, and optimization is
306 * flagged as complete, we use this one instead. */
308 material->optimized_pass :
309 material->pass;
310}
311
316
318{
319 switch (GPU_pass_status(mat->pass)) {
320 case GPU_PASS_SUCCESS:
321 return GPU_MAT_SUCCESS;
322 case GPU_PASS_QUEUED:
323 return GPU_MAT_QUEUED;
324 default:
325 return GPU_MAT_FAILED;
326 }
327}
328
345
350
355
360
365
367{
368 return (mat->flag & flag) != 0;
369}
370
372{
373 return mat->flag;
374}
375
377{
378 if ((flag & GPU_MATFLAG_GLOSSY) && (mat->flag & GPU_MATFLAG_GLOSSY)) {
379 /* Tag material using multiple glossy BSDF as using clear coat. */
380 mat->flag |= GPU_MATFLAG_COAT;
381 }
382 mat->flag |= flag;
383}
384
386{
387 material->ubo = GPU_uniformbuf_create_from_list(inputs, material->name.c_str());
388}
389
394
396{
397 return material->graph.attributes;
398}
399
401{
402 return material->graph.textures;
403}
404
406{
407 const GPUUniformAttrList *attrs = &material->graph.uniform_attrs;
408 return attrs->count > 0 ? attrs : nullptr;
409}
410
412{
413 const ListBase *attrs = &material->graph.layer_attrs;
414 return !BLI_listbase_is_empty(attrs) ? attrs : nullptr;
415}
416
418{
419 return &material->graph;
420}
421
422/* Resources */
423
425 GPUMaterial *mat, int width, int height, const float *pixels, float *row)
426{
427 /* In order to put all sky textures into one 2D array texture,
428 * we need them to be the same size. */
429 BLI_assert(width == GPU_SKY_WIDTH);
430 BLI_assert(height == GPU_SKY_HEIGHT);
431 UNUSED_VARS_NDEBUG(width, height);
432
433 if (mat->sky_builder == nullptr) {
434 mat->sky_builder = MEM_mallocN<GPUSkyBuilder>("GPUSkyBuilder");
435 mat->sky_builder->current_layer = 0;
436 }
437
438 int layer = mat->sky_builder->current_layer;
439 *row = float(layer);
440
441 if (*row == MAX_GPU_SKIES) {
442 printf("Too many sky textures in shader!\n");
443 }
444 else {
445 float *dst = (float *)mat->sky_builder->pixels[layer];
446 memcpy(dst, pixels, sizeof(float) * GPU_SKY_WIDTH * GPU_SKY_HEIGHT * 4);
447 mat->sky_builder->current_layer += 1;
448 }
449
450 return &mat->sky_tex;
451}
452
454 int size,
455 const float *pixels,
456 float *r_row)
457{
458 /* In order to put all the color-bands into one 1D array texture,
459 * we need them to be the same size. */
460 BLI_assert(size == CM_TABLE + 1);
462
463 if (mat->coba_builder == nullptr) {
464 mat->coba_builder = MEM_mallocN<GPUColorBandBuilder>("GPUColorBandBuilder");
465 mat->coba_builder->current_layer = 0;
466 }
467
468 int layer = mat->coba_builder->current_layer;
469 *r_row = float(layer);
470
471 if (*r_row == MAX_COLOR_BAND) {
472 printf("Too many color band in shader! Remove some Curve, Black Body or Color Ramp Node.\n");
473 }
474 else {
475 float *dst = (float *)mat->coba_builder->pixels[layer];
476 memcpy(dst, pixels, sizeof(float) * (CM_TABLE + 1) * 4);
477 mat->coba_builder->current_layer += 1;
478 }
479
480 return &mat->coba_tex;
481}
482
484{
485 if (mat->coba_builder == nullptr) {
486 return;
487 }
488
489 GPUColorBandBuilder *builder = mat->coba_builder;
490
491 mat->coba_tex = GPU_texture_create_1d_array("mat_ramp",
492 CM_TABLE + 1,
493 builder->current_layer,
494 1,
495 blender::gpu::TextureFormat::SFLOAT_16_16_16_16,
497 (float *)builder->pixels);
498
499 MEM_freeN(builder);
500 mat->coba_builder = nullptr;
501}
502
504{
505 if (mat->sky_builder == nullptr) {
506 return;
507 }
508
509 mat->sky_tex = GPU_texture_create_2d_array("mat_sky",
513 1,
514 blender::gpu::TextureFormat::SFLOAT_32_32_32_32,
516 (float *)mat->sky_builder->pixels);
517
519 mat->sky_builder = nullptr;
520}
521
522/* Code generation */
523
525{
526 if (!material->graph.outlink_surface) {
527 material->graph.outlink_surface = link;
528 material->has_surface_output = true;
529 }
530}
531
533{
534 if (!material->graph.outlink_volume) {
535 material->graph.outlink_volume = link;
536 material->has_volume_output = true;
537 }
538}
539
541{
542 if (!material->graph.outlink_displacement) {
543 material->graph.outlink_displacement = link;
544 material->has_displacement_output = true;
545 }
546}
547
549{
550 if (!material->graph.outlink_thickness) {
551 material->graph.outlink_thickness = link;
552 }
553}
554
556{
558 aov_link->outlink = link;
559 aov_link->hash = hash;
560 BLI_addtail(&material->graph.outlink_aovs, aov_link);
561}
562
564{
566 compositor_link->outlink = link;
567 BLI_addtail(&material->graph.outlink_compositor, compositor_link);
568}
569
571 GPUType return_type,
572 GPUNodeLink **link)
573{
574 /* Force cast to return type. */
575 switch (return_type) {
576 case GPU_FLOAT:
577 GPU_link(material, "set_value", *link, link);
578 break;
579 case GPU_VEC3:
580 GPU_link(material, "set_rgb", *link, link);
581 break;
582 case GPU_VEC4:
583 GPU_link(material, "set_rgba", *link, link);
584 break;
585 default:
586 BLI_assert(0);
587 break;
588 }
589
591 func_link->outlink = *link;
592 SNPRINTF(func_link->name, "ntree_fn%d", material->generated_function_len++);
593 BLI_addtail(&material->graph.material_functions, func_link);
594
595 return func_link->name;
596}
void BKE_id_free(Main *bmain, void *idv)
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
#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:604
Platform independent time functions.
#define UNUSED_VARS_NDEBUG(...)
#define GPU_SKY_WIDTH
#define GPU_SKY_HEIGHT
#define CM_TABLE
void(*)(void *thunk, GPUMaterial *material) ConstructGPUMaterialFn
eGPUMaterialEngine
GPUMaterialStatus
@ GPU_MAT_QUEUED
@ GPU_MAT_FAILED
@ GPU_MAT_SUCCESS
GPUType
@ GPU_VEC4
@ GPU_VEC3
@ GPU_FLOAT
eGPUMaterialFlag
@ GPU_MATFLAG_UPDATED
@ GPU_MATFLAG_GLOSSY
@ GPU_MATFLAG_COAT
eGPUMaterialOptimizationStatus
@ GPU_MAT_OPTIMIZATION_QUEUED
@ GPU_MAT_OPTIMIZATION_SUCCESS
@ GPU_MAT_OPTIMIZATION_SKIP
GPUPass *(*)(void *thunk, GPUMaterial *mat) GPUMaterialPassReplacementCallbackFn
void(*)(void *thunk, GPUMaterial *mat, struct GPUCodegenOutput *codegen) GPUCodegenCallbackFn
bool GPU_link(GPUMaterial *mat, const char *name,...)
blender::gpu::Shader * GPU_pass_shader_get(GPUPass *pass)
Definition gpu_pass.cc:175
@ GPU_PASS_QUEUED
Definition GPU_pass.hh:22
@ GPU_PASS_SUCCESS
Definition GPU_pass.hh:23
void GPU_pass_ensure_its_ready(GPUPass *pass)
Definition gpu_pass.cc:288
GPUPassStatus GPU_pass_status(GPUPass *pass)
Definition gpu_pass.cc:161
void GPU_pass_release(GPUPass *pass)
Definition gpu_pass.cc:187
void GPU_pass_acquire(GPUPass *pass)
Definition gpu_pass.cc:180
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:342
uint64_t GPU_pass_compilation_timestamp(GPUPass *pass)
Definition gpu_pass.cc:199
bool GPU_pass_should_optimize(GPUPass *pass)
Definition gpu_pass.cc:166
blender::gpu::Texture * GPU_texture_create_2d_array(const char *name, int width, int height, int layer_len, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
blender::gpu::Texture * GPU_texture_create_1d_array(const char *name, int width, int layer_len, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
@ GPU_TEXTURE_USAGE_SHADER_READ
void GPU_texture_free(blender::gpu::Texture *texture)
void GPU_uniformbuf_free(blender::gpu::UniformBuf *ubo)
blender::gpu::UniformBuf * GPU_uniformbuf_create_from_list(ListBase *inputs, const char *name)
blender::gpu::UniformBuf * GPU_uniformbuf_create_ex(size_t size, const void *data, 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
nullptr float
GPUMaterialFromNodeTreeResult 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)
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)
blender::gpu::Shader * GPU_material_get_shader(GPUMaterial *material)
GPUPass * GPU_material_get_pass(GPUMaterial *material)
bool GPU_material_has_surface_output(GPUMaterial *mat)
void GPU_material_free_single(GPUMaterial *material)
blender::gpu::Texture ** 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)
void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash)
#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)
GPUMaterialStatus GPU_material_status(GPUMaterial *mat)
GPUMaterial * GPU_material_from_callbacks(eGPUMaterialEngine engine, ConstructGPUMaterialFn construct_function_cb, GPUCodegenCallbackFn generate_code_function_cb, void *thunk)
blender::gpu::Texture ** gpu_material_sky_texture_layer_set(GPUMaterial *mat, int width, int height, const float *pixels, float *row)
void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag)
blender::gpu::UniformBuf * GPU_material_uniform_buffer_get(GPUMaterial *material)
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)
char * GPU_material_split_sub_function(GPUMaterial *material, GPUType return_type, GPUNodeLink **link)
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
bool GPU_material_has_volume_output(GPUMaterial *mat)
eGPUMaterialOptimizationStatus GPU_material_optimization_status(GPUMaterial *mat)
void gpu_node_graph_free(GPUNodeGraph *graph)
void gpu_node_graph_free_nodes(GPUNodeGraph *graph)
#define printf(...)
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
static void error(const char *str)
bNodeTree * node_tree_add_tree(Main *bmain, StringRef name, StringRef idname)
Definition node.cc:4085
bool inline_shader_node_tree(const bNodeTree &src_tree, bNodeTree &dst_tree, InlineShaderNodeTreeParams &params)
static blender::bke::bNodeSocketTemplate inputs[]
#define hash
Definition noise_c.cc:154
const char * name
float pixels[MAX_COLOR_BAND][CM_TABLE+1][4]
GPUSkyBuilder * sky_builder
GPUColorBandBuilder * coba_builder
GPUMaterial(eGPUMaterialEngine engine)
eGPUMaterialFlag flag
blender::gpu::Texture * coba_tex
blender::gpu::Texture * sky_tex
blender::gpu::UniformBuf * ubo
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]
char name[258]
Definition DNA_ID.h:432
void * data
ListBase materials
Definition BKE_main.hh:284
ListBase worlds
Definition BKE_main.hh:291
char idname[64]
void * ntreeGPUMaterialNodes
Definition stubs.c:42
uint8_t flag
Definition wm_window.cc:145