diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2013-12-26 15:24:42 +0400 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2013-12-26 15:24:42 +0400 |
commit | 709041ed0b7e1848068c9d53543ed114229a9f5b (patch) | |
tree | edf2cd930e6bcfd9bbc9ab7fd21490effb069e09 /source/blender/render/intern | |
parent | 7025a1bd7830c3bb58ea7f6a3dba8089869591eb (diff) |
Threaded object update and EvaluationContext
Summary:
Made objects update happening from multiple threads. It is a task-based
scheduling system which uses current dependency graph for spawning new
tasks. This means threading happens on object level, but the system is
flexible enough for higher granularity.
Technical details:
- Uses task scheduler which was recently committed to trunk
(that one which Brecht ported from Cycles).
- Added two utility functions to dependency graph:
* DAG_threaded_update_begin, which is called to initialize threaded
objects update. It will also schedule root DAG node to the queue,
hence starting evaluation process.
Initialization will calculate how much parents are to be evaluation
before current DAG node can be scheduled. This value is used by task
threads for faster detecting which nodes might be scheduled.
* DAG_threaded_update_handle_node_updated which is called from task
thread function when node was fully handled.
This function decreases num_pending_parents of node children and
schedules children with zero valency.
As it might have become clear, task thread receives DAG nodes and
decides which callback to call for it.
Currently only BKE_object_handle_update is called for object nodes.
In the future it'll call node->callback() from Ali's new DAG.
- This required adding some workarounds to the render pipeline.
Mainly to stop using get_object_dm() from modifiers' apply callback.
Such a call was only a workaround for dependency graph glitch when
rendering scene with, say, boolean modifiers before displaying
this scene.
Such change moves workaround from one place to another, so overall
hackentropy remains the same.
- Added paradigm of EvaluaitonContext. Currently it's more like just a
more reliable replacement for G.is_rendering which fails in some
circumstances.
Future idea of this context is to also store all the local data needed
for objects evaluation such as local time, Copy-on-Write data and so.
There're two types of EvaluationContext:
* Context used for viewport updated and owned by Main. In the future
this context might be easily moved to Window or Screen to allo
per-window/per-screen local time.
* Context used by render engines to evaluate objects for render purposes.
Render engine is an owner of this context.
This context is passed to all object update routines.
Reviewers: brecht, campbellbarton
Reviewed By: brecht
CC: lukastoenne
Differential Revision: https://developer.blender.org/D94
Diffstat (limited to 'source/blender/render/intern')
4 files changed, 126 insertions, 20 deletions
diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index 2a43cab7bce..02a9f1eb347 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -52,6 +52,7 @@ #include "BLI_sys_types.h" // for intptr_t support +struct EvaluationContext; struct Object; struct MemArena; struct VertTableNode; @@ -270,6 +271,7 @@ struct Render struct ReportList *reports; struct ImagePool *pool; + struct EvaluationContext *eval_ctx; }; /* ------------------------------------------------------------------------- */ diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index 1f3e961c151..e3a8c57de66 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -39,8 +39,8 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_rand.h" +#include "BLI_task.h" #include "BLI_memarena.h" -#include "BLI_ghash.h" #include "BLI_linklist.h" #ifdef WITH_FREESTYLE # include "BLI_edgehash.h" @@ -79,6 +79,7 @@ #include "BKE_constraint.h" #include "BKE_displist.h" #include "BKE_deform.h" +#include "BKE_depsgraph.h" #include "BKE_DerivedMesh.h" #include "BKE_effect.h" #include "BKE_global.h" @@ -2221,7 +2222,7 @@ static void init_render_mball(Render *re, ObjectRen *obr) need_orco= 1; } - BKE_displist_make_mball_forRender(re->scene, ob, &dispbase); + BKE_displist_make_mball_forRender(re->eval_ctx, re->scene, ob, &dispbase); dl= dispbase.first; if (dl == NULL) return; @@ -4981,7 +4982,7 @@ static void database_init_objects(Render *re, unsigned int renderlay, int nolamp /* create list of duplis generated by this object, particle * system need to have render settings set for dupli particles */ dupli_render_particle_set(re, ob, timeoffset, 0, 1); - lb= object_duplilist(re->scene, ob, TRUE); + lb= object_duplilist(re->eval_ctx, re->scene, ob); dupli_render_particle_set(re, ob, timeoffset, 0, 0); for (dob= lb->first; dob; dob= dob->next) { @@ -5133,12 +5134,12 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l /* applies changes fully */ if ((re->r.scemode & (R_NO_FRAME_UPDATE|R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))==0) - BKE_scene_update_for_newframe(re->main, re->scene, lay); + BKE_scene_update_for_newframe(re->eval_ctx, re->main, re->scene, lay); /* if no camera, viewmat should have been set! */ if (use_camera_view && camera) { /* called before but need to call again in case of lens animation from the - * above call to BKE_scene_update_for_newframe, fixes bug. [#22702]. + * above call to BKE_scene_update_for_newframe_render, fixes bug. [#22702]. * following calls don't depend on 'RE_SetCamera' */ RE_SetCamera(re, camera); @@ -5310,7 +5311,7 @@ static void database_fromscene_vectors(Render *re, Scene *scene, unsigned int la /* applies changes fully */ scene->r.cfra += timeoffset; - BKE_scene_update_for_newframe(re->main, re->scene, lay); + BKE_scene_update_for_newframe(re->eval_ctx, re->main, re->scene, lay); /* if no camera, viewmat should have been set! */ if (camera) { diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index c3628e99d04..cccfeed6e47 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -459,7 +459,7 @@ int RE_engine_render(Render *re, int do_all) lay &= non_excluded_lay; } - BKE_scene_update_for_newframe(re->main, re->scene, lay); + BKE_scene_update_for_newframe(re->eval_ctx, re->main, re->scene, lay); } /* create render result */ diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index ba8265a83fe..4ec7ce1c0d2 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -59,9 +59,11 @@ #include "BKE_animsys.h" /* <------ should this be here?, needed for sequencer update */ #include "BKE_camera.h" +#include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_node.h" #include "BKE_pointcache.h" #include "BKE_report.h" @@ -373,6 +375,8 @@ Render *RE_NewRender(const char *name) BLI_addtail(&RenderGlobal.renderlist, re); BLI_strncpy(re->name, name, RE_MAXNAME); BLI_rw_mutex_init(&re->resultmutex); + re->eval_ctx = MEM_callocN(sizeof(EvaluationContext), "re->eval_ctx"); + re->eval_ctx->for_render = true; } RE_InitRenderCB(re); @@ -420,6 +424,7 @@ void RE_FreeRender(Render *re) render_result_free(re->pushedresult); BLI_remlink(&RenderGlobal.renderlist, re); + MEM_freeN(re->eval_ctx); MEM_freeN(re); } @@ -1320,8 +1325,9 @@ static void do_render_blur_3d(Render *re) re->i.curblur = 0; /* stats */ /* make sure motion blur changes get reset to current frame */ - if ((re->r.scemode & (R_NO_FRAME_UPDATE|R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))==0) - BKE_scene_update_for_newframe(re->main, re->scene, re->lay); + if ((re->r.scemode & (R_NO_FRAME_UPDATE|R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))==0) { + BKE_scene_update_for_newframe(re->eval_ctx, re->main, re->scene, re->lay); + } /* weak... the display callback wants an active renderlayer pointer... */ re->result->renlay = render_get_active_layer(re, re->result); @@ -1590,21 +1596,117 @@ static bool rlayer_node_uses_alpha(bNodeTree *ntree, bNode *node) return false; } +/* Issue here is that it's possible that object which is used by boolean, + * array or shrinkwrap modifiers weren't displayed in the viewport before + * rendering. This leads to situations when apply() of this modifiers + * could not get ob->derivedFinal and modifiers are not being applied. + * + * This was worked around by direct call of get_derived_final() from those + * modifiers, but such approach leads to write conflicts with threaded + * update. + * + * Here we make sure derivedFinal will be calculated by update_for_newframe + * function later in the pipeline and all the modifiers are applied + * properly without hacks from their side. + * - sergey - + */ +#define DEPSGRAPH_WORKAROUND_HACK + +#ifdef DEPSGRAPH_WORKAROUND_HACK +static bool allow_render_mesh_object(Object *ob) +{ + /* override not showing object when duplis are used with particles */ + if (ob->transflag & OB_DUPLIPARTS) { + /* pass */ /* let particle system(s) handle showing vs. not showing */ + } + else if ((ob->transflag & OB_DUPLI) && !(ob->transflag & OB_DUPLIFRAMES)) { + return false; + } + return true; +} + +static void tag_dependend_objects_for_render(Scene *scene, int renderlay) +{ + Scene *sce_iter; + Base *base; + for (SETLOOPER(scene, sce_iter, base)) { + Object *object = base->object; + + if ((base->lay & renderlay) == 0) { + continue; + } + + if (object->type == OB_MESH) { + if (allow_render_mesh_object(object)) { + ModifierData *md; + VirtualModifierData virtualModifierData; + + for (md = modifiers_getVirtualModifierList(object, &virtualModifierData); + md; + md = md->next) + { + if (!modifier_isEnabled(scene, md, eModifierMode_Render)) { + continue; + } + + if (md->type == eModifierType_Boolean) { + BooleanModifierData *bmd = (BooleanModifierData *)md; + if (bmd->object && bmd->object->type == OB_MESH) { + DAG_id_tag_update(&bmd->object->id, OB_RECALC_DATA); + } + } + else if (md->type == eModifierType_Array) { + ArrayModifierData *amd = (ArrayModifierData *)md; + if (amd->start_cap && amd->start_cap->type == OB_MESH) { + DAG_id_tag_update(&amd->start_cap->id, OB_RECALC_DATA); + } + if (amd->end_cap && amd->end_cap->type == OB_MESH) { + DAG_id_tag_update(&amd->end_cap->id, OB_RECALC_DATA); + } + } + else if (md->type == eModifierType_Shrinkwrap) { + ShrinkwrapModifierData *smd = (ShrinkwrapModifierData *)md; + if (smd->target && smd->target->type == OB_MESH) { + DAG_id_tag_update(&smd->target->id, OB_RECALC_DATA); + } + } + } + } + } + } +} +#endif + static void tag_scenes_for_render(Render *re) { bNode *node; Scene *sce; +#ifdef DEPSGRAPH_WORKAROUND_HACK + int renderlay = re->lay; +#endif - for (sce = re->main->scene.first; sce; sce = sce->id.next) + for (sce = re->main->scene.first; sce; sce = sce->id.next) { sce->id.flag &= ~LIB_DOIT; +#ifdef DEPSGRAPH_WORKAROUND_HACK + tag_dependend_objects_for_render(sce, renderlay); +#endif + } #ifdef WITH_FREESTYLE - for (sce = re->freestyle_bmain.scene.first; sce; sce = sce->id.next) + for (sce = re->freestyle_bmain.scene.first; sce; sce = sce->id.next) { sce->id.flag &= ~LIB_DOIT; +#ifdef DEPSGRAPH_WORKAROUND_HACK + tag_dependend_objects_for_render(sce, renderlay); +#endif + } #endif - if (RE_GetCamera(re) && composite_needs_render(re->scene, 1)) + if (RE_GetCamera(re) && composite_needs_render(re->scene, 1)) { re->scene->id.flag |= LIB_DOIT; +#ifdef DEPSGRAPH_WORKAROUND_HACK + tag_dependend_objects_for_render(re->scene, renderlay); +#endif + } if (re->scene->nodetree == NULL) return; @@ -1632,6 +1734,9 @@ static void tag_scenes_for_render(Render *re) if ((node->id->flag & LIB_DOIT) == 0) { node->flag |= NODE_TEST; node->id->flag |= LIB_DOIT; +#ifdef DEPSGRAPH_WORKAROUND_HACK + tag_dependend_objects_for_render((Scene *) node->id, renderlay); +#endif } } } @@ -2020,7 +2125,7 @@ static void do_render_composite_fields_blur_3d(Render *re) R.stats_draw = re->stats_draw; if (update_newframe) - BKE_scene_update_for_newframe(re->main, re->scene, re->lay); + BKE_scene_update_for_newframe(re->eval_ctx, re->main, re->scene, re->lay); if (re->r.scemode & R_FULL_SAMPLE) do_merge_fullsample(re, ntree); @@ -2095,14 +2200,12 @@ static void do_render_seq(Render *re) if ((re->r.mode & R_BORDER) && (re->r.mode & R_CROP) == 0) { /* if border rendering is used and cropping is disabled, final buffer should * be as large as the whole frame */ - context = BKE_sequencer_new_render_data(re->main, re->scene, - re->winx, re->winy, - 100); + context = BKE_sequencer_new_render_data(re->eval_ctx, re->main, re->scene, + re->winx, re->winy, 100); } else { - context = BKE_sequencer_new_render_data(re->main, re->scene, - re->result->rectx, re->result->recty, - 100); + context = BKE_sequencer_new_render_data(re->eval_ctx, re->main, re->scene, + re->result->rectx, re->result->recty, 100); } out = BKE_sequencer_give_ibuf(context, cfra, 0); @@ -2704,7 +2807,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri else updatelay = re->lay; - BKE_scene_update_for_newframe(bmain, scene, updatelay); + BKE_scene_update_for_newframe(re->eval_ctx, bmain, scene, updatelay); continue; } else |