1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2006 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup gpu
22 *
23 * Manages materials, lights and textures.
24 */
25
26 #include <math.h>
27 #include <string.h>
28
29 #include "MEM_guardedalloc.h"
30
31 #include "DNA_material_types.h"
32 #include "DNA_scene_types.h"
33 #include "DNA_world_types.h"
34
35 #include "BLI_ghash.h"
36 #include "BLI_listbase.h"
37 #include "BLI_math.h"
38 #include "BLI_string.h"
39 #include "BLI_string_utils.h"
40 #include "BLI_utildefines.h"
41
42 #include "BKE_main.h"
43 #include "BKE_material.h"
44 #include "BKE_node.h"
45 #include "BKE_scene.h"
46
47 #include "GPU_material.h"
48 #include "GPU_shader.h"
49 #include "GPU_texture.h"
50 #include "GPU_uniform_buffer.h"
51
52 #include "DRW_engine.h"
53
54 #include "gpu_codegen.h"
55 #include "gpu_node_graph.h"
56
57 /* Structs */
58 #define MAX_COLOR_BAND 128
59
60 typedef struct GPUColorBandBuilder {
61 float pixels[MAX_COLOR_BAND][CM_TABLE + 1][4];
62 int current_layer;
63 } GPUColorBandBuilder;
64
65 struct GPUMaterial {
66 Scene *scene; /* DEPRECATED was only useful for lights. */
67 Material *ma;
68
69 eGPUMaterialStatus status;
70
71 const void *engine_type; /* attached engine type */
72 int options; /* to identify shader variations (shadow, probe, world background...) */
73 bool is_volume_shader; /* is volumetric shader */
74
75 /* Nodes */
76 GPUNodeGraph graph;
77
78 /* for binding the material */
79 GPUPass *pass;
80
81 /* XXX: Should be in Material. But it depends on the output node
82 * used and since the output selection is different for GPUMaterial...
83 */
84 bool has_volume_output;
85 bool has_surface_output;
86
87 /* Only used by Eevee to know which bsdf are used. */
88 eGPUMatFlag flag;
89
90 /* Used by 2.8 pipeline */
91 GPUUniformBuf *ubo; /* UBOs for shader uniforms. */
92
93 /* Eevee SSS */
94 GPUUniformBuf *sss_profile; /* UBO containing SSS profile. */
95 GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */
96 float sss_enabled;
97 float sss_radii[3];
98 int sss_samples;
99 short int sss_falloff;
100 float sss_sharpness;
101 bool sss_dirty;
102
103 GPUTexture *coba_tex; /* 1D Texture array containing all color bands. */
104 GPUColorBandBuilder *coba_builder;
105
106 GSet *used_libraries;
107
108 #ifndef NDEBUG
109 char name[64];
110 #endif
111 };
112
113 enum {
114 GPU_USE_SURFACE_OUTPUT = (1 << 0),
115 GPU_USE_VOLUME_OUTPUT = (1 << 1),
116 };
117
118 /* Functions */
119
120 /* Returns the address of the future pointer to coba_tex */
gpu_material_ramp_texture_row_set(GPUMaterial * mat,int size,float * pixels,float * row)121 GPUTexture **gpu_material_ramp_texture_row_set(GPUMaterial *mat,
122 int size,
123 float *pixels,
124 float *row)
125 {
126 /* In order to put all the color-bands into one 1D array texture,
127 * we need them to be the same size. */
128 BLI_assert(size == CM_TABLE + 1);
129 UNUSED_VARS_NDEBUG(size);
130
131 if (mat->coba_builder == NULL) {
132 mat->coba_builder = MEM_mallocN(sizeof(GPUColorBandBuilder), "GPUColorBandBuilder");
133 mat->coba_builder->current_layer = 0;
134 }
135
136 int layer = mat->coba_builder->current_layer;
137 *row = (float)layer;
138
139 if (*row == MAX_COLOR_BAND) {
140 printf("Too many color band in shader! Remove some Curve, Black Body or Color Ramp Node.\n");
141 }
142 else {
143 float *dst = (float *)mat->coba_builder->pixels[layer];
144 memcpy(dst, pixels, sizeof(float) * (CM_TABLE + 1) * 4);
145 mat->coba_builder->current_layer += 1;
146 }
147
148 return &mat->coba_tex;
149 }
150
gpu_material_ramp_texture_build(GPUMaterial * mat)151 static void gpu_material_ramp_texture_build(GPUMaterial *mat)
152 {
153 if (mat->coba_builder == NULL) {
154 return;
155 }
156
157 GPUColorBandBuilder *builder = mat->coba_builder;
158
159 mat->coba_tex = GPU_texture_create_1d_array(
160 "mat_ramp", CM_TABLE + 1, builder->current_layer, 1, GPU_RGBA16F, (float *)builder->pixels);
161
162 MEM_freeN(builder);
163 mat->coba_builder = NULL;
164 }
165
gpu_material_free_single(GPUMaterial * material)166 static void gpu_material_free_single(GPUMaterial *material)
167 {
168 /* Cancel / wait any pending lazy compilation. */
169 DRW_deferred_shader_remove(material);
170
171 gpu_node_graph_free(&material->graph);
172
173 if (material->pass != NULL) {
174 GPU_pass_release(material->pass);
175 }
176 if (material->ubo != NULL) {
177 GPU_uniformbuf_free(material->ubo);
178 }
179 if (material->sss_tex_profile != NULL) {
180 GPU_texture_free(material->sss_tex_profile);
181 }
182 if (material->sss_profile != NULL) {
183 GPU_uniformbuf_free(material->sss_profile);
184 }
185 if (material->coba_tex != NULL) {
186 GPU_texture_free(material->coba_tex);
187 }
188
189 BLI_gset_free(material->used_libraries, NULL);
190 }
191
GPU_material_free(ListBase * gpumaterial)192 void GPU_material_free(ListBase *gpumaterial)
193 {
194 LISTBASE_FOREACH (LinkData *, link, gpumaterial) {
195 GPUMaterial *material = link->data;
196 gpu_material_free_single(material);
197 MEM_freeN(material);
198 }
199 BLI_freelistN(gpumaterial);
200 }
201
GPU_material_scene(GPUMaterial * material)202 Scene *GPU_material_scene(GPUMaterial *material)
203 {
204 return material->scene;
205 }
206
GPU_material_get_pass(GPUMaterial * material)207 GPUPass *GPU_material_get_pass(GPUMaterial *material)
208 {
209 return material->pass;
210 }
211
GPU_material_get_shader(GPUMaterial * material)212 GPUShader *GPU_material_get_shader(GPUMaterial *material)
213 {
214 return material->pass ? GPU_pass_shader_get(material->pass) : NULL;
215 }
216
217 /* Return can be NULL if it's a world material. */
GPU_material_get_material(GPUMaterial * material)218 Material *GPU_material_get_material(GPUMaterial *material)
219 {
220 return material->ma;
221 }
222
GPU_material_uniform_buffer_get(GPUMaterial * material)223 GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material)
224 {
225 return material->ubo;
226 }
227
228 /**
229 * Create dynamic UBO from parameters
230 *
231 * \param inputs: Items are #LinkData, data is #GPUInput (`BLI_genericNodeN(GPUInput)`).
232 */
GPU_material_uniform_buffer_create(GPUMaterial * material,ListBase * inputs)233 void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs)
234 {
235 #ifndef NDEBUG
236 const char *name = material->name;
237 #else
238 const char *name = "Material";
239 #endif
240 material->ubo = GPU_uniformbuf_create_from_list(inputs, name);
241 }
242
243 /* Eevee Subsurface scattering. */
244 /* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */
245
246 #define SSS_SAMPLES 65
247 #define SSS_EXPONENT 2.0f /* Importance sampling exponent */
248
249 typedef struct GPUSssKernelData {
250 float kernel[SSS_SAMPLES][4];
251 float param[3], max_radius;
252 int samples;
253 int pad[3];
254 } GPUSssKernelData;
255
256 BLI_STATIC_ASSERT_ALIGN(GPUSssKernelData, 16)
257
sss_calculate_offsets(GPUSssKernelData * kd,int count,float exponent)258 static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponent)
259 {
260 float step = 2.0f / (float)(count - 1);
261 for (int i = 0; i < count; i++) {
262 float o = ((float)i) * step - 1.0f;
263 float sign = (o < 0.0f) ? -1.0f : 1.0f;
264 float ofs = sign * fabsf(powf(o, exponent));
265 kd->kernel[i][3] = ofs;
266 }
267 }
268
269 #define GAUSS_TRUNCATE 12.46f
gaussian_profile(float r,float radius)270 static float gaussian_profile(float r, float radius)
271 {
272 const float v = radius * radius * (0.25f * 0.25f);
273 const float Rm = sqrtf(v * GAUSS_TRUNCATE);
274
275 if (r >= Rm) {
276 return 0.0f;
277 }
278 return expf(-r * r / (2.0f * v)) / (2.0f * M_PI * v);
279 }
280
281 #define BURLEY_TRUNCATE 16.0f
282 #define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE)
burley_profile(float r,float d)283 static float burley_profile(float r, float d)
284 {
285 float exp_r_3_d = expf(-r / (3.0f * d));
286 float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d;
287 return (exp_r_d + exp_r_3_d) / (4.0f * d);
288 }
289
cubic_profile(float r,float radius,float sharpness)290 static float cubic_profile(float r, float radius, float sharpness)
291 {
292 float Rm = radius * (1.0f + sharpness);
293
294 if (r >= Rm) {
295 return 0.0f;
296 }
297 /* custom variation with extra sharpness, to match the previous code */
298 const float y = 1.0f / (1.0f + sharpness);
299 float Rmy, ry, ryinv;
300
301 Rmy = powf(Rm, y);
302 ry = powf(r, y);
303 ryinv = (r > 0.0f) ? powf(r, y - 1.0f) : 0.0f;
304
305 const float Rmy5 = (Rmy * Rmy) * (Rmy * Rmy) * Rmy;
306 const float f = Rmy - ry;
307 const float num = f * (f * f) * (y * ryinv);
308
309 return (10.0f * num) / (Rmy5 * M_PI);
310 }
311
eval_profile(float r,short falloff_type,float sharpness,float param)312 static float eval_profile(float r, short falloff_type, float sharpness, float param)
313 {
314 r = fabsf(r);
315
316 if (falloff_type == SHD_SUBSURFACE_BURLEY || falloff_type == SHD_SUBSURFACE_RANDOM_WALK) {
317 return burley_profile(r, param) / BURLEY_TRUNCATE_CDF;
318 }
319 if (falloff_type == SHD_SUBSURFACE_CUBIC) {
320 return cubic_profile(r, param, sharpness);
321 }
322
323 return gaussian_profile(r, param);
324 }
325
326 /* Resolution for each sample of the precomputed kernel profile */
327 #define INTEGRAL_RESOLUTION 32
eval_integral(float x0,float x1,short falloff_type,float sharpness,float param)328 static float eval_integral(float x0, float x1, short falloff_type, float sharpness, float param)
329 {
330 const float range = x1 - x0;
331 const float step = range / INTEGRAL_RESOLUTION;
332 float integral = 0.0f;
333
334 for (int i = 0; i < INTEGRAL_RESOLUTION; i++) {
335 float x = x0 + range * ((float)i + 0.5f) / (float)INTEGRAL_RESOLUTION;
336 float y = eval_profile(x, falloff_type, sharpness, param);
337 integral += y * step;
338 }
339
340 return integral;
341 }
342 #undef INTEGRAL_RESOLUTION
343
compute_sss_kernel(GPUSssKernelData * kd,const float radii[3],int sample_len,int falloff_type,float sharpness)344 static void compute_sss_kernel(
345 GPUSssKernelData *kd, const float radii[3], int sample_len, int falloff_type, float sharpness)
346 {
347 float rad[3];
348 /* Minimum radius */
349 rad[0] = MAX2(radii[0], 1e-15f);
350 rad[1] = MAX2(radii[1], 1e-15f);
351 rad[2] = MAX2(radii[2], 1e-15f);
352
353 /* Christensen-Burley fitting */
354 float l[3], d[3];
355
356 if (falloff_type == SHD_SUBSURFACE_BURLEY || falloff_type == SHD_SUBSURFACE_RANDOM_WALK) {
357 mul_v3_v3fl(l, rad, 0.25f * M_1_PI);
358 const float A = 1.0f;
359 const float s = 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f);
360 /* XXX 0.6f Out of nowhere to match cycles! Empirical! Can be tweak better. */
361 mul_v3_v3fl(d, l, 0.6f / s);
362 mul_v3_v3fl(rad, d, BURLEY_TRUNCATE);
363 kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
364
365 copy_v3_v3(kd->param, d);
366 }
367 else if (falloff_type == SHD_SUBSURFACE_CUBIC) {
368 copy_v3_v3(kd->param, rad);
369 mul_v3_fl(rad, 1.0f + sharpness);
370 kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
371 }
372 else {
373 kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
374
375 copy_v3_v3(kd->param, rad);
376 }
377
378 /* Compute samples locations on the 1d kernel [-1..1] */
379 sss_calculate_offsets(kd, sample_len, SSS_EXPONENT);
380
381 /* Weights sum for normalization */
382 float sum[3] = {0.0f, 0.0f, 0.0f};
383
384 /* Compute integral of each sample footprint */
385 for (int i = 0; i < sample_len; i++) {
386 float x0, x1;
387
388 if (i == 0) {
389 x0 = kd->kernel[0][3] - fabsf(kd->kernel[0][3] - kd->kernel[1][3]) / 2.0f;
390 }
391 else {
392 x0 = (kd->kernel[i - 1][3] + kd->kernel[i][3]) / 2.0f;
393 }
394
395 if (i == sample_len - 1) {
396 x1 = kd->kernel[sample_len - 1][3] +
397 fabsf(kd->kernel[sample_len - 2][3] - kd->kernel[sample_len - 1][3]) / 2.0f;
398 }
399 else {
400 x1 = (kd->kernel[i][3] + kd->kernel[i + 1][3]) / 2.0f;
401 }
402
403 x0 *= kd->max_radius;
404 x1 *= kd->max_radius;
405
406 kd->kernel[i][0] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[0]);
407 kd->kernel[i][1] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[1]);
408 kd->kernel[i][2] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[2]);
409
410 sum[0] += kd->kernel[i][0];
411 sum[1] += kd->kernel[i][1];
412 sum[2] += kd->kernel[i][2];
413 }
414
415 for (int i = 0; i < 3; i++) {
416 if (sum[i] > 0.0f) {
417 /* Normalize */
418 for (int j = 0; j < sample_len; j++) {
419 kd->kernel[j][i] /= sum[i];
420 }
421 }
422 else {
423 /* Avoid 0 kernel sum. */
424 kd->kernel[sample_len / 2][i] = 1.0f;
425 }
426 }
427
428 /* Put center sample at the start of the array (to sample first) */
429 float tmpv[4];
430 copy_v4_v4(tmpv, kd->kernel[sample_len / 2]);
431 for (int i = sample_len / 2; i > 0; i--) {
432 copy_v4_v4(kd->kernel[i], kd->kernel[i - 1]);
433 }
434 copy_v4_v4(kd->kernel[0], tmpv);
435
436 kd->samples = sample_len;
437 }
438
439 #define INTEGRAL_RESOLUTION 512
compute_sss_translucence_kernel(const GPUSssKernelData * kd,int resolution,short falloff_type,float sharpness,float ** output)440 static void compute_sss_translucence_kernel(const GPUSssKernelData *kd,
441 int resolution,
442 short falloff_type,
443 float sharpness,
444 float **output)
445 {
446 float(*texels)[4];
447 texels = MEM_callocN(sizeof(float[4]) * resolution, "compute_sss_translucence_kernel");
448 *output = (float *)texels;
449
450 /* Last texel should be black, hence the - 1. */
451 for (int i = 0; i < resolution - 1; i++) {
452 /* Distance from surface. */
453 float d = kd->max_radius * ((float)i + 0.00001f) / ((float)resolution);
454
455 /* For each distance d we compute the radiance incoming from an hypothetic parallel plane. */
456 /* Compute radius of the footprint on the hypothetic plane */
457 float r_fp = sqrtf(kd->max_radius * kd->max_radius - d * d);
458 float r_step = r_fp / INTEGRAL_RESOLUTION;
459 float area_accum = 0.0f;
460 for (float r = 0.0f; r < r_fp; r += r_step) {
461 /* Compute distance to the "shading" point through the medium. */
462 /* r_step * 0.5f to put sample between the area borders */
463 float dist = hypotf(r + r_step * 0.5f, d);
464
465 float profile[3];
466 profile[0] = eval_profile(dist, falloff_type, sharpness, kd->param[0]);
467 profile[1] = eval_profile(dist, falloff_type, sharpness, kd->param[1]);
468 profile[2] = eval_profile(dist, falloff_type, sharpness, kd->param[2]);
469
470 /* Since the profile and configuration are radially symmetrical we
471 * can just evaluate it once and weight it accordingly */
472 float r_next = r + r_step;
473 float disk_area = (M_PI * r_next * r_next) - (M_PI * r * r);
474
475 mul_v3_fl(profile, disk_area);
476 add_v3_v3(texels[i], profile);
477 area_accum += disk_area;
478 }
479 /* Normalize over the disk. */
480 mul_v3_fl(texels[i], 1.0f / (area_accum));
481 }
482
483 /* Normalize */
484 for (int j = resolution - 2; j > 0; j--) {
485 texels[j][0] /= (texels[0][0] > 0.0f) ? texels[0][0] : 1.0f;
486 texels[j][1] /= (texels[0][1] > 0.0f) ? texels[0][1] : 1.0f;
487 texels[j][2] /= (texels[0][2] > 0.0f) ? texels[0][2] : 1.0f;
488 }
489
490 /* First texel should be white */
491 texels[0][0] = (texels[0][0] > 0.0f) ? 1.0f : 0.0f;
492 texels[0][1] = (texels[0][1] > 0.0f) ? 1.0f : 0.0f;
493 texels[0][2] = (texels[0][2] > 0.0f) ? 1.0f : 0.0f;
494
495 /* dim the last few texels for smoother transition */
496 mul_v3_fl(texels[resolution - 2], 0.25f);
497 mul_v3_fl(texels[resolution - 3], 0.5f);
498 mul_v3_fl(texels[resolution - 4], 0.75f);
499 }
500 #undef INTEGRAL_RESOLUTION
501
GPU_material_sss_profile_create(GPUMaterial * material,float radii[3],const short * falloff_type,const float * sharpness)502 void GPU_material_sss_profile_create(GPUMaterial *material,
503 float radii[3],
504 const short *falloff_type,
505 const float *sharpness)
506 {
507 copy_v3_v3(material->sss_radii, radii);
508 material->sss_falloff = (falloff_type) ? *falloff_type : 0.0;
509 material->sss_sharpness = (sharpness) ? *sharpness : 0.0;
510 material->sss_dirty = true;
511 material->sss_enabled = true;
512
513 /* Update / Create UBO */
514 if (material->sss_profile == NULL) {
515 material->sss_profile = GPU_uniformbuf_create(sizeof(GPUSssKernelData));
516 }
517 }
518
GPU_material_sss_profile_get(GPUMaterial * material,int sample_len,GPUTexture ** tex_profile)519 struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material,
520 int sample_len,
521 GPUTexture **tex_profile)
522 {
523 if (!material->sss_enabled) {
524 return NULL;
525 }
526
527 if (material->sss_dirty || (material->sss_samples != sample_len)) {
528 GPUSssKernelData kd;
529
530 float sharpness = material->sss_sharpness;
531
532 /* XXX Black magic but it seems to fit. Maybe because we integrate -1..1 */
533 sharpness *= 0.5f;
534
535 compute_sss_kernel(&kd, material->sss_radii, sample_len, material->sss_falloff, sharpness);
536
537 /* Update / Create UBO */
538 GPU_uniformbuf_update(material->sss_profile, &kd);
539
540 /* Update / Create Tex */
541 float *translucence_profile;
542 compute_sss_translucence_kernel(
543 &kd, 64, material->sss_falloff, sharpness, &translucence_profile);
544
545 if (material->sss_tex_profile != NULL) {
546 GPU_texture_free(material->sss_tex_profile);
547 }
548
549 material->sss_tex_profile = GPU_texture_create_1d(
550 "sss_tex_profile", 64, 1, GPU_RGBA16F, translucence_profile);
551
552 MEM_freeN(translucence_profile);
553
554 material->sss_samples = sample_len;
555 material->sss_dirty = false;
556 }
557
558 if (tex_profile != NULL) {
559 *tex_profile = material->sss_tex_profile;
560 }
561 return material->sss_profile;
562 }
563
GPU_material_create_sss_profile_ubo(void)564 struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void)
565 {
566 return GPU_uniformbuf_create(sizeof(GPUSssKernelData));
567 }
568
569 #undef SSS_EXPONENT
570 #undef SSS_SAMPLES
571
GPU_material_attributes(GPUMaterial * material)572 ListBase GPU_material_attributes(GPUMaterial *material)
573 {
574 return material->graph.attributes;
575 }
576
GPU_material_textures(GPUMaterial * material)577 ListBase GPU_material_textures(GPUMaterial *material)
578 {
579 return material->graph.textures;
580 }
581
GPU_material_volume_grids(GPUMaterial * material)582 ListBase GPU_material_volume_grids(GPUMaterial *material)
583 {
584 return material->graph.volume_grids;
585 }
586
GPU_material_output_link(GPUMaterial * material,GPUNodeLink * link)587 void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link)
588 {
589 if (!material->graph.outlink) {
590 material->graph.outlink = link;
591 }
592 }
593
gpu_material_node_graph(GPUMaterial * material)594 GPUNodeGraph *gpu_material_node_graph(GPUMaterial *material)
595 {
596 return &material->graph;
597 }
598
gpu_material_used_libraries(GPUMaterial * material)599 GSet *gpu_material_used_libraries(GPUMaterial *material)
600 {
601 return material->used_libraries;
602 }
603
604 /* Return true if the material compilation has not yet begin or begin. */
GPU_material_status(GPUMaterial * mat)605 eGPUMaterialStatus GPU_material_status(GPUMaterial *mat)
606 {
607 return mat->status;
608 }
609
610 /* Code generation */
611
GPU_material_has_surface_output(GPUMaterial * mat)612 bool GPU_material_has_surface_output(GPUMaterial *mat)
613 {
614 return mat->has_surface_output;
615 }
616
GPU_material_has_volume_output(GPUMaterial * mat)617 bool GPU_material_has_volume_output(GPUMaterial *mat)
618 {
619 return mat->has_volume_output;
620 }
621
GPU_material_is_volume_shader(GPUMaterial * mat)622 bool GPU_material_is_volume_shader(GPUMaterial *mat)
623 {
624 return mat->is_volume_shader;
625 }
626
GPU_material_flag_set(GPUMaterial * mat,eGPUMatFlag flag)627 void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag)
628 {
629 mat->flag |= flag;
630 }
631
GPU_material_flag_get(GPUMaterial * mat,eGPUMatFlag flag)632 bool GPU_material_flag_get(GPUMaterial *mat, eGPUMatFlag flag)
633 {
634 return (mat->flag & flag) != 0;
635 }
636
GPU_material_from_nodetree_find(ListBase * gpumaterials,const void * engine_type,int options)637 GPUMaterial *GPU_material_from_nodetree_find(ListBase *gpumaterials,
638 const void *engine_type,
639 int options)
640 {
641 LISTBASE_FOREACH (LinkData *, link, gpumaterials) {
642 GPUMaterial *current_material = (GPUMaterial *)link->data;
643 if (current_material->engine_type == engine_type && current_material->options == options) {
644 return current_material;
645 }
646 }
647
648 return NULL;
649 }
650
651 /**
652 * \note Caller must use #GPU_material_from_nodetree_find to re-use existing materials,
653 * This is enforced since constructing other arguments to this function may be expensive
654 * so only do this when they are needed.
655 */
GPU_material_from_nodetree(Scene * scene,struct Material * ma,struct bNodeTree * ntree,ListBase * gpumaterials,const void * engine_type,const int options,const bool is_volume_shader,const char * vert_code,const char * geom_code,const char * frag_lib,const char * defines,const char * name,GPUMaterialEvalCallbackFn callback)656 GPUMaterial *GPU_material_from_nodetree(Scene *scene,
657 struct Material *ma,
658 struct bNodeTree *ntree,
659 ListBase *gpumaterials,
660 const void *engine_type,
661 const int options,
662 const bool is_volume_shader,
663 const char *vert_code,
664 const char *geom_code,
665 const char *frag_lib,
666 const char *defines,
667 const char *name,
668 GPUMaterialEvalCallbackFn callback)
669 {
670 LinkData *link;
671 bool has_volume_output, has_surface_output;
672
673 /* Caller must re-use materials. */
674 BLI_assert(GPU_material_from_nodetree_find(gpumaterials, engine_type, options) == NULL);
675
676 /* HACK: Eevee assume this to create Ghash keys. */
677 BLI_assert(sizeof(GPUPass) > 16);
678
679 /* allocate material */
680 GPUMaterial *mat = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial");
681 mat->ma = ma;
682 mat->scene = scene;
683 mat->engine_type = engine_type;
684 mat->options = options;
685 mat->is_volume_shader = is_volume_shader;
686 #ifndef NDEBUG
687 BLI_snprintf(mat->name, sizeof(mat->name), "%s", name);
688 #else
689 UNUSED_VARS(name);
690 #endif
691
692 mat->used_libraries = BLI_gset_new(
693 BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUMaterial.used_libraries");
694
695 /* localize tree to create links for reroute and mute */
696 bNodeTree *localtree = ntreeLocalize(ntree);
697 ntreeGPUMaterialNodes(localtree, mat, &has_surface_output, &has_volume_output);
698
699 gpu_material_ramp_texture_build(mat);
700
701 mat->has_surface_output = has_surface_output;
702 mat->has_volume_output = has_volume_output;
703
704 if (mat->graph.outlink) {
705 if (callback) {
706 callback(mat, options, &vert_code, &geom_code, &frag_lib, &defines);
707 }
708 /* HACK: this is only for eevee. We add the define here after the nodetree evaluation. */
709 if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) {
710 defines = BLI_string_joinN(defines,
711 "#ifndef USE_ALPHA_BLEND\n"
712 "# define USE_SSS\n"
713 "#endif\n");
714 }
715 /* Create source code and search pass cache for an already compiled version. */
716 mat->pass = GPU_generate_pass(mat, &mat->graph, vert_code, geom_code, frag_lib, defines);
717
718 if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) {
719 MEM_freeN((char *)defines);
720 }
721
722 if (mat->pass == NULL) {
723 /* We had a cache hit and the shader has already failed to compile. */
724 mat->status = GPU_MAT_FAILED;
725 gpu_node_graph_free(&mat->graph);
726 }
727 else {
728 GPUShader *sh = GPU_pass_shader_get(mat->pass);
729 if (sh != NULL) {
730 /* We had a cache hit and the shader is already compiled. */
731 mat->status = GPU_MAT_SUCCESS;
732 gpu_node_graph_free_nodes(&mat->graph);
733 }
734 else {
735 mat->status = GPU_MAT_QUEUED;
736 }
737 }
738 }
739 else {
740 mat->status = GPU_MAT_FAILED;
741 gpu_node_graph_free(&mat->graph);
742 }
743
744 /* Only free after GPU_pass_shader_get where GPUUniformBuf
745 * read data from the local tree. */
746 ntreeFreeLocalTree(localtree);
747 MEM_freeN(localtree);
748
749 /* note that even if building the shader fails in some way, we still keep
750 * it to avoid trying to compile again and again, and simply do not use
751 * the actual shader on drawing */
752
753 link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink");
754 link->data = mat;
755 BLI_addtail(gpumaterials, link);
756
757 return mat;
758 }
759
GPU_material_compile(GPUMaterial * mat)760 void GPU_material_compile(GPUMaterial *mat)
761 {
762 bool success;
763
764 BLI_assert(mat->status == GPU_MAT_QUEUED);
765 BLI_assert(mat->pass);
766
767 /* NOTE: The shader may have already been compiled here since we are
768 * sharing GPUShader across GPUMaterials. In this case it's a no-op. */
769 #ifndef NDEBUG
770 success = GPU_pass_compile(mat->pass, mat->name);
771 #else
772 success = GPU_pass_compile(mat->pass, __func__);
773 #endif
774
775 if (success) {
776 GPUShader *sh = GPU_pass_shader_get(mat->pass);
777 if (sh != NULL) {
778 mat->status = GPU_MAT_SUCCESS;
779 gpu_node_graph_free_nodes(&mat->graph);
780 }
781 }
782 else {
783 mat->status = GPU_MAT_FAILED;
784 GPU_pass_release(mat->pass);
785 mat->pass = NULL;
786 gpu_node_graph_free(&mat->graph);
787 }
788 }
789
GPU_materials_free(Main * bmain)790 void GPU_materials_free(Main *bmain)
791 {
792 Material *ma;
793 World *wo;
794
795 for (ma = bmain->materials.first; ma; ma = ma->id.next) {
796 GPU_material_free(&ma->gpumaterial);
797 }
798
799 for (wo = bmain->worlds.first; wo; wo = wo->id.next) {
800 GPU_material_free(&wo->gpumaterial);
801 }
802
803 BKE_material_defaults_free_gpu();
804 }
805