From 322dc723165a705c34df1e3246e500da1a9927de Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 14 Feb 2020 10:47:20 +0100 Subject: Cleanup: refactor GPU material attribute and texture requests --- source/blender/gpu/GPU_material.h | 26 +++- source/blender/gpu/GPU_shader.h | 10 -- source/blender/gpu/intern/gpu_codegen.c | 188 ++++++++++++++--------------- source/blender/gpu/intern/gpu_material.c | 22 ++-- source/blender/gpu/intern/gpu_node_graph.c | 54 ++------- source/blender/gpu/intern/gpu_node_graph.h | 22 ++-- 6 files changed, 147 insertions(+), 175 deletions(-) (limited to 'source/blender/gpu') diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 8c166ed6b64..075829065df 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -39,7 +39,6 @@ struct GPUNodeLink; struct GPUNodeStack; struct GPUTexture; struct GPUUniformBuffer; -struct GPUVertAttrLayers; struct Image; struct ImageUser; struct ListBase; @@ -161,7 +160,6 @@ GPUNodeLink *GPU_uniformbuffer_link_out(struct GPUMaterial *mat, const int index); void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link); -eGPUBuiltin GPU_get_material_builtins(GPUMaterial *material); void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3], @@ -201,8 +199,6 @@ struct GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material); void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs); struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void); -void GPU_material_vertex_attrs(GPUMaterial *material, struct GPUVertAttrLayers *attrs); - bool GPU_material_use_domain_surface(GPUMaterial *mat); bool GPU_material_use_domain_volume(GPUMaterial *mat); @@ -213,6 +209,28 @@ void GPU_pass_cache_init(void); void GPU_pass_cache_garbage_collect(void); void GPU_pass_cache_free(void); +/* Requested Material Attributes and Textures */ + +typedef struct GPUMaterialAttribute { + struct GPUMaterialAttribute *next, *prev; + int type; /* CustomDataType */ + char name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */ + int id; +} GPUMaterialAttribute; + +typedef struct GPUMaterialTexture { + struct GPUMaterialTexture *next, *prev; + eGPUType type; + struct Image *ima; + struct ImageUser *iuser; + struct GPUTexture **colorband; + char shadername[32]; /* Name of sampler in GLSL. */ + int id; +} GPUMaterialTexture; + +ListBase GPU_material_attributes(GPUMaterial *material); +ListBase GPU_material_textures(GPUMaterial *material); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 1339873ec67..d5716cd1b31 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -382,16 +382,6 @@ void GPU_shader_free_builtin_shaders(void); * This makes sure the GPUVertexFormat name buffer does not overflow. */ #define GPU_MAX_ATTR 15 -typedef struct GPUVertAttrLayers { - struct { - int type; /* CustomDataType */ - int attr_id; - char name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */ - } layer[GPU_MAX_ATTR]; - - int totlayer; -} GPUVertAttrLayers; - #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 2f9af536b8c..ed6bf20cff4 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -70,16 +70,13 @@ extern char datatoc_common_view_lib_glsl[]; static GPUPass *pass_cache = NULL; static SpinLock pass_cache_spin; -static uint32_t gpu_pass_hash(const char *frag_gen, const char *defs, GPUVertAttrLayers *attrs) +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)); - if (attrs) { - for (int att_idx = 0; att_idx < attrs->totlayer; att_idx++) { - char *name = attrs->layer[att_idx].name; - BLI_hash_mm2a_add(&hm2a, (uchar *)name, strlen(name)); - } + for (GPUMaterialAttribute *attr = attributes->first; attr; attr = attr->next) { + BLI_hash_mm2a_add(&hm2a, (uchar *)attr->name, strlen(attr->name)); } if (defs) { BLI_hash_mm2a_add(&hm2a, (uchar *)defs, strlen(defs)); @@ -302,55 +299,17 @@ static const char *gpu_builtin_name(eGPUBuiltin builtin) } } -/* assign only one texid per buffer to avoid sampling the same texture twice */ -static void codegen_set_texid(GHash *bindhash, GPUInput *input, int *texid, void *key1, int key2) -{ - GHashPair pair = {key1, POINTER_FROM_INT(key2)}; - if (BLI_ghash_haskey(bindhash, &pair)) { - /* Reuse existing texid */ - input->texid = POINTER_AS_INT(BLI_ghash_lookup(bindhash, &pair)); - } - else { - /* Allocate new texid */ - input->texid = *texid; - (*texid)++; - input->bindtex = true; - void *key = BLI_ghashutil_pairalloc(key1, POINTER_FROM_INT(key2)); - BLI_ghash_insert(bindhash, key, POINTER_FROM_INT(input->texid)); - } -} - static void codegen_set_unique_ids(ListBase *nodes) { - GHash *bindhash; GPUNode *node; GPUInput *input; GPUOutput *output; - int id = 1, texid = 0; - - bindhash = BLI_ghash_pair_new("codegen_set_unique_ids1 gh"); + int id = 1; 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++; - - /* 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, input->type); - } - else if (input->coba) { - /* input is color band texture, check coba pointer */ - codegen_set_texid(bindhash, input, &texid, input->coba, 0); - } - else { - /* Either input->ima or input->coba should be non-NULL. */ - BLI_assert(0); - } - } } for (output = node->outputs.first; output; output = output->next) { @@ -358,8 +317,6 @@ static void codegen_set_unique_ids(ListBase *nodes) output->id = id++; } } - - BLI_ghash_free(bindhash, BLI_ghashutil_pairfree, NULL); } /** @@ -380,7 +337,7 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, DynStr *ds, /* create exactly one sampler for each texture */ if (codegen_input_has_texture(input) && input->bindtex) { const char *type; - if (input->coba || input->type == GPU_TEX1D_ARRAY) { + if (input->colorband || input->type == GPU_TEX1D_ARRAY) { type = "sampler1DArray"; } else if (input->type == GPU_TEX2D_ARRAY) { @@ -570,10 +527,7 @@ static void codegen_call_functions(DynStr *ds, ListBase *nodes, GPUOutput *final BLI_dynstr_append(ds, ";\n"); } -static char *code_generate_fragment(GPUMaterial *material, - ListBase *nodes, - GPUOutput *output, - int *rbuiltins) +static char *code_generate_fragment(GPUMaterial *material, ListBase *nodes, GPUOutput *output) { DynStr *ds = BLI_dynstr_new(); char *code; @@ -584,7 +538,7 @@ static char *code_generate_fragment(GPUMaterial *material, #endif codegen_set_unique_ids(nodes); - *rbuiltins = builtins = codegen_process_uniforms_functions(material, ds, nodes); + builtins = codegen_process_uniforms_functions(material, ds, nodes); if (builtins & (GPU_OBJECT_INFO | GPU_OBJECT_COLOR)) { BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); @@ -1099,45 +1053,90 @@ GPUShader *GPU_pass_shader_get(GPUPass *pass) return pass->shader; } -/* Vertex Attributes */ +/* Requested Attributes */ -static void gpu_nodes_get_vertex_attrs(ListBase *nodes, GPUVertAttrLayers *attrs) +static ListBase gpu_nodes_requested_attributes(ListBase *nodes) { - GPUNode *node; - GPUInput *input; - int a; + ListBase attributes = {NULL}; + int num_attributes = 0; - /* convert attributes requested by node inputs to an array of layers, + /* Convert attributes requested by node inputs to list, checking for * checking for duplicates and assigning id's starting from zero. */ + for (GPUNode *node = nodes->first; node; node = node->next) { + for (GPUInput *input = node->inputs.first; input; input = input->next) { + if (input->source != GPU_SOURCE_ATTR) { + continue; + } - memset(attrs, 0, sizeof(*attrs)); - - for (node = nodes->first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { - if (input->source == GPU_SOURCE_ATTR) { - for (a = 0; a < attrs->totlayer; a++) { - if (attrs->layer[a].type == input->attr_type && - STREQ(attrs->layer[a].name, input->attr_name)) { - break; - } + GPUMaterialAttribute *attr = attributes.first; + for (; attr; attr = attr->next) { + if (attr->type == input->attr_type && STREQ(attr->name, input->attr_name)) { + break; } + } - if (a < GPU_MAX_ATTR) { - if (a == attrs->totlayer) { - input->attr_id = attrs->totlayer++; - input->attr_first = true; + /* Add new requested attribute if it's within GPU limits. */ + if (attr == NULL && num_attributes < GPU_MAX_ATTR) { + attr = MEM_callocN(sizeof(*attr), __func__); + attr->type = input->attr_type; + STRNCPY(attr->name, input->attr_name); + attr->id = num_attributes++; + BLI_addtail(&attributes, attr); - attrs->layer[a].type = input->attr_type; - attrs->layer[a].attr_id = input->attr_id; - BLI_strncpy(attrs->layer[a].name, input->attr_name, sizeof(attrs->layer[a].name)); - } - else { - input->attr_id = attrs->layer[a].attr_id; - } + input->attr_id = attr->id; + input->attr_first = true; + } + else if (attr != NULL) { + input->attr_id = attr->id; + } + } + } + + return attributes; +} + +/* Requested Textures */ + +static ListBase gpu_nodes_requested_textures(ListBase *nodes) +{ + ListBase textures = {NULL}; + int num_textures = 0; + + /* Convert textures requested by node inputs to list, checking for + * checking for duplicates and assigning id's starting from zero. */ + for (GPUNode *node = nodes->first; node; node = node->next) { + for (GPUInput *input = node->inputs.first; input; input = input->next) { + if (!codegen_input_has_texture(input)) { + continue; + } + + GPUMaterialTexture *tex = textures.first; + for (; tex; tex = tex->next) { + if (tex->ima == input->ima && tex->colorband == input->colorband) { + break; } } + + if (tex == NULL) { + tex = MEM_callocN(sizeof(*tex), __func__); + tex->ima = input->ima; + tex->iuser = input->iuser; + tex->colorband = input->colorband; + tex->id = num_textures++; + BLI_snprintf(tex->shadername, sizeof(tex->shadername), "samp%d", tex->id); + BLI_addtail(&textures, tex); + + input->texid = tex->id; + input->bindtex = true; + } + else { + input->texid = tex->id; + input->bindtex = false; + } } } + + return textures; } /* Pass create/free */ @@ -1155,21 +1154,19 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, const char *frag_lib, const char *defines) { - char *vertexcode, *geometrycode, *fragmentcode; - GPUPass *pass = NULL, *pass_hash = NULL; - /* 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_nodes_get_vertex_attrs(&graph->nodes, &graph->attrs); + + graph->attributes = gpu_nodes_requested_attributes(&graph->nodes); + graph->textures = gpu_nodes_requested_textures(&graph->nodes); /* generate code */ - char *fragmentgen = code_generate_fragment( - material, &graph->nodes, graph->outlink->output, &graph->builtins); + char *fragmentgen = code_generate_fragment(material, &graph->nodes, graph->outlink->output); /* Cache lookup: Reuse shaders already compiled */ - uint32_t hash = gpu_pass_hash(fragmentgen, defines, &graph->attrs); - pass_hash = gpu_pass_cache_lookup(hash); + 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. */ @@ -1187,13 +1184,14 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, GSet *used_libraries = gpu_material_used_libraries(material); char *tmp = gpu_material_library_generate_code(used_libraries, frag_lib); - geometrycode = code_generate_geometry(&graph->nodes, geom_code, defines); - vertexcode = code_generate_vertex(&graph->nodes, vert_code, (geometrycode != NULL)); - fragmentcode = BLI_strdupcat(tmp, fragmentgen); + char *geometrycode = code_generate_geometry(&graph->nodes, geom_code, defines); + char *vertexcode = code_generate_vertex(&graph->nodes, vert_code, (geometrycode != NULL)); + char *fragmentcode = BLI_strdupcat(tmp, fragmentgen); MEM_freeN(fragmentgen); MEM_freeN(tmp); + GPUPass *pass = NULL; if (pass_hash) { /* Cache lookup: Reuse shaders already compiled */ pass = gpu_pass_cache_resolve_collision( @@ -1201,16 +1199,16 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, } 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; } - MEM_SAFE_FREE(vertexcode); - MEM_SAFE_FREE(fragmentcode); - MEM_SAFE_FREE(geometrycode); - pass->refcount += 1; } else { diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 9eed3e4619b..d8d3e5a2ff2 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -197,11 +197,6 @@ void GPU_material_free(ListBase *gpumaterial) BLI_freelistN(gpumaterial); } -eGPUBuiltin GPU_get_material_builtins(GPUMaterial *material) -{ - return material->graph.builtins; -} - Scene *GPU_material_scene(GPUMaterial *material) { return material->scene; @@ -567,9 +562,14 @@ struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void) #undef SSS_EXPONENT #undef SSS_SAMPLES -void GPU_material_vertex_attrs(GPUMaterial *material, GPUVertAttrLayers *r_attrs) +ListBase GPU_material_attributes(GPUMaterial *material) { - *r_attrs = material->graph.attrs; + return material->graph.attributes; +} + +ListBase GPU_material_textures(GPUMaterial *material) +{ + return material->graph.textures; } void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link) @@ -696,13 +696,14 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, if (mat->pass == NULL) { /* We had a cache hit and the shader has already failed to compile. */ mat->status = GPU_MAT_FAILED; + gpu_node_graph_free(&mat->graph); } else { GPUShader *sh = GPU_pass_shader_get(mat->pass); if (sh != NULL) { /* We had a cache hit and the shader is already compiled. */ mat->status = GPU_MAT_SUCCESS; - gpu_node_graph_extract_dynamic_inputs(sh, &mat->graph); + gpu_node_graph_free_nodes(&mat->graph); } else { mat->status = GPU_MAT_QUEUED; @@ -711,6 +712,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, } else { mat->status = GPU_MAT_FAILED; + gpu_node_graph_free(&mat->graph); } /* Only free after GPU_pass_shader_get where GPUUniformBuffer @@ -748,14 +750,14 @@ void GPU_material_compile(GPUMaterial *mat) GPUShader *sh = GPU_pass_shader_get(mat->pass); if (sh != NULL) { mat->status = GPU_MAT_SUCCESS; - gpu_node_graph_extract_dynamic_inputs(sh, &mat->graph); + gpu_node_graph_free_nodes(&mat->graph); } } else { mat->status = GPU_MAT_FAILED; - gpu_node_graph_free(&mat->graph); GPU_pass_release(mat->pass); mat->pass = NULL; + gpu_node_graph_free(&mat->graph); } } diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index 432c2d773b5..b271ac48f75 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -111,7 +111,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType break; case GPU_NODE_LINK_COLORBAND: input->source = GPU_SOURCE_TEX; - input->coba = link->coba; + input->colorband = link->colorband; break; case GPU_NODE_LINK_IMAGE_BLENDER: case GPU_NODE_LINK_IMAGE_TILEMAP: @@ -295,7 +295,7 @@ GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *ro { GPUNodeLink *link = gpu_node_link_create(); link->link_type = GPU_NODE_LINK_COLORBAND; - link->coba = gpu_material_ramp_texture_row_set(mat, size, pixels, row); + link->colorband = gpu_material_ramp_texture_row_set(mat, size, pixels, row); MEM_freeN(pixels); return link; } @@ -460,7 +460,8 @@ static void gpu_node_free(GPUNode *node) MEM_freeN(node); } -void gpu_node_graph_free(GPUNodeGraph *graph) +/* Free intermediate node graph. */ +void gpu_node_graph_free_nodes(GPUNodeGraph *graph) { GPUNode *node; @@ -470,51 +471,14 @@ void gpu_node_graph_free(GPUNodeGraph *graph) gpu_inputs_free(&graph->inputs); graph->outlink = NULL; - graph->builtins = 0; - memset(&graph->attrs, 0, sizeof(graph->attrs)); } -/* Extract Dynamic Inputs */ - -void gpu_node_graph_extract_dynamic_inputs(GPUShader *shader, GPUNodeGraph *graph) +/* Free both node graph and requested attributes and textures. */ +void gpu_node_graph_free(GPUNodeGraph *graph) { - GPUNode *node; - GPUInput *next, *input; - - if (!shader) { - return; - } - - while ((node = BLI_pophead(&graph->nodes))) { - for (input = node->inputs.first; input; input = next) { - next = input->next; - - /* attributes don't need to be bound, they already have - * an id that the drawing functions will use. Builtins have - * constant names. */ - if (ELEM(input->source, GPU_SOURCE_ATTR, GPU_SOURCE_BUILTIN)) { - continue; - } - - if (input->source == GPU_SOURCE_TEX) { - BLI_snprintf(input->shadername, sizeof(input->shadername), "samp%d", input->texid); - } - else { - BLI_snprintf(input->shadername, sizeof(input->shadername), "unf%d", input->id); - } - - if (input->source == GPU_SOURCE_TEX) { - if (input->bindtex) { - input->shaderloc = GPU_shader_get_uniform_ensure(shader, input->shadername); - /* extract nodes */ - BLI_remlink(&node->inputs, input); - BLI_addtail(&graph->inputs, input); - } - } - } - - gpu_node_free(node); - } + gpu_node_graph_free_nodes(graph); + BLI_freelistN(&graph->attributes); + BLI_freelistN(&graph->textures); } /* Prune Unused Nodes */ diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 51e188e3f5e..2ffde8dba39 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -83,7 +83,7 @@ struct GPUNodeLink { /* GPU_NODE_LINK_BUILTIN */ eGPUBuiltin builtin; /* GPU_NODE_LINK_COLORBAND */ - struct GPUTexture **coba; + struct GPUTexture **colorband; /* GPU_NODE_LINK_OUTPUT */ struct GPUOutput *output; /* GPU_NODE_LINK_ATTR */ @@ -129,12 +129,12 @@ typedef struct GPUInput { eGPUBuiltin builtin; /* builtin uniform */ /* GPU_SOURCE_TEX */ struct { - struct GPUTexture **coba; /* input texture, only set at runtime */ - struct Image *ima; /* image */ - struct ImageUser *iuser; /* image user */ - bool bindtex; /* input is responsible for binding the texture? */ - int texid; /* number for multitexture, starting from zero */ - eGPUType textype; /* texture type (2D, 1D Array ...) */ + struct GPUTexture **colorband; /* input texture, only set at runtime */ + struct Image *ima; /* image */ + struct ImageUser *iuser; /* image user */ + bool bindtex; /* input is responsible for binding the texture? */ + int texid; /* number for multitexture, starting from zero */ + eGPUType textype; /* texture type (2D, 1D Array ...) */ }; /* GPU_SOURCE_ATTR */ struct { @@ -158,15 +158,15 @@ typedef struct GPUNodeGraph { ListBase inputs; GPUNodeLink *outlink; - /* Needed attributes. */ - GPUVertAttrLayers attrs; - int builtins; + /* Requested attributes and textures. */ + ListBase attributes; + ListBase textures; } GPUNodeGraph; /* Node Graph */ void gpu_node_graph_prune_unused(GPUNodeGraph *graph); -void gpu_node_graph_extract_dynamic_inputs(struct GPUShader *shader, GPUNodeGraph *graph); +void gpu_node_graph_free_nodes(GPUNodeGraph *graph); void gpu_node_graph_free(GPUNodeGraph *graph); /* Material calls */ -- cgit v1.2.3