From c354ea0ef12a2b214456b39832a141ed22479734 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 21 Jul 2009 20:28:32 +0000 Subject: 2.5: Render This adds a RenderEngine type to RNA, which can be subclassed in python (c++ will follow once we support subclassing there). It's very basic, but plugs into the pipeline nicely. Two example scripts: http://www.pasteall.org/6635/python http://www.pasteall.org/6636/python Issues: * Render runs in a separate thread, and there is unrestricted access, so it's possible to crash blender with unsafe access. * Save buffers and full sample are not supported yet. --- source/blender/editors/screen/screen_ops.c | 25 +-- source/blender/makesdna/DNA_scene_types.h | 4 +- source/blender/makesrna/intern/rna_render.c | 140 +++++++++++++++- source/blender/makesrna/intern/rna_scene.c | 64 ++++++++ source/blender/render/CMakeLists.txt | 1 + source/blender/render/SConscript | 2 +- source/blender/render/extern/include/RE_pipeline.h | 46 +++++- source/blender/render/intern/source/Makefile | 1 + source/blender/render/intern/source/pipeline.c | 176 ++++++++++++++++++++- source/blender/windowmanager/intern/wm_init_exit.c | 1 + 10 files changed, 442 insertions(+), 18 deletions(-) (limited to 'source') diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index e13e27412b8..4a42d1ed369 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -2486,20 +2486,25 @@ static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str) else if(scene->r.scemode & R_SINGLE_LAYER) spos+= sprintf(spos, "Single Layer | "); - spos+= sprintf(spos, "Fra:%d Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface); - if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo); - if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand); - spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory); - - if(rs->curfield) - spos+= sprintf(spos, "Field %d ", rs->curfield); - if(rs->curblur) - spos+= sprintf(spos, "Blur %d ", rs->curblur); + if(rs->statstr) { + spos+= sprintf(spos, "%s ", rs->statstr); + } + else { + spos+= sprintf(spos, "Fra:%d Ve:%d Fa:%d ", (scene->r.cfra), rs->totvert, rs->totface); + if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo); + if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand); + spos+= sprintf(spos, "La:%d Mem:%.2fM (%.2fM) ", rs->totlamp, megs_used_memory, mmap_used_memory); + + if(rs->curfield) + spos+= sprintf(spos, "Field %d ", rs->curfield); + if(rs->curblur) + spos+= sprintf(spos, "Blur %d ", rs->curblur); + } BLI_timestr(rs->lastframetime, info_time_str); spos+= sprintf(spos, "Time:%s ", info_time_str); - if(rs->infostr) + if(rs->infostr && rs->infostr[0]) spos+= sprintf(spos, "| %s ", rs->infostr); /* very weak... but 512 characters is quite safe */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 8e517ad7760..60176dd5e6e 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -235,7 +235,7 @@ typedef struct RenderData { */ int mode; - /* render engine, octree resolution */ + /* render engine (deprecated), octree resolution */ short renderer, ocres; /** @@ -334,6 +334,8 @@ typedef struct RenderData { float pad2; // XXX deprecated since 2.5 struct Text *dometext; // XXX deprecated since 2.5 + /* render engine */ + char engine[32]; } RenderData; /* control render convert and shading engine */ diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 7eacb4079e5..0ed4e9fb80f 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -34,7 +34,6 @@ #include "WM_types.h" #include "RE_pipeline.h" -#include "RE_render_ext.h" #ifdef RNA_RUNTIME @@ -47,6 +46,82 @@ #include "WM_api.h" +/* RenderEngine */ + +static void engine_render(RenderEngine *engine, struct Scene *scene) +{ + PointerRNA ptr; + ParameterList list; + FunctionRNA *func; + + RNA_pointer_create(NULL, engine->type->ext.srna, engine, &ptr); + func= RNA_struct_find_function(&ptr, "render"); + + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "scene", &scene); + engine->type->ext.call(&ptr, func, &list); + + RNA_parameter_list_free(&list); +} + +static void rna_RenderEngine_unregister(const bContext *C, StructRNA *type) +{ + RenderEngineType *et= RNA_struct_blender_type_get(type); + + if(!et) + return; + + BLI_freelinkN(&R_engines, et); + RNA_struct_free(&BLENDER_RNA, type); +} + +static StructRNA *rna_RenderEngine_register(const bContext *C, ReportList *reports, void *data, StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free) +{ + RenderEngineType *et, dummyet = {0}; + RenderEngine dummyengine= {0}; + PointerRNA dummyptr; + int have_function[1]; + + /* setup dummy engine & engine type to store static properties in */ + dummyengine.type= &dummyet; + RNA_pointer_create(NULL, &RNA_RenderEngine, &dummyengine, &dummyptr); + + /* validate the python class */ + if(validate(&dummyptr, data, have_function) != 0) + return NULL; + + /* check if we have registered this engine type before, and remove it */ + for(et=R_engines.first; et; et=et->next) { + if(strcmp(et->idname, dummyet.idname) == 0) { + if(et->ext.srna) + rna_RenderEngine_unregister(C, et->ext.srna); + break; + } + } + + /* create a new engine type */ + et= MEM_callocN(sizeof(RenderEngineType), "python buttons engine"); + memcpy(et, &dummyet, sizeof(dummyet)); + + et->ext.srna= RNA_def_struct(&BLENDER_RNA, et->idname, "RenderEngine"); + et->ext.data= data; + et->ext.call= call; + et->ext.free= free; + RNA_struct_blender_type_set(et->ext.srna, et); + + et->render= (have_function[0])? engine_render: NULL; + + BLI_addtail(&R_engines, et); + + return et->ext.srna; +} + +static StructRNA* rna_RenderEngine_refine(PointerRNA *ptr) +{ + RenderEngine *engine= (RenderEngine*)ptr->data; + return (engine->type && engine->type->ext.srna)? engine->type->ext.srna: &RNA_RenderEngine; +} + static void rna_RenderResult_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { RenderResult *rr= (RenderResult*)ptr->data; @@ -96,6 +171,68 @@ static int rna_RenderPass_rect_length(PointerRNA *ptr) #else // RNA_RUNTIME +static void rna_def_render_engine(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + FunctionRNA *func; + + srna= RNA_def_struct(brna, "RenderEngine", NULL); + RNA_def_struct_sdna(srna, "RenderEngine"); + RNA_def_struct_ui_text(srna, "Render Engine", "Render engine."); + RNA_def_struct_refine_func(srna, "rna_RenderEngine_refine"); + RNA_def_struct_register_funcs(srna, "rna_RenderEngine_register", "rna_RenderEngine_unregister"); + + /* render */ + func= RNA_def_function(srna, "render", NULL); + RNA_def_function_ui_description(func, "Render scene into an image."); + RNA_def_function_flag(func, FUNC_REGISTER); + RNA_def_pointer(func, "scene", "Scene", "", ""); + + func= RNA_def_function(srna, "begin_result", "RE_engine_begin_result"); + prop= RNA_def_int(func, "x", 0, 0, INT_MAX, "X", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop= RNA_def_int(func, "y", 0, 0, INT_MAX, "Y", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop= RNA_def_int(func, "w", 0, 0, INT_MAX, "Width", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop= RNA_def_int(func, "h", 0, 0, INT_MAX, "Height", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop= RNA_def_pointer(func, "result", "RenderResult", "Result", ""); + RNA_def_function_return(func, prop); + + func= RNA_def_function(srna, "update_result", "RE_engine_update_result"); + prop= RNA_def_pointer(func, "result", "RenderResult", "Result", ""); + RNA_def_property_flag(prop, PROP_REQUIRED); + + func= RNA_def_function(srna, "end_result", "RE_engine_end_result"); + prop= RNA_def_pointer(func, "result", "RenderResult", "Result", ""); + RNA_def_property_flag(prop, PROP_REQUIRED); + + func= RNA_def_function(srna, "test_break", "RE_engine_test_break"); + prop= RNA_def_boolean(func, "do_break", 0, "Break", ""); + RNA_def_function_return(func, prop); + + func= RNA_def_function(srna, "update_stats", "RE_engine_update_stats"); + prop= RNA_def_string(func, "stats", "", 0, "Stats", ""); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop= RNA_def_string(func, "info", "", 0, "Info", ""); + RNA_def_property_flag(prop, PROP_REQUIRED); + + /* registration */ + RNA_define_verify_sdna(0); + + prop= RNA_def_property(srna, "idname", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "type->idname"); + RNA_def_property_flag(prop, PROP_REGISTER); + + prop= RNA_def_property(srna, "label", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "type->name"); + RNA_def_property_flag(prop, PROP_REGISTER); + + RNA_define_verify_sdna(1); +} + static void rna_def_render_result(BlenderRNA *brna) { StructRNA *srna; @@ -205,6 +342,7 @@ static void rna_def_render_pass(BlenderRNA *brna) void RNA_def_render(BlenderRNA *brna) { + rna_def_render_engine(brna); rna_def_render_result(brna); rna_def_render_layer(brna); rna_def_render_pass(brna); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index f23a14d99fb..bd23fc9e1f6 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -60,6 +60,8 @@ EnumPropertyItem prop_mode_items[] ={ #include "BLI_threads.h" +#include "RE_pipeline.h" + PointerRNA rna_Scene_objects_get(CollectionPropertyIterator *iter) { ListBaseIterator *internal= iter->internal; @@ -163,6 +165,48 @@ static void rna_SceneRenderData_active_layer_index_range(PointerRNA *ptr, int *m *max= MAX2(0, *max); } +static void rna_SceneRenderData_engine_set(PointerRNA *ptr, int value) +{ + RenderData *rd= (RenderData*)ptr->data; + RenderEngineType *type= BLI_findlink(&R_engines, value); + + if(type) + BLI_strncpy(rd->engine, type->idname, sizeof(rd->engine)); +} + +static EnumPropertyItem *rna_SceneRenderData_engine_itemf(bContext *C, PointerRNA *ptr, int *free) +{ + RenderEngineType *type; + EnumPropertyItem *item= NULL; + EnumPropertyItem tmp = {0, "", 0, "", ""}; + int a=0, totitem= 0; + + for(type=R_engines.first; type; type=type->next, a++) { + tmp.value= a; + tmp.identifier= type->idname; + tmp.name= type->name; + RNA_enum_item_add(&item, &totitem, &tmp); + } + + RNA_enum_item_end(&item, &totitem); + *free= 1; + + return item; +} + +static int rna_SceneRenderData_engine_get(PointerRNA *ptr) +{ + RenderData *rd= (RenderData*)ptr->data; + RenderEngineType *type; + int a= 0; + + for(type=R_engines.first; type; type=type->next, a++) + if(strcmp(type->idname, rd->engine) == 0) + return a; + + return 0; +} + static void rna_SceneRenderLayer_name_set(PointerRNA *ptr, const char *value) { Scene *scene= (Scene*)ptr->id.data; @@ -183,6 +227,11 @@ static void rna_SceneRenderLayer_name_set(PointerRNA *ptr, const char *value) } } +static int rna_SceneRenderData_multiple_engines_get(PointerRNA *ptr) +{ + return (BLI_countlist(&R_engines) > 1); +} + static void rna_SceneRenderLayer_layer_set(PointerRNA *ptr, const int *values) { SceneRenderLayer *rl= (SceneRenderLayer*)ptr->data; @@ -997,6 +1046,10 @@ static void rna_def_scene_render_data(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}}; #endif + static EnumPropertyItem engine_items[] = { + {0, "BLENDER", 0, "Blender", ""}, + {0, NULL, 0, NULL, NULL}}; + srna= RNA_def_struct(brna, "SceneRenderData", NULL); RNA_def_struct_sdna(srna, "RenderData"); RNA_def_struct_nested(brna, srna, "Scene"); @@ -1520,6 +1573,17 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_int_funcs(prop, "rna_SceneRenderData_active_layer_index_get", "rna_SceneRenderData_active_layer_index_set", "rna_SceneRenderData_active_layer_index_range"); RNA_def_property_ui_text(prop, "Active Layer Index", "Active index in render layer array."); RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL); + + /* engine */ + prop= RNA_def_property(srna, "engine", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, engine_items); + RNA_def_property_enum_funcs(prop, "rna_SceneRenderData_engine_get", "rna_SceneRenderData_engine_set", "rna_SceneRenderData_engine_itemf"); + RNA_def_property_ui_text(prop, "Engine", "Engine to use for rendering."); + + prop= RNA_def_property(srna, "multiple_engines", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_SceneRenderData_multiple_engines_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Multiple Engine", "More than one rendering engine is available."); } void RNA_def_scene(BlenderRNA *brna) diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt index 8ba7dc123e7..411bd61c3ff 100644 --- a/source/blender/render/CMakeLists.txt +++ b/source/blender/render/CMakeLists.txt @@ -30,6 +30,7 @@ SET(INC intern/include ../../../intern/guardedalloc ../blenlib ../makesdna extern/include ../blenkernel ../radiosity/extern/include ../imbuf ../quicktime ../include ../../kernel/gen_messaging ../blenloader + ../makesrna ) IF(WITH_OPENEXR) diff --git a/source/blender/render/SConscript b/source/blender/render/SConscript index 173f5db8482..dbd9f65254b 100644 --- a/source/blender/render/SConscript +++ b/source/blender/render/SConscript @@ -4,7 +4,7 @@ Import ('env') cflags='' sources = env.Glob('intern/source/*.c') -incs = 'intern/include #/intern/guardedalloc ../blenlib ../makesdna' +incs = 'intern/include #/intern/guardedalloc ../blenlib ../makesdna ../makesrna' incs += ' extern/include ../blenkernel ../radiosity/extern/include ../imbuf' incs += ' ../include ../blenloader' diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index e0e78739d39..643a381c54f 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -33,12 +33,17 @@ #include "DNA_listBase.h" #include "DNA_vec_types.h" #include "BKE_utildefines.h" +#include "RNA_types.h" -struct Scene; -struct RenderData; +struct bNodeTree; +struct Image; struct NodeBlurData; struct Object; -struct bNodeTree; +struct RenderData; +struct RenderEngine; +struct RenderEngineType; +struct RenderResult; +struct Scene; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* this include is what is exposed of render to outside world */ @@ -130,7 +135,7 @@ typedef struct RenderStats { int totface, totvert, totstrand, tothalo, totlamp, totpart; short curfield, curblur, curpart, partsdone, convertdone; double starttime, lastframetime; - char *infostr, scenename[32]; + char *infostr, *statstr, scenename[32]; } RenderStats; @@ -233,5 +238,38 @@ void RE_DataBase_GetView(struct Render *re, float mat[][4]); void RE_GetCameraWindow(struct Render *re, struct Object *camera, int frame, float mat[][4]); struct Scene *RE_GetScene(struct Render *re); +/* External Engine */ + + +extern ListBase R_engines; + +typedef struct RenderEngineType { + struct RenderEngineType *next, *prev; + + /* type info */ + char idname[32]; + char name[32]; + + void (*render)(struct RenderEngine *engine, struct Scene *scene); + + /* RNA integration */ + ExtensionRNA ext; +} RenderEngineType; + +typedef struct RenderEngine { + RenderEngineType *type; + struct Render *re; + ListBase fullresult; +} RenderEngine; + +struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h); +void RE_engine_update_result(RenderEngine *engine, struct RenderResult *result); +void RE_engine_end_result(RenderEngine *engine, struct RenderResult *result); + +int RE_engine_test_break(RenderEngine *engine); +void RE_engine_update_stats(RenderEngine *engine, char *stats, char *info); + +void RE_engines_free(void); + #endif /* RE_PIPELINE_H */ diff --git a/source/blender/render/intern/source/Makefile b/source/blender/render/intern/source/Makefile index 22680b26338..3c8d0f637a3 100644 --- a/source/blender/render/intern/source/Makefile +++ b/source/blender/render/intern/source/Makefile @@ -43,6 +43,7 @@ CPPFLAGS += -I../../../radiosity/extern/include CPPFLAGS += -I../../../blenlib CPPFLAGS += -I../../../imbuf CPPFLAGS += -I../../../makesdna +CPPFLAGS += -I../../../makesrna CPPFLAGS += -I../../../blenkernel CPPFLAGS += -I../../../quicktime CPPFLAGS += -I../../../../kernel/gen_messaging diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index db78d0bbb61..801a9729277 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -1664,8 +1664,23 @@ void RE_TileProcessor(Render *re, int firsttile, int threaded) /* ************ This part uses API, for rendering Blender scenes ********** */ +static void external_render_3d(Render *re, RenderEngineType *type); + static void do_render_3d(Render *re) { + RenderEngineType *type; + + /* try external */ + for(type=R_engines.first; type; type=type->next) + if(strcmp(type->idname, re->r.engine) == 0) + break; + + if(type && type->render) { + external_render_3d(re, type); + return; + } + + /* internal */ // re->cfra= cfra; /* <- unused! */ @@ -1681,7 +1696,6 @@ static void do_render_3d(Render *re) if(re->flag & R_HALO) if(!re->test_break(re->tbh)) add_halo_flare(re); - /* free all render verts etc */ RE_Database_Free(re); @@ -2766,3 +2780,163 @@ void RE_init_threadcount(Render *re) re->r.threads = BLI_system_thread_count(); } } + +/************************** External Engines ***************************/ + +static RenderEngineType internal_engine_type = { + NULL, NULL, "BlenderRenderEngine", "Blender", NULL, + NULL, NULL, NULL, NULL}; + +ListBase R_engines = {&internal_engine_type, &internal_engine_type}; + +RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h) +{ + Render *re= engine->re; + RenderResult *result; + rcti disprect; + + /* ensure the coordinates are within the right limits */ + CLAMP(x, 0, re->result->rectx); + CLAMP(y, 0, re->result->recty); + CLAMP(w, 0, re->result->rectx); + CLAMP(h, 0, re->result->recty); + + if(x + w > re->result->rectx) + w= re->result->rectx - x; + if(y + h > re->result->recty) + h= re->result->recty - y; + + /* allocate a render result */ + disprect.xmin= x; + disprect.xmax= x+w; + disprect.ymin= y; + disprect.ymax= y+h; + + if(0) { // XXX (re->r.scemode & R_FULL_SAMPLE)) { + result= new_full_sample_buffers(re, &engine->fullresult, &disprect, 0); + } + else { + result= new_render_result(re, &disprect, 0, RR_USEMEM); + BLI_addtail(&engine->fullresult, result); + } + + return result; +} + +void RE_engine_update_result(RenderEngine *engine, RenderResult *result) +{ + Render *re= engine->re; + + if(result && render_display_draw_enabled(re)) { + result->renlay= result->layers.first; // weak + re->display_draw(re->ddh, result, NULL); + } +} + +void RE_engine_end_result(RenderEngine *engine, RenderResult *result) +{ + Render *re= engine->re; + + if(!result) + return; + + /* merge */ + if(re->result->exrhandle) { + RenderResult *rr, *rrpart; + + // XXX crashes, exr expects very particular part sizes + for(rr= re->result, rrpart= result; rr && rrpart; rr= rr->next, rrpart= rrpart->next) + save_render_result_tile(rr, rrpart); + } + else if(render_display_draw_enabled(re)) { + /* on break, don't merge in result for preview renders, looks nicer */ + if(re->test_break(re->tbh) && (re->r.scemode & R_PREVIEWBUTS)); + else merge_render_result(re->result, result); + } + + /* draw */ + if(!re->test_break(re->tbh) && render_display_draw_enabled(re)) { + result->renlay= result->layers.first; // weak + re->display_draw(re->ddh, result, NULL); + } + + /* free */ + free_render_result(&engine->fullresult, result); +} + +int RE_engine_test_break(RenderEngine *engine) +{ + Render *re= engine->re; + + return re->test_break(re->tbh); +} + +void RE_engine_update_stats(RenderEngine *engine, char *stats, char *info) +{ + Render *re= engine->re; + + re->i.statstr= stats; + re->i.infostr= info; + re->stats_draw(re->sdh, &re->i); + re->i.infostr= NULL; + re->i.statstr= NULL; +} + +static void external_render_3d(Render *re, RenderEngineType *type) +{ + RenderEngine engine; + + if(re->result==NULL || !(re->r.scemode & R_PREVIEWBUTS)) { + RE_FreeRenderResult(re->result); + + if(0) // XXX re->r.scemode & R_FULL_SAMPLE) + re->result= new_full_sample_buffers_exr(re); + else + re->result= new_render_result(re, &re->disprect, 0, 0); // XXX re->r.scemode & (R_EXR_TILE_FILE|R_FULL_SAMPLE)); + } + + if(re->result==NULL) + return; + + /* external */ + memset(&engine, 0, sizeof(engine)); + engine.type= type; + engine.re= re; + + type->render(&engine, re->scene); + + free_render_result(&engine.fullresult, engine.fullresult.first); + + if(re->result->exrhandle) { + RenderResult *rr; + + save_empty_result_tiles(re); + + for(rr= re->result; rr; rr= rr->next) { + IMB_exr_close(rr->exrhandle); + rr->exrhandle= NULL; + } + + free_render_result(&re->fullresult, re->result); + re->result= NULL; + + read_render_result(re, 0); + } +} + +void RE_engines_free() +{ + RenderEngineType *type, *next; + + for(type=R_engines.first; type; type=next) { + next= type->next; + + if(type != &internal_engine_type) { + if(type->ext.free) + type->ext.free(type->ext.data); + + MEM_freeN(type); + } + } +} + diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 5c34b19c1f4..3d8efc018c4 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -239,6 +239,7 @@ void WM_exit(bContext *C) BLF_exit(); RE_FreeAllRender(); + RE_engines_free(); // free_txt_data(); -- cgit v1.2.3