diff options
Diffstat (limited to 'source/blender/gpu/intern/gpu_codegen.c')
-rw-r--r-- | source/blender/gpu/intern/gpu_codegen.c | 1123 |
1 files changed, 0 insertions, 1123 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(); -} |