Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/gpu/intern')
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c1123
-rw-r--r--source/blender/gpu/intern/gpu_codegen.cc825
-rw-r--r--source/blender/gpu/intern/gpu_codegen.h28
-rw-r--r--source/blender/gpu/intern/gpu_immediate_util.c143
-rw-r--r--source/blender/gpu/intern/gpu_init_exit.c2
-rw-r--r--source/blender/gpu/intern/gpu_material.c339
-rw-r--r--source/blender/gpu/intern/gpu_material_library.c904
-rw-r--r--source/blender/gpu/intern/gpu_material_library.h27
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.c117
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.h54
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc44
-rw-r--r--source/blender/gpu/intern/gpu_shader_builder_stubs.cc5
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.cc28
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.hh11
-rw-r--r--source/blender/gpu/intern/gpu_shader_dependency.cc321
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 {