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/gpu_codegen.c')
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c1604
1 files changed, 901 insertions, 703 deletions
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index cc005e5b6e1..7f2818a35fd 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -36,17 +36,24 @@
#include "DNA_customdata_types.h"
#include "DNA_image_types.h"
#include "DNA_material_types.h"
+#include "DNA_node_types.h"
#include "BLI_blenlib.h"
+#include "BLI_hash_mm2a.h"
+#include "BLI_link_utils.h"
#include "BLI_utildefines.h"
#include "BLI_dynstr.h"
#include "BLI_ghash.h"
+#include "BLI_threads.h"
+
+#include "PIL_time.h"
#include "GPU_extensions.h"
#include "GPU_glew.h"
#include "GPU_material.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
+#include "GPU_uniformbuffer.h"
#include "BLI_sys_types.h" /* for intptr_t support */
@@ -56,21 +63,80 @@
#include <stdarg.h>
extern char datatoc_gpu_shader_material_glsl[];
-extern char datatoc_gpu_shader_vertex_glsl[];
-extern char datatoc_gpu_shader_vertex_world_glsl[];
extern char datatoc_gpu_shader_geometry_glsl[];
static char *glsl_material_library = NULL;
+/* -------------------- 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 = NULL;
+static SpinLock pass_cache_spin;
+
+static uint32_t gpu_pass_hash(const char *frag_gen, const char *defs, GPUVertexAttribs *attribs)
+{
+ BLI_HashMurmur2A hm2a;
+ BLI_hash_mm2a_init(&hm2a, 0);
+ BLI_hash_mm2a_add(&hm2a, (uchar *)frag_gen, strlen(frag_gen));
+ if (attribs) {
+ for (int att_idx = 0; att_idx < attribs->totlayer; att_idx++) {
+ char *name = attribs->layer[att_idx].name;
+ BLI_hash_mm2a_add(&hm2a, (uchar *)name, strlen(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) && (strcmp(pass->defines, defs) != 0)) { /* Pass */ }
+ else if ((geom != NULL) && (strcmp(pass->geometrycode, geom) != 0)) { /* Pass */ }
+ else if ((strcmp(pass->fragmentcode, frag) == 0) &&
+ (strcmp(pass->vertexcode, vert) == 0))
+ {
+ BLI_spin_unlock(&pass_cache_spin);
+ return pass;
+ }
+ }
+ BLI_spin_unlock(&pass_cache_spin);
+ return NULL;
+}
+
+/* -------------------- GPU Codegen ------------------ */
/* type definitions and constants */
-enum {
- MAX_FUNCTION_NAME = 64
-};
-enum {
- MAX_PARAMETER = 32
-};
+#define MAX_FUNCTION_NAME 64
+#define MAX_PARAMETER 32
typedef enum {
FUNCTION_QUAL_IN,
@@ -88,7 +154,7 @@ typedef struct GPUFunction {
/* Indices match the GPUType enum */
static const char *GPU_DATATYPE_STR[17] = {
"", "float", "vec2", "vec3", "vec4",
- NULL, NULL, NULL, NULL, "mat3", NULL, NULL, NULL, NULL, NULL, NULL, "mat4",
+ NULL, NULL, NULL, NULL, "mat3", NULL, NULL, NULL, NULL, NULL, NULL, "mat4"
};
/* GLSL code parsing for finding function definitions.
@@ -119,7 +185,7 @@ static char *gpu_str_skip_token(char *str, char *token, int max)
/* skip a variable/function name */
while (*str) {
- if (ELEM(*str, ' ', '(', ')', ',', '\t', '\n', '\r'))
+ if (ELEM(*str, ' ', '(', ')', ',', ';', '\t', '\n', '\r'))
break;
else {
if (token && len < max - 1) {
@@ -137,7 +203,7 @@ static char *gpu_str_skip_token(char *str, char *token, int max)
/* skip the next special characters:
* note the missing ')' */
while (*str) {
- if (ELEM(*str, ' ', '(', ',', '\t', '\n', '\r'))
+ if (ELEM(*str, ' ', '(', ',', ';', '\t', '\n', '\r'))
str++;
else
break;
@@ -172,7 +238,7 @@ static void gpu_parse_functions_string(GHash *hash, char *code)
/* test for type */
type = GPU_NONE;
- for (i = 1; i <= 16; i++) {
+ for (i = 1; i < ARRAY_SIZE(GPU_DATATYPE_STR); i++) {
if (GPU_DATATYPE_STR[i] && gpu_str_prefix(code, GPU_DATATYPE_STR[i])) {
type = i;
break;
@@ -185,9 +251,19 @@ static void gpu_parse_functions_string(GHash *hash, char *code)
if (!type && gpu_str_prefix(code, "sampler2DShadow")) {
type = GPU_SHADOW2D;
}
+ if (!type && gpu_str_prefix(code, "sampler1DArray")) {
+ type = GPU_TEX1D_ARRAY;
+ }
if (!type && gpu_str_prefix(code, "sampler2D")) {
type = GPU_TEX2D;
}
+ if (!type && gpu_str_prefix(code, "sampler3D")) {
+ type = GPU_TEX3D;
+ }
+
+ if (!type && gpu_str_prefix(code, "Closure")) {
+ type = GPU_CLOSURE;
+ }
if (type) {
/* add parameter */
@@ -343,7 +419,7 @@ static void codegen_convert_datatype(DynStr *ds, int from, int to, const char *t
else if (from == GPU_FLOAT)
BLI_dynstr_appendf(ds, "vec3(%s, %s, %s)", name, name, name);
}
- else {
+ else if (to == GPU_VEC4) {
if (from == GPU_VEC3)
BLI_dynstr_appendf(ds, "vec4(%s, 1.0)", name);
else if (from == GPU_VEC2)
@@ -351,6 +427,19 @@ static void codegen_convert_datatype(DynStr *ds, int from, int to, const char *t
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 GPUType type, float *data)
@@ -372,10 +461,8 @@ static int codegen_input_has_texture(GPUInput *input)
{
if (input->link)
return 0;
- else if (input->ima || input->prv)
- return 1;
else
- return input->tex != NULL;
+ return (input->source == GPU_SOURCE_TEX);
}
const char *GPU_builtin_name(GPUBuiltin builtin)
@@ -412,6 +499,14 @@ const char *GPU_builtin_name(GPUBuiltin builtin)
return "unfparticleangvel";
else if (builtin == GPU_OBJECT_INFO)
return "unfobjectinfo";
+ else if (builtin == GPU_VOLUME_DENSITY)
+ return "sampdensity";
+ else if (builtin == GPU_VOLUME_FLAME)
+ return "sampflame";
+ else if (builtin == GPU_VOLUME_TEMPERATURE)
+ return "unftemperature";
+ else if (builtin == GPU_BARYCENTRIC_TEXCO)
+ return "unfbarycentrictex";
else
return "";
}
@@ -434,89 +529,67 @@ static void codegen_set_texid(GHash *bindhash, GPUInput *input, int *texid, void
static void codegen_set_unique_ids(ListBase *nodes)
{
- GHash *bindhash, *definehash;
+ GHash *bindhash;
GPUNode *node;
GPUInput *input;
GPUOutput *output;
int id = 1, texid = 0;
bindhash = BLI_ghash_ptr_new("codegen_set_unique_ids1 gh");
- definehash = BLI_ghash_ptr_new("codegen_set_unique_ids2 gh");
for (node = nodes->first; node; node = node->next) {
for (input = node->inputs.first; input; input = input->next) {
/* set id for unique names of uniform variables */
input->id = id++;
- input->bindtex = false;
- input->definetex = false;
- /* set texid used for settings texture slot with multitexture */
- if (codegen_input_has_texture(input) &&
- ((input->source == GPU_SOURCE_TEX) || (input->source == GPU_SOURCE_TEX_PIXEL)))
- {
- /* assign only one texid per buffer to avoid sampling
- * the same texture twice */
- if (input->link) {
- /* input is texture from buffer */
- codegen_set_texid(bindhash, input, &texid, input->link);
- }
- else if (input->ima) {
+ /* set texid used for settings texture slot */
+ if (codegen_input_has_texture(input)) {
+ input->bindtex = false;
+ if (input->ima) {
/* input is texture from image */
codegen_set_texid(bindhash, input, &texid, input->ima);
}
- else if (input->prv) {
- /* input is texture from preview render */
- codegen_set_texid(bindhash, input, &texid, input->prv);
+ else if (input->coba) {
+ /* input is color band texture, check coba pointer */
+ codegen_set_texid(bindhash, input, &texid, input->coba);
}
- else if (input->tex) {
- /* input is user created texture, check tex pointer */
- codegen_set_texid(bindhash, input, &texid, input->tex);
- }
-
- /* make sure this pixel is defined exactly once */
- if (input->source == GPU_SOURCE_TEX_PIXEL) {
- if (input->ima) {
- if (!BLI_ghash_haskey(definehash, input->ima)) {
- input->definetex = true;
- BLI_ghash_insert(definehash, input->ima, SET_INT_IN_POINTER(input->texid));
- }
- }
- else {
- if (!BLI_ghash_haskey(definehash, input->link)) {
- input->definetex = true;
- BLI_ghash_insert(definehash, input->link, SET_INT_IN_POINTER(input->texid));
- }
- }
+ else {
+ /* Either input->ima or input->coba should be non-NULL. */
+ BLI_assert(0);
}
}
}
- for (output = node->outputs.first; output; output = output->next)
+ for (output = node->outputs.first; output; output = output->next) {
/* set id for unique names of tmp variables storing output */
output->id = id++;
+ }
}
BLI_ghash_free(bindhash, NULL, NULL);
- BLI_ghash_free(definehash, NULL, NULL);
}
-static int codegen_print_uniforms_functions(DynStr *ds, ListBase *nodes)
+/**
+ * It will create an UBO for GPUMaterial if there is any GPU_DYNAMIC_UBO.
+ */
+static int codegen_process_uniforms_functions(GPUMaterial *material, DynStr *ds, ListBase *nodes)
{
GPUNode *node;
GPUInput *input;
const char *name;
int builtins = 0;
+ ListBase ubo_inputs = {NULL, NULL};
/* print uniforms */
for (node = nodes->first; node; node = node->next) {
for (input = node->inputs.first; input; input = input->next) {
- if ((input->source == GPU_SOURCE_TEX) || (input->source == GPU_SOURCE_TEX_PIXEL)) {
+ if (input->source == GPU_SOURCE_TEX) {
/* create exactly one sampler for each texture */
if (codegen_input_has_texture(input) && input->bindtex) {
- BLI_dynstr_appendf(ds, "uniform %s samp%d;\n",
- (input->textype == GPU_TEX2D) ? "sampler2D" :
- (input->textype == GPU_TEXCUBE) ? "samplerCube" : "sampler2DShadow",
- input->texid);
+ BLI_dynstr_appendf(
+ ds, "uniform %s samp%d;\n",
+ (input->coba) ? "sampler1DArray" : "sampler2D",
+ input->texid);
}
}
else if (input->source == GPU_SOURCE_BUILTIN) {
@@ -525,50 +598,69 @@ static int codegen_print_uniforms_functions(DynStr *ds, ListBase *nodes)
builtins |= input->builtin;
name = GPU_builtin_name(input->builtin);
- if (gpu_str_prefix(name, "unf")) {
- BLI_dynstr_appendf(ds, "uniform %s %s;\n",
- GPU_DATATYPE_STR[input->type], name);
+ if (gpu_str_prefix(name, "samp")) {
+ if ((input->builtin == GPU_VOLUME_DENSITY) ||
+ (input->builtin == GPU_VOLUME_FLAME))
+ {
+ BLI_dynstr_appendf(ds, "uniform sampler3D %s;\n", name);
+ }
+ }
+ else if (gpu_str_prefix(name, "unf")) {
+ BLI_dynstr_appendf(
+ ds, "uniform %s %s;\n",
+ GPU_DATATYPE_STR[input->type], name);
}
else {
- BLI_dynstr_appendf(ds, "%s %s %s;\n",
- GLEW_VERSION_3_0 ? "in" : "varying",
- GPU_DATATYPE_STR[input->type], name);
+ BLI_dynstr_appendf(
+ ds, "%s %s %s;\n",
+ GLEW_VERSION_3_0 ? "in" : "varying",
+ GPU_DATATYPE_STR[input->type], name);
}
}
}
- else if (input->source == GPU_SOURCE_VEC_UNIFORM) {
- if (input->dynamicvec) {
- /* only create uniforms for dynamic vectors */
- BLI_dynstr_appendf(ds, "uniform %s unf%d;\n",
- GPU_DATATYPE_STR[input->type], input->id);
- }
- else {
- /* for others use const so the compiler can do folding */
- BLI_dynstr_appendf(ds, "const %s cons%d = ",
- GPU_DATATYPE_STR[input->type], input->id);
- codegen_print_datatype(ds, input->type, input->vec);
- BLI_dynstr_append(ds, ";\n");
+ 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_DATATYPE_STR[input->type], input->id);
+ codegen_print_datatype(ds, input->type, input->vec);
+ BLI_dynstr_append(ds, ";\n");
+ }
else if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
-#ifdef WITH_OPENSUBDIV
- bool skip_opensubdiv = input->attribtype == CD_TANGENT;
- if (skip_opensubdiv) {
- BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n");
- }
-#endif
- BLI_dynstr_appendf(ds, "%s %s var%d;\n",
- GLEW_VERSION_3_0 ? "in" : "varying",
- GPU_DATATYPE_STR[input->type], input->attribid);
-#ifdef WITH_OPENSUBDIV
- if (skip_opensubdiv) {
- BLI_dynstr_appendf(ds, "#endif\n");
- }
-#endif
+ BLI_dynstr_appendf(
+ ds, "%s %s var%d;\n",
+ GLEW_VERSION_3_0 ? "in" : "varying",
+ GPU_DATATYPE_STR[input->type], input->attribid);
}
}
}
+ /* 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);
+
+ for (LinkData *link = ubo_inputs.first; link; link = link->next) {
+ input = link->data;
+ BLI_dynstr_appendf(
+ ds, "\t%s unf%d;\n",
+ GPU_DATATYPE_STR[input->type], input->id);
+ }
+ BLI_dynstr_append(ds, "};\n");
+ BLI_freelistN(&ubo_inputs);
+ }
+
BLI_dynstr_append(ds, "\n");
return builtins;
@@ -577,25 +669,20 @@ static int codegen_print_uniforms_functions(DynStr *ds, ListBase *nodes)
static void codegen_declare_tmps(DynStr *ds, ListBase *nodes)
{
GPUNode *node;
- GPUInput *input;
GPUOutput *output;
for (node = nodes->first; node; node = node->next) {
- /* load pixels from textures */
- for (input = node->inputs.first; input; input = input->next) {
- if (input->source == GPU_SOURCE_TEX_PIXEL) {
- if (codegen_input_has_texture(input) && input->definetex) {
- BLI_dynstr_appendf(ds, "\tvec4 tex%d = texture2D(", input->texid);
- BLI_dynstr_appendf(ds, "samp%d, gl_TexCoord[%d].st);\n",
- input->texid, input->texid);
- }
- }
- }
-
/* declare temporary variables for node output storage */
for (output = node->outputs.first; output; output = output->next) {
- BLI_dynstr_appendf(ds, "\t%s tmp%d;\n",
- GPU_DATATYPE_STR[output->type], output->id);
+ if (output->type == GPU_CLOSURE) {
+ BLI_dynstr_appendf(
+ ds, "\tClosure tmp%d;\n", output->id);
+ }
+ else {
+ BLI_dynstr_appendf(
+ ds, "\t%s tmp%d;\n",
+ GPU_DATATYPE_STR[output->type], output->id);
+ }
}
}
@@ -614,34 +701,45 @@ static void codegen_call_functions(DynStr *ds, ListBase *nodes, GPUOutput *final
for (input = node->inputs.first; input; input = input->next) {
if (input->source == GPU_SOURCE_TEX) {
BLI_dynstr_appendf(ds, "samp%d", input->texid);
- if (input->link)
- BLI_dynstr_appendf(ds, ", gl_TexCoord[%d].st", input->texid);
}
- else if (input->source == GPU_SOURCE_TEX_PIXEL) {
- codegen_convert_datatype(ds, input->link->output->type, input->type,
- "tmp", input->link->output->id);
+ 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) {
- if (input->builtin == GPU_VIEW_NORMAL)
+ /* 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_BARYCENTRIC_TEXCO)
+ BLI_dynstr_append(ds, "barytexco");
+ else if (input->builtin == GPU_OBJECT_MATRIX)
+ BLI_dynstr_append(ds, "objmat");
+ 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
BLI_dynstr_append(ds, GPU_builtin_name(input->builtin));
}
- else if (input->source == GPU_SOURCE_VEC_UNIFORM) {
- if (input->dynamicvec)
- BLI_dynstr_appendf(ds, "unf%d", input->id);
- else
- BLI_dynstr_appendf(ds, "cons%d", input->id);
+ 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_ATTRIB) {
BLI_dynstr_appendf(ds, "var%d", input->attribid);
}
- else if (input->source == GPU_SOURCE_OPENGL_BUILTIN) {
- if (input->oglbuiltin == GPU_MATCAP_NORMAL)
- BLI_dynstr_append(ds, "gl_SecondaryColor");
- else if (input->oglbuiltin == GPU_COLOR)
- BLI_dynstr_append(ds, "gl_Color");
- }
BLI_dynstr_append(ds, ", ");
}
@@ -655,73 +753,71 @@ static void codegen_call_functions(DynStr *ds, ListBase *nodes, GPUOutput *final
BLI_dynstr_append(ds, ");\n");
}
- BLI_dynstr_append(ds, "\n\tgl_FragColor = ");
- codegen_convert_datatype(ds, finaloutput->type, GPU_VEC4, "tmp", finaloutput->id);
+ BLI_dynstr_appendf(ds, "\n\treturn tmp%d", finaloutput->id);
BLI_dynstr_append(ds, ";\n");
}
-static char *code_generate_fragment(ListBase *nodes, GPUOutput *output)
+static char *code_generate_fragment(GPUMaterial *material, ListBase *nodes, GPUOutput *output)
{
DynStr *ds = BLI_dynstr_new();
char *code;
int builtins;
-#ifdef WITH_OPENSUBDIV
- GPUNode *node;
- GPUInput *input;
-#endif
-
-
#if 0
BLI_dynstr_append(ds, FUNCTION_PROTOTYPES);
#endif
codegen_set_unique_ids(nodes);
- builtins = codegen_print_uniforms_functions(ds, nodes);
+ builtins = codegen_process_uniforms_functions(material, ds, nodes);
-#if 0
- if (G.debug & G_DEBUG)
- BLI_dynstr_appendf(ds, "/* %s */\n", name);
-#endif
- BLI_dynstr_append(ds, "void main()\n{\n");
+ if (builtins & GPU_BARYCENTRIC_TEXCO)
+ BLI_dynstr_append(ds, "\tin vec2 barycentricTexCo;\n");
- if (builtins & GPU_VIEW_NORMAL)
- BLI_dynstr_append(ds, "\tvec3 facingnormal = gl_FrontFacing? varnormal: -varnormal;\n");
+ BLI_dynstr_append(ds, "Closure nodetree_exec(void)\n{\n");
- /* Calculate tangent space. */
-#ifdef WITH_OPENSUBDIV
- {
- bool has_tangent = false;
- for (node = nodes->first; node; node = node->next) {
- for (input = node->inputs.first; input; input = input->next) {
- if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
- if (input->attribtype == CD_TANGENT) {
- BLI_dynstr_appendf(ds, "#ifdef USE_OPENSUBDIV\n");
- BLI_dynstr_appendf(ds, "\t%s var%d;\n",
- GPU_DATATYPE_STR[input->type],
- input->attribid);
- if (has_tangent == false) {
- BLI_dynstr_appendf(ds, "\tvec3 Q1 = dFdx(inpt.v.position.xyz);\n");
- BLI_dynstr_appendf(ds, "\tvec3 Q2 = dFdy(inpt.v.position.xyz);\n");
- BLI_dynstr_appendf(ds, "\tvec2 st1 = dFdx(inpt.v.uv);\n");
- BLI_dynstr_appendf(ds, "\tvec2 st2 = dFdy(inpt.v.uv);\n");
- BLI_dynstr_appendf(ds, "\tvec3 T = normalize(Q1 * st2.t - Q2 * st1.t);\n");
- }
- BLI_dynstr_appendf(ds, "\tvar%d = vec4(T, 1.0);\n", input->attribid);
- BLI_dynstr_appendf(ds, "#endif\n");
- }
- }
- }
- }
+ if (builtins & GPU_BARYCENTRIC_TEXCO) {
+ BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
+ BLI_dynstr_append(ds, "\tvec2 barytexco = vec2((fract(barycentricTexCo.y) != 0.0)\n"
+ "\t ? barycentricTexCo.x\n"
+ "\t : 1.0 - barycentricTexCo.x,\n"
+ "\t 0.0);\n");
+ BLI_dynstr_append(ds, "#else\n");
+ BLI_dynstr_append(ds, "\tvec2 barytexco = barycentricTexCo;\n");
+ BLI_dynstr_append(ds, "#endif\n");
}
-#endif
+ /* TODO(fclem) get rid of that. */
+ if (builtins & GPU_VIEW_MATRIX)
+ BLI_dynstr_append(ds, "\tmat4 viewmat = ViewMatrix;\n");
+ if (builtins & GPU_CAMERA_TEXCO_FACTORS)
+ BLI_dynstr_append(ds, "\tvec4 camtexfac = CameraTexCoFactors;\n");
+ if (builtins & GPU_OBJECT_MATRIX)
+ BLI_dynstr_append(ds, "\tmat4 objmat = ModelMatrix;\n");
+ if (builtins & GPU_INVERSE_OBJECT_MATRIX)
+ BLI_dynstr_append(ds, "\tmat4 objinv = ModelMatrixInverse;\n");
+ if (builtins & GPU_INVERSE_VIEW_MATRIX)
+ BLI_dynstr_append(ds, "\tmat4 viewinv = ViewMatrixInverse;\n");
+ if (builtins & GPU_VIEW_NORMAL)
+ BLI_dynstr_append(ds, "\tvec3 facingnormal = gl_FrontFacing? viewNormal: -viewNormal;\n");
+ if (builtins & GPU_VIEW_POSITION)
+ BLI_dynstr_append(ds, "\tvec3 viewposition = viewPosition;\n");
codegen_declare_tmps(ds, nodes);
codegen_call_functions(ds, nodes, output);
BLI_dynstr_append(ds, "}\n");
+ /* XXX This cannot go into gpu_shader_material.glsl because main() would be parsed and generate error */
+ /* Old glsl mode compat. */
+ BLI_dynstr_append(ds, "#ifndef NODETREE_EXEC\n");
+ BLI_dynstr_append(ds, "out vec4 fragColor;\n");
+ BLI_dynstr_append(ds, "void main()\n");
+ BLI_dynstr_append(ds, "{\n");
+ BLI_dynstr_append(ds, "\tClosure cl = nodetree_exec();\n");
+ BLI_dynstr_append(ds, "\tfragColor = vec4(cl.radiance, cl.opacity);\n");
+ BLI_dynstr_append(ds, "}\n");
+ BLI_dynstr_append(ds, "#endif\n\n");
+
/* create shader */
code = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
@@ -733,103 +829,199 @@ static char *code_generate_fragment(ListBase *nodes, GPUOutput *output)
return code;
}
-static char *code_generate_vertex(ListBase *nodes, const GPUMatType type)
+static const char *attrib_prefix_get(CustomDataType type)
+{
+ switch (type) {
+ case CD_ORCO: return "orco";
+ case CD_MTFACE: return "u";
+ case CD_TANGENT: return "t";
+ case CD_MCOL: return "c";
+ case CD_AUTO_FROM_NAME: return "a";
+ default: BLI_assert(false && "GPUVertAttr Prefix type not found : This should not happen!"); return "";
+ }
+}
+
+static char *code_generate_vertex(ListBase *nodes, const char *vert_code, bool use_geom)
{
DynStr *ds = BLI_dynstr_new();
GPUNode *node;
GPUInput *input;
char *code;
- char *vertcode = NULL;
+ int builtins = 0;
+
+ /* Hairs uv and col attribs are passed by bufferTextures. */
+ BLI_dynstr_append(
+ ds,
+ "#ifdef HAIR_SHADER\n"
+ "#define DEFINE_ATTRIB(type, attr) uniform samplerBuffer attr\n"
+ "#else\n"
+ "#define DEFINE_ATTRIB(type, attr) in type attr\n"
+ "#endif\n"
+ );
for (node = nodes->first; node; node = node->next) {
for (input = node->inputs.first; input; input = input->next) {
+ if (input->source == GPU_SOURCE_BUILTIN) {
+ builtins |= input->builtin;
+ }
if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
-#ifdef WITH_OPENSUBDIV
- bool skip_opensubdiv = ELEM(input->attribtype, CD_MTFACE, CD_TANGENT);
- if (skip_opensubdiv) {
- BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n");
+ /* XXX FIXME : see notes in mesh_render_data_create() */
+ /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */
+ if (input->attribtype == CD_ORCO) {
+ /* orco is computed from local positions, see bellow */
+ BLI_dynstr_appendf(ds, "uniform vec3 OrcoTexCoFactors[2];\n");
}
-#endif
- BLI_dynstr_appendf(ds, "%s %s att%d;\n",
- GLEW_VERSION_3_0 ? "in" : "attribute",
- GPU_DATATYPE_STR[input->type], input->attribid);
- BLI_dynstr_appendf(ds, "uniform int att%d_info;\n", input->attribid);
- BLI_dynstr_appendf(ds, "%s %s var%d;\n",
- GLEW_VERSION_3_0 ? "out" : "varying",
- GPU_DATATYPE_STR[input->type], input->attribid);
-#ifdef WITH_OPENSUBDIV
- if (skip_opensubdiv) {
- BLI_dynstr_appendf(ds, "#endif\n");
+ else if (input->attribname[0] == '\0') {
+ BLI_dynstr_appendf(ds, "DEFINE_ATTRIB(%s, %s);\n", GPU_DATATYPE_STR[input->type], attrib_prefix_get(input->attribtype));
+ BLI_dynstr_appendf(ds, "#define att%d %s\n", input->attribid, attrib_prefix_get(input->attribtype));
}
-#endif
+ else {
+ uint hash = BLI_ghashutil_strhash_p(input->attribname);
+ BLI_dynstr_appendf(
+ ds, "DEFINE_ATTRIB(%s, %s%u);\n",
+ GPU_DATATYPE_STR[input->type], attrib_prefix_get(input->attribtype), hash);
+ BLI_dynstr_appendf(
+ ds, "#define att%d %s%u\n",
+ input->attribid, attrib_prefix_get(input->attribtype), hash);
+ /* Auto attrib can be vertex color byte buffer.
+ * We need to know and convert them to linear space in VS. */
+ if (!use_geom && input->attribtype == CD_AUTO_FROM_NAME) {
+ BLI_dynstr_appendf(ds, "uniform bool ba%u;\n", hash);
+ BLI_dynstr_appendf(ds, "#define att%d_is_srgb ba%u\n", input->attribid, hash);
+ }
+ }
+ BLI_dynstr_appendf(
+ ds, "out %s var%d%s;\n",
+ GPU_DATATYPE_STR[input->type], input->attribid, use_geom ? "g" : "");
}
}
}
+ if (builtins & GPU_BARYCENTRIC_TEXCO) {
+ BLI_dynstr_appendf(
+ ds, "out vec2 barycentricTexCo%s;\n",
+ use_geom ? "g" : "");
+ }
+
BLI_dynstr_append(ds, "\n");
- switch (type) {
- case GPU_MATERIAL_TYPE_MESH:
- vertcode = datatoc_gpu_shader_vertex_glsl;
- break;
- case GPU_MATERIAL_TYPE_WORLD:
- vertcode = datatoc_gpu_shader_vertex_world_glsl;
- break;
- default:
- fprintf(stderr, "invalid material type, set one after GPU_material_construct_begin\n");
- break;
+ BLI_dynstr_append(
+ ds,
+ "#define ATTRIB\n"
+ "uniform mat3 NormalMatrix;\n"
+ "uniform mat4 ModelMatrixInverse;\n"
+ "vec3 srgb_to_linear_attrib(vec3 c) {\n"
+ "\tc = max(c, vec3(0.0));\n"
+ "\tvec3 c1 = c * (1.0 / 12.92);\n"
+ "\tvec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4));\n"
+ "\treturn mix(c1, c2, step(vec3(0.04045), c));\n"
+ "}\n\n"
+ );
+
+ /* Prototype because defined later. */
+ BLI_dynstr_append(
+ ds,
+ "vec2 hair_get_customdata_vec2(const samplerBuffer);\n"
+ "vec3 hair_get_customdata_vec3(const samplerBuffer);\n"
+ "vec4 hair_get_customdata_vec4(const samplerBuffer);\n"
+ "vec3 hair_get_strand_pos(void);\n"
+ "int hair_get_base_id(void);\n"
+ "\n"
+ );
+
+ BLI_dynstr_append(ds, "void pass_attrib(in vec3 position) {\n");
+
+ BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
+
+ if (builtins & GPU_BARYCENTRIC_TEXCO) {
+ /* To match cycles without breaking into individual segment we encode if we need to invert
+ * the first component into the second component. We invert if the barycentricTexCo.y
+ * is NOT 0.0 or 1.0. */
+ BLI_dynstr_appendf(
+ ds, "\tint _base_id = hair_get_base_id();\n");
+ BLI_dynstr_appendf(
+ ds, "\tbarycentricTexCo%s.x = float((_base_id %% 2) == 1);\n",
+ use_geom ? "g" : "");
+ BLI_dynstr_appendf(
+ ds, "\tbarycentricTexCo%s.y = float(((_base_id %% 4) %% 3) > 0);\n",
+ use_geom ? "g" : "");
}
- BLI_dynstr_append(ds, vertcode);
-
- for (node = nodes->first; node; node = node->next)
- for (input = node->inputs.first; input; input = input->next)
+ for (node = nodes->first; node; node = node->next) {
+ for (input = node->inputs.first; input; input = input->next) {
if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
- if (input->attribtype == CD_TANGENT) { /* silly exception */
-#ifdef WITH_OPENSUBDIV
- BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n");
-#endif
+ if (input->attribtype == CD_TANGENT) {
+ /* Not supported by hairs */
BLI_dynstr_appendf(
- ds, "\tvar%d.xyz = normalize(gl_NormalMatrix * att%d.xyz);\n",
- input->attribid, input->attribid);
+ ds, "\tvar%d%s = vec4(0.0);\n",
+ input->attribid, use_geom ? "g" : "");
+ }
+ else if (input->attribtype == CD_ORCO) {
BLI_dynstr_appendf(
- ds, "\tvar%d.w = att%d.w;\n",
- input->attribid, input->attribid);
-#ifdef WITH_OPENSUBDIV
- BLI_dynstr_appendf(ds, "#endif\n");
-#endif
+ ds, "\tvar%d%s = OrcoTexCoFactors[0] + (ModelMatrixInverse * vec4(hair_get_strand_pos(), 1.0)).xyz * OrcoTexCoFactors[1];\n",
+ input->attribid, use_geom ? "g" : "");
}
else {
-#ifdef WITH_OPENSUBDIV
- bool is_mtface = input->attribtype == CD_MTFACE;
- if (is_mtface) {
- BLI_dynstr_appendf(ds, "#ifndef USE_OPENSUBDIV\n");
- }
-#endif
- BLI_dynstr_appendf(ds, "\tset_var_from_attr(att%d, att%d_info, var%d);\n",
- input->attribid, input->attribid, input->attribid);
-#ifdef WITH_OPENSUBDIV
- if (is_mtface) {
- BLI_dynstr_appendf(ds, "#endif\n");
- }
-#endif
+ BLI_dynstr_appendf(
+ ds, "\tvar%d%s = hair_get_customdata_%s(att%d);\n",
+ input->attribid, use_geom ? "g" : "", GPU_DATATYPE_STR[input->type], input->attribid);
}
}
- /* unfortunately special handling is needed here because we abuse gl_Color/gl_SecondaryColor flat shading */
- else if (input->source == GPU_SOURCE_OPENGL_BUILTIN) {
- if (input->oglbuiltin == GPU_MATCAP_NORMAL) {
- /* remap to 0.0 - 1.0 range. This is done because OpenGL 2.0 clamps colors
- * between shader stages and we want the full range of the normal */
- BLI_dynstr_appendf(ds, "\tvec3 matcapcol = vec3(0.5) * varnormal + vec3(0.5);\n");
- BLI_dynstr_appendf(ds, "\tgl_FrontSecondaryColor = vec4(matcapcol, 1.0);\n");
+ }
+ }
+
+ BLI_dynstr_append(ds, "#else /* MESH_SHADER */\n");
+
+ if (builtins & GPU_BARYCENTRIC_TEXCO) {
+ BLI_dynstr_appendf(
+ ds, "\tbarycentricTexCo%s.x = float((gl_VertexID %% 3) == 0);\n",
+ use_geom ? "g" : "");
+ BLI_dynstr_appendf(
+ ds, "\tbarycentricTexCo%s.y = float((gl_VertexID %% 3) == 1);\n",
+ use_geom ? "g" : "");
+ }
+
+ for (node = nodes->first; node; node = node->next) {
+ for (input = node->inputs.first; input; input = input->next) {
+ if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
+ if (input->attribtype == CD_TANGENT) { /* silly exception */
+ BLI_dynstr_appendf(
+ ds, "\tvar%d%s.xyz = normalize(NormalMatrix * att%d.xyz);\n",
+ input->attribid, use_geom ? "g" : "", input->attribid);
+ BLI_dynstr_appendf(
+ ds, "\tvar%d%s.w = att%d.w;\n",
+ input->attribid, use_geom ? "g" : "", input->attribid);
+ }
+ else if (input->attribtype == CD_ORCO) {
+ BLI_dynstr_appendf(
+ ds, "\tvar%d%s = OrcoTexCoFactors[0] + position * OrcoTexCoFactors[1];\n",
+ input->attribid, use_geom ? "g" : "");
+ }
+ else if (input->attribtype == CD_MCOL) {
+ BLI_dynstr_appendf(
+ ds, "\tvar%d%s = srgb_to_linear_attrib(att%d);\n",
+ input->attribid, use_geom ? "g" : "", input->attribid);
}
- else if (input->oglbuiltin == GPU_COLOR) {
- BLI_dynstr_appendf(ds, "\tgl_FrontColor = gl_Color;\n");
+ else if (input->attribtype == CD_AUTO_FROM_NAME) {
+ BLI_dynstr_appendf(
+ ds, "\tvar%d%s = (att%d_is_srgb) ? srgb_to_linear_attrib(att%d) : att%d;\n",
+ input->attribid, use_geom ? "g" : "",
+ input->attribid, input->attribid, input->attribid);
+ }
+ else {
+ BLI_dynstr_appendf(
+ ds, "\tvar%d%s = att%d;\n",
+ input->attribid, use_geom ? "g" : "", input->attribid);
}
}
+ }
+ }
+ BLI_dynstr_append(ds, "#endif /* HAIR_SHADER */\n");
BLI_dynstr_append(ds, "}\n");
+ BLI_dynstr_append(ds, vert_code);
+
code = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
@@ -841,65 +1033,65 @@ static char *code_generate_vertex(ListBase *nodes, const GPUMatType type)
return code;
}
-static char *code_generate_geometry(ListBase *nodes, bool use_opensubdiv)
+static char *code_generate_geometry(ListBase *nodes, const char *geom_code)
{
-#ifdef WITH_OPENSUBDIV
- if (use_opensubdiv) {
- DynStr *ds = BLI_dynstr_new();
- GPUNode *node;
- GPUInput *input;
- char *code;
-
- /* Generate varying declarations. */
- for (node = nodes->first; node; node = node->next) {
- for (input = node->inputs.first; input; input = input->next) {
- if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
- if (input->attribtype == CD_MTFACE) {
- /* NOTE: For now we are using varying on purpose,
- * otherwise we are not able to write to the varying.
- */
- BLI_dynstr_appendf(ds, "%s %s var%d%s;\n",
- "varying",
- GPU_DATATYPE_STR[input->type],
- input->attribid,
- "");
- BLI_dynstr_appendf(ds, "uniform int fvar%d_offset;\n",
- input->attribid);
- }
- }
- }
- }
+ DynStr *ds = BLI_dynstr_new();
+ GPUNode *node;
+ GPUInput *input;
+ char *code;
+ int builtins = 0;
- BLI_dynstr_append(ds, datatoc_gpu_shader_geometry_glsl);
+ /* Create prototype because attributes cannot be declared before layout. */
+ BLI_dynstr_appendf(ds, "void pass_attrib(in int vert);\n");
+ BLI_dynstr_append(ds, "#define ATTRIB\n");
- /* Generate varying assignments. */
- for (node = nodes->first; node; node = node->next) {
- for (input = node->inputs.first; input; input = input->next) {
- if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
- if (input->attribtype == CD_MTFACE) {
- BLI_dynstr_appendf(
- ds,
- "\tINTERP_FACE_VARYING_ATT_2(var%d, "
- "int(texelFetch(FVarDataOffsetBuffer, fvar%d_offset).r), st);\n",
- input->attribid,
- input->attribid);
- }
- }
+ BLI_dynstr_append(ds, geom_code);
+
+ /* Generate varying declarations. */
+ for (node = nodes->first; node; node = node->next) {
+ for (input = node->inputs.first; input; input = input->next) {
+ if (input->source == GPU_SOURCE_BUILTIN) {
+ builtins |= input->builtin;
+ }
+ if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
+ BLI_dynstr_appendf(
+ ds, "in %s var%dg[];\n",
+ GPU_DATATYPE_STR[input->type],
+ input->attribid);
+ BLI_dynstr_appendf(
+ ds, "out %s var%d;\n",
+ GPU_DATATYPE_STR[input->type],
+ input->attribid);
}
}
+ }
- BLI_dynstr_append(ds, "}\n");
- code = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
+ if (builtins & GPU_BARYCENTRIC_TEXCO) {
+ BLI_dynstr_appendf(ds, "in vec2 barycentricTexCog[];\n");
+ BLI_dynstr_appendf(ds, "out vec2 barycentricTexCo[];\n");
+ }
- //if (G.debug & G_DEBUG) printf("%s\n", code);
+ /* Generate varying assignments. */
+ BLI_dynstr_appendf(ds, "void pass_attrib(in int vert) {\n");
- return code;
+ if (builtins & GPU_BARYCENTRIC_TEXCO) {
+ BLI_dynstr_appendf(ds, "\tbarycentricTexCo = barycentricTexCog;\n");
}
-#else
- UNUSED_VARS(nodes, use_opensubdiv);
-#endif
- return NULL;
+
+ for (node = nodes->first; node; node = node->next) {
+ for (input = node->inputs.first; input; input = input->next) {
+ if (input->source == GPU_SOURCE_ATTRIB && input->attribfirst) {
+ /* TODO let shader choose what to do depending on what the attrib is. */
+ BLI_dynstr_appendf(ds, "\tvar%d = var%dg[vert];\n", input->attribid, input->attribid);
+ }
+ }
+ }
+ BLI_dynstr_append(ds, "}\n");
+
+ code = BLI_dynstr_get_cstring(ds);
+ BLI_dynstr_free(ds);
+
+ return code;
}
void GPU_code_generate_glsl_lib(void)
@@ -923,20 +1115,17 @@ void GPU_code_generate_glsl_lib(void)
/* GPU pass binding/unbinding */
-GPUShader *GPU_pass_shader(GPUPass *pass)
+GPUShader *GPU_pass_shader_get(GPUPass *pass)
{
return pass->shader;
}
-static void gpu_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes)
+void GPU_nodes_extract_dynamic_inputs(GPUShader *shader, ListBase *inputs, ListBase *nodes)
{
- GPUShader *shader = pass->shader;
GPUNode *node;
GPUInput *next, *input;
- ListBase *inputs = &pass->inputs;
- int extract, z;
- memset(inputs, 0, sizeof(*inputs));
+ BLI_listbase_clear(inputs);
if (!shader)
return;
@@ -944,146 +1133,33 @@ static void gpu_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes)
GPU_shader_bind(shader);
for (node = nodes->first; node; node = node->next) {
- z = 0;
+ int z = 0;
for (input = node->inputs.first; input; input = next, z++) {
next = input->next;
/* attributes don't need to be bound, they already have
- * an id that the drawing functions will use */
- if (input->source == GPU_SOURCE_ATTRIB) {
-#ifdef WITH_OPENSUBDIV
- /* We do need mtface attributes for later, so we can
- * update face-varuing variables offset in the texture
- * buffer for proper sampling from the shader.
- *
- * We don't do anything about attribute itself, we
- * only use it to learn which uniform name is to be
- * updated.
- *
- * TODO(sergey): We can add ad extra uniform input
- * for the offset, which will be purely internal and
- * which would avoid having such an exceptions.
- */
- if (input->attribtype != CD_MTFACE) {
- continue;
- }
-#else
- continue;
-#endif
- }
- if (input->source == GPU_SOURCE_BUILTIN ||
- input->source == GPU_SOURCE_OPENGL_BUILTIN)
- {
+ * an id that the drawing functions will use. Builtins have
+ * constant names. */
+ if (ELEM(input->source, GPU_SOURCE_ATTRIB, GPU_SOURCE_BUILTIN)) {
continue;
}
- if (input->ima || input->tex || input->prv)
+ if (input->source == GPU_SOURCE_TEX)
BLI_snprintf(input->shadername, sizeof(input->shadername), "samp%d", input->texid);
- else
+ else {
BLI_snprintf(input->shadername, sizeof(input->shadername), "unf%d", input->id);
-
- /* pass non-dynamic uniforms to opengl */
- extract = 0;
-
- if (input->ima || input->tex || input->prv) {
- if (input->bindtex)
- extract = 1;
- }
- else if (input->dynamicvec)
- extract = 1;
-
- if (extract)
- input->shaderloc = GPU_shader_get_uniform(shader, input->shadername);
-
-#ifdef WITH_OPENSUBDIV
- if (input->source == GPU_SOURCE_ATTRIB &&
- input->attribtype == CD_MTFACE)
- {
- extract = 1;
- }
-#endif
-
- /* extract nodes */
- if (extract) {
- BLI_remlink(&node->inputs, input);
- BLI_addtail(inputs, input);
}
- }
- }
-
- GPU_shader_unbind();
-}
-
-void GPU_pass_bind(GPUPass *pass, double time, int mipmap)
-{
- GPUInput *input;
- GPUShader *shader = pass->shader;
- ListBase *inputs = &pass->inputs;
-
- if (!shader)
- return;
-
- GPU_shader_bind(shader);
-
- /* create the textures */
- for (input = inputs->first; input; input = input->next) {
- if (input->ima)
- input->tex = GPU_texture_from_blender(input->ima, input->iuser, input->textarget, input->image_isdata, time, mipmap);
- else if (input->prv)
- input->tex = GPU_texture_from_preview(input->prv, mipmap);
- }
-
- /* bind the textures, in second loop so texture binding during
- * create doesn't overwrite already bound textures */
- for (input = inputs->first; input; input = input->next) {
- if (input->tex && input->bindtex) {
- GPU_texture_bind(input->tex, input->texid);
- GPU_shader_uniform_texture(shader, input->shaderloc, input->tex);
- }
- }
-}
-
-void GPU_pass_update_uniforms(GPUPass *pass)
-{
- GPUInput *input;
- GPUShader *shader = pass->shader;
- ListBase *inputs = &pass->inputs;
- if (!shader)
- return;
-
- /* pass dynamic inputs to opengl, others were removed */
- for (input = inputs->first; input; input = input->next) {
- if (!(input->ima || input->tex || input->prv)) {
- if (input->dynamictype == GPU_DYNAMIC_MAT_HARD) {
- // The hardness is actually a short pointer, so we convert it here
- float val = (float)(*(short *)input->dynamicvec);
- GPU_shader_uniform_vector(shader, input->shaderloc, 1, 1, &val);
- }
- else {
- GPU_shader_uniform_vector(shader, input->shaderloc, input->type, 1,
- input->dynamicvec);
+ if (input->source == GPU_SOURCE_TEX) {
+ if (input->bindtex) {
+ input->shaderloc = GPU_shader_get_uniform(shader, input->shadername);
+ /* extract nodes */
+ BLI_remlink(&node->inputs, input);
+ BLI_addtail(inputs, input);
+ }
}
}
}
-}
-
-void GPU_pass_unbind(GPUPass *pass)
-{
- GPUInput *input;
- GPUShader *shader = pass->shader;
- ListBase *inputs = &pass->inputs;
-
- if (!shader)
- return;
-
- for (input = inputs->first; input; input = input->next) {
- if (input->tex && input->bindtex)
- GPU_texture_unbind(input->tex);
-
- if (input->ima || input->prv)
- input->tex = NULL;
- }
GPU_shader_unbind();
}
@@ -1093,7 +1169,6 @@ void GPU_pass_unbind(GPUPass *pass)
static GPUNodeLink *GPU_node_link_create(void)
{
GPUNodeLink *link = MEM_callocN(sizeof(GPUNodeLink), "GPUNodeLink");
- link->type = GPU_NONE;
link->users++;
return link;
@@ -1130,16 +1205,15 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType
GPUNode *outnode;
const char *name;
- if (link->output) {
+ if (link->link_type == GPU_NODE_LINK_OUTPUT) {
outnode = link->output->node;
name = outnode->name;
input = outnode->inputs.first;
- if ((STREQ(name, "set_value") || STREQ(name, "set_rgb")) &&
+ if ((STREQ(name, "set_value") || STREQ(name, "set_rgb") || STREQ(name, "set_rgba")) &&
(input->type == type))
{
input = MEM_dupallocN(outnode->inputs.first);
- input->type = type;
if (input->link)
input->link->users++;
BLI_addtail(&node->inputs, input);
@@ -1149,120 +1223,140 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType
input = MEM_callocN(sizeof(GPUInput), "GPUInput");
input->node = node;
+ input->type = type;
- if (link->builtin) {
- /* builtin uniform */
- input->type = type;
- input->source = GPU_SOURCE_BUILTIN;
- input->builtin = link->builtin;
-
- MEM_freeN(link);
+ 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;
+ link->users++;
+ break;
+ case GPU_NODE_LINK_COLORBAND:
+ input->source = GPU_SOURCE_TEX;
+ input->coba = link->coba;
+ break;
+ case GPU_NODE_LINK_IMAGE_BLENDER:
+ input->source = GPU_SOURCE_TEX;
+ input->ima = link->ima;
+ input->iuser = link->iuser;
+ input->image_isdata = link->image_isdata;
+ break;
+ case GPU_NODE_LINK_ATTRIB:
+ input->source = GPU_SOURCE_ATTRIB;
+ input->attribtype = link->attribtype;
+ BLI_strncpy(input->attribname, link->attribname, sizeof(input->attribname));
+ break;
+ case GPU_NODE_LINK_CONSTANT:
+ input->source = (type == GPU_CLOSURE) ? GPU_SOURCE_STRUCT : GPU_SOURCE_CONSTANT;
+ break;
+ case GPU_NODE_LINK_UNIFORM:
+ input->source = GPU_SOURCE_UNIFORM;
+ break;
+ default:
+ break;
}
- else if (link->oglbuiltin) {
- /* builtin uniform */
- input->type = type;
- input->source = GPU_SOURCE_OPENGL_BUILTIN;
- input->oglbuiltin = link->oglbuiltin;
- MEM_freeN(link);
+ if (ELEM(input->source, GPU_SOURCE_CONSTANT, GPU_SOURCE_UNIFORM)) {
+ memcpy(input->vec, link->data, type * sizeof(float));
}
- else if (link->output) {
- /* link to a node output */
- input->type = type;
- input->source = GPU_SOURCE_TEX_PIXEL;
- input->link = link;
- link->users++;
- }
- else if (link->dynamictex) {
- /* dynamic texture, GPUTexture is updated/deleted externally */
- input->type = type;
- input->source = GPU_SOURCE_TEX;
-
- input->tex = link->dynamictex;
- input->textarget = GL_TEXTURE_2D;
- input->textype = type;
- input->dynamictex = true;
- input->dynamicdata = link->ptr2;
+
+ if (link->link_type != GPU_NODE_LINK_OUTPUT) {
MEM_freeN(link);
}
- else if (link->texture) {
- /* small texture created on the fly, like for colorbands */
- input->type = GPU_VEC4;
- input->source = GPU_SOURCE_TEX;
- input->textype = type;
+ BLI_addtail(&node->inputs, input);
+}
-#if 0
- input->tex = GPU_texture_create_2D(link->texturesize, link->texturesize, link->ptr2, NULL);
-#endif
- input->tex = GPU_texture_create_2D(link->texturesize, 1, link->ptr1, GPU_HDR_NONE, NULL);
- input->textarget = GL_TEXTURE_2D;
- MEM_freeN(link->ptr1);
- MEM_freeN(link);
+static const char *gpu_uniform_set_function_from_type(eNodeSocketDatatype type)
+{
+ switch (type) {
+ case SOCK_FLOAT:
+ return "set_value";
+ case SOCK_VECTOR:
+ return "set_rgb";
+ case SOCK_RGBA:
+ return "set_rgba";
+ default:
+ BLI_assert(!"No gpu function for non-supported eNodeSocketDatatype");
+ return NULL;
}
- else if (link->image) {
- /* blender image */
- input->type = GPU_VEC4;
- input->source = GPU_SOURCE_TEX;
+}
- if (link->image == GPU_NODE_LINK_IMAGE_PREVIEW) {
- input->prv = link->ptr1;
- input->textarget = GL_TEXTURE_2D;
- input->textype = GPU_TEX2D;
- }
- else if (link->image == GPU_NODE_LINK_IMAGE_BLENDER) {
- input->ima = link->ptr1;
- input->iuser = link->ptr2;
- input->image_isdata = link->image_isdata;
- input->textarget = GL_TEXTURE_2D;
- input->textype = GPU_TEX2D;
- }
- else if (link->image == GPU_NODE_LINK_IMAGE_CUBE_MAP) {
- input->ima = link->ptr1;
- input->iuser = link->ptr2;
- input->image_isdata = link->image_isdata;
- input->textarget = GL_TEXTURE_CUBE_MAP;
- input->textype = GPU_TEXCUBE;
- }
- MEM_freeN(link);
+/**
+ * Link stack uniform buffer.
+ * This is called for the input/output sockets that are note connected.
+ */
+static GPUNodeLink *gpu_uniformbuffer_link(
+ GPUMaterial *mat, bNode *node, GPUNodeStack *stack, const int index, const eNodeSocketInOut in_out)
+{
+ bNodeSocket *socket;
+
+ /* Some nodes can have been create on the fly and does
+ * not have an original to point to. (i.e. the bump from
+ * ntree_shader_relink_displacement). In this case just
+ * revert to static constant folding. */
+ if (node->original == NULL) {
+ return NULL;
}
- else if (link->attribtype) {
- /* vertex attribute */
- input->type = type;
- input->source = GPU_SOURCE_ATTRIB;
- input->attribtype = link->attribtype;
- BLI_strncpy(input->attribname, link->attribname, sizeof(input->attribname));
- MEM_freeN(link);
+ if (in_out == SOCK_IN) {
+ socket = BLI_findlink(&node->original->inputs, index);
}
else {
- /* uniform vector */
- input->type = type;
- input->source = GPU_SOURCE_VEC_UNIFORM;
-
- memcpy(input->vec, link->ptr1, type * sizeof(float));
- if (link->dynamic) {
- input->dynamicvec = link->ptr1;
- input->dynamictype = link->dynamictype;
- input->dynamicdata = link->ptr2;
- }
- MEM_freeN(link);
+ socket = BLI_findlink(&node->original->outputs, index);
}
- BLI_addtail(&node->inputs, input);
+ BLI_assert(socket != NULL);
+ BLI_assert(socket->in_out == in_out);
+
+ if ((socket->flag & SOCK_HIDE_VALUE) == 0) {
+ GPUNodeLink *link;
+ switch (socket->type) {
+ case SOCK_FLOAT:
+ {
+ bNodeSocketValueFloat *socket_data = socket->default_value;
+ link = GPU_uniform(&socket_data->value);
+ break;
+ }
+ case SOCK_VECTOR:
+ {
+ bNodeSocketValueRGBA *socket_data = socket->default_value;
+ link = GPU_uniform(socket_data->value);
+ break;
+ }
+ case SOCK_RGBA:
+ {
+ bNodeSocketValueRGBA *socket_data = socket->default_value;
+ link = GPU_uniform(socket_data->value);
+ break;
+ }
+ default:
+ return NULL;
+ break;
+ }
+
+ if (in_out == SOCK_IN) {
+ GPU_link(mat, gpu_uniform_set_function_from_type(socket->type), link, &stack->link);
+ }
+ return link;
+ }
+ return NULL;
}
-static void gpu_node_input_socket(GPUNode *node, GPUNodeStack *sock)
+static void gpu_node_input_socket(GPUMaterial *material, bNode *bnode, GPUNode *node, GPUNodeStack *sock, const int index)
{
- GPUNodeLink *link;
-
if (sock->link) {
gpu_node_input_link(node, sock->link, sock->type);
}
+ else if ((material != NULL) && (gpu_uniformbuffer_link(material, bnode, sock, index, SOCK_IN) != NULL)) {
+ gpu_node_input_link(node, sock->link, sock->type);
+ }
else {
- link = GPU_node_link_create();
- link->ptr1 = sock->vec;
- gpu_node_input_link(node, link, sock->type);
+ gpu_node_input_link(node, GPU_constant(sock->vec), sock->type);
}
}
@@ -1275,7 +1369,7 @@ static void gpu_node_output(GPUNode *node, const GPUType type, GPUNodeLink **lin
if (link) {
*link = output->link = GPU_node_link_create();
- output->link->type = type;
+ output->link->link_type = GPU_NODE_LINK_OUTPUT;
output->link->output = output;
/* note: the caller owns the reference to the link, GPUOutput
@@ -1286,15 +1380,13 @@ static void gpu_node_output(GPUNode *node, const GPUType type, GPUNodeLink **lin
BLI_addtail(&node->outputs, output);
}
-static void gpu_inputs_free(ListBase *inputs)
+void GPU_inputs_free(ListBase *inputs)
{
GPUInput *input;
for (input = inputs->first; input; input = input->next) {
if (input->link)
gpu_node_link_free(input->link);
- else if (input->tex && !input->dynamictex)
- GPU_texture_free(input->tex);
}
BLI_freelistN(inputs);
@@ -1304,7 +1396,7 @@ static void gpu_node_free(GPUNode *node)
{
GPUOutput *output;
- gpu_inputs_free(&node->inputs);
+ GPU_inputs_free(&node->inputs);
for (output = node->outputs.first; output; output = output->next)
if (output->link) {
@@ -1327,7 +1419,7 @@ static void gpu_nodes_free(ListBase *nodes)
/* vertex attributes */
-static void gpu_nodes_get_vertex_attributes(ListBase *nodes, GPUVertexAttribs *attribs)
+void GPU_nodes_get_vertex_attributes(ListBase *nodes, GPUVertexAttribs *attribs)
{
GPUNode *node;
GPUInput *input;
@@ -1352,12 +1444,13 @@ static void gpu_nodes_get_vertex_attributes(ListBase *nodes, GPUVertexAttribs *a
if (a < GPU_MAX_ATTRIB) {
if (a == attribs->totlayer) {
input->attribid = attribs->totlayer++;
- input->attribfirst = 1;
+ input->attribfirst = true;
attribs->layer[a].type = input->attribtype;
attribs->layer[a].attribid = input->attribid;
- BLI_strncpy(attribs->layer[a].name, input->attribname,
- sizeof(attribs->layer[a].name));
+ BLI_strncpy(
+ attribs->layer[a].name, input->attribname,
+ sizeof(attribs->layer[a].name));
}
else {
input->attribid = attribs->layer[a].attribid;
@@ -1368,127 +1461,63 @@ static void gpu_nodes_get_vertex_attributes(ListBase *nodes, GPUVertexAttribs *a
}
}
-static void gpu_nodes_get_builtin_flag(ListBase *nodes, int *builtin)
-{
- GPUNode *node;
- GPUInput *input;
-
- *builtin = 0;
-
- for (node = nodes->first; node; node = node->next)
- for (input = node->inputs.first; input; input = input->next)
- if (input->source == GPU_SOURCE_BUILTIN)
- *builtin |= input->builtin;
-}
-
/* varargs linking */
GPUNodeLink *GPU_attribute(const CustomDataType type, const char *name)
{
GPUNodeLink *link = GPU_node_link_create();
-
- link->attribtype = type;
+ link->link_type = GPU_NODE_LINK_ATTRIB;
link->attribname = name;
-
+ /* Fall back to the UV layer, which matches old behavior. */
+ if (type == CD_AUTO_FROM_NAME && name[0] == '\0') {
+ link->attribtype = CD_MTFACE;
+ }
+ else {
+ link->attribtype = type;
+ }
return link;
}
-GPUNodeLink *GPU_uniform(float *num)
+GPUNodeLink *GPU_constant(float *num)
{
GPUNodeLink *link = GPU_node_link_create();
-
- link->ptr1 = num;
- link->ptr2 = NULL;
-
+ link->link_type = GPU_NODE_LINK_CONSTANT;
+ link->data = num;
return link;
}
-GPUNodeLink *GPU_dynamic_uniform(float *num, GPUDynamicType dynamictype, void *data)
+GPUNodeLink *GPU_uniform(float *num)
{
GPUNodeLink *link = GPU_node_link_create();
-
- link->ptr1 = num;
- link->ptr2 = data;
- link->dynamic = true;
- link->dynamictype = dynamictype;
-
-
+ link->link_type = GPU_NODE_LINK_UNIFORM;
+ link->data = num;
return link;
}
GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, bool is_data)
{
GPUNodeLink *link = GPU_node_link_create();
-
- link->image = GPU_NODE_LINK_IMAGE_BLENDER;
- link->ptr1 = ima;
- link->ptr2 = iuser;
- link->image_isdata = is_data;
-
- return link;
-}
-
-GPUNodeLink *GPU_cube_map(Image *ima, ImageUser *iuser, bool is_data)
-{
- GPUNodeLink *link = GPU_node_link_create();
-
- link->image = GPU_NODE_LINK_IMAGE_CUBE_MAP;
- link->ptr1 = ima;
- link->ptr2 = iuser;
+ link->link_type = GPU_NODE_LINK_IMAGE_BLENDER;
+ link->ima = ima;
+ link->iuser = iuser;
link->image_isdata = is_data;
-
- return link;
-}
-
-GPUNodeLink *GPU_image_preview(PreviewImage *prv)
-{
- GPUNodeLink *link = GPU_node_link_create();
-
- link->image = GPU_NODE_LINK_IMAGE_PREVIEW;
- link->ptr1 = prv;
-
return link;
}
-
-GPUNodeLink *GPU_texture(int size, float *pixels)
+GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *row)
{
GPUNodeLink *link = GPU_node_link_create();
-
- link->texture = true;
- link->texturesize = size;
- link->ptr1 = pixels;
-
- return link;
-}
-
-GPUNodeLink *GPU_dynamic_texture(GPUTexture *tex, GPUDynamicType dynamictype, void *data)
-{
- GPUNodeLink *link = GPU_node_link_create();
-
- link->dynamic = true;
- link->dynamictex = tex;
- link->dynamictype = dynamictype;
- link->ptr2 = data;
-
+ link->link_type = GPU_NODE_LINK_COLORBAND;
+ link->coba = gpu_material_ramp_texture_row_set(mat, size, pixels, row);
+ MEM_freeN(pixels);
return link;
}
GPUNodeLink *GPU_builtin(GPUBuiltin builtin)
{
GPUNodeLink *link = GPU_node_link_create();
-
+ link->link_type = GPU_NODE_LINK_BUILTIN;
link->builtin = builtin;
-
- return link;
-}
-
-GPUNodeLink *GPU_opengl_builtin(GPUOpenGLBuiltin builtin)
-{
- GPUNodeLink *link = GPU_node_link_create();
-
- link->oglbuiltin = builtin;
-
return link;
}
@@ -1526,7 +1555,7 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...)
return true;
}
-bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNodeStack *out, ...)
+bool GPU_stack_link(GPUMaterial *material, bNode *bnode, const char *name, GPUNodeStack *in, GPUNodeStack *out, ...)
{
GPUNode *node;
GPUFunction *function;
@@ -1545,16 +1574,20 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod
totout = 0;
if (in) {
- for (i = 0; in[i].type != GPU_NONE; i++) {
- gpu_node_input_socket(node, &in[i]);
- totin++;
+ for (i = 0; !in[i].end; i++) {
+ if (in[i].type != GPU_NONE) {
+ gpu_node_input_socket(material, bnode, node, &in[i], i);
+ totin++;
+ }
}
}
if (out) {
- for (i = 0; out[i].type != GPU_NONE; i++) {
- gpu_node_output(node, out[i].type, &out[i].link);
- totout++;
+ for (i = 0; !out[i].end; i++) {
+ if (out[i].type != GPU_NONE) {
+ gpu_node_output(node, out[i].type, &out[i].link);
+ totout++;
+ }
}
}
@@ -1572,7 +1605,7 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod
if (totin == 0) {
link = va_arg(params, GPUNodeLink *);
if (link->socket)
- gpu_node_input_socket(node, link->socket);
+ gpu_node_input_socket(NULL, NULL, node, link->socket, -1);
else
gpu_node_input_link(node, link, function->paramtype[i]);
}
@@ -1582,30 +1615,14 @@ bool GPU_stack_link(GPUMaterial *mat, const char *name, GPUNodeStack *in, GPUNod
}
va_end(params);
- gpu_material_add_node(mat, node);
+ gpu_material_add_node(material, node);
return true;
}
-int GPU_link_changed(GPUNodeLink *link)
+GPUNodeLink *GPU_uniformbuffer_link_out(GPUMaterial *mat, bNode *node, GPUNodeStack *stack, const int index)
{
- GPUNode *node;
- GPUInput *input;
- const char *name;
-
- if (link->output) {
- node = link->output->node;
- name = node->name;
-
- if (STREQ(name, "set_value") || STREQ(name, "set_rgb")) {
- input = node->inputs.first;
- return (input->link != NULL);
- }
-
- return 1;
- }
- else
- return 0;
+ return gpu_uniformbuffer_link(mat, node, stack, index, SOCK_OUT);
}
/* Pass create/free */
@@ -1628,7 +1645,7 @@ static void gpu_nodes_tag(GPUNodeLink *link)
gpu_nodes_tag(input->link);
}
-static void gpu_nodes_prune(ListBase *nodes, GPUNodeLink *outlink)
+void GPU_nodes_prune(ListBase *nodes, GPUNodeLink *outlink)
{
GPUNode *node, *next;
@@ -1647,91 +1664,227 @@ static void gpu_nodes_prune(ListBase *nodes, GPUNodeLink *outlink)
}
}
-GPUPass *GPU_generate_pass(
- ListBase *nodes, GPUNodeLink *outlink,
- GPUVertexAttribs *attribs, int *builtins,
- const GPUMatType type, const char *UNUSED(name),
- const bool use_opensubdiv,
- const bool use_new_shading)
+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_new(
+ GPUMaterial *material,
+ GPUNodeLink *frag_outlink,
+ struct GPUVertexAttribs *attribs,
+ ListBase *nodes,
+ const char *vert_code,
+ const char *geom_code,
+ const char *frag_lib,
+ const char *defines)
{
- GPUShader *shader;
- GPUPass *pass;
char *vertexcode, *geometrycode, *fragmentcode;
+ GPUPass *pass = NULL, *pass_hash = NULL;
-#if 0
- if (!FUNCTION_LIB) {
- GPU_nodes_free(nodes);
- return NULL;
+ /* prune unused nodes */
+ GPU_nodes_prune(nodes, frag_outlink);
+
+ GPU_nodes_get_vertex_attributes(nodes, attribs);
+
+ /* generate code */
+ char *fragmentgen = code_generate_fragment(material, nodes, frag_outlink->output);
+
+ /* Cache lookup: Reuse shaders already compiled */
+ uint32_t hash = gpu_pass_hash(fragmentgen, defines, attribs);
+ 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_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;
}
-#endif
- /* prune unused nodes */
- gpu_nodes_prune(nodes, outlink);
-
- gpu_nodes_get_vertex_attributes(nodes, attribs);
- gpu_nodes_get_builtin_flag(nodes, builtins);
-
- /* generate code and compile with opengl */
- fragmentcode = code_generate_fragment(nodes, outlink->output);
- vertexcode = code_generate_vertex(nodes, type);
- geometrycode = code_generate_geometry(nodes, use_opensubdiv);
-
- int flags = GPU_SHADER_FLAGS_NONE;
- if (use_opensubdiv) {
- flags |= GPU_SHADER_FLAGS_SPECIAL_OPENSUBDIV;
- }
- if (use_new_shading) {
- flags |= GPU_SHADER_FLAGS_NEW_SHADING;
- }
- shader = GPU_shader_create_ex(vertexcode,
- fragmentcode,
- geometrycode,
- glsl_material_library,
- NULL,
- 0,
- 0,
- 0,
- flags);
-
- /* failed? */
- if (!shader) {
- if (fragmentcode)
- MEM_freeN(fragmentcode);
- if (vertexcode)
- MEM_freeN(vertexcode);
- memset(attribs, 0, sizeof(*attribs));
- memset(builtins, 0, sizeof(*builtins));
- gpu_nodes_free(nodes);
- return NULL;
+ /* Either the shader is not compiled or there is a hash collision...
+ * continue generating the shader strings. */
+ char *tmp = BLI_strdupcat(frag_lib, glsl_material_library);
+
+ vertexcode = code_generate_vertex(nodes, vert_code, (geom_code != NULL));
+ geometrycode = (geom_code) ? code_generate_geometry(nodes, geom_code) : NULL;
+ fragmentcode = BLI_strdupcat(tmp, fragmentgen);
+
+ MEM_freeN(fragmentgen);
+ MEM_freeN(tmp);
+
+ if (pass_hash) {
+ /* Cache lookup: Reuse shaders already compiled */
+ pass = gpu_pass_cache_resolve_collision(pass_hash, vertexcode, geometrycode, fragmentcode, defines, hash);
}
- /* create pass */
- pass = MEM_callocN(sizeof(GPUPass), "GPUPass");
+ 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 NULL;
+ }
- pass->output = outlink->output;
- pass->shader = shader;
- pass->fragmentcode = fragmentcode;
- pass->geometrycode = geometrycode;
- pass->vertexcode = vertexcode;
- pass->libcode = glsl_material_library;
+ MEM_SAFE_FREE(vertexcode);
+ MEM_SAFE_FREE(fragmentcode);
+ MEM_SAFE_FREE(geometrycode);
- /* extract dynamic inputs and throw away nodes */
- gpu_nodes_extract_dynamic_inputs(pass, nodes);
- gpu_nodes_free(nodes);
+ 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;
}
-void GPU_pass_free(GPUPass *pass)
+static int count_active_texture_sampler(GPUShader *shader, char *source)
{
- GPU_shader_free(pass->shader);
- gpu_inputs_free(&pass->inputs);
- if (pass->fragmentcode)
- MEM_freeN(pass->fragmentcode);
- if (pass->geometrycode)
- MEM_freeN(pass->geometrycode);
- if (pass->vertexcode)
- MEM_freeN(pass->vertexcode);
+ char *code = source;
+ int samplers_id[64]; /* Remember this is per stage. */
+ int sampler_len = 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 (gpu_str_prefix(code, "sampler")) {
+ /* Move past "uniform". */
+ code += 7;
+ /* Skip sampler type suffix. */
+ while (*code != ' ' && *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. */
+ bool is_duplicate = false;
+ for (int i = 0; i < sampler_len; ++i) {
+ if (samplers_id[i] == id) {
+ is_duplicate = true;
+ }
+ }
+
+ if (!is_duplicate) {
+ samplers_id[sampler_len] = id;
+ sampler_len++;
+ }
+ }
+ }
+ }
+
+ return sampler_len;
+}
+
+static bool gpu_pass_shader_validate(GPUPass *pass)
+{
+ if (pass->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(pass->shader, pass->vertexcode);
+ int frag_samplers_len = count_active_texture_sampler(pass->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()) ||
+ (frag_samplers_len > GPU_max_textures_vert()))
+ {
+ return false;
+ }
+
+ if (pass->geometrycode) {
+ int geom_samplers_len = count_active_texture_sampler(pass->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());
+}
+
+void GPU_pass_compile(GPUPass *pass, const char *shname)
+{
+ if (!pass->compiled) {
+ pass->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 behaviour. */
+ if (!gpu_pass_shader_validate(pass)) {
+ if (pass->shader != NULL) {
+ fprintf(stderr, "GPUShader: error: too many samplers in shader.\n");
+ GPU_shader_free(pass->shader);
+ }
+ pass->shader = NULL;
+ }
+ pass->compiled = true;
+ }
+}
+
+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);
}
@@ -1739,3 +1892,48 @@ void GPU_pass_free_nodes(ListBase *nodes)
{
gpu_nodes_free(nodes);
}
+
+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);
+}