diff options
-rw-r--r-- | source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl | 2 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager_data.c | 13 | ||||
-rw-r--r-- | source/blender/editors/space_view3d/view3d_draw.c | 3 | ||||
-rw-r--r-- | source/blender/gpu/GPU_material.h | 4 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_codegen.c | 233 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_codegen.h | 22 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_material.c | 23 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_init_exit.c | 2 |
8 files changed, 201 insertions, 101 deletions
diff --git a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl index d9e12bcc4bd..5ecf6323255 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl @@ -23,8 +23,6 @@ layout(location = 0) out vec4 FragColor; layout(location = 1) out vec4 sssColor; #endif -uniform mat4 ProjectionMatrix; - float get_view_z_from_depth(float depth) { if (ProjectionMatrix[3][3] == 0.0) { diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 09ed1443701..4648c321f26 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -582,15 +582,14 @@ static DRWShadingGroup *drw_shgroup_material_create_ex(GPUPass *gpupass, DRWPass return grp; } -static DRWShadingGroup *drw_shgroup_material_inputs( - DRWShadingGroup *grp, struct GPUMaterial *material, GPUPass *gpupass) +static DRWShadingGroup *drw_shgroup_material_inputs(DRWShadingGroup *grp, struct GPUMaterial *material) { /* TODO : Ideally we should not convert. But since the whole codegen * is relying on GPUPass we keep it as is for now. */ - /* Converting dynamic GPUInput to DRWUniform */ - ListBase *inputs = &gpupass->inputs; + ListBase *inputs = GPU_material_get_inputs(material); + /* Converting dynamic GPUInput to DRWUniform */ for (GPUInput *input = inputs->first; input; input = input->next) { /* Textures */ if (input->ima) { @@ -656,7 +655,7 @@ DRWShadingGroup *DRW_shgroup_material_create( if (shgroup) { drw_interface_init(shgroup, GPU_pass_shader(gpupass)); - drw_shgroup_material_inputs(shgroup, material, gpupass); + drw_shgroup_material_inputs(shgroup, material); } return shgroup; @@ -673,7 +672,7 @@ DRWShadingGroup *DRW_shgroup_material_instance_create( shgroup->instance_geom = geom; drw_call_calc_orco(ob->data, shgroup->instance_orcofac); drw_interface_instance_init(shgroup, GPU_pass_shader(gpupass), geom, format); - drw_shgroup_material_inputs(shgroup, material, gpupass); + drw_shgroup_material_inputs(shgroup, material); } return shgroup; @@ -693,7 +692,7 @@ DRWShadingGroup *DRW_shgroup_material_empty_tri_batch_create( drw_interface_init(shgroup, GPU_pass_shader(gpupass)); shgroup->type = DRW_SHG_TRIANGLE_BATCH; shgroup->instance_count = tri_count * 3; - drw_shgroup_material_inputs(shgroup, material, gpupass); + drw_shgroup_material_inputs(shgroup, material); } return shgroup; diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 02d45be5aa4..b14128ab400 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1922,6 +1922,9 @@ void view3d_main_region_draw(const bContext *C, ARegion *ar) BLI_rcti_translate(&rect, -ar->winrct.xmin, -ar->winrct.ymin); GPU_viewport_draw_to_screen(rv3d->viewport, &rect); + GPU_free_images_old(); + GPU_pass_cache_garbage_collect(); + v3d->flag |= V3D_INVALID_BACKBUF; } diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 6df465ac753..1d2b234e7f3 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -270,6 +270,7 @@ bool GPU_material_bound(GPUMaterial *material); struct Scene *GPU_material_scene(GPUMaterial *material); GPUMatType GPU_Material_get_type(GPUMaterial *material); struct GPUPass *GPU_material_get_pass(GPUMaterial *material); +struct ListBase *GPU_material_get_inputs(GPUMaterial *material); GPUMaterialStatus GPU_material_status(GPUMaterial *mat); struct GPUUniformBuffer *GPU_material_get_uniform_buffer(GPUMaterial *material); @@ -377,6 +378,9 @@ void GPU_material_update_fvar_offset(GPUMaterial *gpu_material, struct DerivedMesh *dm); #endif +void GPU_pass_cache_garbage_collect(void); +void GPU_pass_cache_free(void); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index b6f92497063..7cd403d2721 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -39,10 +39,14 @@ #include "DNA_node_types.h" #include "BLI_blenlib.h" +#include "BLI_hash_mm2a.h" +#include "BLI_linklist.h" #include "BLI_utildefines.h" #include "BLI_dynstr.h" #include "BLI_ghash.h" +#include "PIL_time.h" + #include "GPU_extensions.h" #include "GPU_glew.h" #include "GPU_material.h" @@ -64,6 +68,50 @@ 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. + **/ + +static LinkNode *pass_cache = NULL; /* GPUPass */ + +static uint32_t gpu_pass_hash(const char *vert, const char *geom, const char *frag, const char *defs) +{ + BLI_HashMurmur2A hm2a; + BLI_hash_mm2a_init(&hm2a, 0); + BLI_hash_mm2a_add(&hm2a, (unsigned char *)frag, strlen(frag)); + BLI_hash_mm2a_add(&hm2a, (unsigned char *)vert, strlen(vert)); + if (defs) + BLI_hash_mm2a_add(&hm2a, (unsigned char *)defs, strlen(defs)); + if (geom) + BLI_hash_mm2a_add(&hm2a, (unsigned char *)geom, strlen(geom)); + + return BLI_hash_mm2a_end(&hm2a); +} + +/* Search by hash then by exact string match. */ +static GPUPass *gpu_pass_cache_lookup( + const char *vert, const char *geom, const char *frag, const char *defs, uint32_t hash) +{ + for (LinkNode *ln = pass_cache; ln; ln = ln->next) { + GPUPass *pass = (GPUPass *)ln->link; + if (pass->hash == hash) { + /* Note: Could be made faster if that becomes a real bottleneck. */ + 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)) + { + return pass; + } + } + } + return NULL; +} + +/* -------------------- GPU Codegen ------------------ */ /* type definitions and constants */ @@ -1175,15 +1223,13 @@ GPUShader *GPU_pass_shader(GPUPass *pass) return pass->shader; } -static void gpu_nodes_extract_dynamic_inputs_new(GPUPass *pass, ListBase *nodes) +static void gpu_nodes_extract_dynamic_inputs_new(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; @@ -1236,15 +1282,13 @@ static void gpu_nodes_extract_dynamic_inputs_new(GPUPass *pass, ListBase *nodes) GPU_shader_unbind(); } -static void gpu_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes) +static 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; @@ -1322,11 +1366,10 @@ static void gpu_nodes_extract_dynamic_inputs(GPUPass *pass, ListBase *nodes) GPU_shader_unbind(); } -void GPU_pass_bind(GPUPass *pass, double time, int mipmap) +void GPU_pass_bind(GPUPass *pass, ListBase *inputs, double time, int mipmap) { GPUInput *input; GPUShader *shader = pass->shader; - ListBase *inputs = &pass->inputs; if (!shader) return; @@ -1351,11 +1394,10 @@ void GPU_pass_bind(GPUPass *pass, double time, int mipmap) } } -void GPU_pass_update_uniforms(GPUPass *pass) +void GPU_pass_update_uniforms(GPUPass *pass, ListBase *inputs) { GPUInput *input; GPUShader *shader = pass->shader; - ListBase *inputs = &pass->inputs; if (!shader) return; @@ -1376,11 +1418,10 @@ void GPU_pass_update_uniforms(GPUPass *pass) } } -void GPU_pass_unbind(GPUPass *pass) +void GPU_pass_unbind(GPUPass *pass, ListBase *inputs) { GPUInput *input; GPUShader *shader = pass->shader; - ListBase *inputs = &pass->inputs; if (!shader) return; @@ -1679,7 +1720,7 @@ 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; @@ -1697,7 +1738,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) { @@ -2072,78 +2113,83 @@ void GPU_nodes_prune(ListBase *nodes, GPUNodeLink *outlink) } GPUPass *GPU_generate_pass_new( - struct GPUMaterial *material, - ListBase *nodes, struct GPUNodeLink *frag_outlink, - GPUVertexAttribs *attribs, + GPUMaterial *material, + GPUNodeLink *frag_outlink, struct GPUVertexAttribs *attribs, + ListBase *nodes, ListBase *inputs, const char *vert_code, const char *geom_code, const char *frag_lib, const char *defines) { + char *vertexcode, *geometrycode, *fragmentcode; GPUShader *shader; GPUPass *pass; - char *vertexgen, *fragmentgen, *tmp; - char *vertexcode, *geometrycode, *fragmentcode; /* prune unused nodes */ GPU_nodes_prune(nodes, frag_outlink); GPU_nodes_get_vertex_attributes(nodes, attribs); - /* generate code and compile with opengl */ - fragmentgen = code_generate_fragment(material, nodes, frag_outlink->output, true); - vertexgen = code_generate_vertex_new(nodes, vert_code, (geom_code != NULL)); + /* generate code */ + char *fragmentgen = code_generate_fragment(material, nodes, frag_outlink->output, true); + char *tmp = BLI_strdupcat(frag_lib, glsl_material_library); - tmp = BLI_strdupcat(frag_lib, glsl_material_library); + vertexcode = code_generate_vertex_new(nodes, vert_code, (geom_code != NULL)); + geometrycode = (geom_code) ? code_generate_geometry_new(nodes, geom_code) : NULL; fragmentcode = BLI_strdupcat(tmp, fragmentgen); - vertexcode = BLI_strdup(vertexgen); - - if (geom_code) { - geometrycode = code_generate_geometry_new(nodes, geom_code); - } - else { - geometrycode = NULL; - } - - shader = GPU_shader_create(vertexcode, - fragmentcode, - geometrycode, - NULL, - defines); + MEM_freeN(fragmentgen); MEM_freeN(tmp); - /* failed? */ + /* Cache lookup: Reuse shaders already compiled */ + uint32_t hash = gpu_pass_hash(vertexcode, geometrycode, fragmentcode, defines); + pass = gpu_pass_cache_lookup(vertexcode, geometrycode, fragmentcode, defines, hash); + if (pass) { + /* Cache hit. Reuse the same GPUPass and GPUShader. */ + shader = pass->shader; + pass->refcount += 1; + + MEM_SAFE_FREE(vertexcode); + MEM_SAFE_FREE(fragmentcode); + MEM_SAFE_FREE(geometrycode); + } + else { + /* Cache miss. (Re)compile the shader. */ + shader = GPU_shader_create(vertexcode, + fragmentcode, + geometrycode, + NULL, + defines); + + /* 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 = shader; + pass->refcount = 1; + pass->hash = hash; + pass->vertexcode = vertexcode; + pass->fragmentcode = fragmentcode; + pass->geometrycode = geometrycode; + pass->libcode = glsl_material_library; + pass->defines = (defines) ? BLI_strdup(defines) : NULL; + + BLI_linklist_prepend(&pass_cache, pass); + } + + /* did compilation failed ? */ if (!shader) { - if (fragmentcode) - MEM_freeN(fragmentcode); - if (vertexcode) - MEM_freeN(vertexcode); - if (geometrycode) - MEM_freeN(geometrycode); - MEM_freeN(fragmentgen); - MEM_freeN(vertexgen); gpu_nodes_free(nodes); + /* Pass will not be used. Don't increment refcount. */ + pass->refcount--; return NULL; } - - /* create pass */ - pass = MEM_callocN(sizeof(GPUPass), "GPUPass"); - pass->shader = shader; - pass->fragmentcode = fragmentcode; - pass->geometrycode = geometrycode; - pass->vertexcode = vertexcode; - pass->libcode = glsl_material_library; - - /* extract dynamic inputs and throw away nodes */ - gpu_nodes_extract_dynamic_inputs_new(pass, nodes); - - MEM_freeN(fragmentgen); - MEM_freeN(vertexgen); - - return pass; + else { + gpu_nodes_extract_dynamic_inputs_new(shader, inputs, nodes); + return pass; + } } +/* TODO(fclem) Remove for 2.8 */ GPUPass *GPU_generate_pass( - ListBase *nodes, GPUNodeLink *outlink, + ListBase *nodes, ListBase *inputs, GPUNodeLink *outlink, GPUVertexAttribs *attribs, int *builtins, const GPUMatType type, const char *UNUSED(name), const bool use_opensubdiv, @@ -2199,30 +2245,36 @@ GPUPass *GPU_generate_pass( /* create pass */ pass = MEM_callocN(sizeof(GPUPass), "GPUPass"); - + pass->refcount = 1; pass->shader = shader; pass->fragmentcode = fragmentcode; pass->geometrycode = geometrycode; pass->vertexcode = vertexcode; pass->libcode = glsl_material_library; + BLI_linklist_prepend(&pass_cache, pass); + /* extract dynamic inputs and throw away nodes */ - gpu_nodes_extract_dynamic_inputs(pass, nodes); + gpu_nodes_extract_dynamic_inputs(shader, inputs, nodes); gpu_nodes_free(nodes); return pass; } -void GPU_pass_free(GPUPass *pass) +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); 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); + MEM_SAFE_FREE(pass->fragmentcode); + MEM_SAFE_FREE(pass->geometrycode); + MEM_SAFE_FREE(pass->vertexcode); + MEM_SAFE_FREE(pass->defines); MEM_freeN(pass); } @@ -2231,3 +2283,34 @@ 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; + + LinkNode *next, **prev_ln = &pass_cache; + for (LinkNode *ln = pass_cache; ln; ln = next) { + GPUPass *pass = (GPUPass *)ln->link; + next = ln->next; + if (pass->refcount == 0) { + gpu_pass_free(pass); + /* Remove from list */ + MEM_freeN(ln); + *prev_ln = next; + } + else { + prev_ln = &ln->next; + } + } +} + +void GPU_pass_cache_free(void) +{ + BLI_linklist_free(pass_cache, (LinkNodeFreeFP)gpu_pass_free); +} diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index 0f8218c9c15..dab0bc3f8b1 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -157,25 +157,27 @@ typedef struct GPUInput { } GPUInput; struct GPUPass { - ListBase inputs; struct GPUShader *shader; char *fragmentcode; char *geometrycode; char *vertexcode; + char *defines; const char *libcode; + unsigned int refcount; /* Orphaned GPUPasses gets freed by the garbage collector. */ + uint32_t hash; /* Identity hash generated from all GLSL code. */ }; typedef struct GPUPass GPUPass; GPUPass *GPU_generate_pass_new( - struct GPUMaterial *material, - ListBase *nodes, struct GPUNodeLink *frag_outlink, - struct GPUVertexAttribs *attribs, + GPUMaterial *material, + GPUNodeLink *frag_outlink, struct GPUVertexAttribs *attribs, + ListBase *nodes, ListBase *inputs, const char *vert_code, const char *geom_code, const char *frag_lib, const char *defines); GPUPass *GPU_generate_pass( - ListBase *nodes, struct GPUNodeLink *outlink, + ListBase *nodes, ListBase *inputs, struct GPUNodeLink *outlink, struct GPUVertexAttribs *attribs, int *builtin, const GPUMatType type, const char *name, const bool use_opensubdiv, @@ -186,13 +188,15 @@ struct GPUShader *GPU_pass_shader(GPUPass *pass); void GPU_nodes_get_vertex_attributes(ListBase *nodes, struct GPUVertexAttribs *attribs); void GPU_nodes_prune(ListBase *nodes, struct GPUNodeLink *outlink); -void GPU_pass_bind(GPUPass *pass, double time, int mipmap); -void GPU_pass_update_uniforms(GPUPass *pass); -void GPU_pass_unbind(GPUPass *pass); +void GPU_pass_bind(GPUPass *pass, ListBase *inputs, double time, int mipmap); +void GPU_pass_update_uniforms(GPUPass *pass, ListBase *inputs); +void GPU_pass_unbind(GPUPass *pass, ListBase *inputs); -void GPU_pass_free(GPUPass *pass); +void GPU_pass_release(GPUPass *pass); void GPU_pass_free_nodes(ListBase *nodes); +void GPU_inputs_free(ListBase *inputs); + void gpu_codegen_init(void); void gpu_codegen_exit(void); diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 2b92f285218..6eeac4236dc 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -116,6 +116,7 @@ struct GPUMaterial { /* for binding the material */ GPUPass *pass; + ListBase inputs; /* GPUInput */ GPUVertexAttribs attribs; int builtins; int alpha, obcolalpha; @@ -221,7 +222,7 @@ static int gpu_material_construct_end(GPUMaterial *material, const char *passnam { if (material->outlink) { GPUNodeLink *outlink = material->outlink; - material->pass = GPU_generate_pass(&material->nodes, outlink, + material->pass = GPU_generate_pass(&material->nodes, &material->inputs, outlink, &material->attribs, &material->builtins, material->type, passname, material->is_opensubdiv, @@ -282,9 +283,10 @@ void GPU_material_free(ListBase *gpumaterial) DRW_deferred_shader_remove(material); GPU_pass_free_nodes(&material->nodes); + GPU_inputs_free(&material->inputs); if (material->pass) - GPU_pass_free(material->pass); + GPU_pass_release(material->pass); if (material->ubo != NULL) { GPU_uniformbuffer_free(material->ubo); @@ -356,7 +358,7 @@ void GPU_material_bind( } /* note material must be bound before setting uniforms */ - GPU_pass_bind(material->pass, time, mipmap); + GPU_pass_bind(material->pass, &material->inputs, time, mipmap); /* handle per material built-ins */ if (material->builtins & GPU_VIEW_MATRIX) { @@ -376,7 +378,7 @@ void GPU_material_bind( } } - GPU_pass_update_uniforms(material->pass); + GPU_pass_update_uniforms(material->pass, &material->inputs); material->bound = 1; } @@ -449,7 +451,7 @@ void GPU_material_unbind(GPUMaterial *material) { if (material->pass) { material->bound = 0; - GPU_pass_unbind(material->pass); + GPU_pass_unbind(material->pass, &material->inputs); } } @@ -473,6 +475,11 @@ GPUPass *GPU_material_get_pass(GPUMaterial *material) return material->pass; } +ListBase *GPU_material_get_inputs(GPUMaterial *material) +{ + return &material->inputs; +} + GPUUniformBuffer *GPU_material_get_uniform_buffer(GPUMaterial *material) { return material->ubo; @@ -2562,7 +2569,7 @@ void GPU_material_generate_pass( BLI_assert(mat->pass == NULL); /* Only run once! */ if (mat->outlink) { mat->pass = GPU_generate_pass_new( - mat, &mat->nodes, mat->outlink, &mat->attribs, vert_code, geom_code, frag_lib, defines); + mat, mat->outlink, &mat->attribs, &mat->nodes, &mat->inputs, vert_code, geom_code, frag_lib, defines); mat->status = (mat->pass) ? GPU_MAT_SUCCESS : GPU_MAT_FAILED; } } @@ -2729,7 +2736,7 @@ GPUShaderExport *GPU_shader_export(struct Scene *scene, struct Material *ma) if (pass && pass->fragmentcode && pass->vertexcode) { shader = MEM_callocN(sizeof(GPUShaderExport), "GPUShaderExport"); - for (input = pass->inputs.first; input; input = input->next) { + for (input = mat->inputs.first; input; input = input->next) { GPUInputUniform *uniform = MEM_callocN(sizeof(GPUInputUniform), "GPUInputUniform"); if (input->ima) { @@ -2906,7 +2913,7 @@ void GPU_material_update_fvar_offset(GPUMaterial *gpu_material, { GPUPass *pass = gpu_material->pass; GPUShader *shader = (pass != NULL ? pass->shader : NULL); - ListBase *inputs = (pass != NULL ? &pass->inputs : NULL); + ListBase *inputs = (pass != NULL ? &gpu_material->inputs : NULL); GPUInput *input; if (shader == NULL) { diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index a2f5f1b1e10..98ac3eebb57 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -116,6 +116,7 @@ #include "BLF_api.h" #include "BLT_lang.h" +#include "GPU_material.h" #include "GPU_buffers.h" #include "GPU_draw.h" #include "GPU_init_exit.h" @@ -553,6 +554,7 @@ void WM_exit_ext(bContext *C, const bool do_python) BLF_exit(); if (!G.background) { + GPU_pass_cache_free(); DRW_opengl_context_destroy(); } |