diff options
Diffstat (limited to 'source/blender/gpu/intern')
-rw-r--r-- | source/blender/gpu/intern/gpu_codegen.c | 1123 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_codegen.cc | 825 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_codegen.h | 28 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_immediate_util.c | 143 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_init_exit.c | 2 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_material.c | 339 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_material_library.c | 904 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_material_library.h | 27 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_node_graph.c | 117 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_node_graph.h | 54 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_shader.cc | 44 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_shader_builder_stubs.cc | 5 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_shader_create_info.cc | 28 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_shader_create_info.hh | 11 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_shader_dependency.cc | 321 |
15 files changed, 1621 insertions, 2350 deletions
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c deleted file mode 100644 index e462308d3ee..00000000000 --- a/source/blender/gpu/intern/gpu_codegen.c +++ /dev/null @@ -1,1123 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2005 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup gpu - * - * Convert material node-trees to GLSL. - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_customdata_types.h" -#include "DNA_image_types.h" - -#include "BLI_blenlib.h" -#include "BLI_dynstr.h" -#include "BLI_ghash.h" -#include "BLI_hash_mm2a.h" -#include "BLI_link_utils.h" -#include "BLI_threads.h" -#include "BLI_utildefines.h" - -#include "PIL_time.h" - -#include "BKE_material.h" - -#include "GPU_capabilities.h" -#include "GPU_material.h" -#include "GPU_shader.h" -#include "GPU_uniform_buffer.h" -#include "GPU_vertex_format.h" - -#include "BLI_sys_types.h" /* for intptr_t support */ - -#include "gpu_codegen.h" -#include "gpu_material_library.h" -#include "gpu_node_graph.h" - -#include <stdarg.h> -#include <string.h> - -extern char datatoc_gpu_shader_codegen_lib_glsl[]; -extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; - -/* -------------------- GPUPass Cache ------------------ */ -/** - * Internal shader cache: This prevent the shader recompilation / stall when - * using undo/redo AND also allows for GPUPass reuse if the Shader code is the - * same for 2 different Materials. Unused GPUPasses are free by Garbage collection. - */ - -/* Only use one linked-list that contains the GPUPasses grouped by hash. */ -static GPUPass *pass_cache = NULL; -static SpinLock pass_cache_spin; - -static uint32_t gpu_pass_hash(const char *frag_gen, const char *defs, ListBase *attributes) -{ - BLI_HashMurmur2A hm2a; - BLI_hash_mm2a_init(&hm2a, 0); - BLI_hash_mm2a_add(&hm2a, (uchar *)frag_gen, strlen(frag_gen)); - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attributes) { - BLI_hash_mm2a_add(&hm2a, (uchar *)attr->name, strlen(attr->name)); - } - if (defs) { - BLI_hash_mm2a_add(&hm2a, (uchar *)defs, strlen(defs)); - } - - return BLI_hash_mm2a_end(&hm2a); -} - -/* Search by hash only. Return first pass with the same hash. - * There is hash collision if (pass->next && pass->next->hash == hash) */ -static GPUPass *gpu_pass_cache_lookup(uint32_t hash) -{ - BLI_spin_lock(&pass_cache_spin); - /* Could be optimized with a Lookup table. */ - for (GPUPass *pass = pass_cache; pass; pass = pass->next) { - if (pass->hash == hash) { - BLI_spin_unlock(&pass_cache_spin); - return pass; - } - } - BLI_spin_unlock(&pass_cache_spin); - return NULL; -} - -/* Check all possible passes with the same hash. */ -static GPUPass *gpu_pass_cache_resolve_collision(GPUPass *pass, - const char *vert, - const char *geom, - const char *frag, - const char *defs, - uint32_t hash) -{ - BLI_spin_lock(&pass_cache_spin); - /* Collision, need to `strcmp` the whole shader. */ - for (; pass && (pass->hash == hash); pass = pass->next) { - if ((defs != NULL) && (!STREQ(pass->defines, defs))) { /* Pass */ - } - else if ((geom != NULL) && (!STREQ(pass->geometrycode, geom))) { /* Pass */ - } - else if ((!STREQ(pass->fragmentcode, frag) == 0) && (STREQ(pass->vertexcode, vert))) { - BLI_spin_unlock(&pass_cache_spin); - return pass; - } - } - BLI_spin_unlock(&pass_cache_spin); - return NULL; -} - -/* GLSL code generation */ - -static void codegen_convert_datatype(DynStr *ds, int from, int to, const char *tmp, int id) -{ - char name[1024]; - - BLI_snprintf(name, sizeof(name), "%s%d", tmp, id); - - if (from == to) { - BLI_dynstr_append(ds, name); - } - else if (to == GPU_FLOAT) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "dot(%s.rgb, vec3(0.2126, 0.7152, 0.0722))", name); - } - else if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "(%s.r + %s.g + %s.b) / 3.0", name, name, name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "%s.r", name); - } - } - else if (to == GPU_VEC2) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, %s.a)", name, name, name, name); - } - else if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, 1.0)", name, name, name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "vec2(%s, 1.0)", name); - } - } - else if (to == GPU_VEC3) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "%s.rgb", name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "vec3(%s.r, %s.r, %s.r)", name, name, name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "vec3(%s, %s, %s)", name, name, name); - } - } - else if (to == GPU_VEC4) { - if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "vec4(%s, 1.0)", name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "vec4(%s.r, %s.r, %s.r, %s.g)", name, name, name, name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "vec4(%s, %s, %s, 1.0)", name, name, name); - } - } - else if (to == GPU_CLOSURE) { - if (from == GPU_VEC4) { - BLI_dynstr_appendf(ds, "closure_emission(%s.rgb)", name); - } - else if (from == GPU_VEC3) { - BLI_dynstr_appendf(ds, "closure_emission(%s.rgb)", name); - } - else if (from == GPU_VEC2) { - BLI_dynstr_appendf(ds, "closure_emission(%s.rrr)", name); - } - else if (from == GPU_FLOAT) { - BLI_dynstr_appendf(ds, "closure_emission(vec3(%s, %s, %s))", name, name, name); - } - } - else { - BLI_dynstr_append(ds, name); - } -} - -static void codegen_print_datatype(DynStr *ds, const eGPUType type, float *data) -{ - int i; - - BLI_dynstr_appendf(ds, "%s(", gpu_data_type_to_string(type)); - - for (i = 0; i < type; i++) { - BLI_dynstr_appendf(ds, "%.12f", data[i]); - if (i == type - 1) { - BLI_dynstr_append(ds, ")"); - } - else { - BLI_dynstr_append(ds, ", "); - } - } -} - -static const char *gpu_builtin_name(eGPUBuiltin builtin) -{ - if (builtin == GPU_VIEW_MATRIX) { - return "unfviewmat"; - } - if (builtin == GPU_OBJECT_MATRIX) { - return "unfobmat"; - } - if (builtin == GPU_INVERSE_VIEW_MATRIX) { - return "unfinvviewmat"; - } - if (builtin == GPU_INVERSE_OBJECT_MATRIX) { - return "unfinvobmat"; - } - if (builtin == GPU_LOC_TO_VIEW_MATRIX) { - return "unflocaltoviewmat"; - } - if (builtin == GPU_INVERSE_LOC_TO_VIEW_MATRIX) { - return "unfinvlocaltoviewmat"; - } - if (builtin == GPU_VIEW_POSITION) { - return "varposition"; - } - if (builtin == GPU_WORLD_NORMAL) { - return "varwnormal"; - } - if (builtin == GPU_VIEW_NORMAL) { - return "varnormal"; - } - if (builtin == GPU_OBJECT_COLOR) { - return "unfobjectcolor"; - } - if (builtin == GPU_AUTO_BUMPSCALE) { - return "unfobautobumpscale"; - } - if (builtin == GPU_CAMERA_TEXCO_FACTORS) { - return "unfcameratexfactors"; - } - if (builtin == GPU_PARTICLE_SCALAR_PROPS) { - return "unfparticlescalarprops"; - } - if (builtin == GPU_PARTICLE_LOCATION) { - return "unfparticleco"; - } - if (builtin == GPU_PARTICLE_VELOCITY) { - return "unfparticlevel"; - } - if (builtin == GPU_PARTICLE_ANG_VELOCITY) { - return "unfparticleangvel"; - } - if (builtin == GPU_OBJECT_INFO) { - return "unfobjectinfo"; - } - if (builtin == GPU_BARYCENTRIC_TEXCO) { - return "unfbarycentrictex"; - } - if (builtin == GPU_BARYCENTRIC_DIST) { - return "unfbarycentricdist"; - } - return ""; -} - -static void codegen_set_unique_ids(GPUNodeGraph *graph) -{ - int id = 1; - - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - /* set id for unique names of uniform variables */ - input->id = id++; - } - - LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { - /* set id for unique names of tmp variables storing output */ - output->id = id++; - } - } -} - -/** - * It will create an UBO for GPUMaterial if there is any GPU_DYNAMIC_UBO. - */ -static int codegen_process_uniforms_functions(GPUMaterial *material, - DynStr *ds, - GPUNodeGraph *graph) -{ - const char *name; - int builtins = 0; - ListBase ubo_inputs = {NULL, NULL}; - - /* Textures */ - LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph->textures) { - if (tex->colorband) { - BLI_dynstr_appendf(ds, "uniform sampler1DArray %s;\n", tex->sampler_name); - } - else if (tex->tiled_mapping_name[0]) { - BLI_dynstr_appendf(ds, "uniform sampler2DArray %s;\n", tex->sampler_name); - BLI_dynstr_appendf(ds, "uniform sampler1DArray %s;\n", tex->tiled_mapping_name); - } - else { - BLI_dynstr_appendf(ds, "uniform sampler2D %s;\n", tex->sampler_name); - } - } - - /* Volume Grids */ - LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph->volume_grids) { - BLI_dynstr_appendf(ds, "uniform sampler3D %s;\n", grid->sampler_name); - BLI_dynstr_appendf(ds, "uniform mat4 %s = mat4(0.0);\n", grid->transform_name); - } - - /* Print other uniforms */ - - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - if (input->source == GPU_SOURCE_BUILTIN) { - /* only define each builtin uniform/varying once */ - if (!(builtins & input->builtin)) { - builtins |= input->builtin; - name = gpu_builtin_name(input->builtin); - - if (BLI_str_startswith(name, "unf")) { - BLI_dynstr_appendf(ds, "uniform %s %s;\n", gpu_data_type_to_string(input->type), name); - } - else { - BLI_dynstr_appendf(ds, "in %s %s;\n", gpu_data_type_to_string(input->type), name); - } - } - } - else if (input->source == GPU_SOURCE_STRUCT) { - /* Add other struct here if needed. */ - BLI_dynstr_appendf(ds, "Closure strct%d = CLOSURE_DEFAULT;\n", input->id); - } - else if (input->source == GPU_SOURCE_UNIFORM) { - if (!input->link) { - /* We handle the UBOuniforms separately. */ - BLI_addtail(&ubo_inputs, BLI_genericNodeN(input)); - } - } - else if (input->source == GPU_SOURCE_CONSTANT) { - BLI_dynstr_appendf( - ds, "const %s cons%d = ", gpu_data_type_to_string(input->type), input->id); - codegen_print_datatype(ds, input->type, input->vec); - BLI_dynstr_append(ds, ";\n"); - } - } - } - - /* Handle the UBO block separately. */ - if ((material != NULL) && !BLI_listbase_is_empty(&ubo_inputs)) { - GPU_material_uniform_buffer_create(material, &ubo_inputs); - - /* Inputs are sorted */ - BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME); - - LISTBASE_FOREACH (LinkData *, link, &ubo_inputs) { - GPUInput *input = (GPUInput *)(link->data); - BLI_dynstr_appendf(ds, " %s unf%d;\n", gpu_data_type_to_string(input->type), input->id); - } - BLI_dynstr_append(ds, "};\n"); - BLI_freelistN(&ubo_inputs); - } - - /* Generate the uniform attribute UBO if necessary. */ - if (!BLI_listbase_is_empty(&graph->uniform_attrs.list)) { - BLI_dynstr_append(ds, "\nstruct UniformAttributes {\n"); - LISTBASE_FOREACH (GPUUniformAttr *, attr, &graph->uniform_attrs.list) { - BLI_dynstr_appendf(ds, " vec4 attr%d;\n", attr->id); - } - BLI_dynstr_append(ds, "};\n"); - BLI_dynstr_appendf(ds, "layout (std140) uniform %s {\n", GPU_ATTRIBUTE_UBO_BLOCK_NAME); - BLI_dynstr_append(ds, " UniformAttributes uniform_attrs[DRW_RESOURCE_CHUNK_LEN];\n"); - BLI_dynstr_append(ds, "};\n"); - BLI_dynstr_append(ds, "#define GET_UNIFORM_ATTR(name) (uniform_attrs[resource_id].name)\n"); - } - - BLI_dynstr_append(ds, "\n"); - - return builtins; -} - -static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph) -{ - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - /* declare temporary variables for node output storage */ - LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { - if (output->type == GPU_CLOSURE) { - BLI_dynstr_appendf(ds, " Closure tmp%d;\n", output->id); - } - else { - BLI_dynstr_appendf(ds, " %s tmp%d;\n", gpu_data_type_to_string(output->type), output->id); - } - } - } - BLI_dynstr_append(ds, "\n"); -} - -static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph) -{ - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - BLI_dynstr_appendf(ds, " %s(", node->name); - - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - if (input->source == GPU_SOURCE_TEX) { - BLI_dynstr_append(ds, input->texture->sampler_name); - } - else if (input->source == GPU_SOURCE_TEX_TILED_MAPPING) { - BLI_dynstr_append(ds, input->texture->tiled_mapping_name); - } - else if (input->source == GPU_SOURCE_VOLUME_GRID) { - BLI_dynstr_append(ds, input->volume_grid->sampler_name); - } - else if (input->source == GPU_SOURCE_VOLUME_GRID_TRANSFORM) { - BLI_dynstr_append(ds, input->volume_grid->transform_name); - } - else if (input->source == GPU_SOURCE_OUTPUT) { - codegen_convert_datatype( - ds, input->link->output->type, input->type, "tmp", input->link->output->id); - } - else if (input->source == GPU_SOURCE_BUILTIN) { - /* TODO(fclem): get rid of that. */ - if (input->builtin == GPU_INVERSE_VIEW_MATRIX) { - BLI_dynstr_append(ds, "viewinv"); - } - else if (input->builtin == GPU_VIEW_MATRIX) { - BLI_dynstr_append(ds, "viewmat"); - } - else if (input->builtin == GPU_CAMERA_TEXCO_FACTORS) { - BLI_dynstr_append(ds, "camtexfac"); - } - else if (input->builtin == GPU_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, "localtoviewmat"); - } - else if (input->builtin == GPU_INVERSE_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, "invlocaltoviewmat"); - } - else if (input->builtin == GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "barycentricDist"); - } - else if (input->builtin == GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "barytexco"); - } - else if (input->builtin == GPU_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "objmat"); - } - else if (input->builtin == GPU_OBJECT_INFO) { - BLI_dynstr_append(ds, "ObjectInfo"); - } - else if (input->builtin == GPU_OBJECT_COLOR) { - BLI_dynstr_append(ds, "ObjectColor"); - } - else if (input->builtin == GPU_INVERSE_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "objinv"); - } - else if (input->builtin == GPU_VIEW_POSITION) { - BLI_dynstr_append(ds, "viewposition"); - } - else if (input->builtin == GPU_VIEW_NORMAL) { - BLI_dynstr_append(ds, "facingnormal"); - } - else if (input->builtin == GPU_WORLD_NORMAL) { - BLI_dynstr_append(ds, "facingwnormal"); - } - else { - BLI_dynstr_append(ds, gpu_builtin_name(input->builtin)); - } - } - else if (input->source == GPU_SOURCE_STRUCT) { - BLI_dynstr_appendf(ds, "strct%d", input->id); - } - else if (input->source == GPU_SOURCE_UNIFORM) { - BLI_dynstr_appendf(ds, "unf%d", input->id); - } - else if (input->source == GPU_SOURCE_CONSTANT) { - BLI_dynstr_appendf(ds, "cons%d", input->id); - } - else if (input->source == GPU_SOURCE_ATTR) { - codegen_convert_datatype(ds, input->attr->gputype, input->type, "var", input->attr->id); - } - else if (input->source == GPU_SOURCE_UNIFORM_ATTR) { - BLI_dynstr_appendf(ds, "GET_UNIFORM_ATTR(attr%d)", input->uniform_attr->id); - } - - BLI_dynstr_append(ds, ", "); - } - - LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { - BLI_dynstr_appendf(ds, "tmp%d", output->id); - if (output->next) { - BLI_dynstr_append(ds, ", "); - } - } - - BLI_dynstr_append(ds, ");\n"); - } -} - -static void codegen_final_output(DynStr *ds, GPUOutput *finaloutput) -{ - BLI_dynstr_appendf(ds, "return tmp%d;\n", finaloutput->id); -} - -static char *code_generate_fragment(GPUMaterial *material, - GPUNodeGraph *graph, - const char *interface_str) -{ - DynStr *ds = BLI_dynstr_new(); - char *code; - int builtins; - - codegen_set_unique_ids(graph); - - /* Attributes, Shader stage interface. */ - if (interface_str) { - BLI_dynstr_appendf(ds, "in codegenInterface {%s};\n\n", interface_str); - } - - builtins = codegen_process_uniforms_functions(material, ds, graph); - - if (builtins & (GPU_OBJECT_INFO | GPU_OBJECT_COLOR)) { - BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); - } - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); - } - - BLI_dynstr_append(ds, "Closure nodetree_exec(void)\n{\n"); - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, " vec2 barytexco = barycentric_resolve(barycentricTexCo);\n"); - } - /* TODO(fclem): get rid of that. */ - if (builtins & GPU_VIEW_MATRIX) { - BLI_dynstr_append(ds, " #define viewmat ViewMatrix\n"); - } - if (builtins & GPU_CAMERA_TEXCO_FACTORS) { - BLI_dynstr_append(ds, " #define camtexfac CameraTexCoFactors\n"); - } - if (builtins & GPU_OBJECT_MATRIX) { - BLI_dynstr_append(ds, " #define objmat ModelMatrix\n"); - } - if (builtins & GPU_INVERSE_OBJECT_MATRIX) { - BLI_dynstr_append(ds, " #define objinv ModelMatrixInverse\n"); - } - if (builtins & GPU_INVERSE_VIEW_MATRIX) { - BLI_dynstr_append(ds, " #define viewinv ViewMatrixInverse\n"); - } - if (builtins & GPU_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, " #define localtoviewmat (ViewMatrix * ModelMatrix)\n"); - } - if (builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, - " #define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n"); - } - if (builtins & GPU_VIEW_NORMAL) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, " vec3 n;\n"); - BLI_dynstr_append(ds, " world_normals_get(n);\n"); - BLI_dynstr_append(ds, " vec3 facingnormal = transform_direction(ViewMatrix, n);\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, " vec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n"); - BLI_dynstr_append(ds, "#endif\n"); - } - if (builtins & GPU_WORLD_NORMAL) { - BLI_dynstr_append(ds, " vec3 facingwnormal;\n"); - if (builtins & GPU_VIEW_NORMAL) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, " facingwnormal = n;\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); - BLI_dynstr_append(ds, "#endif\n"); - } - else { - BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); - } - } - if (builtins & GPU_VIEW_POSITION) { - BLI_dynstr_append(ds, " #define viewposition viewPosition\n"); - } - - codegen_declare_tmps(ds, graph); - codegen_call_functions(ds, graph); - - BLI_dynstr_append(ds, " #ifndef VOLUMETRICS\n"); - BLI_dynstr_append(ds, " if (renderPassAOV) {\n"); - BLI_dynstr_append(ds, " switch (render_pass_aov_hash()) {\n"); - GSet *aovhashes_added = BLI_gset_int_new(__func__); - LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) { - void *aov_key = POINTER_FROM_INT(aovlink->hash); - if (BLI_gset_haskey(aovhashes_added, aov_key)) { - continue; - } - BLI_dynstr_appendf(ds, " case %d: {\n ", aovlink->hash); - codegen_final_output(ds, aovlink->outlink->output); - BLI_dynstr_append(ds, " }\n"); - BLI_gset_add(aovhashes_added, aov_key); - } - BLI_gset_free(aovhashes_added, NULL); - BLI_dynstr_append(ds, " default: {\n"); - BLI_dynstr_append(ds, " Closure no_aov = CLOSURE_DEFAULT;\n"); - BLI_dynstr_append(ds, " no_aov.holdout = 1.0;\n"); - BLI_dynstr_append(ds, " return no_aov;\n"); - BLI_dynstr_append(ds, " }\n"); - BLI_dynstr_append(ds, " }\n"); - BLI_dynstr_append(ds, " } else {\n"); - BLI_dynstr_append(ds, " #else /* VOLUMETRICS */\n"); - BLI_dynstr_append(ds, " {\n"); - BLI_dynstr_append(ds, " #endif /* VOLUMETRICS */\n "); - codegen_final_output(ds, graph->outlink->output); - BLI_dynstr_append(ds, " }\n"); - - BLI_dynstr_append(ds, "}\n"); - - /* create shader */ - code = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - -#if 0 - if (G.debug & G_DEBUG) { - printf("%s\n", code); - } -#endif - - return code; -} - -static const char *attr_prefix_get(CustomDataType type) -{ - switch (type) { - case CD_ORCO: - return "orco"; - case CD_MTFACE: - return "u"; - case CD_TANGENT: - return "t"; - case CD_MCOL: - case CD_MLOOPCOL: - return "c"; - case CD_PROP_COLOR: - return "c"; - case CD_AUTO_FROM_NAME: - return "a"; - case CD_HAIRLENGTH: - return "hl"; - default: - BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!"); - return ""; - } -} - -/* We talk about shader stage interface, not to be mistaken with GPUShaderInterface. */ -static char *code_generate_interface(GPUNodeGraph *graph, int builtins) -{ - if (BLI_listbase_is_empty(&graph->attributes) && - (builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0) { - return NULL; - } - - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, "\n"); - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - if (attr->type == CD_HAIRLENGTH) { - BLI_dynstr_appendf(ds, "float var%d;\n", attr->id); - } - else { - BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); - } - } - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n"); - } - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "vec3 barycentricDist;\n"); - } - - char *code = BLI_dynstr_get_cstring(ds); - - BLI_dynstr_free(ds); - - return code; -} - -static char *code_generate_vertex(GPUNodeGraph *graph, - const char *interface_str, - const char *vert_code, - int builtins) -{ - DynStr *ds = BLI_dynstr_new(); - - BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); - - /* Inputs */ - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - const char *type_str = gpu_data_type_to_string(attr->gputype); - const char *prefix = attr_prefix_get(attr->type); - /* XXX FIXME: see notes in mesh_render_data_create() */ - /* NOTE: Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ - if (attr->type == CD_ORCO) { - /* OPTI: orco is computed from local positions, but only if no modifier is present. */ - BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); - BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n"); - } - else if (attr->type == CD_HAIRLENGTH) { - BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); - BLI_dynstr_append(ds, "DEFINE_ATTR(float, hairLen);\n"); - } - else if (attr->name[0] == '\0') { - BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix); - BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix); - } - else { - char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - GPU_vertformat_safe_attr_name(attr->name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s%s);\n", type_str, prefix, attr_safe_name); - BLI_dynstr_appendf(ds, "#define att%d %s%s\n", attr->id, prefix, attr_safe_name); - } - } - - /* Outputs interface */ - if (interface_str) { - BLI_dynstr_appendf(ds, "out codegenInterface {%s};\n\n", interface_str); - } - - /* Prototype. Needed for hair functions. */ - BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv);\n"); - BLI_dynstr_append(ds, "#define USE_ATTR\n\n"); - - BLI_dynstr_append(ds, vert_code); - BLI_dynstr_append(ds, "\n\n"); - - BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n"); - - /* GPU_BARYCENTRIC_TEXCO cannot be computed based on gl_VertexID - * for MESH_SHADER because of indexed drawing. In this case a - * geometry shader is needed. */ - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_appendf(ds, " barycentricTexCo = barycentric_get();\n"); - } - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_appendf(ds, " barycentricDist = vec3(0);\n"); - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - if (attr->type == CD_TANGENT) { /* silly exception */ - BLI_dynstr_appendf(ds, " var%d = tangent_get(att%d, normalmat);\n", attr->id, attr->id); - } - else if (attr->type == CD_ORCO) { - BLI_dynstr_appendf( - ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id); - } - else if (attr->type == CD_HAIRLENGTH) { - BLI_dynstr_appendf(ds, " var%d = hair_len_get(hair_get_strand_id(), hairLen);\n", attr->id); - } - else { - const char *type_str = gpu_data_type_to_string(attr->gputype); - BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id); - } - } - - BLI_dynstr_append(ds, "}\n"); - - char *code = BLI_dynstr_get_cstring(ds); - - BLI_dynstr_free(ds); - -#if 0 - if (G.debug & G_DEBUG) { - printf("%s\n", code); - } -#endif - - return code; -} - -static char *code_generate_geometry(GPUNodeGraph *graph, - const char *interface_str, - const char *geom_code, - int builtins) -{ - if (!geom_code) { - return NULL; - } - - DynStr *ds = BLI_dynstr_new(); - - /* Attributes, Shader interface; */ - if (interface_str) { - BLI_dynstr_appendf(ds, "in codegenInterface {%s} dataAttrIn[];\n\n", interface_str); - BLI_dynstr_appendf(ds, "out codegenInterface {%s} dataAttrOut;\n\n", interface_str); - } - - BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); - - if (builtins & GPU_BARYCENTRIC_DIST) { - /* geom_code should do something with this, but may not. */ - BLI_dynstr_append(ds, "#define DO_BARYCENTRIC_DISTANCES\n"); - } - - /* Generate varying assignments. */ - BLI_dynstr_append(ds, "#define USE_ATTR\n"); - /* This needs to be a define. Some drivers don't like variable vert index inside dataAttrIn. */ - BLI_dynstr_append(ds, "#define pass_attr(vert) {\\\n"); - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\\\n"); - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - /* TODO: let shader choose what to do depending on what the attribute is. */ - BLI_dynstr_appendf(ds, "dataAttrOut.var%d = dataAttrIn[vert].var%d;\\\n", attr->id, attr->id); - } - BLI_dynstr_append(ds, "}\n\n"); - - BLI_dynstr_append(ds, geom_code); - - char *code = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - - return code; -} - -GPUShader *GPU_pass_shader_get(GPUPass *pass) -{ - return pass->shader; -} - -/* Pass create/free */ - -static bool gpu_pass_is_valid(GPUPass *pass) -{ - /* Shader is not null if compilation is successful. */ - return (pass->compiled == false || pass->shader != NULL); -} - -GPUPass *GPU_generate_pass(GPUMaterial *material, - GPUNodeGraph *graph, - const char *vert_code, - const char *geom_code, - const char *frag_lib, - const char *defines) -{ - /* Prune the unused nodes and extract attributes before compiling so the - * generated VBOs are ready to accept the future shader. */ - gpu_node_graph_prune_unused(graph); - gpu_node_graph_finalize_uniform_attrs(graph); - - int builtins = 0; - LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - if (input->source == GPU_SOURCE_BUILTIN) { - builtins |= input->builtin; - } - } - } - /* generate code */ - char *interface_str = code_generate_interface(graph, builtins); - char *fragmentgen = code_generate_fragment(material, graph, interface_str); - - /* Cache lookup: Reuse shaders already compiled */ - uint32_t hash = gpu_pass_hash(fragmentgen, defines, &graph->attributes); - GPUPass *pass_hash = gpu_pass_cache_lookup(hash); - - if (pass_hash && (pass_hash->next == NULL || pass_hash->next->hash != hash)) { - /* No collision, just return the pass. */ - MEM_SAFE_FREE(interface_str); - MEM_freeN(fragmentgen); - if (!gpu_pass_is_valid(pass_hash)) { - /* Shader has already been created but failed to compile. */ - return NULL; - } - pass_hash->refcount += 1; - return pass_hash; - } - - /* Either the shader is not compiled or there is a hash collision... - * continue generating the shader strings. */ - GSet *used_libraries = gpu_material_used_libraries(material); - char *tmp = gpu_material_library_generate_code(used_libraries, frag_lib); - - char *geometrycode = code_generate_geometry(graph, interface_str, geom_code, builtins); - char *vertexcode = code_generate_vertex(graph, interface_str, vert_code, builtins); - char *fragmentcode = BLI_strdupcat(tmp, fragmentgen); - - MEM_SAFE_FREE(interface_str); - MEM_freeN(fragmentgen); - MEM_freeN(tmp); - - GPUPass *pass = NULL; - if (pass_hash) { - /* Cache lookup: Reuse shaders already compiled */ - pass = gpu_pass_cache_resolve_collision( - pass_hash, vertexcode, geometrycode, fragmentcode, defines, hash); - } - - if (pass) { - MEM_SAFE_FREE(vertexcode); - MEM_SAFE_FREE(fragmentcode); - MEM_SAFE_FREE(geometrycode); - - /* Cache hit. Reuse the same GPUPass and GPUShader. */ - if (!gpu_pass_is_valid(pass)) { - /* Shader has already been created but failed to compile. */ - return NULL; - } - - pass->refcount += 1; - } - else { - /* We still create a pass even if shader compilation - * fails to avoid trying to compile again and again. */ - pass = MEM_callocN(sizeof(GPUPass), "GPUPass"); - pass->shader = NULL; - pass->refcount = 1; - pass->hash = hash; - pass->vertexcode = vertexcode; - pass->fragmentcode = fragmentcode; - pass->geometrycode = geometrycode; - pass->defines = (defines) ? BLI_strdup(defines) : NULL; - pass->compiled = false; - - BLI_spin_lock(&pass_cache_spin); - if (pass_hash != NULL) { - /* Add after the first pass having the same hash. */ - pass->next = pass_hash->next; - pass_hash->next = pass; - } - else { - /* No other pass have same hash, just prepend to the list. */ - BLI_LINKS_PREPEND(pass_cache, pass); - } - BLI_spin_unlock(&pass_cache_spin); - } - - return pass; -} - -static int count_active_texture_sampler(GPUShader *shader, const char *source) -{ - const char *code = source; - - /* Remember this is per stage. */ - GSet *sampler_ids = BLI_gset_int_new(__func__); - int num_samplers = 0; - - while ((code = strstr(code, "uniform "))) { - /* Move past "uniform". */ - code += 7; - /* Skip following spaces. */ - while (*code == ' ') { - code++; - } - /* Skip "i" from potential isamplers. */ - if (*code == 'i') { - code++; - } - /* Skip following spaces. */ - if (BLI_str_startswith(code, "sampler")) { - /* Move past "uniform". */ - code += 7; - /* Skip sampler type suffix. */ - while (!ELEM(*code, ' ', '\0')) { - code++; - } - /* Skip following spaces. */ - while (*code == ' ') { - code++; - } - - if (*code != '\0') { - char sampler_name[64]; - code = gpu_str_skip_token(code, sampler_name, sizeof(sampler_name)); - int id = GPU_shader_get_uniform(shader, sampler_name); - - if (id == -1) { - continue; - } - /* Catch duplicates. */ - if (BLI_gset_add(sampler_ids, POINTER_FROM_INT(id))) { - num_samplers++; - } - } - } - } - - BLI_gset_free(sampler_ids, NULL); - - return num_samplers; -} - -static bool gpu_pass_shader_validate(GPUPass *pass, GPUShader *shader) -{ - if (shader == NULL) { - return false; - } - - /* NOTE: The only drawback of this method is that it will count a sampler - * used in the fragment shader and only declared (but not used) in the vertex - * shader as used by both. But this corner case is not happening for now. */ - int vert_samplers_len = count_active_texture_sampler(shader, pass->vertexcode); - int frag_samplers_len = count_active_texture_sampler(shader, pass->fragmentcode); - - int total_samplers_len = vert_samplers_len + frag_samplers_len; - - /* Validate against opengl limit. */ - if ((frag_samplers_len > GPU_max_textures_frag()) || - (vert_samplers_len > GPU_max_textures_vert())) { - return false; - } - - if (pass->geometrycode) { - int geom_samplers_len = count_active_texture_sampler(shader, pass->geometrycode); - total_samplers_len += geom_samplers_len; - if (geom_samplers_len > GPU_max_textures_geom()) { - return false; - } - } - - return (total_samplers_len <= GPU_max_textures()); -} - -bool GPU_pass_compile(GPUPass *pass, const char *shname) -{ - bool success = true; - if (!pass->compiled) { - GPUShader *shader = GPU_shader_create( - pass->vertexcode, pass->fragmentcode, pass->geometrycode, NULL, pass->defines, shname); - - /* NOTE: Some drivers / gpu allows more active samplers than the opengl limit. - * We need to make sure to count active samplers to avoid undefined behavior. */ - if (!gpu_pass_shader_validate(pass, shader)) { - success = false; - if (shader != NULL) { - fprintf(stderr, "GPUShader: error: too many samplers in shader.\n"); - GPU_shader_free(shader); - shader = NULL; - } - } - pass->shader = shader; - pass->compiled = true; - } - - return success; -} - -void GPU_pass_release(GPUPass *pass) -{ - BLI_assert(pass->refcount > 0); - pass->refcount--; -} - -static void gpu_pass_free(GPUPass *pass) -{ - BLI_assert(pass->refcount == 0); - if (pass->shader) { - GPU_shader_free(pass->shader); - } - MEM_SAFE_FREE(pass->fragmentcode); - MEM_SAFE_FREE(pass->geometrycode); - MEM_SAFE_FREE(pass->vertexcode); - MEM_SAFE_FREE(pass->defines); - MEM_freeN(pass); -} - -void GPU_pass_cache_garbage_collect(void) -{ - static int lasttime = 0; - const int shadercollectrate = 60; /* hardcoded for now. */ - int ctime = (int)PIL_check_seconds_timer(); - - if (ctime < shadercollectrate + lasttime) { - return; - } - - lasttime = ctime; - - BLI_spin_lock(&pass_cache_spin); - GPUPass *next, **prev_pass = &pass_cache; - for (GPUPass *pass = pass_cache; pass; pass = next) { - next = pass->next; - if (pass->refcount == 0) { - /* Remove from list */ - *prev_pass = next; - gpu_pass_free(pass); - } - else { - prev_pass = &pass->next; - } - } - BLI_spin_unlock(&pass_cache_spin); -} - -void GPU_pass_cache_init(void) -{ - BLI_spin_init(&pass_cache_spin); -} - -void GPU_pass_cache_free(void) -{ - BLI_spin_lock(&pass_cache_spin); - while (pass_cache) { - GPUPass *next = pass_cache->next; - gpu_pass_free(pass_cache); - pass_cache = next; - } - BLI_spin_unlock(&pass_cache_spin); - - BLI_spin_end(&pass_cache_spin); -} - -/* Module */ - -void gpu_codegen_init(void) -{ -} - -void gpu_codegen_exit(void) -{ - BKE_material_defaults_free_gpu(); - GPU_shader_free_builtin_shaders(); -} diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc new file mode 100644 index 00000000000..8963fa45c96 --- /dev/null +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -0,0 +1,825 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2005 Blender Foundation. */ + +/** \file + * \ingroup gpu + * + * Convert material node-trees to GLSL. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_customdata_types.h" +#include "DNA_image_types.h" + +#include "BLI_blenlib.h" +#include "BLI_dynstr.h" +#include "BLI_ghash.h" +#include "BLI_hash_mm2a.h" +#include "BLI_link_utils.h" +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "PIL_time.h" + +#include "BKE_material.h" +#include "BKE_world.h" + +#include "GPU_capabilities.h" +#include "GPU_material.h" +#include "GPU_shader.h" +#include "GPU_uniform_buffer.h" +#include "GPU_vertex_format.h" + +#include "BLI_sys_types.h" /* for intptr_t support */ + +#include "gpu_codegen.h" +#include "gpu_material_library.h" +#include "gpu_node_graph.h" +#include "gpu_shader_create_info.hh" +#include "gpu_shader_dependency_private.h" + +#include <stdarg.h> +#include <string.h> + +#include <sstream> +#include <string> + +using namespace blender::gpu::shader; + +struct GPUCodegenCreateInfo : ShaderCreateInfo { + struct NameBuffer { + char attr_names[16][GPU_MAX_SAFE_ATTR_NAME + 1]; + char var_names[16][8]; + }; + + /** Optional generated interface. */ + StageInterfaceInfo *interface_generated = nullptr; + /** Optional name buffer containing names referenced by StringRefNull. */ + NameBuffer *name_buffer = nullptr; + + GPUCodegenCreateInfo(const char *name) : ShaderCreateInfo(name){}; + ~GPUCodegenCreateInfo() + { + delete interface_generated; + MEM_delete(name_buffer); + }; +}; + +struct GPUPass { + struct GPUPass *next; + + GPUShader *shader; + GPUCodegenCreateInfo *create_info = nullptr; + /** Orphaned GPUPasses gets freed by the garbage collector. */ + uint refcount; + /** Identity hash generated from all GLSL code. */ + uint32_t hash; + /** Did we already tried to compile the attached GPUShader. */ + bool compiled; +}; + +/* -------------------------------------------------------------------- */ +/** \name GPUPass Cache + * + * Internal shader cache: This prevent the shader recompilation / stall when + * using undo/redo AND also allows for GPUPass reuse if the Shader code is the + * same for 2 different Materials. Unused GPUPasses are free by Garbage collection. + */ + +/* Only use one linklist that contains the GPUPasses grouped by hash. */ +static GPUPass *pass_cache = nullptr; +static SpinLock pass_cache_spin; + +/* Search by hash only. Return first pass with the same hash. + * There is hash collision if (pass->next && pass->next->hash == hash) */ +static GPUPass *gpu_pass_cache_lookup(uint32_t hash) +{ + BLI_spin_lock(&pass_cache_spin); + /* Could be optimized with a Lookup table. */ + for (GPUPass *pass = pass_cache; pass; pass = pass->next) { + if (pass->hash == hash) { + BLI_spin_unlock(&pass_cache_spin); + return pass; + } + } + BLI_spin_unlock(&pass_cache_spin); + return nullptr; +} + +static void gpu_pass_cache_insert_after(GPUPass *node, GPUPass *pass) +{ + BLI_spin_lock(&pass_cache_spin); + if (node != nullptr) { + /* Add after the first pass having the same hash. */ + pass->next = node->next; + node->next = pass; + } + else { + /* No other pass have same hash, just prepend to the list. */ + BLI_LINKS_PREPEND(pass_cache, pass); + } + BLI_spin_unlock(&pass_cache_spin); +} + +/* Check all possible passes with the same hash. */ +static GPUPass *gpu_pass_cache_resolve_collision(GPUPass *pass, + GPUShaderCreateInfo *info, + uint32_t hash) +{ + BLI_spin_lock(&pass_cache_spin); + for (; pass && (pass->hash == hash); pass = pass->next) { + if (*reinterpret_cast<ShaderCreateInfo *>(info) == + *reinterpret_cast<ShaderCreateInfo *>(pass->create_info)) { + BLI_spin_unlock(&pass_cache_spin); + return pass; + } + } + BLI_spin_unlock(&pass_cache_spin); + return nullptr; +} + +static bool gpu_pass_is_valid(GPUPass *pass) +{ + /* Shader is not null if compilation is successful. */ + return (pass->compiled == false || pass->shader != nullptr); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Type > string conversion + * \{ */ + +static std::ostream &operator<<(std::ostream &stream, const GPUInput *input) +{ + switch (input->source) { + case GPU_SOURCE_FUNCTION_CALL: + case GPU_SOURCE_OUTPUT: + return stream << "tmp" << input->id; + case GPU_SOURCE_CONSTANT: + return stream << "cons" << input->id; + case GPU_SOURCE_UNIFORM: + return stream << "node_tree.u" << input->id; + case GPU_SOURCE_ATTR: + return stream << "var_attrs.v" << input->attr->id; + case GPU_SOURCE_UNIFORM_ATTR: + return stream << "unf_attrs[resource_id].attr" << input->uniform_attr->id; + case GPU_SOURCE_STRUCT: + return stream << "strct" << input->id; + case GPU_SOURCE_TEX: + return stream << input->texture->sampler_name; + case GPU_SOURCE_TEX_TILED_MAPPING: + return stream << input->texture->tiled_mapping_name; + case GPU_SOURCE_VOLUME_GRID: + return stream << input->volume_grid->sampler_name; + case GPU_SOURCE_VOLUME_GRID_TRANSFORM: + return stream << input->volume_grid->transform_name; + default: + BLI_assert(0); + return stream; + } +} + +static std::ostream &operator<<(std::ostream &stream, const GPUOutput *output) +{ + return stream << "tmp" << output->id; +} + +/* Trick type to change overload and keep a somewhat nice syntax. */ +struct GPUConstant : public GPUInput { +}; + +/* Print data constructor (i.e: vec2(1.0f, 1.0f)). */ +static std::ostream &operator<<(std::ostream &stream, const GPUConstant *input) +{ + stream << input->type << "("; + for (int i = 0; i < input->type; i++) { + char formated_float[32]; + /* Print with the maximum precision for single precision float using scientific notation. + * See https://stackoverflow.com/questions/16839658/#answer-21162120 */ + SNPRINTF(formated_float, "%.9g", input->vec[i]); + stream << formated_float; + if (i < input->type - 1) { + stream << ", "; + } + } + stream << ")"; + return stream; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GLSL code generation + * \{ */ + +class GPUCodegen { + public: + GPUMaterial &mat; + GPUNodeGraph &graph; + GPUCodegenOutput output = {}; + GPUCodegenCreateInfo *create_info = nullptr; + + private: + uint32_t hash_ = 0; + BLI_HashMurmur2A hm2a_; + ListBase ubo_inputs_ = {nullptr, nullptr}; + + public: + GPUCodegen(GPUMaterial *mat_, GPUNodeGraph *graph_) : mat(*mat_), graph(*graph_) + { + BLI_hash_mm2a_init(&hm2a_, GPU_material_uuid_get(&mat)); + BLI_hash_mm2a_add_int(&hm2a_, GPU_material_flag(&mat)); + create_info = new GPUCodegenCreateInfo("codegen"); + output.create_info = reinterpret_cast<GPUShaderCreateInfo *>( + static_cast<ShaderCreateInfo *>(create_info)); + + if (GPU_material_flag_get(mat_, GPU_MATFLAG_OBJECT_INFO)) { + create_info->additional_info("draw_object_infos"); + } + } + + ~GPUCodegen() + { + MEM_SAFE_FREE(output.attr_load); + MEM_SAFE_FREE(output.surface); + MEM_SAFE_FREE(output.volume); + MEM_SAFE_FREE(output.thickness); + MEM_SAFE_FREE(output.displacement); + MEM_SAFE_FREE(output.material_functions); + delete create_info; + BLI_freelistN(&ubo_inputs_); + }; + + void generate_graphs(); + void generate_uniform_buffer(); + void generate_attribs(); + void generate_resources(); + void generate_library(); + + uint32_t hash_get() const + { + return hash_; + } + + private: + void set_unique_ids(); + + void node_serialize(std::stringstream &eval_ss, const GPUNode *node); + char *graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link); + + static char *extract_c_str(std::stringstream &stream) + { + auto string = stream.str(); + return BLI_strdup(string.c_str()); + } +}; + +static char attr_prefix_get(CustomDataType type) +{ + switch (type) { + case CD_MTFACE: + return 'u'; + case CD_TANGENT: + return 't'; + case CD_MCOL: + case CD_MLOOPCOL: + return 'c'; + case CD_PROP_COLOR: + return 'c'; + case CD_AUTO_FROM_NAME: + return 'a'; + case CD_HAIRLENGTH: + return 'l'; + default: + BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!"); + return '\0'; + } +} + +void GPUCodegen::generate_attribs() +{ + if (BLI_listbase_is_empty(&graph.attributes)) { + output.attr_load = nullptr; + return; + } + + GPUCodegenCreateInfo &info = *create_info; + + info.name_buffer = MEM_new<GPUCodegenCreateInfo::NameBuffer>("info.name_buffer"); + info.interface_generated = new StageInterfaceInfo("codegen_iface", "var_attrs"); + StageInterfaceInfo &iface = *info.interface_generated; + info.vertex_out(iface); + + /* Input declaration, loading / assignment to interface and geometry shader passthrough. */ + std::stringstream decl_ss, iface_ss, load_ss; + + int slot = 15; + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph.attributes) { + + /* NOTE: Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ + if (attr->type == CD_ORCO) { + /* OPTI: orco is computed from local positions, but only if no modifier is present. */ + STRNCPY(info.name_buffer->attr_names[slot], "orco"); + } + else { + char *name = info.name_buffer->attr_names[slot]; + name[0] = attr_prefix_get(static_cast<CustomDataType>(attr->type)); + name[1] = '\0'; + if (attr->name[0] != '\0') { + /* XXX FIXME: see notes in mesh_render_data_create() */ + GPU_vertformat_safe_attr_name(attr->name, &name[1], GPU_MAX_SAFE_ATTR_NAME); + } + } + SNPRINTF(info.name_buffer->var_names[slot], "v%d", attr->id); + + blender::StringRefNull attr_name = info.name_buffer->attr_names[slot]; + blender::StringRefNull var_name = info.name_buffer->var_names[slot]; + + eGPUType input_type, iface_type; + + load_ss << "var_attrs." << var_name; + switch (attr->type) { + case CD_ORCO: + /* Need vec4 to detect usage of default attribute. */ + input_type = GPU_VEC4; + iface_type = GPU_VEC3; + load_ss << " = attr_load_orco(" << attr_name << ");\n"; + break; + case CD_HAIRLENGTH: + iface_type = input_type = GPU_FLOAT; + load_ss << " = attr_load_" << input_type << "(" << attr_name << ");\n"; + break; + case CD_TANGENT: + iface_type = input_type = GPU_VEC4; + load_ss << " = attr_load_tangent(" << attr_name << ");\n"; + break; + case CD_MTFACE: + iface_type = input_type = GPU_VEC3; + load_ss << " = attr_load_uv(" << attr_name << ");\n"; + break; + case CD_MCOL: + iface_type = input_type = GPU_VEC4; + load_ss << " = attr_load_color(" << attr_name << ");\n"; + break; + default: + iface_type = input_type = GPU_VEC4; + load_ss << " = attr_load_" << input_type << "(" << attr_name << ");\n"; + break; + } + + info.vertex_in(slot--, to_type(input_type), attr_name); + iface.smooth(to_type(iface_type), var_name); + } + + output.attr_load = extract_c_str(load_ss); +} + +void GPUCodegen::generate_resources() +{ + GPUCodegenCreateInfo &info = *create_info; + + std::stringstream ss; + + /* Textures. */ + LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph.textures) { + if (tex->colorband) { + info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->sampler_name, Frequency::BATCH); + } + else if (tex->tiled_mapping_name[0] != '\0') { + info.sampler(0, ImageType::FLOAT_2D_ARRAY, tex->sampler_name, Frequency::BATCH); + info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->tiled_mapping_name, Frequency::BATCH); + } + else { + info.sampler(0, ImageType::FLOAT_2D, tex->sampler_name, Frequency::BATCH); + } + } + /* Volume Grids. */ + LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph.volume_grids) { + info.sampler(0, ImageType::FLOAT_3D, grid->sampler_name, Frequency::BATCH); + /* TODO(@fclem): Global uniform. To put in an UBO. */ + info.push_constant(Type::MAT4, grid->transform_name); + } + + if (!BLI_listbase_is_empty(&ubo_inputs_)) { + /* NOTE: generate_uniform_buffer() should have sorted the inputs before this. */ + ss << "struct NodeTree {\n"; + LISTBASE_FOREACH (LinkData *, link, &ubo_inputs_) { + GPUInput *input = (GPUInput *)(link->data); + ss << input->type << " u" << input->id << ";\n"; + } + ss << "};\n\n"; + + info.uniform_buf(0, "NodeTree", GPU_UBO_BLOCK_NAME, Frequency::BATCH); + } + + if (!BLI_listbase_is_empty(&graph.uniform_attrs.list)) { + ss << "struct UniformAttrs {\n"; + LISTBASE_FOREACH (GPUUniformAttr *, attr, &graph.uniform_attrs.list) { + ss << "vec4 attr" << attr->id << ";\n"; + } + ss << "};\n\n"; + + /* TODO(fclem): Use the macro for length. Currently not working for EEVEE. */ + /* DRW_RESOURCE_CHUNK_LEN = 512 */ + info.uniform_buf(0, "UniformAttrs", GPU_ATTRIBUTE_UBO_BLOCK_NAME "[512]", Frequency::BATCH); + } + + info.typedef_source_generated = ss.str(); +} + +void GPUCodegen::generate_library() +{ + GPUCodegenCreateInfo &info = *create_info; + + void *value; + GSetIterState pop_state = {}; + while (BLI_gset_pop(graph.used_libraries, &pop_state, &value)) { + auto deps = gpu_shader_dependency_get_resolved_source((const char *)value); + info.dependencies_generated.extend_non_duplicates(deps); + } +} + +void GPUCodegen::node_serialize(std::stringstream &eval_ss, const GPUNode *node) +{ + /* Declare constants. */ + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + switch (input->source) { + case GPU_SOURCE_FUNCTION_CALL: + eval_ss << input->type << " " << input << "; " << input->function_call << input << ");\n"; + break; + case GPU_SOURCE_STRUCT: + eval_ss << input->type << " " << input << " = CLOSURE_DEFAULT;\n"; + break; + case GPU_SOURCE_CONSTANT: + eval_ss << input->type << " " << input << " = " << (GPUConstant *)input << ";\n"; + break; + default: + break; + } + } + /* Declare temporary variables for node output storage. */ + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { + eval_ss << output->type << " " << output << ";\n"; + } + + /* Function call. */ + eval_ss << node->name << "("; + /* Input arguments. */ + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + switch (input->source) { + case GPU_SOURCE_OUTPUT: + case GPU_SOURCE_ATTR: { + /* These inputs can have non matching types. Do conversion. */ + eGPUType to = input->type; + eGPUType from = (input->source == GPU_SOURCE_ATTR) ? input->attr->gputype : + input->link->output->type; + if (from != to) { + /* Use defines declared inside codegen_lib (i.e: vec4_from_float). */ + eval_ss << to << "_from_" << from << "("; + } + + if (input->source == GPU_SOURCE_ATTR) { + eval_ss << input; + } + else { + eval_ss << input->link->output; + } + + if (from != to) { + eval_ss << ")"; + } + break; + } + default: + eval_ss << input; + break; + } + eval_ss << ", "; + } + /* Output arguments. */ + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { + eval_ss << output; + if (output->next) { + eval_ss << ", "; + } + } + eval_ss << ");\n\n"; +} + +char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link) +{ + if (output_link == nullptr) { + return nullptr; + } + + std::stringstream eval_ss; + /* NOTE: The node order is already top to bottom (or left to right in node editor) + * because of the evaluation order inside ntreeExecGPUNodes(). */ + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + if ((node->tag & tree_tag) == 0) { + continue; + } + node_serialize(eval_ss, node); + } + eval_ss << "return " << output_link->output << ";\n"; + + char *eval_c_str = extract_c_str(eval_ss); + BLI_hash_mm2a_add(&hm2a_, (uchar *)eval_c_str, eval_ss.str().size()); + return eval_c_str; +} + +void GPUCodegen::generate_uniform_buffer() +{ + /* Extract uniform inputs. */ + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + if (input->source == GPU_SOURCE_UNIFORM && !input->link) { + /* We handle the UBO uniforms separately. */ + BLI_addtail(&ubo_inputs_, BLI_genericNodeN(input)); + } + } + } + if (!BLI_listbase_is_empty(&ubo_inputs_)) { + /* This sorts the inputs based on size. */ + GPU_material_uniform_buffer_create(&mat, &ubo_inputs_); + } +} + +/* Sets id for unique names for all inputs, resources and temp variables. */ +void GPUCodegen::set_unique_ids() +{ + int id = 1; + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + input->id = id++; + } + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { + output->id = id++; + } + } +} + +void GPUCodegen::generate_graphs() +{ + set_unique_ids(); + + output.surface = graph_serialize(GPU_NODE_TAG_SURFACE | GPU_NODE_TAG_AOV, graph.outlink_surface); + output.volume = graph_serialize(GPU_NODE_TAG_VOLUME, graph.outlink_volume); + output.displacement = graph_serialize(GPU_NODE_TAG_DISPLACEMENT, graph.outlink_displacement); + output.thickness = graph_serialize(GPU_NODE_TAG_THICKNESS, graph.outlink_thickness); + + if (!BLI_listbase_is_empty(&graph.material_functions)) { + std::stringstream eval_ss; + eval_ss << "\n/* Generated Functions */\n\n"; + LISTBASE_FOREACH (GPUNodeGraphFunctionLink *, func_link, &graph.material_functions) { + char *fn = graph_serialize(GPU_NODE_TAG_FUNCTION, func_link->outlink); + eval_ss << "float " << func_link->name << "() {\n" << fn << "}\n\n"; + MEM_SAFE_FREE(fn); + } + output.material_functions = extract_c_str(eval_ss); + } + + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph.attributes) { + BLI_hash_mm2a_add(&hm2a_, (uchar *)attr->name, strlen(attr->name)); + } + + hash_ = BLI_hash_mm2a_end(&hm2a_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPUPass + * \{ */ + +GPUPass *GPU_generate_pass(GPUMaterial *material, + GPUNodeGraph *graph, + GPUCodegenCallbackFn finalize_source_cb, + void *thunk) +{ + /* Prune the unused nodes and extract attributes before compiling so the + * generated VBOs are ready to accept the future shader. */ + gpu_node_graph_prune_unused(graph); + gpu_node_graph_finalize_uniform_attrs(graph); + + GPUCodegen codegen(material, graph); + codegen.generate_graphs(); + codegen.generate_uniform_buffer(); + + /* Cache lookup: Reuse shaders already compiled. */ + GPUPass *pass_hash = gpu_pass_cache_lookup(codegen.hash_get()); + + /* FIXME(fclem): This is broken. Since we only check for the hash and not the full source + * there is no way to have a collision currently. Some advocated to only use a bigger hash. */ + if (pass_hash && (pass_hash->next == nullptr || pass_hash->next->hash != codegen.hash_get())) { + if (!gpu_pass_is_valid(pass_hash)) { + /* Shader has already been created but failed to compile. */ + return nullptr; + } + /* No collision, just return the pass. */ + pass_hash->refcount += 1; + return pass_hash; + } + + /* Either the shader is not compiled or there is a hash collision... + * continue generating the shader strings. */ + codegen.generate_attribs(); + codegen.generate_resources(); + codegen.generate_library(); + + /* Make engine add its own code and implement the generated functions. */ + finalize_source_cb(thunk, material, &codegen.output); + + GPUPass *pass = nullptr; + if (pass_hash) { + /* Cache lookup: Reuse shaders already compiled. */ + pass = gpu_pass_cache_resolve_collision( + pass_hash, codegen.output.create_info, codegen.hash_get()); + } + + if (pass) { + /* Cache hit. Reuse the same GPUPass and GPUShader. */ + if (!gpu_pass_is_valid(pass)) { + /* Shader has already been created but failed to compile. */ + return nullptr; + } + pass->refcount += 1; + } + else { + /* We still create a pass even if shader compilation + * fails to avoid trying to compile again and again. */ + pass = (GPUPass *)MEM_callocN(sizeof(GPUPass), "GPUPass"); + pass->shader = nullptr; + pass->refcount = 1; + pass->create_info = codegen.create_info; + pass->hash = codegen.hash_get(); + pass->compiled = false; + + codegen.create_info = nullptr; + + gpu_pass_cache_insert_after(pass_hash, pass); + } + return pass; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Compilation + * \{ */ + +static int count_active_texture_sampler(GPUPass *pass, GPUShader *shader) +{ + int num_samplers = 0; + + for (const ShaderCreateInfo::Resource &res : pass->create_info->pass_resources_) { + if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) { + if (GPU_shader_get_uniform(shader, res.sampler.name.c_str()) != -1) { + num_samplers += 1; + } + } + } + + return num_samplers; +} + +static bool gpu_pass_shader_validate(GPUPass *pass, GPUShader *shader) +{ + if (shader == nullptr) { + return false; + } + + /* NOTE: The only drawback of this method is that it will count a sampler + * used in the fragment shader and only declared (but not used) in the vertex + * shader as used by both. But this corner case is not happening for now. */ + int active_samplers_len = count_active_texture_sampler(pass, shader); + + /* Validate against opengl limit. */ + if ((active_samplers_len > GPU_max_textures_frag()) || + (active_samplers_len > GPU_max_textures_vert())) { + return false; + } + + if (pass->create_info->geometry_source_.is_empty() == false) { + if (active_samplers_len > GPU_max_textures_geom()) { + return false; + } + } + + return (active_samplers_len * 3 <= GPU_max_textures()); +} + +bool GPU_pass_compile(GPUPass *pass, const char *shname) +{ + bool success = true; + if (!pass->compiled) { + GPUShaderCreateInfo *info = reinterpret_cast<GPUShaderCreateInfo *>( + static_cast<ShaderCreateInfo *>(pass->create_info)); + + pass->create_info->name_ = shname; + + GPUShader *shader = GPU_shader_create_from_info(info); + + /* NOTE: Some drivers / gpu allows more active samplers than the opengl limit. + * We need to make sure to count active samplers to avoid undefined behavior. */ + if (!gpu_pass_shader_validate(pass, shader)) { + success = false; + if (shader != nullptr) { + fprintf(stderr, "GPUShader: error: too many samplers in shader.\n"); + GPU_shader_free(shader); + shader = nullptr; + } + } + pass->shader = shader; + pass->compiled = true; + } + return success; +} + +GPUShader *GPU_pass_shader_get(GPUPass *pass) +{ + return pass->shader; +} + +void GPU_pass_release(GPUPass *pass) +{ + BLI_assert(pass->refcount > 0); + pass->refcount--; +} + +static void gpu_pass_free(GPUPass *pass) +{ + BLI_assert(pass->refcount == 0); + if (pass->shader) { + GPU_shader_free(pass->shader); + } + delete pass->create_info; + MEM_freeN(pass); +} + +void GPU_pass_cache_garbage_collect(void) +{ + static int lasttime = 0; + const int shadercollectrate = 60; /* hardcoded for now. */ + int ctime = (int)PIL_check_seconds_timer(); + + if (ctime < shadercollectrate + lasttime) { + return; + } + + lasttime = ctime; + + BLI_spin_lock(&pass_cache_spin); + GPUPass *next, **prev_pass = &pass_cache; + for (GPUPass *pass = pass_cache; pass; pass = next) { + next = pass->next; + if (pass->refcount == 0) { + /* Remove from list */ + *prev_pass = next; + gpu_pass_free(pass); + } + else { + prev_pass = &pass->next; + } + } + BLI_spin_unlock(&pass_cache_spin); +} + +void GPU_pass_cache_init(void) +{ + BLI_spin_init(&pass_cache_spin); +} + +void GPU_pass_cache_free(void) +{ + BLI_spin_lock(&pass_cache_spin); + while (pass_cache) { + GPUPass *next = pass_cache->next; + gpu_pass_free(pass_cache); + pass_cache = next; + } + BLI_spin_unlock(&pass_cache_spin); + + BLI_spin_end(&pass_cache_spin); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Module + * \{ */ + +void gpu_codegen_init(void) +{ +} + +void gpu_codegen_exit(void) +{ + // BKE_world_defaults_free_gpu(); + BKE_material_defaults_free_gpu(); + GPU_shader_free_builtin_shaders(); +} + +/** \} */
\ No newline at end of file diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index 0e99476d3ea..95a672c0400 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -9,36 +9,24 @@ #pragma once +#include "GPU_material.h" +#include "GPU_shader.h" + #ifdef __cplusplus extern "C" { #endif -struct GPUMaterial; struct GPUNodeGraph; -struct GPUShader; - -typedef struct GPUPass { - struct GPUPass *next; - struct GPUShader *shader; - char *fragmentcode; - char *geometrycode; - char *vertexcode; - char *defines; - uint refcount; /* Orphaned GPUPasses gets freed by the garbage collector. */ - uint32_t hash; /* Identity hash generated from all GLSL code. */ - bool compiled; /* Did we already tried to compile the attached GPUShader. */ -} GPUPass; +typedef struct GPUPass GPUPass; /* Pass */ -GPUPass *GPU_generate_pass(struct GPUMaterial *material, +GPUPass *GPU_generate_pass(GPUMaterial *material, struct GPUNodeGraph *graph, - const char *vert_code, - const char *geom_code, - const char *frag_lib, - const char *defines); -struct GPUShader *GPU_pass_shader_get(GPUPass *pass); + GPUCodegenCallbackFn finalize_source_cb, + void *thunk); +GPUShader *GPU_pass_shader_get(GPUPass *pass); bool GPU_pass_compile(GPUPass *pass, const char *shname); void GPU_pass_release(GPUPass *pass); diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c index cf8837ab26e..67035853594 100644 --- a/source/blender/gpu/intern/gpu_immediate_util.c +++ b/source/blender/gpu/intern/gpu_immediate_util.c @@ -142,12 +142,27 @@ static void imm_draw_circle(GPUPrimType prim_type, float radius_y, int nsegments) { - immBegin(prim_type, nsegments); - for (int i = 0; i < nsegments; i++) { - const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); - immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle))); + if (prim_type == GPU_PRIM_LINE_LOOP) { + /* Note(Metal/AMD): For small primitives, line list more efficient than line strip.. */ + immBegin(GPU_PRIM_LINES, nsegments * 2); + + immVertex2f(shdr_pos, x + (radius_x * cosf(0.0f)), y + (radius_y * sinf(0.0f))); + for (int i = 1; i < nsegments; i++) { + const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); + immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle))); + immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle))); + } + immVertex2f(shdr_pos, x + (radius_x * cosf(0.0f)), y + (radius_y * sinf(0.0f))); + immEnd(); + } + else { + immBegin(prim_type, nsegments); + for (int i = 0; i < nsegments; i++) { + const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); + immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle))); + } + immEnd(); } - immEnd(); } void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float radius, int nsegments) @@ -194,12 +209,42 @@ static void imm_draw_circle_partial(GPUPrimType prim_type, immEnd(); } +static void imm_draw_circle_partial_3d(GPUPrimType prim_type, + uint pos, + float x, + float y, + float z, + float rad, + int nsegments, + float start, + float sweep) +{ + /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */ + const float angle_start = -(DEG2RADF(start)) + (float)(M_PI / 2); + const float angle_end = -(DEG2RADF(sweep) - angle_start); + nsegments += 1; + immBegin(prim_type, nsegments); + for (int i = 0; i < nsegments; i++) { + const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1))); + const float angle_sin = sinf(angle); + const float angle_cos = cosf(angle); + immVertex3f(pos, x + rad * angle_cos, y + rad * angle_sin, z); + } + immEnd(); +} + void imm_draw_circle_partial_wire_2d( uint pos, float x, float y, float radius, int nsegments, float start, float sweep) { imm_draw_circle_partial(GPU_PRIM_LINE_STRIP, pos, x, y, radius, nsegments, start, sweep); } +void imm_draw_circle_partial_wire_3d( + uint pos, float x, float y, float z, float rad, int nsegments, float start, float sweep) +{ + imm_draw_circle_partial_3d(GPU_PRIM_LINE_STRIP, pos, x, y, z, rad, nsegments, start, sweep); +} + static void imm_draw_disk_partial(GPUPrimType prim_type, uint pos, float x, @@ -229,6 +274,36 @@ static void imm_draw_disk_partial(GPUPrimType prim_type, immEnd(); } +static void imm_draw_disk_partial_3d(GPUPrimType prim_type, + uint pos, + float x, + float y, + float z, + float rad_inner, + float rad_outer, + int nsegments, + float start, + float sweep) +{ + /* to avoid artifacts */ + const float max_angle = 3 * 360; + CLAMP(sweep, -max_angle, max_angle); + + /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */ + const float angle_start = -(DEG2RADF(start)) + (float)M_PI_2; + const float angle_end = -(DEG2RADF(sweep) - angle_start); + nsegments += 1; + immBegin(prim_type, nsegments * 2); + for (int i = 0; i < nsegments; i++) { + const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1))); + const float angle_sin = sinf(angle); + const float angle_cos = cosf(angle); + immVertex3f(pos, x + rad_inner * angle_cos, y + rad_inner * angle_sin, z); + immVertex3f(pos, x + rad_outer * angle_cos, y + rad_outer * angle_sin, z); + } + immEnd(); +} + void imm_draw_disk_partial_fill_2d(uint pos, float x, float y, @@ -241,16 +316,44 @@ void imm_draw_disk_partial_fill_2d(uint pos, imm_draw_disk_partial( GPU_PRIM_TRI_STRIP, pos, x, y, rad_inner, rad_outer, nsegments, start, sweep); } +void imm_draw_disk_partial_fill_3d(uint pos, + float x, + float y, + float z, + float rad_inner, + float rad_outer, + int nsegments, + float start, + float sweep) +{ + imm_draw_disk_partial_3d( + GPU_PRIM_TRI_STRIP, pos, x, y, z, rad_inner, rad_outer, nsegments, start, sweep); +} static void imm_draw_circle_3D( GPUPrimType prim_type, uint pos, float x, float y, float radius, int nsegments) { - immBegin(prim_type, nsegments); - for (int i = 0; i < nsegments; i++) { - float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); - immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f); + if (prim_type == GPU_PRIM_LINE_LOOP) { + /* Note(Metal/AMD): For small primitives, line list more efficient than line strip. */ + immBegin(GPU_PRIM_LINES, nsegments * 2); + + immVertex3f(pos, x + radius * cosf(0.0f), y + radius * sinf(0.0f), 0.0f); + for (int i = 1; i < nsegments; i++) { + float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); + immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f); + immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f); + } + immVertex3f(pos, x + radius * cosf(0.0f), y + radius * sinf(0.0f), 0.0f); + immEnd(); + } + else { + immBegin(prim_type, nsegments); + for (int i = 0; i < nsegments; i++) { + float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments); + immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f); + } + immEnd(); } - immEnd(); } void imm_draw_circle_wire_3d(uint pos, float x, float y, float radius, int nsegments) @@ -270,22 +373,38 @@ void imm_draw_circle_fill_3d(uint pos, float x, float y, float radius, int nsegm void imm_draw_box_wire_2d(uint pos, float x1, float y1, float x2, float y2) { - immBegin(GPU_PRIM_LINE_LOOP, 4); + /* Note(Metal/AMD): For small primitives, line list more efficient than line-strip. */ + immBegin(GPU_PRIM_LINES, 8); immVertex2f(pos, x1, y1); immVertex2f(pos, x1, y2); + + immVertex2f(pos, x1, y2); immVertex2f(pos, x2, y2); + + immVertex2f(pos, x2, y2); + immVertex2f(pos, x2, y1); + immVertex2f(pos, x2, y1); + immVertex2f(pos, x1, y1); immEnd(); } void imm_draw_box_wire_3d(uint pos, float x1, float y1, float x2, float y2) { /* use this version when GPUVertFormat has a vec3 position */ - immBegin(GPU_PRIM_LINE_LOOP, 4); + /* Note(Metal/AMD): For small primitives, line list more efficient than line-strip. */ + immBegin(GPU_PRIM_LINES, 8); immVertex3f(pos, x1, y1, 0.0f); immVertex3f(pos, x1, y2, 0.0f); + + immVertex3f(pos, x1, y2, 0.0f); + immVertex3f(pos, x2, y2, 0.0f); + immVertex3f(pos, x2, y2, 0.0f); immVertex3f(pos, x2, y1, 0.0f); + + immVertex3f(pos, x2, y1, 0.0f); + immVertex3f(pos, x1, y1, 0.0f); immEnd(); } diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c index 7ccb0f89dc3..e97c9e9c829 100644 --- a/source/blender/gpu/intern/gpu_init_exit.c +++ b/source/blender/gpu/intern/gpu_init_exit.c @@ -39,7 +39,6 @@ void GPU_init(void) gpu_shader_create_info_init(); gpu_codegen_init(); - gpu_material_library_init(); gpu_batch_init(); @@ -56,7 +55,6 @@ void GPU_exit(void) gpu_batch_exit(); - gpu_material_library_exit(); gpu_codegen_exit(); gpu_shader_dependency_exit(); diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 49732240125..711a3943a25 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -16,7 +16,6 @@ #include "DNA_scene_types.h" #include "DNA_world_types.h" -#include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string.h" @@ -27,6 +26,7 @@ #include "BKE_material.h" #include "BKE_node.h" #include "BKE_scene.h" +#include "BKE_world.h" #include "NOD_shader.h" @@ -49,56 +49,50 @@ typedef struct GPUColorBandBuilder { } GPUColorBandBuilder; struct GPUMaterial { - Scene *scene; /* DEPRECATED was only useful for lights. */ - Material *ma; - + /* Contains GPUShader and source code for deferred compilation. + * Can be shared between similar material (i.e: sharing same nodetree topology). */ + GPUPass *pass; + /** UBOs for this material parameters. */ + GPUUniformBuf *ubo; + /** Compilation status. Do not use if shader is not GPU_MAT_SUCCESS. */ eGPUMaterialStatus status; - - const void *engine_type; /* attached engine type */ - int options; /* to identify shader variations (shadow, probe, world background...) */ - bool is_volume_shader; /* is volumetric shader */ - - /* Nodes */ + /** Some flags about the nodetree & the needed resources. */ + eGPUMaterialFlag flag; + /* Identify shader variations (shadow, probe, world background...). + * Should be unique even across render engines. */ + uint64_t uuid; + /* Number of generated function. */ + int generated_function_len; + /** Object type for attribute fetching. */ + bool is_volume_shader; + + /** DEPRECATED Currently only used for deferred compilation. */ + Scene *scene; + /** Source material, might be null. */ + Material *ma; + /** 1D Texture array containing all color bands. */ + GPUTexture *coba_tex; + /** Builder for coba_tex. */ + GPUColorBandBuilder *coba_builder; + /* Low level node graph(s). Also contains resources needed by the material. */ GPUNodeGraph graph; - /* for binding the material */ - GPUPass *pass; - - /* XXX: Should be in Material. But it depends on the output node - * used and since the output selection is different for GPUMaterial... - */ - bool has_volume_output; + /** DEPRECATED: To remove. */ bool has_surface_output; - - /* Only used by Eevee to know which BSDF are used. */ - eGPUMatFlag flag; - - /* Used by 2.8 pipeline */ - GPUUniformBuf *ubo; /* UBOs for shader uniforms. */ - - /* Eevee SSS */ + bool has_volume_output; + /** DEPRECATED: To remove. */ GPUUniformBuf *sss_profile; /* UBO containing SSS profile. */ GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */ - float sss_enabled; + bool sss_enabled; float sss_radii[3]; int sss_samples; bool sss_dirty; - GPUTexture *coba_tex; /* 1D Texture array containing all color bands. */ - GPUColorBandBuilder *coba_builder; - - GSet *used_libraries; - #ifndef NDEBUG char name[64]; #endif }; -enum { - GPU_USE_SURFACE_OUTPUT = (1 << 0), - GPU_USE_VOLUME_OUTPUT = (1 << 1), -}; - /* Functions */ GPUTexture **gpu_material_ramp_texture_row_set(GPUMaterial *mat, @@ -159,17 +153,15 @@ static void gpu_material_free_single(GPUMaterial *material) if (material->ubo != NULL) { GPU_uniformbuf_free(material->ubo); } - if (material->sss_tex_profile != NULL) { - GPU_texture_free(material->sss_tex_profile); + if (material->coba_tex != NULL) { + GPU_texture_free(material->coba_tex); } if (material->sss_profile != NULL) { GPU_uniformbuf_free(material->sss_profile); } - if (material->coba_tex != NULL) { - GPU_texture_free(material->coba_tex); + if (material->sss_tex_profile != NULL) { + GPU_texture_free(material->sss_tex_profile); } - - BLI_gset_free(material->used_libraries, NULL); } void GPU_material_free(ListBase *gpumaterial) @@ -217,17 +209,40 @@ void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs) material->ubo = GPU_uniformbuf_create_from_list(inputs, name); } +ListBase GPU_material_attributes(GPUMaterial *material) +{ + return material->graph.attributes; +} + +ListBase GPU_material_textures(GPUMaterial *material) +{ + return material->graph.textures; +} + +ListBase GPU_material_volume_grids(GPUMaterial *material) +{ + return material->graph.volume_grids; +} + +GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material) +{ + GPUUniformAttrList *attrs = &material->graph.uniform_attrs; + return attrs->count > 0 ? attrs : NULL; +} + +#if 1 /* End of life code. */ /* Eevee Subsurface scattering. */ /* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */ -#define SSS_SAMPLES 65 -#define SSS_EXPONENT 2.0f /* Importance sampling exponent */ +# define SSS_SAMPLES 65 +# define SSS_EXPONENT 2.0f /* Importance sampling exponent */ typedef struct GPUSssKernelData { float kernel[SSS_SAMPLES][4]; float param[3], max_radius; + float avg_inv_radius; int samples; - int pad[3]; + int pad[2]; } GPUSssKernelData; BLI_STATIC_ASSERT_ALIGN(GPUSssKernelData, 16) @@ -243,8 +258,8 @@ static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponen } } -#define BURLEY_TRUNCATE 16.0f -#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) +# define BURLEY_TRUNCATE 16.0f +# define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) static float burley_profile(float r, float d) { float exp_r_3_d = expf(-r / (3.0f * d)); @@ -259,7 +274,7 @@ static float eval_profile(float r, float param) } /* Resolution for each sample of the precomputed kernel profile */ -#define INTEGRAL_RESOLUTION 32 +# define INTEGRAL_RESOLUTION 32 static float eval_integral(float x0, float x1, float param) { const float range = x1 - x0; @@ -274,7 +289,7 @@ static float eval_integral(float x0, float x1, float param) return integral; } -#undef INTEGRAL_RESOLUTION +# undef INTEGRAL_RESOLUTION static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int sample_len) { @@ -284,6 +299,8 @@ static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int s rad[1] = MAX2(radii[1], 1e-15f); rad[2] = MAX2(radii[2], 1e-15f); + kd->avg_inv_radius = 3.0f / (rad[0] + rad[1] + rad[2]); + /* Christensen-Burley fitting */ float l[3], d[3]; @@ -358,7 +375,7 @@ static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int s kd->samples = sample_len; } -#define INTEGRAL_RESOLUTION 512 +# define INTEGRAL_RESOLUTION 512 static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, int resolution, float **output) @@ -417,10 +434,14 @@ static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, mul_v3_fl(texels[resolution - 3], 0.5f); mul_v3_fl(texels[resolution - 4], 0.75f); } -#undef INTEGRAL_RESOLUTION +# undef INTEGRAL_RESOLUTION -void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]) +bool GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]) { + /* Enable only once. */ + if (material->sss_enabled) { + return false; + } copy_v3_v3(material->sss_radii, radii); material->sss_dirty = true; material->sss_enabled = true; @@ -429,6 +450,7 @@ void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]) if (material->sss_profile == NULL) { material->sss_profile = GPU_uniformbuf_create(sizeof(GPUSssKernelData)); } + return true; } struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, @@ -475,34 +497,37 @@ struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void) return GPU_uniformbuf_create(sizeof(GPUSssKernelData)); } -#undef SSS_EXPONENT -#undef SSS_SAMPLES - -ListBase GPU_material_attributes(GPUMaterial *material) -{ - return material->graph.attributes; -} +# undef SSS_EXPONENT +# undef SSS_SAMPLES +#endif -ListBase GPU_material_textures(GPUMaterial *material) +void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link) { - return material->graph.textures; + if (!material->graph.outlink_surface) { + material->graph.outlink_surface = link; + material->has_surface_output = true; + } } -ListBase GPU_material_volume_grids(GPUMaterial *material) +void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link) { - return material->graph.volume_grids; + if (!material->graph.outlink_volume) { + material->graph.outlink_volume = link; + material->has_volume_output = true; + } } -GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material) +void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link) { - GPUUniformAttrList *attrs = &material->graph.uniform_attrs; - return attrs->count > 0 ? attrs : NULL; + if (!material->graph.outlink_displacement) { + material->graph.outlink_displacement = link; + } } -void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link) +void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link) { - if (!material->graph.outlink) { - material->graph.outlink = link; + if (!material->graph.outlink_thickness) { + material->graph.outlink_thickness = link; } } @@ -514,23 +539,71 @@ void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, BLI_addtail(&material->graph.outlink_aovs, aov_link); } +char *GPU_material_split_sub_function(GPUMaterial *material, + eGPUType return_type, + GPUNodeLink **link) +{ + /* Force cast to return type. */ + switch (return_type) { + case GPU_FLOAT: + GPU_link(material, "set_value", *link, link); + break; + case GPU_VEC3: + GPU_link(material, "set_rgb", *link, link); + break; + case GPU_VEC4: + GPU_link(material, "set_rgba", *link, link); + break; + default: + BLI_assert(0); + break; + } + + GPUNodeGraphFunctionLink *func_link = MEM_callocN(sizeof(GPUNodeGraphFunctionLink), __func__); + func_link->outlink = *link; + SNPRINTF(func_link->name, "ntree_fn%d", material->generated_function_len++); + BLI_addtail(&material->graph.material_functions, func_link); + + /* Set value to break the link with the main graph. */ + switch (return_type) { + case GPU_FLOAT: + GPU_link(material, "set_value_one", link); + break; + case GPU_VEC3: + GPU_link(material, "set_rgb_one", link); + break; + case GPU_VEC4: + GPU_link(material, "set_rgba_one", link); + break; + default: + BLI_assert(0); + break; + } + return func_link->name; +} + GPUNodeGraph *gpu_material_node_graph(GPUMaterial *material) { return &material->graph; } -GSet *gpu_material_used_libraries(GPUMaterial *material) +eGPUMaterialStatus GPU_material_status(GPUMaterial *mat) { - return material->used_libraries; + return mat->status; } -eGPUMaterialStatus GPU_material_status(GPUMaterial *mat) +void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status) { - return mat->status; + mat->status = status; } /* Code generation */ +bool GPU_material_is_volume_shader(GPUMaterial *mat) +{ + return mat->is_volume_shader; +} + bool GPU_material_has_surface_output(GPUMaterial *mat) { return mat->has_surface_output; @@ -541,100 +614,79 @@ bool GPU_material_has_volume_output(GPUMaterial *mat) return mat->has_volume_output; } -bool GPU_material_is_volume_shader(GPUMaterial *mat) +void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag) { - return mat->is_volume_shader; + mat->flag |= flag; } -void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag) +bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag) { - mat->flag |= flag; + return (mat->flag & flag) != 0; } -bool GPU_material_flag_get(GPUMaterial *mat, eGPUMatFlag flag) +eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat) { - return (mat->flag & flag) != 0; + return mat->flag; } -GPUMaterial *GPU_material_from_nodetree_find(ListBase *gpumaterials, - const void *engine_type, - int options) +/* Note: Consumes the flags. */ +bool GPU_material_recalc_flag_get(GPUMaterial *mat) { - LISTBASE_FOREACH (LinkData *, link, gpumaterials) { - GPUMaterial *current_material = (GPUMaterial *)link->data; - if (current_material->engine_type == engine_type && current_material->options == options) { - return current_material; - } - } + bool updated = (mat->flag & GPU_MATFLAG_UPDATED) != 0; + mat->flag &= ~GPU_MATFLAG_UPDATED; + return updated; +} - return NULL; +uint64_t GPU_material_uuid_get(GPUMaterial *mat) +{ + return mat->uuid; } GPUMaterial *GPU_material_from_nodetree(Scene *scene, - struct Material *ma, - struct bNodeTree *ntree, + Material *ma, + 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) + uint64_t shader_uuid, + bool is_volume_shader, + bool is_lookdev, + GPUCodegenCallbackFn callback, + void *thunk) { - LinkData *link; - bool has_volume_output, has_surface_output; - - /* Caller must re-use materials. */ - BLI_assert(GPU_material_from_nodetree_find(gpumaterials, engine_type, options) == NULL); - - /* HACK: Eevee assume this to create #GHash keys. */ - BLI_assert(sizeof(GPUPass) > 16); + /* Search if this material is not already compiled. */ + LISTBASE_FOREACH (LinkData *, link, gpumaterials) { + GPUMaterial *mat = (GPUMaterial *)link->data; + if (mat->uuid == shader_uuid) { + return mat; + } + } - /* allocate material */ GPUMaterial *mat = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial"); mat->ma = ma; mat->scene = scene; - mat->engine_type = engine_type; - mat->options = options; + mat->uuid = shader_uuid; + mat->flag = GPU_MATFLAG_UPDATED; mat->is_volume_shader = is_volume_shader; + mat->graph.used_libraries = BLI_gset_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUNodeGraph.used_libraries"); #ifndef NDEBUG BLI_snprintf(mat->name, sizeof(mat->name), "%s", name); #else UNUSED_VARS(name); #endif + if (is_lookdev) { + mat->flag |= GPU_MATFLAG_LOOKDEV_HACK; + } - mat->used_libraries = BLI_gset_new( - BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUMaterial.used_libraries"); - - /* localize tree to create links for reroute and mute */ + /* Localize tree to create links for reroute and mute. */ bNodeTree *localtree = ntreeLocalize(ntree); - ntreeGPUMaterialNodes(localtree, mat, &has_surface_output, &has_volume_output); + ntreeGPUMaterialNodes(localtree, mat); gpu_material_ramp_texture_build(mat); - mat->has_surface_output = has_surface_output; - mat->has_volume_output = has_volume_output; - - if (mat->graph.outlink) { - if (callback) { - callback(mat, options, &vert_code, &geom_code, &frag_lib, &defines); - } - /* HACK: this is only for eevee. We add the define here after the nodetree evaluation. */ - if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) { - defines = BLI_string_joinN(defines, - "#ifndef USE_ALPHA_BLEND\n" - "# define USE_SSS\n" - "#endif\n"); - } + { /* Create source code and search pass cache for an already compiled version. */ - mat->pass = GPU_generate_pass(mat, &mat->graph, vert_code, geom_code, frag_lib, defines); - - if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) { - MEM_freeN((char *)defines); - } + mat->pass = GPU_generate_pass(mat, &mat->graph, callback, thunk); if (mat->pass == NULL) { /* We had a cache hit and the shader has already failed to compile. */ @@ -649,26 +701,20 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, gpu_node_graph_free_nodes(&mat->graph); } else { - mat->status = GPU_MAT_QUEUED; + mat->status = GPU_MAT_CREATED; } } } - else { - mat->status = GPU_MAT_FAILED; - gpu_node_graph_free(&mat->graph); - } - /* Only free after GPU_pass_shader_get where GPUUniformBuf - * read data from the local tree. */ + /* Only free after GPU_pass_shader_get where GPUUniformBuf read data from the local tree. */ ntreeFreeLocalTree(localtree); BLI_assert(!localtree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */ MEM_freeN(localtree); - /* note that even if building the shader fails in some way, we still keep + /* Note that even if building the shader fails in some way, we still keep * it to avoid trying to compile again and again, and simply do not use - * the actual shader on drawing */ - - link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink"); + * the actual shader on drawing. */ + LinkData *link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink"); link->data = mat; BLI_addtail(gpumaterials, link); @@ -690,6 +736,8 @@ void GPU_material_compile(GPUMaterial *mat) success = GPU_pass_compile(mat->pass, __func__); #endif + mat->flag |= GPU_MATFLAG_UPDATED; + if (success) { GPUShader *sh = GPU_pass_shader_get(mat->pass); if (sh != NULL) { @@ -715,5 +763,6 @@ void GPU_materials_free(Main *bmain) GPU_material_free(&wo->gpumaterial); } + // BKE_world_defaults_free_gpu(); BKE_material_defaults_free_gpu(); } diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c deleted file mode 100644 index ca5c3d4bb45..00000000000 --- a/source/blender/gpu/intern/gpu_material_library.c +++ /dev/null @@ -1,904 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2005 Blender Foundation. All rights reserved. */ - -/** \file - * \ingroup gpu - * - * GPU material library parsing and code generation. - */ - -#include <stdio.h> -#include <string.h> - -#include "MEM_guardedalloc.h" - -#include "BLI_dynstr.h" -#include "BLI_ghash.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" - -#include "gpu_material_library.h" - -/* List of all gpu_shader_material_*.glsl files used by GLSL materials. These - * will be parsed to make all functions in them available to use for GPU_link(). - * - * If a file uses functions from another file, it must be added to the list of - * dependencies, and be placed after that file in the list. */ - -extern char datatoc_gpu_shader_material_add_shader_glsl[]; -extern char datatoc_gpu_shader_material_ambient_occlusion_glsl[]; -extern char datatoc_gpu_shader_material_anisotropic_glsl[]; -extern char datatoc_gpu_shader_material_attribute_glsl[]; -extern char datatoc_gpu_shader_material_background_glsl[]; -extern char datatoc_gpu_shader_material_bevel_glsl[]; -extern char datatoc_gpu_shader_material_wavelength_glsl[]; -extern char datatoc_gpu_shader_material_blackbody_glsl[]; -extern char datatoc_gpu_shader_material_bright_contrast_glsl[]; -extern char datatoc_gpu_shader_material_bump_glsl[]; -extern char datatoc_gpu_shader_material_camera_glsl[]; -extern char datatoc_gpu_shader_material_clamp_glsl[]; -extern char datatoc_gpu_shader_material_color_ramp_glsl[]; -extern char datatoc_gpu_shader_material_color_util_glsl[]; -extern char datatoc_gpu_shader_material_combine_hsv_glsl[]; -extern char datatoc_gpu_shader_material_combine_rgb_glsl[]; -extern char datatoc_gpu_shader_material_combine_xyz_glsl[]; -extern char datatoc_gpu_shader_material_diffuse_glsl[]; -extern char datatoc_gpu_shader_material_displacement_glsl[]; -extern char datatoc_gpu_shader_material_eevee_specular_glsl[]; -extern char datatoc_gpu_shader_material_emission_glsl[]; -extern char datatoc_gpu_shader_material_float_curve_glsl[]; -extern char datatoc_gpu_shader_material_fractal_noise_glsl[]; -extern char datatoc_gpu_shader_material_fresnel_glsl[]; -extern char datatoc_gpu_shader_material_gamma_glsl[]; -extern char datatoc_gpu_shader_material_geometry_glsl[]; -extern char datatoc_gpu_shader_material_glass_glsl[]; -extern char datatoc_gpu_shader_material_glossy_glsl[]; -extern char datatoc_gpu_shader_material_hair_info_glsl[]; -extern char datatoc_gpu_shader_material_hash_glsl[]; -extern char datatoc_gpu_shader_material_holdout_glsl[]; -extern char datatoc_gpu_shader_material_hue_sat_val_glsl[]; -extern char datatoc_gpu_shader_material_invert_glsl[]; -extern char datatoc_gpu_shader_material_layer_weight_glsl[]; -extern char datatoc_gpu_shader_material_light_falloff_glsl[]; -extern char datatoc_gpu_shader_material_light_path_glsl[]; -extern char datatoc_gpu_shader_material_mapping_glsl[]; -extern char datatoc_gpu_shader_material_map_range_glsl[]; -extern char datatoc_gpu_shader_material_math_glsl[]; -extern char datatoc_gpu_shader_material_math_util_glsl[]; -extern char datatoc_gpu_shader_material_mix_rgb_glsl[]; -extern char datatoc_gpu_shader_material_mix_shader_glsl[]; -extern char datatoc_gpu_shader_material_noise_glsl[]; -extern char datatoc_gpu_shader_material_normal_glsl[]; -extern char datatoc_gpu_shader_material_normal_map_glsl[]; -extern char datatoc_gpu_shader_material_object_info_glsl[]; -extern char datatoc_gpu_shader_material_output_aov_glsl[]; -extern char datatoc_gpu_shader_material_output_material_glsl[]; -extern char datatoc_gpu_shader_material_output_world_glsl[]; -extern char datatoc_gpu_shader_material_particle_info_glsl[]; -extern char datatoc_gpu_shader_material_point_info_glsl[]; -extern char datatoc_gpu_shader_material_principled_glsl[]; -extern char datatoc_gpu_shader_material_refraction_glsl[]; -extern char datatoc_gpu_shader_material_rgb_curves_glsl[]; -extern char datatoc_gpu_shader_material_rgb_to_bw_glsl[]; -extern char datatoc_gpu_shader_material_separate_hsv_glsl[]; -extern char datatoc_gpu_shader_material_separate_rgb_glsl[]; -extern char datatoc_gpu_shader_material_separate_xyz_glsl[]; -extern char datatoc_gpu_shader_material_set_glsl[]; -extern char datatoc_gpu_shader_material_shader_to_rgba_glsl[]; -extern char datatoc_gpu_shader_material_squeeze_glsl[]; -extern char datatoc_gpu_shader_material_subsurface_scattering_glsl[]; -extern char datatoc_gpu_shader_material_tangent_glsl[]; -extern char datatoc_gpu_shader_material_tex_brick_glsl[]; -extern char datatoc_gpu_shader_material_tex_checker_glsl[]; -extern char datatoc_gpu_shader_material_tex_environment_glsl[]; -extern char datatoc_gpu_shader_material_tex_gradient_glsl[]; -extern char datatoc_gpu_shader_material_tex_image_glsl[]; -extern char datatoc_gpu_shader_material_tex_magic_glsl[]; -extern char datatoc_gpu_shader_material_tex_musgrave_glsl[]; -extern char datatoc_gpu_shader_material_tex_noise_glsl[]; -extern char datatoc_gpu_shader_material_tex_sky_glsl[]; -extern char datatoc_gpu_shader_material_texture_coordinates_glsl[]; -extern char datatoc_gpu_shader_material_tex_voronoi_glsl[]; -extern char datatoc_gpu_shader_material_tex_wave_glsl[]; -extern char datatoc_gpu_shader_material_tex_white_noise_glsl[]; -extern char datatoc_gpu_shader_material_toon_glsl[]; -extern char datatoc_gpu_shader_material_translucent_glsl[]; -extern char datatoc_gpu_shader_material_transparent_glsl[]; -extern char datatoc_gpu_shader_material_uv_map_glsl[]; -extern char datatoc_gpu_shader_material_vector_curves_glsl[]; -extern char datatoc_gpu_shader_material_vector_displacement_glsl[]; -extern char datatoc_gpu_shader_material_vector_math_glsl[]; -extern char datatoc_gpu_shader_material_vector_rotate_glsl[]; -extern char datatoc_gpu_shader_material_velvet_glsl[]; -extern char datatoc_gpu_shader_material_vertex_color_glsl[]; -extern char datatoc_gpu_shader_material_volume_absorption_glsl[]; -extern char datatoc_gpu_shader_material_volume_info_glsl[]; -extern char datatoc_gpu_shader_material_volume_principled_glsl[]; -extern char datatoc_gpu_shader_material_volume_scatter_glsl[]; -extern char datatoc_gpu_shader_material_wireframe_glsl[]; -extern char datatoc_gpu_shader_material_world_normals_glsl[]; - -static GPUMaterialLibrary gpu_shader_material_math_util_library = { - .code = datatoc_gpu_shader_material_math_util_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_color_util_library = { - .code = datatoc_gpu_shader_material_color_util_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_hash_library = { - .code = datatoc_gpu_shader_material_hash_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_noise_library = { - .code = datatoc_gpu_shader_material_noise_glsl, - .dependencies = {&gpu_shader_material_hash_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_fractal_noise_library = { - .code = datatoc_gpu_shader_material_fractal_noise_glsl, - .dependencies = {&gpu_shader_material_noise_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_add_shader_library = { - .code = datatoc_gpu_shader_material_add_shader_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_ambient_occlusion_library = { - .code = datatoc_gpu_shader_material_ambient_occlusion_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_glossy_library = { - .code = datatoc_gpu_shader_material_glossy_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_anisotropic_library = { - .code = datatoc_gpu_shader_material_anisotropic_glsl, - .dependencies = {&gpu_shader_material_glossy_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_attribute_library = { - .code = datatoc_gpu_shader_material_attribute_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_background_library = { - .code = datatoc_gpu_shader_material_background_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_bevel_library = { - .code = datatoc_gpu_shader_material_bevel_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_wavelength_library = { - .code = datatoc_gpu_shader_material_wavelength_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_blackbody_library = { - .code = datatoc_gpu_shader_material_blackbody_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_bright_contrast_library = { - .code = datatoc_gpu_shader_material_bright_contrast_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_bump_library = { - .code = datatoc_gpu_shader_material_bump_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_camera_library = { - .code = datatoc_gpu_shader_material_camera_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_clamp_library = { - .code = datatoc_gpu_shader_material_clamp_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_color_ramp_library = { - .code = datatoc_gpu_shader_material_color_ramp_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_combine_hsv_library = { - .code = datatoc_gpu_shader_material_combine_hsv_glsl, - .dependencies = {&gpu_shader_material_color_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_combine_rgb_library = { - .code = datatoc_gpu_shader_material_combine_rgb_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_combine_xyz_library = { - .code = datatoc_gpu_shader_material_combine_xyz_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_diffuse_library = { - .code = datatoc_gpu_shader_material_diffuse_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_displacement_library = { - .code = datatoc_gpu_shader_material_displacement_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_eevee_specular_library = { - .code = datatoc_gpu_shader_material_eevee_specular_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_emission_library = { - .code = datatoc_gpu_shader_material_emission_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_float_curve_library = { - .code = datatoc_gpu_shader_material_float_curve_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_fresnel_library = { - .code = datatoc_gpu_shader_material_fresnel_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_gamma_library = { - .code = datatoc_gpu_shader_material_gamma_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tangent_library = { - .code = datatoc_gpu_shader_material_tangent_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_geometry_library = { - .code = datatoc_gpu_shader_material_geometry_glsl, - .dependencies = {&gpu_shader_material_tangent_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_glass_library = { - .code = datatoc_gpu_shader_material_glass_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_hair_info_library = { - .code = datatoc_gpu_shader_material_hair_info_glsl, - .dependencies = {&gpu_shader_material_hash_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_holdout_library = { - .code = datatoc_gpu_shader_material_holdout_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_hue_sat_val_library = { - .code = datatoc_gpu_shader_material_hue_sat_val_glsl, - .dependencies = {&gpu_shader_material_color_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_invert_library = { - .code = datatoc_gpu_shader_material_invert_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_layer_weight_library = { - .code = datatoc_gpu_shader_material_layer_weight_glsl, - .dependencies = {&gpu_shader_material_fresnel_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_light_falloff_library = { - .code = datatoc_gpu_shader_material_light_falloff_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_light_path_library = { - .code = datatoc_gpu_shader_material_light_path_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_mapping_library = { - .code = datatoc_gpu_shader_material_mapping_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_map_range_library = { - .code = datatoc_gpu_shader_material_map_range_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_math_library = { - .code = datatoc_gpu_shader_material_math_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_mix_rgb_library = { - .code = datatoc_gpu_shader_material_mix_rgb_glsl, - .dependencies = {&gpu_shader_material_color_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_mix_shader_library = { - .code = datatoc_gpu_shader_material_mix_shader_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_normal_library = { - .code = datatoc_gpu_shader_material_normal_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_normal_map_library = { - .code = datatoc_gpu_shader_material_normal_map_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_object_info_library = { - .code = datatoc_gpu_shader_material_object_info_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_output_aov_library = { - .code = datatoc_gpu_shader_material_output_aov_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_output_material_library = { - .code = datatoc_gpu_shader_material_output_material_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_output_world_library = { - .code = datatoc_gpu_shader_material_output_world_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_particle_info_library = { - .code = datatoc_gpu_shader_material_particle_info_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_point_info_library = { - .code = datatoc_gpu_shader_material_point_info_glsl, - .dependencies = {&gpu_shader_material_hash_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_principled_library = { - .code = datatoc_gpu_shader_material_principled_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_refraction_library = { - .code = datatoc_gpu_shader_material_refraction_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_rgb_curves_library = { - .code = datatoc_gpu_shader_material_rgb_curves_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_rgb_to_bw_library = { - .code = datatoc_gpu_shader_material_rgb_to_bw_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_separate_hsv_library = { - .code = datatoc_gpu_shader_material_separate_hsv_glsl, - .dependencies = {&gpu_shader_material_color_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_separate_rgb_library = { - .code = datatoc_gpu_shader_material_separate_rgb_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_separate_xyz_library = { - .code = datatoc_gpu_shader_material_separate_xyz_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_set_library = { - .code = datatoc_gpu_shader_material_set_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_shader_to_rgba_library = { - .code = datatoc_gpu_shader_material_shader_to_rgba_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_squeeze_library = { - .code = datatoc_gpu_shader_material_squeeze_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_subsurface_scattering_library = { - .code = datatoc_gpu_shader_material_subsurface_scattering_glsl, - .dependencies = {&gpu_shader_material_diffuse_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_brick_library = { - .code = datatoc_gpu_shader_material_tex_brick_glsl, - .dependencies = {&gpu_shader_material_math_util_library, - &gpu_shader_material_hash_library, - NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_checker_library = { - .code = datatoc_gpu_shader_material_tex_checker_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_environment_library = { - .code = datatoc_gpu_shader_material_tex_environment_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_gradient_library = { - .code = datatoc_gpu_shader_material_tex_gradient_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_image_library = { - .code = datatoc_gpu_shader_material_tex_image_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_magic_library = { - .code = datatoc_gpu_shader_material_tex_magic_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_musgrave_library = { - .code = datatoc_gpu_shader_material_tex_musgrave_glsl, - .dependencies = {&gpu_shader_material_noise_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_noise_library = { - .code = datatoc_gpu_shader_material_tex_noise_glsl, - .dependencies = {&gpu_shader_material_fractal_noise_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_sky_library = { - .code = datatoc_gpu_shader_material_tex_sky_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_texture_coordinates_library = { - .code = datatoc_gpu_shader_material_texture_coordinates_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_voronoi_library = { - .code = datatoc_gpu_shader_material_tex_voronoi_glsl, - .dependencies = {&gpu_shader_material_math_util_library, - &gpu_shader_material_hash_library, - NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_wave_library = { - .code = datatoc_gpu_shader_material_tex_wave_glsl, - .dependencies = {&gpu_shader_material_fractal_noise_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_tex_white_noise_library = { - .code = datatoc_gpu_shader_material_tex_white_noise_glsl, - .dependencies = {&gpu_shader_material_hash_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_toon_library = { - .code = datatoc_gpu_shader_material_toon_glsl, - .dependencies = {&gpu_shader_material_diffuse_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_translucent_library = { - .code = datatoc_gpu_shader_material_translucent_glsl, - .dependencies = {&gpu_shader_material_diffuse_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_transparent_library = { - .code = datatoc_gpu_shader_material_transparent_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_uv_map_library = { - .code = datatoc_gpu_shader_material_uv_map_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_vector_curves_library = { - .code = datatoc_gpu_shader_material_vector_curves_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_vector_displacement_library = { - .code = datatoc_gpu_shader_material_vector_displacement_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_vector_math_library = { - .code = datatoc_gpu_shader_material_vector_math_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_vector_rotate_library = { - .code = datatoc_gpu_shader_material_vector_rotate_glsl, - .dependencies = {&gpu_shader_material_math_util_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_velvet_library = { - .code = datatoc_gpu_shader_material_velvet_glsl, - .dependencies = {&gpu_shader_material_diffuse_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_vertex_color_library = { - .code = datatoc_gpu_shader_material_vertex_color_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_volume_absorption_library = { - .code = datatoc_gpu_shader_material_volume_absorption_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_volume_info_library = { - .code = datatoc_gpu_shader_material_volume_info_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_volume_principled_library = { - .code = datatoc_gpu_shader_material_volume_principled_glsl, - .dependencies = {&gpu_shader_material_blackbody_library, NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_volume_scatter_library = { - .code = datatoc_gpu_shader_material_volume_scatter_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_wireframe_library = { - .code = datatoc_gpu_shader_material_wireframe_glsl, - .dependencies = {NULL}, -}; - -static GPUMaterialLibrary gpu_shader_material_world_normals_library = { - .code = datatoc_gpu_shader_material_world_normals_glsl, - .dependencies = {&gpu_shader_material_texture_coordinates_library, NULL}, -}; - -static GPUMaterialLibrary *gpu_material_libraries[] = { - &gpu_shader_material_math_util_library, - &gpu_shader_material_color_util_library, - &gpu_shader_material_hash_library, - &gpu_shader_material_noise_library, - &gpu_shader_material_float_curve_library, - &gpu_shader_material_fractal_noise_library, - &gpu_shader_material_add_shader_library, - &gpu_shader_material_ambient_occlusion_library, - &gpu_shader_material_glossy_library, - &gpu_shader_material_anisotropic_library, - &gpu_shader_material_attribute_library, - &gpu_shader_material_background_library, - &gpu_shader_material_bevel_library, - &gpu_shader_material_wavelength_library, - &gpu_shader_material_blackbody_library, - &gpu_shader_material_bright_contrast_library, - &gpu_shader_material_bump_library, - &gpu_shader_material_camera_library, - &gpu_shader_material_clamp_library, - &gpu_shader_material_color_ramp_library, - &gpu_shader_material_combine_hsv_library, - &gpu_shader_material_combine_rgb_library, - &gpu_shader_material_combine_xyz_library, - &gpu_shader_material_diffuse_library, - &gpu_shader_material_displacement_library, - &gpu_shader_material_eevee_specular_library, - &gpu_shader_material_emission_library, - &gpu_shader_material_fresnel_library, - &gpu_shader_material_gamma_library, - &gpu_shader_material_tangent_library, - &gpu_shader_material_geometry_library, - &gpu_shader_material_glass_library, - &gpu_shader_material_hair_info_library, - &gpu_shader_material_holdout_library, - &gpu_shader_material_hue_sat_val_library, - &gpu_shader_material_invert_library, - &gpu_shader_material_layer_weight_library, - &gpu_shader_material_light_falloff_library, - &gpu_shader_material_light_path_library, - &gpu_shader_material_mapping_library, - &gpu_shader_material_map_range_library, - &gpu_shader_material_math_library, - &gpu_shader_material_mix_rgb_library, - &gpu_shader_material_mix_shader_library, - &gpu_shader_material_normal_library, - &gpu_shader_material_normal_map_library, - &gpu_shader_material_object_info_library, - &gpu_shader_material_output_aov_library, - &gpu_shader_material_output_material_library, - &gpu_shader_material_output_world_library, - &gpu_shader_material_particle_info_library, - &gpu_shader_material_point_info_library, - &gpu_shader_material_principled_library, - &gpu_shader_material_refraction_library, - &gpu_shader_material_rgb_curves_library, - &gpu_shader_material_rgb_to_bw_library, - &gpu_shader_material_separate_hsv_library, - &gpu_shader_material_separate_rgb_library, - &gpu_shader_material_separate_xyz_library, - &gpu_shader_material_set_library, - &gpu_shader_material_shader_to_rgba_library, - &gpu_shader_material_squeeze_library, - &gpu_shader_material_subsurface_scattering_library, - &gpu_shader_material_tex_brick_library, - &gpu_shader_material_tex_checker_library, - &gpu_shader_material_tex_environment_library, - &gpu_shader_material_tex_gradient_library, - &gpu_shader_material_tex_image_library, - &gpu_shader_material_tex_magic_library, - &gpu_shader_material_tex_musgrave_library, - &gpu_shader_material_tex_noise_library, - &gpu_shader_material_tex_sky_library, - &gpu_shader_material_texture_coordinates_library, - &gpu_shader_material_tex_voronoi_library, - &gpu_shader_material_tex_wave_library, - &gpu_shader_material_tex_white_noise_library, - &gpu_shader_material_toon_library, - &gpu_shader_material_translucent_library, - &gpu_shader_material_transparent_library, - &gpu_shader_material_uv_map_library, - &gpu_shader_material_vector_curves_library, - &gpu_shader_material_vector_displacement_library, - &gpu_shader_material_vector_math_library, - &gpu_shader_material_vector_rotate_library, - &gpu_shader_material_velvet_library, - &gpu_shader_material_vertex_color_library, - &gpu_shader_material_volume_absorption_library, - &gpu_shader_material_volume_info_library, - &gpu_shader_material_volume_principled_library, - &gpu_shader_material_volume_scatter_library, - &gpu_shader_material_wireframe_library, - &gpu_shader_material_world_normals_library, - NULL}; - -/* GLSL code parsing for finding function definitions. - * These are stored in a hash for lookup when creating a material. */ - -static GHash *FUNCTION_HASH = NULL; - -const char *gpu_str_skip_token(const char *str, char *token, int max) -{ - int len = 0; - - /* skip a variable/function name */ - while (*str) { - if (ELEM(*str, ' ', '(', ')', ',', ';', '\t', '\n', '\r')) { - break; - } - - if (token && len < max - 1) { - *token = *str; - token++; - len++; - } - str++; - } - - if (token) { - *token = '\0'; - } - - /* skip the next special characters: - * note the missing ')' */ - while (*str) { - if (ELEM(*str, ' ', '(', ',', ';', '\t', '\n', '\r')) { - str++; - } - else { - break; - } - } - - return str; -} - -/* Indices match the eGPUType enum */ -static const char *GPU_DATATYPE_STR[17] = { - "", - "float", - "vec2", - "vec3", - "vec4", - NULL, - NULL, - NULL, - NULL, - "mat3", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - "mat4", -}; - -const char *gpu_data_type_to_string(const eGPUType type) -{ - return GPU_DATATYPE_STR[type]; -} - -static void gpu_parse_material_library(GHash *hash, GPUMaterialLibrary *library) -{ - GPUFunction *function; - eGPUType type; - GPUFunctionQual qual; - int i; - const char *code = library->code; - - while ((code = strstr(code, "void "))) { - function = MEM_callocN(sizeof(GPUFunction), "GPUFunction"); - function->library = library; - - code = gpu_str_skip_token(code, NULL, 0); - code = gpu_str_skip_token(code, function->name, MAX_FUNCTION_NAME); - - /* get parameters */ - while (*code && *code != ')') { - if (BLI_str_startswith(code, "const ")) { - code = gpu_str_skip_token(code, NULL, 0); - } - - /* test if it's an input or output */ - qual = FUNCTION_QUAL_IN; - if (BLI_str_startswith(code, "out ")) { - qual = FUNCTION_QUAL_OUT; - } - if (BLI_str_startswith(code, "inout ")) { - qual = FUNCTION_QUAL_INOUT; - } - if ((qual != FUNCTION_QUAL_IN) || BLI_str_startswith(code, "in ")) { - code = gpu_str_skip_token(code, NULL, 0); - } - - /* test for type */ - type = GPU_NONE; - for (i = 1; i < ARRAY_SIZE(GPU_DATATYPE_STR); i++) { - if (GPU_DATATYPE_STR[i] && BLI_str_startswith(code, GPU_DATATYPE_STR[i])) { - type = i; - break; - } - } - - if (!type && BLI_str_startswith(code, "samplerCube")) { - type = GPU_TEXCUBE; - } - if (!type && BLI_str_startswith(code, "sampler2DShadow")) { - type = GPU_SHADOW2D; - } - if (!type && BLI_str_startswith(code, "sampler1DArray")) { - type = GPU_TEX1D_ARRAY; - } - if (!type && BLI_str_startswith(code, "sampler2DArray")) { - type = GPU_TEX2D_ARRAY; - } - if (!type && BLI_str_startswith(code, "sampler2D")) { - type = GPU_TEX2D; - } - if (!type && BLI_str_startswith(code, "sampler3D")) { - type = GPU_TEX3D; - } - - if (!type && BLI_str_startswith(code, "Closure")) { - type = GPU_CLOSURE; - } - - if (type) { - /* add parameter */ - code = gpu_str_skip_token(code, NULL, 0); - code = gpu_str_skip_token(code, NULL, 0); - function->paramqual[function->totparam] = qual; - function->paramtype[function->totparam] = type; - function->totparam++; - } - else { - fprintf(stderr, "GPU invalid function parameter in %s.\n", function->name); - break; - } - } - - if (function->name[0] == '\0' || function->totparam == 0) { - fprintf(stderr, "GPU functions parse error.\n"); - MEM_freeN(function); - break; - } - - BLI_ghash_insert(hash, function->name, function); - } -} - -/* Module */ - -void gpu_material_library_init(void) -{ - /* Only parse GLSL shader files once. */ - if (FUNCTION_HASH) { - return; - } - - FUNCTION_HASH = BLI_ghash_str_new("GPU_lookup_function gh"); - for (int i = 0; gpu_material_libraries[i]; i++) { - gpu_parse_material_library(FUNCTION_HASH, gpu_material_libraries[i]); - } -} - -void gpu_material_library_exit(void) -{ - if (FUNCTION_HASH) { - BLI_ghash_free(FUNCTION_HASH, NULL, MEM_freeN); - FUNCTION_HASH = NULL; - } -} - -/* Code Generation */ - -static void gpu_material_use_library_with_dependencies(GSet *used_libraries, - GPUMaterialLibrary *library) -{ - if (BLI_gset_add(used_libraries, library->code)) { - for (int i = 0; library->dependencies[i]; i++) { - gpu_material_use_library_with_dependencies(used_libraries, library->dependencies[i]); - } - } -} - -GPUFunction *gpu_material_library_use_function(GSet *used_libraries, const char *name) -{ - GPUFunction *function = BLI_ghash_lookup(FUNCTION_HASH, (const void *)name); - if (function) { - gpu_material_use_library_with_dependencies(used_libraries, function->library); - } - return function; -} - -char *gpu_material_library_generate_code(GSet *used_libraries, const char *frag_lib) -{ - DynStr *ds = BLI_dynstr_new(); - - if (frag_lib) { - BLI_dynstr_append(ds, frag_lib); - } - - /* Always include those because they may be needed by the execution function. */ - gpu_material_use_library_with_dependencies(used_libraries, - &gpu_shader_material_world_normals_library); - - /* Add library code in order, for dependencies. */ - for (int i = 0; gpu_material_libraries[i]; i++) { - GPUMaterialLibrary *library = gpu_material_libraries[i]; - if (BLI_gset_haskey(used_libraries, library->code)) { - BLI_dynstr_append(ds, library->code); - } - } - - char *result = BLI_dynstr_get_cstring(ds); - BLI_dynstr_free(ds); - - return result; -} diff --git a/source/blender/gpu/intern/gpu_material_library.h b/source/blender/gpu/intern/gpu_material_library.h index 96e1a265c72..ffc2228a49b 100644 --- a/source/blender/gpu/intern/gpu_material_library.h +++ b/source/blender/gpu/intern/gpu_material_library.h @@ -10,16 +10,15 @@ #include "GPU_material.h" +#ifdef __cplusplus +extern "C" { +#endif + #define MAX_FUNCTION_NAME 64 #define MAX_PARAMETER 36 struct GSet; -typedef struct GPUMaterialLibrary { - char *code; - struct GPUMaterialLibrary *dependencies[8]; -} GPUMaterialLibrary; - typedef enum { FUNCTION_QUAL_IN, FUNCTION_QUAL_OUT, @@ -31,20 +30,12 @@ typedef struct GPUFunction { eGPUType paramtype[MAX_PARAMETER]; GPUFunctionQual paramqual[MAX_PARAMETER]; int totparam; - GPUMaterialLibrary *library; + /* TOOD(@fclem): Clean that void pointer. */ + void *source; /* GPUSource */ } GPUFunction; -/* Module */ - -void gpu_material_library_init(void); -void gpu_material_library_exit(void); - -/* Code Generation */ - GPUFunction *gpu_material_library_use_function(struct GSet *used_libraries, const char *name); -char *gpu_material_library_generate_code(struct GSet *used_libraries, const char *frag_lib); - -/* Code Parsing */ -const char *gpu_str_skip_token(const char *str, char *token, int max); -const char *gpu_data_type_to_string(eGPUType type); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index 57e415a8183..d9143a12d5b 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -87,10 +87,6 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType input->type = type; switch (link->link_type) { - case GPU_NODE_LINK_BUILTIN: - input->source = GPU_SOURCE_BUILTIN; - input->builtin = link->builtin; - break; case GPU_NODE_LINK_OUTPUT: input->source = GPU_SOURCE_OUTPUT; input->link = link; @@ -132,6 +128,11 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType case GPU_NODE_LINK_UNIFORM: input->source = GPU_SOURCE_UNIFORM; break; + case GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN: + input->source = GPU_SOURCE_FUNCTION_CALL; + /* NOTE(@fclem): End of function call is the return variable set during codegen. */ + SNPRINTF(input->function_call, "dF_branch(%s(), ", link->function_name); + break; default: break; } @@ -478,6 +479,11 @@ GPUNodeLink *GPU_attribute(GPUMaterial *mat, const CustomDataType type, const ch GPUNodeGraph *graph = gpu_material_node_graph(mat); GPUMaterialAttribute *attr = gpu_node_graph_add_attribute(graph, type, name); + if (type == CD_ORCO) { + /* OPTI: orco might be computed from local positions and needs object infos. */ + GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO); + } + /* Dummy fallback if out of slots. */ if (attr == NULL) { static const float zero_data[GPU_MAX_CONSTANT_DATA] = {0.0f}; @@ -523,6 +529,14 @@ GPUNodeLink *GPU_uniform(const float *num) return link; } +GPUNodeLink *GPU_differentiate_float_function(const char *function_name) +{ + GPUNodeLink *link = gpu_node_link_create(); + link->link_type = GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN; + link->function_name = function_name; + return link; +} + GPUNodeLink *GPU_image(GPUMaterial *mat, Image *ima, ImageUser *iuser, @@ -588,41 +602,35 @@ GPUNodeLink *GPU_volume_grid(GPUMaterial *mat, transform_link->volume_grid = link->volume_grid; transform_link->volume_grid->users++; + GPUNodeLink *cos_link = GPU_attribute(mat, CD_ORCO, ""); + /* Two special cases, where we adjust the output values of smoke grids to * bring the into standard range without having to modify the grid values. */ if (STREQ(name, "color")) { - GPU_link(mat, "node_attribute_volume_color", link, transform_link, &link); + GPU_link(mat, "node_attribute_volume_color", link, transform_link, cos_link, &link); } else if (STREQ(name, "temperature")) { - GPU_link(mat, "node_attribute_volume_temperature", link, transform_link, &link); + GPU_link(mat, "node_attribute_volume_temperature", link, transform_link, cos_link, &link); } else { - GPU_link(mat, "node_attribute_volume", link, transform_link, &link); + GPU_link(mat, "node_attribute_volume", link, transform_link, cos_link, &link); } return link; } -GPUNodeLink *GPU_builtin(eGPUBuiltin builtin) -{ - GPUNodeLink *link = gpu_node_link_create(); - link->link_type = GPU_NODE_LINK_BUILTIN; - link->builtin = builtin; - return link; -} - /* Creating Nodes */ bool GPU_link(GPUMaterial *mat, const char *name, ...) { - GSet *used_libraries = gpu_material_used_libraries(mat); + GPUNodeGraph *graph = gpu_material_node_graph(mat); GPUNode *node; GPUFunction *function; GPUNodeLink *link, **linkptr; va_list params; int i; - function = gpu_material_library_use_function(used_libraries, name); + function = gpu_material_library_use_function(graph->used_libraries, name); if (!function) { fprintf(stderr, "GPU failed to find function %s\n", name); return false; @@ -643,27 +651,25 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...) } va_end(params); - GPUNodeGraph *graph = gpu_material_node_graph(mat); BLI_addtail(&graph->nodes, node); return true; } -bool GPU_stack_link(GPUMaterial *material, - bNode *bnode, - const char *name, - GPUNodeStack *in, - GPUNodeStack *out, - ...) +static bool gpu_stack_link_v(GPUMaterial *material, + bNode *bnode, + const char *name, + GPUNodeStack *in, + GPUNodeStack *out, + va_list params) { - GSet *used_libraries = gpu_material_used_libraries(material); + GPUNodeGraph *graph = gpu_material_node_graph(material); GPUNode *node; GPUFunction *function; GPUNodeLink *link, **linkptr; - va_list params; int i, totin, totout; - function = gpu_material_library_use_function(used_libraries, name); + function = gpu_material_library_use_function(graph->used_libraries, name); if (!function) { fprintf(stderr, "GPU failed to find function %s\n", name); return false; @@ -691,7 +697,6 @@ bool GPU_stack_link(GPUMaterial *material, } } - va_start(params, out); for (i = 0; i < function->totparam; i++) { if (function->paramqual[i] != FUNCTION_QUAL_IN) { if (totout == 0) { @@ -717,14 +722,27 @@ bool GPU_stack_link(GPUMaterial *material, } } } - va_end(params); - GPUNodeGraph *graph = gpu_material_node_graph(material); BLI_addtail(&graph->nodes, node); return true; } +bool GPU_stack_link(GPUMaterial *material, + bNode *bnode, + const char *name, + GPUNodeStack *in, + GPUNodeStack *out, + ...) +{ + va_list params; + va_start(params, out); + bool valid = gpu_stack_link_v(material, bnode, name, in, out, params); + va_end(params); + + return valid; +} + GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat, bNode *node, GPUNodeStack *stack, @@ -786,12 +804,16 @@ void gpu_node_graph_free_nodes(GPUNodeGraph *graph) gpu_node_free(node); } - graph->outlink = NULL; + graph->outlink_surface = NULL; + graph->outlink_volume = NULL; + graph->outlink_displacement = NULL; + graph->outlink_thickness = NULL; } void gpu_node_graph_free(GPUNodeGraph *graph) { BLI_freelistN(&graph->outlink_aovs); + BLI_freelistN(&graph->material_functions); gpu_node_graph_free_nodes(graph); LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph->volume_grids) { @@ -801,28 +823,32 @@ void gpu_node_graph_free(GPUNodeGraph *graph) BLI_freelistN(&graph->textures); BLI_freelistN(&graph->attributes); GPU_uniform_attr_list_free(&graph->uniform_attrs); + + if (graph->used_libraries) { + BLI_gset_free(graph->used_libraries, NULL); + graph->used_libraries = NULL; + } } /* Prune Unused Nodes */ -static void gpu_nodes_tag(GPUNodeLink *link) +static void gpu_nodes_tag(GPUNodeLink *link, eGPUNodeTag tag) { GPUNode *node; - GPUInput *input; - if (!link->output) { + if (!link || !link->output) { return; } node = link->output->node; - if (node->tag) { + if (node->tag & tag) { return; } - node->tag = true; - for (input = node->inputs.first; input; input = input->next) { + node->tag |= tag; + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->link) { - gpu_nodes_tag(input->link); + gpu_nodes_tag(input->link, tag); } } } @@ -830,18 +856,25 @@ static void gpu_nodes_tag(GPUNodeLink *link) void gpu_node_graph_prune_unused(GPUNodeGraph *graph) { LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { - node->tag = false; + node->tag = GPU_NODE_TAG_NONE; } - gpu_nodes_tag(graph->outlink); + gpu_nodes_tag(graph->outlink_surface, GPU_NODE_TAG_SURFACE); + gpu_nodes_tag(graph->outlink_volume, GPU_NODE_TAG_VOLUME); + gpu_nodes_tag(graph->outlink_displacement, GPU_NODE_TAG_DISPLACEMENT); + gpu_nodes_tag(graph->outlink_thickness, GPU_NODE_TAG_THICKNESS); + LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) { - gpu_nodes_tag(aovlink->outlink); + gpu_nodes_tag(aovlink->outlink, GPU_NODE_TAG_AOV); + } + LISTBASE_FOREACH (GPUNodeGraphFunctionLink *, funclink, &graph->material_functions) { + gpu_nodes_tag(funclink->outlink, GPU_NODE_TAG_FUNCTION); } for (GPUNode *node = graph->nodes.first, *next = NULL; node; node = next) { next = node->next; - if (!node->tag) { + if (node->tag == GPU_NODE_TAG_NONE) { BLI_remlink(&graph->nodes, node); gpu_node_free(node); } diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 5856001d548..024119e1c24 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -12,9 +12,15 @@ #include "DNA_customdata_types.h" #include "DNA_listBase.h" +#include "BLI_ghash.h" + #include "GPU_material.h" #include "GPU_shader.h" +#ifdef __cplusplus +extern "C" { +#endif + struct GPUNode; struct GPUOutput; struct ListBase; @@ -25,19 +31,18 @@ typedef enum eGPUDataSource { GPU_SOURCE_UNIFORM, GPU_SOURCE_ATTR, GPU_SOURCE_UNIFORM_ATTR, - GPU_SOURCE_BUILTIN, GPU_SOURCE_STRUCT, GPU_SOURCE_TEX, GPU_SOURCE_TEX_TILED_MAPPING, GPU_SOURCE_VOLUME_GRID, GPU_SOURCE_VOLUME_GRID_TRANSFORM, + GPU_SOURCE_FUNCTION_CALL, } eGPUDataSource; typedef enum { GPU_NODE_LINK_NONE = 0, GPU_NODE_LINK_ATTR, GPU_NODE_LINK_UNIFORM_ATTR, - GPU_NODE_LINK_BUILTIN, GPU_NODE_LINK_COLORBAND, GPU_NODE_LINK_CONSTANT, GPU_NODE_LINK_IMAGE, @@ -47,15 +52,28 @@ typedef enum { GPU_NODE_LINK_VOLUME_GRID_TRANSFORM, GPU_NODE_LINK_OUTPUT, GPU_NODE_LINK_UNIFORM, + GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN, } GPUNodeLinkType; +typedef enum { + GPU_NODE_TAG_NONE = 0, + GPU_NODE_TAG_SURFACE = (1 << 0), + GPU_NODE_TAG_VOLUME = (1 << 1), + GPU_NODE_TAG_DISPLACEMENT = (1 << 2), + GPU_NODE_TAG_THICKNESS = (1 << 3), + GPU_NODE_TAG_AOV = (1 << 4), + GPU_NODE_TAG_FUNCTION = (1 << 5), +} eGPUNodeTag; + +ENUM_OPERATORS(eGPUNodeTag, GPU_NODE_TAG_FUNCTION) + struct GPUNode { struct GPUNode *next, *prev; const char *name; /* Internal flag to mark nodes during pruning */ - bool tag; + eGPUNodeTag tag; ListBase inputs; ListBase outputs; @@ -70,8 +88,6 @@ struct GPUNodeLink { union { /* GPU_NODE_LINK_CONSTANT | GPU_NODE_LINK_UNIFORM */ const float *data; - /* GPU_NODE_LINK_BUILTIN */ - eGPUBuiltin builtin; /* GPU_NODE_LINK_COLORBAND */ struct GPUTexture **colorband; /* GPU_NODE_LINK_VOLUME_GRID */ @@ -84,6 +100,8 @@ struct GPUNodeLink { struct GPUUniformAttr *uniform_attr; /* GPU_NODE_LINK_IMAGE_BLENDER */ struct GPUMaterialTexture *texture; + /* GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN */ + const char *function_name; }; }; @@ -110,8 +128,6 @@ typedef struct GPUInput { union { /* GPU_SOURCE_CONSTANT | GPU_SOURCE_UNIFORM */ float vec[16]; /* vector data */ - /* GPU_SOURCE_BUILTIN */ - eGPUBuiltin builtin; /* builtin uniform */ /* GPU_SOURCE_TEX | GPU_SOURCE_TEX_TILED_MAPPING */ struct GPUMaterialTexture *texture; /* GPU_SOURCE_ATTR */ @@ -120,6 +136,8 @@ typedef struct GPUInput { struct GPUUniformAttr *uniform_attr; /* GPU_SOURCE_VOLUME_GRID | GPU_SOURCE_VOLUME_GRID_TRANSFORM */ struct GPUMaterialVolumeGrid *volume_grid; + /* GPU_SOURCE_FUNCTION_CALL */ + char function_call[64]; }; } GPUInput; @@ -129,14 +147,25 @@ typedef struct GPUNodeGraphOutputLink { GPUNodeLink *outlink; } GPUNodeGraphOutputLink; +typedef struct GPUNodeGraphFunctionLink { + struct GPUNodeGraphFunctionLink *next, *prev; + char name[16]; + GPUNodeLink *outlink; +} GPUNodeGraphFunctionLink; + typedef struct GPUNodeGraph { /* Nodes */ ListBase nodes; - /* Main Output. */ - GPUNodeLink *outlink; + /* Main Outputs. */ + GPUNodeLink *outlink_surface; + GPUNodeLink *outlink_volume; + GPUNodeLink *outlink_displacement; + GPUNodeLink *outlink_thickness; /* List of GPUNodeGraphOutputLink */ ListBase outlink_aovs; + /* List of GPUNodeGraphFunctionLink */ + ListBase material_functions; /* Requested attributes and textures. */ ListBase attributes; @@ -145,6 +174,9 @@ typedef struct GPUNodeGraph { /* The list of uniform attributes. */ GPUUniformAttrList uniform_attrs; + + /** Set of all the GLSL lib code blocks . */ + GSet *used_libraries; } GPUNodeGraph; /* Node Graph */ @@ -171,4 +203,6 @@ struct GPUTexture **gpu_material_ramp_texture_row_set(struct GPUMaterial *mat, float *pixels, float *row); -struct GSet *gpu_material_used_libraries(struct GPUMaterial *material); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index b434cfbbb0e..fe9aacb95f9 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -249,6 +249,19 @@ const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name) return gpu_shader_create_info_get(info_name); } +bool GPU_shader_create_info_check_error(const GPUShaderCreateInfo *_info, char r_error[128]) +{ + using namespace blender::gpu::shader; + const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(_info); + std::string error = info.check_error(); + if (error.length() == 0) { + return true; + } + + BLI_strncpy(r_error, error.c_str(), 128); + return false; +} + GPUShader *GPU_shader_create_from_info_name(const char *info_name) { using namespace blender::gpu::shader; @@ -270,28 +283,10 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) GPU_debug_group_begin(GPU_DEBUG_SHADER_COMPILATION_GROUP); - /* At least a vertex shader and a fragment shader are required, or only a compute shader. */ - if (info.compute_source_.is_empty()) { - if (info.vertex_source_.is_empty()) { - printf("Missing vertex shader in %s.\n", info.name_.c_str()); - } - if (info.fragment_source_.is_empty()) { - printf("Missing fragment shader in %s.\n", info.name_.c_str()); - } - BLI_assert(!info.vertex_source_.is_empty() && !info.fragment_source_.is_empty()); - } - else { - if (!info.vertex_source_.is_empty()) { - printf("Compute shader has vertex_source_ shader attached in %s.\n", info.name_.c_str()); - } - if (!info.geometry_source_.is_empty()) { - printf("Compute shader has geometry_source_ shader attached in %s.\n", info.name_.c_str()); - } - if (!info.fragment_source_.is_empty()) { - printf("Compute shader has fragment_source_ shader attached in %s.\n", info.name_.c_str()); - } - BLI_assert(info.vertex_source_.is_empty() && info.geometry_source_.is_empty() && - info.fragment_source_.is_empty()); + const std::string error = info.check_error(); + if (!error.empty()) { + printf("%s\n", error.c_str()); + BLI_assert(false); } Shader *shader = GPUBackend::get()->shader_alloc(info.name_.c_str()); @@ -299,7 +294,9 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) std::string defines = shader->defines_declare(info); std::string resources = shader->resources_declare(info); - defines += "#define USE_GPU_SHADER_CREATE_INFO\n"; + if (info.legacy_resource_location_ == false) { + defines += "#define USE_GPU_SHADER_CREATE_INFO\n"; + } Vector<const char *> typedefs; if (!info.typedef_sources_.is_empty() || !info.typedef_source_generated.empty()) { @@ -367,6 +364,7 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) sources.append(resources.c_str()); sources.append(layout.c_str()); sources.append(interface.c_str()); + sources.append(info.geometry_source_generated.c_str()); sources.extend(code); shader->geometry_shader_from_glsl(sources); diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc index 95f9b031fe8..515f65adb73 100644 --- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc +++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc @@ -202,10 +202,7 @@ int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG *UNUSED(subdiv_ccg), /* -------------------------------------------------------------------- */ /** \name Stubs of BKE_node.h * \{ */ -void ntreeGPUMaterialNodes(struct bNodeTree *UNUSED(localtree), - struct GPUMaterial *UNUSED(mat), - bool *UNUSED(has_surface_output), - bool *UNUSED(has_volume_output)) +void ntreeGPUMaterialNodes(struct bNodeTree *UNUSED(localtree), struct GPUMaterial *UNUSED(mat)) { BLI_assert_unreachable(); } diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index 0dd82d4ea44..6e82201b424 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -136,6 +136,34 @@ void ShaderCreateInfo::finalize() } } +std::string ShaderCreateInfo::check_error() const +{ + std::string error; + + /* At least a vertex shader and a fragment shader are required, or only a compute shader. */ + if (this->compute_source_.is_empty()) { + if (this->vertex_source_.is_empty()) { + error += "Missing vertex shader in " + this->name_ + ".\n"; + } + if (this->fragment_source_.is_empty()) { + error += "Missing fragment shader in " + this->name_ + ".\n"; + } + } + else { + if (!this->vertex_source_.is_empty()) { + error += "Compute shader has vertex_source_ shader attached in" + this->name_ + ".\n"; + } + if (!this->geometry_source_.is_empty()) { + error += "Compute shader has geometry_source_ shader attached in" + this->name_ + ".\n"; + } + if (!this->fragment_source_.is_empty()) { + error += "Compute shader has fragment_source_ shader attached in" + this->name_ + ".\n"; + } + } + + return error; +} + void ShaderCreateInfo::validate(const ShaderCreateInfo &other_info) { if (!auto_resource_location_) { diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index 51008993353..3ab96d0d84a 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -284,6 +284,8 @@ struct ShaderCreateInfo { bool auto_resource_location_ = false; /** If true, force depth and stencil tests to always happen before fragment shader invocation. */ bool early_fragment_test_ = false; + /** If true, force the use of the GL shader introspection for resource location. */ + bool legacy_resource_location_ = false; /** Allow optimization when fragment shader writes to `gl_FragDepth`. */ DepthWrite depth_write_ = DepthWrite::ANY; /** @@ -296,6 +298,7 @@ struct ShaderCreateInfo { /** Manually set generated code. */ std::string vertex_source_generated = ""; std::string fragment_source_generated = ""; + std::string geometry_source_generated = ""; std::string typedef_source_generated = ""; /** Manually set generated dependencies. */ Vector<const char *, 0> dependencies_generated; @@ -721,6 +724,12 @@ struct ShaderCreateInfo { return *(Self *)this; } + Self &legacy_resource_location(bool value) + { + legacy_resource_location_ = value; + return *(Self *)this; + } + /** \} */ /* -------------------------------------------------------------------- */ @@ -787,6 +796,8 @@ struct ShaderCreateInfo { /* WARNING: Recursive. */ void finalize(); + std::string check_error() const; + /** Error detection that some backend compilers do not complain about. */ void validate(const ShaderCreateInfo &other_info); diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 2c6845334d3..460b6d32967 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -10,11 +10,14 @@ #include <iomanip> #include <iostream> +#include <sstream> +#include "BLI_ghash.h" #include "BLI_map.hh" #include "BLI_set.hh" #include "BLI_string_ref.hh" +#include "gpu_material_library.h" #include "gpu_shader_create_info.hh" #include "gpu_shader_dependency_private.h" @@ -31,6 +34,7 @@ extern "C" { namespace blender::gpu { using GPUSourceDictionnary = Map<StringRef, struct GPUSource *>; +using GPUFunctionDictionnary = Map<StringRef, struct GPUFunction *>; struct GPUSource { StringRefNull fullpath; @@ -41,7 +45,10 @@ struct GPUSource { shader::BuiltinBits builtins = (shader::BuiltinBits)0; std::string processed_source; - GPUSource(const char *path, const char *file, const char *datatoc) + GPUSource(const char *path, + const char *file, + const char *datatoc, + GPUFunctionDictionnary *g_functions) : fullpath(path), filename(file), source(datatoc) { /* Scan for builtins. */ @@ -92,16 +99,20 @@ struct GPUSource { if (filename.endswith(".h") || filename.endswith(".hh")) { enum_preprocess(); } + + if (is_from_material_library()) { + material_functions_parse(g_functions); + } }; - bool is_in_comment(const StringRef &input, int64_t offset) + static bool is_in_comment(const StringRef &input, int64_t offset) { return (input.rfind("/*", offset) > input.rfind("*/", offset)) || (input.rfind("//", offset) > input.rfind("\n", offset)); } template<bool check_whole_word = true, bool reversed = false, typename T> - int64_t find_str(const StringRef &input, const T keyword, int64_t offset = 0) + static int64_t find_str(const StringRef &input, const T keyword, int64_t offset = 0) { while (true) { if constexpr (reversed) { @@ -114,7 +125,7 @@ struct GPUSource { if constexpr (check_whole_word) { /* Fix false positive if something has "enum" as suffix. */ char previous_char = input[offset - 1]; - if (!(ELEM(previous_char, '\n', '\t', ' ', ':'))) { + if (!(ELEM(previous_char, '\n', '\t', ' ', ':', '(', ','))) { offset += (reversed) ? -1 : 1; continue; } @@ -129,9 +140,13 @@ struct GPUSource { } } +#define find_keyword find_str<true, false> +#define rfind_keyword find_str<true, true> +#define find_token find_str<false, false> +#define rfind_token find_str<false, true> + void print_error(const StringRef &input, int64_t offset, const StringRef message) { - std::cout << " error: " << message << "\n"; StringRef sub = input.substr(0, offset); int64_t line_number = std::count(sub.begin(), sub.end(), '\n') + 1; int64_t line_end = input.find("\n", offset); @@ -152,6 +167,12 @@ struct GPUSource { std::cout << "^\n"; } +#define CHECK(test_value, str, ofs, msg) \ + if ((test_value) == -1) { \ + print_error(str, ofs, msg); \ + continue; \ + } + /** * Transform C,C++ enum declaration into GLSL compatible defines and constants: * @@ -193,16 +214,6 @@ struct GPUSource { int64_t last_pos = 0; const bool is_cpp = filename.endswith(".hh"); -#define find_keyword find_str<true, false> -#define find_token find_str<false, false> -#define rfind_token find_str<false, true> -#define CHECK(test_value, str, ofs, msg) \ - if ((test_value) == -1) { \ - print_error(str, ofs, msg); \ - cursor++; \ - continue; \ - } - while (true) { cursor = find_keyword(input, "enum ", cursor + 1); if (cursor == -1) { @@ -268,10 +279,6 @@ struct GPUSource { return; } -#undef find_keyword -#undef find_token -#undef rfind_token - if (last_pos != 0) { output += input.substr(last_pos); } @@ -279,37 +286,237 @@ struct GPUSource { source = processed_source.c_str(); }; + void material_functions_parse(GPUFunctionDictionnary *g_functions) + { + const StringRefNull input = source; + + const char whitespace_chars[] = " \r\n\t"; + + auto function_parse = [&](const StringRef input, + int64_t &cursor, + StringRef &out_return_type, + StringRef &out_name, + StringRef &out_args) -> bool { + cursor = find_keyword(input, "void ", cursor + 1); + if (cursor == -1) { + return false; + } + int64_t arg_start = find_token(input, '(', cursor); + if (arg_start == -1) { + return false; + } + int64_t arg_end = find_token(input, ')', arg_start); + if (arg_end == -1) { + return false; + } + int64_t body_start = find_token(input, '{', arg_end); + int64_t next_semicolon = find_token(input, ';', arg_end); + if (body_start != -1 && next_semicolon != -1 && body_start > next_semicolon) { + /* Assert no prototypes but could also just skip them. */ + BLI_assert_msg(false, "No prototypes allowed in node GLSL libraries."); + } + int64_t name_start = input.find_first_not_of(whitespace_chars, input.find(' ', cursor)); + if (name_start == -1) { + return false; + } + int64_t name_end = input.find_last_not_of(whitespace_chars, arg_start); + if (name_end == -1) { + return false; + } + /* Only support void type for now. */ + out_return_type = "void"; + out_name = input.substr(name_start, name_end - name_start); + out_args = input.substr(arg_start + 1, arg_end - (arg_start + 1)); + return true; + }; + + auto keyword_parse = [&](const StringRef str, int64_t &cursor) -> StringRef { + int64_t keyword_start = str.find_first_not_of(whitespace_chars, cursor); + if (keyword_start == -1) { + /* No keyword found. */ + return str.substr(0, 0); + } + int64_t keyword_end = str.find_first_of(whitespace_chars, keyword_start); + if (keyword_end == -1) { + /* Last keyword. */ + keyword_end = str.size(); + } + cursor = keyword_end + 1; + return str.substr(keyword_start, keyword_end - keyword_start); + }; + + auto arg_parse = [&](const StringRef str, + int64_t &cursor, + StringRef &out_qualifier, + StringRef &out_type, + StringRef &out_name) -> bool { + int64_t arg_start = cursor + 1; + if (arg_start >= str.size()) { + return false; + } + cursor = find_token(str, ',', arg_start); + if (cursor == -1) { + /* Last argument. */ + cursor = str.size(); + } + const StringRef arg = str.substr(arg_start, cursor - arg_start); + + int64_t keyword_cursor = 0; + out_qualifier = keyword_parse(arg, keyword_cursor); + out_type = keyword_parse(arg, keyword_cursor); + out_name = keyword_parse(arg, keyword_cursor); + if (out_name.is_empty()) { + /* No qualifier case. */ + out_name = out_type; + out_type = out_qualifier; + out_qualifier = arg.substr(0, 0); + } + return true; + }; + + int64_t cursor = -1; + StringRef func_return_type, func_name, func_args; + while (function_parse(input, cursor, func_return_type, func_name, func_args)) { + GPUFunction *func = MEM_new<GPUFunction>(__func__); + func_name.copy(func->name, sizeof(func->name)); + func->source = reinterpret_cast<void *>(this); + + bool insert = g_functions->add(func->name, func); + + /* NOTE: We allow overloading non void function, but only if the function comes from the + * same file. Otherwise the dependency system breaks. */ + if (!insert) { + GPUSource *other_source = reinterpret_cast<GPUSource *>( + g_functions->lookup(func_name)->source); + if (other_source != this) { + print_error(input, + source.find(func_name), + "Function redefinition or overload in two different files ..."); + print_error( + input, other_source->source.find(func_name), "... previous definition was here"); + } + else { + /* Non-void function overload. */ + MEM_delete(func); + } + continue; + } + + if (func_return_type != "void") { + continue; + } + + func->totparam = 0; + int64_t args_cursor = -1; + StringRef arg_qualifier, arg_type, arg_name; + while (arg_parse(func_args, args_cursor, arg_qualifier, arg_type, arg_name)) { + + if (func->totparam >= ARRAY_SIZE(func->paramtype)) { + print_error(input, source.find(func_name), "Too much parameter in function"); + break; + } + + auto parse_qualifier = [](StringRef qualifier) -> GPUFunctionQual { + if (qualifier == "out") { + return FUNCTION_QUAL_OUT; + } + if (qualifier == "inout") { + return FUNCTION_QUAL_INOUT; + } + return FUNCTION_QUAL_IN; + }; + + auto parse_type = [](StringRef type) -> eGPUType { + if (type == "float") { + return GPU_FLOAT; + } + if (type == "vec2") { + return GPU_VEC2; + } + if (type == "vec3") { + return GPU_VEC3; + } + if (type == "vec4") { + return GPU_VEC4; + } + if (type == "mat3") { + return GPU_MAT3; + } + if (type == "mat4") { + return GPU_MAT4; + } + if (type == "sampler1DArray") { + return GPU_TEX1D_ARRAY; + } + if (type == "sampler2DArray") { + return GPU_TEX2D_ARRAY; + } + if (type == "sampler2D") { + return GPU_TEX2D; + } + if (type == "sampler3D") { + return GPU_TEX3D; + } + if (type == "Closure") { + return GPU_CLOSURE; + } + return GPU_NONE; + }; + + func->paramqual[func->totparam] = parse_qualifier(arg_qualifier); + func->paramtype[func->totparam] = parse_type(arg_type); + + if (func->paramtype[func->totparam] == GPU_NONE) { + std::string err = "Unknown parameter type \"" + arg_type + "\""; + int64_t err_ofs = source.find(func_name); + err_ofs = find_keyword(source, arg_name, err_ofs); + err_ofs = rfind_keyword(source, arg_type, err_ofs); + print_error(input, err_ofs, err); + } + + func->totparam++; + } + } + } + +#undef find_keyword +#undef rfind_keyword +#undef find_token +#undef rfind_token + /* Return 1 one error. */ - int init_dependencies(const GPUSourceDictionnary &dict) + int init_dependencies(const GPUSourceDictionnary &dict, + const GPUFunctionDictionnary &g_functions) { - if (dependencies_init) { + if (this->dependencies_init) { return 0; } - dependencies_init = true; - int64_t pos = 0; + this->dependencies_init = true; + int64_t pos = -1; + while (true) { - pos = source.find("pragma BLENDER_REQUIRE(", pos); - if (pos == -1) { - return 0; - } - int64_t start = source.find('(', pos) + 1; - int64_t end = source.find(')', pos); - if (end == -1) { - /* TODO Use clog. */ - std::cout << "Error: " << filename << " : Malformed BLENDER_REQUIRE: Missing \")\"." - << std::endl; - return 1; - } - StringRef dependency_name = source.substr(start, end - start); - GPUSource *dependency_source = dict.lookup_default(dependency_name, nullptr); - if (dependency_source == nullptr) { - /* TODO Use clog. */ - std::cout << "Error: " << filename << " : Dependency not found \"" << dependency_name - << "\"." << std::endl; - return 1; + GPUSource *dependency_source = nullptr; + + { + pos = source.find("pragma BLENDER_REQUIRE(", pos + 1); + if (pos == -1) { + return 0; + } + int64_t start = source.find('(', pos) + 1; + int64_t end = source.find(')', pos); + if (end == -1) { + print_error(source, start, "Malformed BLENDER_REQUIRE: Missing \")\" token"); + return 1; + } + StringRef dependency_name = source.substr(start, end - start); + dependency_source = dict.lookup_default(dependency_name, nullptr); + if (dependency_source == nullptr) { + print_error(source, start, "Dependency not found"); + return 1; + } } /* Recursive. */ - int result = dependency_source->init_dependencies(dict); + int result = dependency_source->init_dependencies(dict, g_functions); if (result != 0) { return 1; } @@ -318,8 +525,8 @@ struct GPUSource { dependencies.append_non_duplicates(dep); } dependencies.append_non_duplicates(dependency_source); - pos++; - }; + } + return 0; } /* Returns the final string with all includes done. */ @@ -339,6 +546,11 @@ struct GPUSource { } return out_builtins; } + + bool is_from_material_library() const + { + return filename.startswith("gpu_shader_material_") && filename.endswith(".glsl"); + } }; } // namespace blender::gpu @@ -346,13 +558,15 @@ struct GPUSource { using namespace blender::gpu; static GPUSourceDictionnary *g_sources = nullptr; +static GPUFunctionDictionnary *g_functions = nullptr; void gpu_shader_dependency_init() { g_sources = new GPUSourceDictionnary(); + g_functions = new GPUFunctionDictionnary(); #define SHADER_SOURCE(datatoc, filename, filepath) \ - g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc)); + g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc, g_functions)); #include "glsl_draw_source_list.h" #include "glsl_gpu_source_list.h" #ifdef WITH_OCIO @@ -362,7 +576,7 @@ void gpu_shader_dependency_init() int errors = 0; for (auto *value : g_sources->values()) { - errors += value->init_dependencies(*g_sources); + errors += value->init_dependencies(*g_sources, *g_functions); } BLI_assert_msg(errors == 0, "Dependency errors detected: Aborting"); UNUSED_VARS_NDEBUG(errors); @@ -373,7 +587,20 @@ void gpu_shader_dependency_exit() for (auto *value : g_sources->values()) { delete value; } + for (auto *value : g_functions->values()) { + MEM_delete(value); + } delete g_sources; + delete g_functions; +} + +GPUFunction *gpu_material_library_use_function(GSet *used_libraries, const char *name) +{ + GPUFunction *function = g_functions->lookup_default(name, nullptr); + BLI_assert_msg(function != nullptr, "Requested function not in the function library"); + GPUSource *source = reinterpret_cast<GPUSource *>(function->source); + BLI_gset_add(used_libraries, const_cast<char *>(source->filename.c_str())); + return function; } namespace blender::gpu::shader { |