Changeset View
Changeset View
Standalone View
Standalone View
source/blender/gpu/intern/gpu_codegen.c
| Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
| #include "GPU_shader.h" | #include "GPU_shader.h" | ||||
| #include "GPU_texture.h" | #include "GPU_texture.h" | ||||
| #include "GPU_uniformbuffer.h" | #include "GPU_uniformbuffer.h" | ||||
| #include "GPU_vertex_format.h" | #include "GPU_vertex_format.h" | ||||
| #include "BLI_sys_types.h" /* for intptr_t support */ | #include "BLI_sys_types.h" /* for intptr_t support */ | ||||
| #include "gpu_codegen.h" | #include "gpu_codegen.h" | ||||
| #include "gpu_material_library.h" | |||||
| #include <string.h> | #include <string.h> | ||||
| #include <stdarg.h> | #include <stdarg.h> | ||||
| extern char datatoc_gpu_shader_material_glsl[]; | |||||
| extern char datatoc_gpu_shader_geometry_glsl[]; | |||||
| static char *glsl_material_library = NULL; | |||||
| /* -------------------- GPUPass Cache ------------------ */ | /* -------------------- GPUPass Cache ------------------ */ | ||||
| /** | /** | ||||
| * Internal shader cache: This prevent the shader recompilation / stall when | * 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 | * 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. | * same for 2 different Materials. Unused GPUPasses are free by Garbage collection. | ||||
| */ | */ | ||||
| /* Only use one linklist that contains the GPUPasses grouped by hash. */ | /* Only use one linklist that contains the GPUPasses grouped by hash. */ | ||||
| ▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | typedef enum { | ||||
| FUNCTION_QUAL_INOUT, | FUNCTION_QUAL_INOUT, | ||||
| } GPUFunctionQual; | } GPUFunctionQual; | ||||
| typedef struct GPUFunction { | typedef struct GPUFunction { | ||||
| char name[MAX_FUNCTION_NAME]; | char name[MAX_FUNCTION_NAME]; | ||||
| eGPUType paramtype[MAX_PARAMETER]; | eGPUType paramtype[MAX_PARAMETER]; | ||||
| GPUFunctionQual paramqual[MAX_PARAMETER]; | GPUFunctionQual paramqual[MAX_PARAMETER]; | ||||
| int totparam; | int totparam; | ||||
| GPUMaterialLibrary *library; | |||||
| } GPUFunction; | } GPUFunction; | ||||
| /* Indices match the eGPUType enum */ | /* Indices match the eGPUType enum */ | ||||
| static const char *GPU_DATATYPE_STR[17] = { | static const char *GPU_DATATYPE_STR[17] = { | ||||
| "", | "", | ||||
| "float", | "float", | ||||
| "vec2", | "vec2", | ||||
| "vec3", | "vec3", | ||||
| ▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | while (*str) { | ||||
| else { | else { | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return str; | return str; | ||||
| } | } | ||||
| static void gpu_parse_functions_string(GHash *hash, char *code) | static void gpu_parse_material_library(GHash *hash, GPUMaterialLibrary *library) | ||||
| { | { | ||||
| GPUFunction *function; | GPUFunction *function; | ||||
| eGPUType type; | eGPUType type; | ||||
| GPUFunctionQual qual; | GPUFunctionQual qual; | ||||
| int i; | int i; | ||||
| char *code = library->code; | |||||
| while ((code = strstr(code, "void "))) { | while ((code = strstr(code, "void "))) { | ||||
| function = MEM_callocN(sizeof(GPUFunction), "GPUFunction"); | function = MEM_callocN(sizeof(GPUFunction), "GPUFunction"); | ||||
| function->library = library; | |||||
| code = gpu_str_skip_token(code, NULL, 0); | code = gpu_str_skip_token(code, NULL, 0); | ||||
| code = gpu_str_skip_token(code, function->name, MAX_FUNCTION_NAME); | code = gpu_str_skip_token(code, function->name, MAX_FUNCTION_NAME); | ||||
| /* get parameters */ | /* get parameters */ | ||||
| while (*code && *code != ')') { | while (*code && *code != ')') { | ||||
| /* test if it's an input or output */ | /* test if it's an input or output */ | ||||
| qual = FUNCTION_QUAL_IN; | qual = FUNCTION_QUAL_IN; | ||||
| ▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | # endif | ||||
| BLI_dynstr_free(ds); | BLI_dynstr_free(ds); | ||||
| return prototypes; | return prototypes; | ||||
| } | } | ||||
| #endif | #endif | ||||
| static GPUFunction *gpu_lookup_function(const char *name) | static GPUFunction *gpu_lookup_function(const char *name) | ||||
| { | { | ||||
| if (!FUNCTION_HASH) { | |||||
| FUNCTION_HASH = BLI_ghash_str_new("GPU_lookup_function gh"); | |||||
| gpu_parse_functions_string(FUNCTION_HASH, glsl_material_library); | |||||
| } | |||||
| return BLI_ghash_lookup(FUNCTION_HASH, (const void *)name); | return BLI_ghash_lookup(FUNCTION_HASH, (const void *)name); | ||||
| } | } | ||||
| void gpu_codegen_init(void) | void gpu_codegen_init(void) | ||||
| { | { | ||||
| GPU_code_generate_glsl_lib(); | GPU_code_generate_glsl_lib(); | ||||
| } | } | ||||
| void gpu_codegen_exit(void) | void gpu_codegen_exit(void) | ||||
| { | { | ||||
| extern Material defmaterial; /* render module abuse... */ | extern Material defmaterial; /* render module abuse... */ | ||||
| if (defmaterial.gpumaterial.first) { | if (defmaterial.gpumaterial.first) { | ||||
| GPU_material_free(&defmaterial.gpumaterial); | GPU_material_free(&defmaterial.gpumaterial); | ||||
| } | } | ||||
| if (FUNCTION_HASH) { | if (FUNCTION_HASH) { | ||||
| BLI_ghash_free(FUNCTION_HASH, NULL, MEM_freeN); | BLI_ghash_free(FUNCTION_HASH, NULL, MEM_freeN); | ||||
| FUNCTION_HASH = NULL; | FUNCTION_HASH = NULL; | ||||
| } | } | ||||
| GPU_shader_free_builtin_shaders(); | GPU_shader_free_builtin_shaders(); | ||||
| if (glsl_material_library) { | |||||
| MEM_freeN(glsl_material_library); | |||||
| glsl_material_library = NULL; | |||||
| } | |||||
| #if 0 | #if 0 | ||||
| if (FUNCTION_PROTOTYPES) { | if (FUNCTION_PROTOTYPES) { | ||||
| MEM_freeN(FUNCTION_PROTOTYPES); | MEM_freeN(FUNCTION_PROTOTYPES); | ||||
| FUNCTION_PROTOTYPES = NULL; | FUNCTION_PROTOTYPES = NULL; | ||||
| } | } | ||||
| if (FUNCTION_LIB) { | if (FUNCTION_LIB) { | ||||
| GPU_shader_free(FUNCTION_LIB); | GPU_shader_free(FUNCTION_LIB); | ||||
| FUNCTION_LIB = NULL; | FUNCTION_LIB = NULL; | ||||
| ▲ Show 20 Lines • Show All 965 Lines • ▼ Show 20 Lines | static char *code_generate_geometry(ListBase *nodes, const char *geom_code, const char *defines) | ||||
| code = BLI_dynstr_get_cstring(ds); | code = BLI_dynstr_get_cstring(ds); | ||||
| BLI_dynstr_free(ds); | BLI_dynstr_free(ds); | ||||
| return code; | return code; | ||||
| } | } | ||||
| void GPU_code_generate_glsl_lib(void) | void GPU_code_generate_glsl_lib(void) | ||||
| { | { | ||||
| DynStr *ds; | /* Only parse GLSL shader files once. */ | ||||
| if (FUNCTION_HASH) { | |||||
| /* only initialize the library once */ | |||||
| if (glsl_material_library) { | |||||
| return; | return; | ||||
| } | } | ||||
| ds = BLI_dynstr_new(); | FUNCTION_HASH = BLI_ghash_str_new("GPU_lookup_function gh"); | ||||
| for (int i = 0; gpu_material_libraries[i].code; i++) { | |||||
| BLI_dynstr_append(ds, datatoc_gpu_shader_material_glsl); | gpu_parse_material_library(FUNCTION_HASH, &gpu_material_libraries[i]); | ||||
| } | |||||
| glsl_material_library = BLI_dynstr_get_cstring(ds); | |||||
| BLI_dynstr_free(ds); | |||||
| } | } | ||||
| /* GPU pass binding/unbinding */ | /* GPU pass binding/unbinding */ | ||||
| GPUShader *GPU_pass_shader_get(GPUPass *pass) | GPUShader *GPU_pass_shader_get(GPUPass *pass) | ||||
| { | { | ||||
| return pass->shader; | return pass->shader; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 385 Lines • ▼ Show 20 Lines | |||||
| GPUNodeLink *GPU_builtin(eGPUBuiltin builtin) | GPUNodeLink *GPU_builtin(eGPUBuiltin builtin) | ||||
| { | { | ||||
| GPUNodeLink *link = GPU_node_link_create(); | GPUNodeLink *link = GPU_node_link_create(); | ||||
| link->link_type = GPU_NODE_LINK_BUILTIN; | link->link_type = GPU_NODE_LINK_BUILTIN; | ||||
| link->builtin = builtin; | link->builtin = builtin; | ||||
| return link; | return link; | ||||
| } | } | ||||
| static void gpu_material_use_library(GPUMaterial *material, GPUMaterialLibrary *library) | |||||
| { | |||||
| GSet *used_libraries = gpu_material_used_libraries(material); | |||||
| BLI_gset_add(used_libraries, library->code); | |||||
fclem: We could bypass dependencies if library is already in the set. Removing the need to do some… | |||||
| if (library->dependencies) { | |||||
| for (int i = 0; library->dependencies[i]; i++) { | |||||
| BLI_gset_add(used_libraries, library->dependencies[i]); | |||||
| } | |||||
| } | |||||
| } | |||||
| bool GPU_link(GPUMaterial *mat, const char *name, ...) | bool GPU_link(GPUMaterial *mat, const char *name, ...) | ||||
| { | { | ||||
| GPUNode *node; | GPUNode *node; | ||||
| GPUFunction *function; | GPUFunction *function; | ||||
| GPUNodeLink *link, **linkptr; | GPUNodeLink *link, **linkptr; | ||||
| va_list params; | va_list params; | ||||
| int i; | int i; | ||||
| function = gpu_lookup_function(name); | function = gpu_lookup_function(name); | ||||
| if (!function) { | if (!function) { | ||||
| fprintf(stderr, "GPU failed to find function %s\n", name); | fprintf(stderr, "GPU failed to find function %s\n", name); | ||||
| return false; | return false; | ||||
| } | } | ||||
| gpu_material_use_library(mat, function->library); | |||||
| node = GPU_node_begin(name); | node = GPU_node_begin(name); | ||||
| va_start(params, name); | va_start(params, name); | ||||
| for (i = 0; i < function->totparam; i++) { | for (i = 0; i < function->totparam; i++) { | ||||
| if (function->paramqual[i] != FUNCTION_QUAL_IN) { | if (function->paramqual[i] != FUNCTION_QUAL_IN) { | ||||
| linkptr = va_arg(params, GPUNodeLink **); | linkptr = va_arg(params, GPUNodeLink **); | ||||
| gpu_node_output(node, function->paramtype[i], linkptr); | gpu_node_output(node, function->paramtype[i], linkptr); | ||||
| } | } | ||||
| Show All 23 Lines | bool GPU_stack_link(GPUMaterial *material, | ||||
| int i, totin, totout; | int i, totin, totout; | ||||
| function = gpu_lookup_function(name); | function = gpu_lookup_function(name); | ||||
| if (!function) { | if (!function) { | ||||
| fprintf(stderr, "GPU failed to find function %s\n", name); | fprintf(stderr, "GPU failed to find function %s\n", name); | ||||
| return false; | return false; | ||||
| } | } | ||||
| gpu_material_use_library(material, function->library); | |||||
| node = GPU_node_begin(name); | node = GPU_node_begin(name); | ||||
| totin = 0; | totin = 0; | ||||
| totout = 0; | totout = 0; | ||||
| if (in) { | if (in) { | ||||
| for (i = 0; !in[i].end; i++) { | for (i = 0; !in[i].end; i++) { | ||||
| if (in[i].type != GPU_NONE) { | if (in[i].type != GPU_NONE) { | ||||
| gpu_node_input_socket(material, bnode, node, &in[i], i); | gpu_node_input_socket(material, bnode, node, &in[i], i); | ||||
| ▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| static bool gpu_pass_is_valid(GPUPass *pass) | static bool gpu_pass_is_valid(GPUPass *pass) | ||||
| { | { | ||||
| /* Shader is not null if compilation is successful. */ | /* Shader is not null if compilation is successful. */ | ||||
| return (pass->compiled == false || pass->shader != NULL); | return (pass->compiled == false || pass->shader != NULL); | ||||
| } | } | ||||
| static char *code_generate_material_library(GPUMaterial *material, const char *frag_lib) | |||||
| { | |||||
| DynStr *ds = BLI_dynstr_new(); | |||||
| if (frag_lib) { | |||||
| BLI_dynstr_append(ds, frag_lib); | |||||
| } | |||||
| GSet *used_libraries = gpu_material_used_libraries(material); | |||||
| /* Add library code in order, for dependencies. */ | |||||
| for (int i = 0; gpu_material_libraries[i].code; 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; | |||||
| } | |||||
| GPUPass *GPU_generate_pass(GPUMaterial *material, | GPUPass *GPU_generate_pass(GPUMaterial *material, | ||||
| GPUNodeLink *frag_outlink, | GPUNodeLink *frag_outlink, | ||||
| struct GPUVertAttrLayers *attrs, | struct GPUVertAttrLayers *attrs, | ||||
| ListBase *nodes, | ListBase *nodes, | ||||
| int *builtins, | int *builtins, | ||||
| const char *vert_code, | const char *vert_code, | ||||
| const char *geom_code, | const char *geom_code, | ||||
| const char *frag_lib, | const char *frag_lib, | ||||
| Show All 22 Lines | if (!gpu_pass_is_valid(pass_hash)) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| pass_hash->refcount += 1; | pass_hash->refcount += 1; | ||||
| return pass_hash; | return pass_hash; | ||||
| } | } | ||||
| /* Either the shader is not compiled or there is a hash collision... | /* Either the shader is not compiled or there is a hash collision... | ||||
| * continue generating the shader strings. */ | * continue generating the shader strings. */ | ||||
| char *tmp = BLI_strdupcat(frag_lib, glsl_material_library); | char *tmp = code_generate_material_library(material, frag_lib); | ||||
| geometrycode = code_generate_geometry(nodes, geom_code, defines); | geometrycode = code_generate_geometry(nodes, geom_code, defines); | ||||
| vertexcode = code_generate_vertex(nodes, vert_code, (geometrycode != NULL)); | vertexcode = code_generate_vertex(nodes, vert_code, (geometrycode != NULL)); | ||||
| fragmentcode = BLI_strdupcat(tmp, fragmentgen); | fragmentcode = BLI_strdupcat(tmp, fragmentgen); | ||||
| MEM_freeN(fragmentgen); | MEM_freeN(fragmentgen); | ||||
| MEM_freeN(tmp); | MEM_freeN(tmp); | ||||
| ▲ Show 20 Lines • Show All 244 Lines • Show Last 20 Lines | |||||
We could bypass dependencies if library is already in the set. Removing the need to do some more gset lookups.