Merge branch 'master' into blender2.8
authorSergey Sharybin <sergey.vfx@gmail.com>
Wed, 21 Feb 2018 09:51:58 +0000 (10:51 +0100)
committerSergey Sharybin <sergey.vfx@gmail.com>
Wed, 21 Feb 2018 09:51:58 +0000 (10:51 +0100)
19 files changed:
1  2 
source/blender/blenkernel/BKE_global.h
source/blender/blenkernel/intern/anim_sys.c
source/blender/blenkernel/intern/armature_update.c
source/blender/blenkernel/intern/curve.c
source/blender/blenkernel/intern/group.c
source/blender/blenkernel/intern/layer.c
source/blender/blenkernel/intern/mask_evaluate.c
source/blender/blenkernel/intern/material.c
source/blender/blenkernel/intern/mesh.c
source/blender/blenkernel/intern/node.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/object_update.c
source/blender/blenkernel/intern/particle_system.c
source/blender/blenkernel/intern/rigidbody.c
source/blender/blenkernel/intern/world.c
source/blender/depsgraph/intern/depsgraph_intern.h
source/blender/depsgraph/intern/depsgraph_tag.cc
source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
source/creator/creator_args.c

index 80a8f750d204aa47be8777fab491e874e4b071f3,f3d44164b1e493423e21f878a5689ce3dbf91633..78d8271458d6e89c4f098efbacb2a5acaace2501
@@@ -122,13 -122,15 +122,16 @@@ enum 
        G_DEBUG_WM =        (1 << 5), /* operator, undo */
        G_DEBUG_JOBS =      (1 << 6), /* jobs time profiling */
        G_DEBUG_FREESTYLE = (1 << 7), /* freestyle messages */
-       G_DEBUG_DEPSGRAPH = (1 << 8), /* depsgraph messages */
-       G_DEBUG_SIMDATA =   (1 << 9), /* sim debug data display */
-       G_DEBUG_GPU_MEM =   (1 << 10), /* gpu memory in status bar */
+       G_DEBUG_DEPSGRAPH_BUILD      = (1 << 8),   /* depsgraph construction messages */
+       G_DEBUG_DEPSGRAPH_EVAL       = (1 << 9),   /* depsgraph evaluation messages */
+       G_DEBUG_DEPSGRAPH_TAG        = (1 << 10),  /* depsgraph tagging messages */
        G_DEBUG_DEPSGRAPH_NO_THREADS = (1 << 11),  /* single threaded depsgraph */
-       G_DEBUG_GPU =        (1 << 12), /* gpu debug */
+       G_DEBUG_DEPSGRAPH = (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_EVAL | G_DEBUG_DEPSGRAPH_TAG),
+       G_DEBUG_SIMDATA =   (1 << 12), /* sim debug data display */
+       G_DEBUG_GPU_MEM =   (1 << 13), /* gpu memory in status bar */
 -      G_DEBUG_GPU =       (1 << 14), /* gpu debug */
 -      G_DEBUG_IO = (1 << 15),   /* IO Debugging (for Collada, ...)*/
++      G_DEBUG_GPU =        (1 << 14), /* gpu debug */
 +      G_DEBUG_IO = (1 << 13),   /* IO Debugging (for Collada, ...)*/
-       G_DEBUG_GPU_SHADERS = (1 << 14),   /* GLSL shaders */
++      G_DEBUG_GPU_SHADERS = (1 << 15),   /* GLSL shaders */
  };
  
  #define G_DEBUG_ALL  (G_DEBUG | G_DEBUG_FFMPEG | G_DEBUG_PYTHON | G_DEBUG_EVENTS | G_DEBUG_WM | G_DEBUG_JOBS | \
index 844a2a50bb3c59fd13579b4798b02dbc60d5b54c,95d9a769749092ebc95165ff763d913be7a4e6bb..631d374397020cabd92052b75758ac29d4588ce6
@@@ -2879,9 -2918,9 +2879,9 @@@ void BKE_animsys_evaluate_all_animation
  /* ************** */
  /* Evaluation API */
  
- #define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH) printf
+ #define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) printf
  
 -void BKE_animsys_eval_animdata(EvaluationContext *eval_ctx, ID *id)
 +void BKE_animsys_eval_animdata(const EvaluationContext *eval_ctx, ID *id)
  {
        AnimData *adt = BKE_animdata_from_id(id);
        Scene *scene = NULL; /* XXX: this is only needed for flushing RNA updates,
index de0dfe2530d14402bf7c7fafbe7833d4156b557a,2e9b2f7d20fa526514c1af02d1662ad8137d9868..203dcbf247cbf0bf3a6e175cdae1258e64bd3a0b
  #include "BKE_global.h"
  #include "BKE_main.h"
  
- #define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH) printf
 -#include "DEG_depsgraph.h"
 -
 -#ifdef WITH_LEGACY_DEPSGRAPH
 -#  define DEBUG_PRINT if (!DEG_depsgraph_use_legacy() && G.debug & G_DEBUG_DEPSGRAPH_EVAL) printf
 -#else
 -#  define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) printf
 -#endif
++#define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) printf
  
  /* ********************** SPLINE IK SOLVER ******************* */
  
index 2a27bad0fb59f4f24a7d68b04acbad23d3c98371,f0759748fee27d9f4e6cf0fa1042e84b72fc937e..b5ad2bcef4e56fa1e12a86802dc2addb8c945153
@@@ -5255,10 -5250,10 +5255,10 @@@ void BKE_curve_rect_from_textbox(const 
  
  /* **** Depsgraph evaluation **** */
  
 -void BKE_curve_eval_geometry(EvaluationContext *UNUSED(eval_ctx),
 +void BKE_curve_eval_geometry(const EvaluationContext *UNUSED(eval_ctx),
                               Curve *curve)
  {
-       if (G.debug & G_DEBUG_DEPSGRAPH) {
+       if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) {
                printf("%s on %s\n", __func__, curve->id.name);
        }
        if (curve->bb == NULL || (curve->bb->flag & BOUNDBOX_DIRTY)) {
index c615f67fe42d2d8678df874e89dccc416939540e,6dc8cc5aff1c30ddfaa8049e276232ff3c6576bf..8fa8da8b0ca1d955a9371fc402e4806e9e360bb1
  #include "BKE_library.h"
  #include "BKE_main.h"
  #include "BKE_object.h"
 -#include "BKE_scene.h" /* BKE_scene_base_find */
 +#include "BKE_scene.h"
  
- #define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH) printf
 -static void free_group_object(GroupObject *go)
 -{
 -      MEM_freeN(go);
 -}
++#define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) printf
  
  /** Free (or release) any data used by this group (does not free the group itself). */
  void BKE_group_free(Group *group)
index 8e7b7f5456b38cf0098787f9c0d4915fc68280e1,0000000000000000000000000000000000000000..82ac666c46b82caeee6c39efdac817174306ad96
mode 100644,000000..100644
--- /dev/null
@@@ -1,2363 -1,0 +1,2363 @@@
- #define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH) printf
 +/*
 + * ***** BEGIN GPL LICENSE BLOCK *****
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * Contributor(s): Dalai Felinto
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/blenkernel/intern/layer.c
 + *  \ingroup bke
 + */
 +
 +#include <string.h>
 +
 +#include "BLI_listbase.h"
 +#include "BLI_string.h"
 +#include "BLI_string_utf8.h"
 +#include "BLI_string_utils.h"
 +#include "BLT_translation.h"
 +
 +#include "BKE_collection.h"
 +#include "BKE_freestyle.h"
 +#include "BKE_global.h"
 +#include "BKE_group.h"
 +#include "BKE_idprop.h"
 +#include "BKE_layer.h"
 +#include "BKE_main.h"
 +#include "BKE_node.h"
 +#include "BKE_workspace.h"
 +
 +#include "DEG_depsgraph.h"
 +
 +#include "DNA_group_types.h"
 +#include "DNA_ID.h"
 +#include "DNA_layer_types.h"
 +#include "DNA_object_types.h"
 +#include "DNA_node_types.h"
 +#include "DNA_scene_types.h"
 +#include "DNA_windowmanager_types.h"
 +#include "DNA_workspace_types.h"
 +
 +#include "DRW_engine.h"
 +
 +#include "MEM_guardedalloc.h"
 +
++#define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) printf
 +
 +/* prototype */
 +struct EngineSettingsCB_Type;
 +static void layer_collections_sync_flags(ListBase *layer_collections_dst, const ListBase *layer_collections_src);
 +static void layer_collection_free(ViewLayer *view_layer, LayerCollection *lc);
 +static void layer_collection_objects_populate(ViewLayer *view_layer, LayerCollection *lc, ListBase *objects);
 +static LayerCollection *layer_collection_add(ViewLayer *view_layer, LayerCollection *parent, SceneCollection *sc);
 +static LayerCollection *find_layer_collection_by_scene_collection(LayerCollection *lc, const SceneCollection *sc);
 +static IDProperty *collection_engine_settings_create(struct EngineSettingsCB_Type *ces_type, const bool populate);
 +static IDProperty *collection_engine_get(IDProperty *root, const int type, const char *engine_name);
 +static void collection_engine_settings_init(IDProperty *root, const bool populate);
 +static void layer_engine_settings_init(IDProperty *root, const bool populate);
 +static void object_bases_iterator_next(BLI_Iterator *iter, const int flag);
 +
 +/* RenderLayer */
 +
 +/**
 + * Returns the ViewLayer to be used for rendering
 + * Most of the time BKE_view_layer_from_workspace_get should be used instead
 + */
 +ViewLayer *BKE_view_layer_from_scene_get(const Scene *scene)
 +{
 +      ViewLayer *view_layer = BLI_findlink(&scene->view_layers, scene->active_view_layer);
 +      BLI_assert(view_layer);
 +      return view_layer;
 +}
 +
 +/**
 + * Returns the ViewLayer to be used for drawing, outliner, and other context related areas.
 + */
 +ViewLayer *BKE_view_layer_from_workspace_get(const struct Scene *scene, const struct WorkSpace *workspace)
 +{
 +      if (BKE_workspace_use_scene_settings_get(workspace)) {
 +              return BKE_view_layer_from_scene_get(scene);
 +      }
 +      else {
 +              return BKE_workspace_view_layer_get(workspace, scene);
 +      }
 +}
 +
 +/**
 + * This is a placeholder to know which areas of the code need to be addressed for the Workspace changes.
 + * Never use this, you should either use BKE_view_layer_from_workspace_get or get ViewLayer explicitly.
 + */
 +ViewLayer *BKE_view_layer_context_active_PLACEHOLDER(const Scene *scene)
 +{
 +      return BKE_view_layer_from_scene_get(scene);
 +}
 +
 +static ViewLayer *view_layer_add(const char *name, SceneCollection *master_scene_collection)
 +{
 +      if (!name) {
 +              name = DATA_("View Layer");
 +      }
 +
 +      IDPropertyTemplate val = {0};
 +      ViewLayer *view_layer = MEM_callocN(sizeof(ViewLayer), "View Layer");
 +      view_layer->flag = VIEW_LAYER_RENDER | VIEW_LAYER_FREESTYLE;
 +
 +      view_layer->properties = IDP_New(IDP_GROUP, &val, ROOT_PROP);
 +      layer_engine_settings_init(view_layer->properties, false);
 +      BLI_strncpy_utf8(view_layer->name, name, sizeof(view_layer->name));
 +
 +      /* Link the master collection by default. */
 +      layer_collection_add(view_layer, NULL, master_scene_collection);
 +
 +      /* Pure rendering pipeline settings. */
 +      view_layer->layflag = 0x7FFF;   /* solid ztra halo edge strand */
 +      view_layer->passflag = SCE_PASS_COMBINED | SCE_PASS_Z;
 +      view_layer->pass_alpha_threshold = 0.5f;
 +      BKE_freestyle_config_init(&view_layer->freestyle_config);
 +
 +      return view_layer;
 +}
 +
 +/**
 + * Add a new view layer
 + * by default, a view layer has the master collection
 + */
 +ViewLayer *BKE_view_layer_add(Scene *scene, const char *name)
 +{
 +      SceneCollection *sc = BKE_collection_master(&scene->id);
 +      ViewLayer *view_layer = view_layer_add(name, sc);
 +
 +      BLI_addtail(&scene->view_layers, view_layer);
 +
 +      /* unique name */
 +      BLI_uniquename(
 +              &scene->view_layers, view_layer, DATA_("ViewLayer"), '.',
 +              offsetof(ViewLayer, name), sizeof(view_layer->name));
 +
 +      return view_layer;
 +}
 +
 +/**
 + * Add a ViewLayer for a Group
 + * It should be added only once
 + */
 +ViewLayer *BKE_view_layer_group_add(Group *group)
 +{
 +      BLI_assert(group->view_layer == NULL);
 +      SceneCollection *sc = BKE_collection_master(&group->id);
 +      ViewLayer *view_layer = view_layer_add(group->id.name + 2, sc);
 +      return view_layer;
 +}
 +
 +void BKE_view_layer_free(ViewLayer *view_layer)
 +{
 +      BKE_view_layer_free_ex(view_layer, true);
 +}
 +
 +/**
 + * Free (or release) any data used by this ViewLayer.
 + */
 +void BKE_view_layer_free_ex(ViewLayer *view_layer, const bool do_id_user)
 +{
 +      view_layer->basact = NULL;
 +
 +      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +              if (base->collection_properties) {
 +                      IDP_FreeProperty(base->collection_properties);
 +                      MEM_freeN(base->collection_properties);
 +              }
 +      }
 +      BLI_freelistN(&view_layer->object_bases);
 +
 +      for (LayerCollection *lc = view_layer->layer_collections.first; lc; lc = lc->next) {
 +              layer_collection_free(NULL, lc);
 +      }
 +      BLI_freelistN(&view_layer->layer_collections);
 +
 +      if (view_layer->properties) {
 +              IDP_FreeProperty(view_layer->properties);
 +              MEM_freeN(view_layer->properties);
 +      }
 +
 +      if (view_layer->properties_evaluated) {
 +              IDP_FreeProperty(view_layer->properties_evaluated);
 +              MEM_freeN(view_layer->properties_evaluated);
 +      }
 +
 +      for (ViewLayerEngineData *sled = view_layer->drawdata.first; sled; sled = sled->next) {
 +              if (sled->storage) {
 +                      if (sled->free) {
 +                              sled->free(sled->storage);
 +                      }
 +                      MEM_freeN(sled->storage);
 +              }
 +      }
 +      BLI_freelistN(&view_layer->drawdata);
 +
 +      MEM_SAFE_FREE(view_layer->stats);
 +
 +      BKE_freestyle_config_free(&view_layer->freestyle_config, do_id_user);
 +
 +      if (view_layer->id_properties) {
 +              IDP_FreeProperty(view_layer->id_properties);
 +              MEM_freeN(view_layer->id_properties);
 +      }
 +
 +      MEM_freeN(view_layer);
 +}
 +
 +/**
 + * Tag all the selected objects of a renderlayer
 + */
 +void BKE_view_layer_selected_objects_tag(ViewLayer *view_layer, const int tag)
 +{
 +      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +              if ((base->flag & BASE_SELECTED) != 0) {
 +                      base->object->flag |= tag;
 +              }
 +              else {
 +                      base->object->flag &= ~tag;
 +              }
 +      }
 +}
 +
 +/**
 + * Return the first ViewLayer for a given id
 + */
 +ViewLayer *BKE_view_layer_first_from_id(const ID *owner_id)
 +{
 +      switch (GS(owner_id->name)) {
 +              case ID_SCE:
 +                      return ((Scene *)owner_id)->view_layers.first;
 +              case ID_GR:
 +                      return ((Group *)owner_id)->view_layer;
 +              default:
 +                      BLI_assert(!"ID doesn't support view layers");
 +                      return NULL;
 +      }
 +}
 +
 +static bool find_scene_collection_in_scene_collections(ListBase *lb, const LayerCollection *lc)
 +{
 +      for (LayerCollection *lcn = lb->first; lcn; lcn = lcn->next) {
 +              if (lcn == lc) {
 +                      return true;
 +              }
 +              if (find_scene_collection_in_scene_collections(&lcn->layer_collections, lc)) {
 +                      return true;
 +              }
 +      }
 +      return false;
 +}
 +
 +/**
 + * Fallback for when a Scene has no camera to use
 + *
 + * \param view_layer: in general you want to use the same ViewLayer that is used
 + * for depsgraph. If rendering you pass the scene active layer, when viewing in the viewport
 + * you want to get ViewLayer from context.
 + */
 +Object *BKE_view_layer_camera_find(ViewLayer *view_layer)
 +{
 +      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +              if (base->object->type == OB_CAMERA) {
 +                      return base->object;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +/**
 + * Find the ViewLayer a LayerCollection belongs to
 + */
 +ViewLayer *BKE_view_layer_find_from_collection(const ID *owner_id, LayerCollection *lc)
 +{
 +      switch (GS(owner_id->name)) {
 +              case ID_GR:
 +                      return ((Group *)owner_id)->view_layer;
 +              case ID_SCE:
 +              {
 +                      Scene *scene = (Scene *)owner_id;
 +                      for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +                              if (find_scene_collection_in_scene_collections(&view_layer->layer_collections, lc)) {
 +                                      return view_layer;
 +                              }
 +                      }
 +                      return NULL;
 +              }
 +              default:
 +                      BLI_assert(!"ID doesn't support scene layers");
 +                      return NULL;
 +      }
 +}
 +
 +/* Base */
 +
 +Base *BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
 +{
 +      return BLI_findptr(&view_layer->object_bases, ob, offsetof(Base, object));
 +}
 +
 +void BKE_view_layer_base_deselect_all(ViewLayer *view_layer)
 +{
 +      Base *base;
 +
 +      for (base = view_layer->object_bases.first; base; base = base->next) {
 +              base->flag &= ~BASE_SELECTED;
 +      }
 +}
 +
 +void BKE_view_layer_base_select(struct ViewLayer *view_layer, Base *selbase)
 +{
 +      view_layer->basact = selbase;
 +      if ((selbase->flag & BASE_SELECTABLED) != 0) {
 +              selbase->flag |= BASE_SELECTED;
 +      }
 +}
 +
 +/****************************************************************************/
 +/* Copying functions for datablocks that use ViewLayer/SceneCollection */
 +
 +/* Find the equivalent SceneCollection in the new tree */
 +static SceneCollection *scene_collection_from_new_tree(
 +        SceneCollection *sc_reference, SceneCollection *sc_dst, SceneCollection *sc_src)
 +{
 +      if (sc_src == sc_reference) {
 +              return sc_dst;
 +      }
 +
 +      for (SceneCollection *nsc_src = sc_src->scene_collections.first, *nsc_dst = sc_dst->scene_collections.first;
 +           nsc_src;
 +           nsc_src = nsc_src->next, nsc_dst = nsc_dst->next)
 +      {
 +              SceneCollection *found = scene_collection_from_new_tree(sc_reference, nsc_dst, nsc_src);
 +              if (found != NULL) {
 +                      return found;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +static void layer_collection_sync_flags(
 +        LayerCollection *layer_collection_dst,
 +        const LayerCollection *layer_collection_src)
 +{
 +      layer_collection_dst->flag = layer_collection_src->flag;
 +
 +      if (layer_collection_dst->properties != NULL) {
 +              IDP_FreeProperty(layer_collection_dst->properties);
 +              MEM_SAFE_FREE(layer_collection_dst->properties);
 +      }
 +
 +      if (layer_collection_src->properties != NULL) {
 +              layer_collection_dst->properties = IDP_CopyProperty(layer_collection_src->properties);
 +      }
 +
 +      layer_collections_sync_flags(&layer_collection_dst->layer_collections,
 +                                   &layer_collection_src->layer_collections);
 +}
 +
 +static void layer_collections_sync_flags(ListBase *layer_collections_dst, const ListBase *layer_collections_src)
 +{
 +      BLI_assert(BLI_listbase_count(layer_collections_dst) == BLI_listbase_count(layer_collections_src));
 +      LayerCollection *layer_collection_dst = (LayerCollection *)layer_collections_dst->first;
 +      const LayerCollection *layer_collection_src = (const LayerCollection *)layer_collections_src->first;
 +      while (layer_collection_dst != NULL) {
 +              layer_collection_sync_flags(layer_collection_dst, layer_collection_src);
 +              layer_collection_dst = layer_collection_dst->next;
 +              layer_collection_src = layer_collection_src->next;
 +      }
 +}
 +
 +static bool layer_collection_sync_if_match(
 +        ListBase *lb,
 +        const SceneCollection *scene_collection_dst,
 +        const SceneCollection *scene_collection_src)
 +{
 +      for (LayerCollection *layer_collection = lb->first;
 +           layer_collection;
 +           layer_collection = layer_collection->next)
 +      {
 +              if (layer_collection->scene_collection == scene_collection_src) {
 +                      LayerCollection *layer_collection_dst =
 +                              BLI_findptr(
 +                                  lb,
 +                                  scene_collection_dst,
 +                                  offsetof(LayerCollection, scene_collection));
 +
 +                      if (layer_collection_dst != NULL) {
 +                              layer_collection_sync_flags(layer_collection_dst, layer_collection);
 +                      }
 +                      return true;
 +              }
 +              else {
 +                      if (layer_collection_sync_if_match(
 +                              &layer_collection->layer_collections,
 +                              scene_collection_dst,
 +                              scene_collection_src))
 +                      {
 +                              return true;
 +                      }
 +              }
 +      }
 +      return false;
 +}
 +
 +/**
 + * Sync sibling collections across all view layers
 + *
 + * Make sure every linked instance of \a scene_collection_dst has the same values
 + * (flags, overrides, ...) as the corresponding scene_collection_src.
 + *
 + * \note expect scene_collection_dst to be scene_collection_src->next, and it also
 + * expects both collections to have the same ammount of sub-collections.
 + */
 +void BKE_layer_collection_sync_flags(
 +        ID *owner_id,
 +        SceneCollection *scene_collection_dst,
 +        SceneCollection *scene_collection_src)
 +{
 +      for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
 +              for (LayerCollection *layer_collection = view_layer->layer_collections.first;
 +                   layer_collection;
 +                   layer_collection = layer_collection->next)
 +              {
 +                      layer_collection_sync_if_match(
 +                                  &layer_collection->layer_collections,
 +                                  scene_collection_dst,
 +                                  scene_collection_src);
 +              }
 +      }
 +}
 +
 +/* recreate the LayerCollection tree */
 +static void layer_collections_recreate(
 +        ViewLayer *view_layer_dst, ListBase *lb_src, SceneCollection *mc_dst, SceneCollection *mc_src)
 +{
 +      for (LayerCollection *lc_src = lb_src->first; lc_src; lc_src = lc_src->next) {
 +              SceneCollection *sc_dst = scene_collection_from_new_tree(lc_src->scene_collection, mc_dst, mc_src);
 +              BLI_assert(sc_dst);
 +
 +              /* instead of synchronizing both trees we simply re-create it */
 +              BKE_collection_link(view_layer_dst, sc_dst);
 +      }
 +}
 +
 +/**
 + * Only copy internal data of ViewLayer from source to already allocated/initialized destination.
 + *
 + * \param mc_src Master Collection the source ViewLayer links in.
 + * \param mc_dst Master Collection the destination ViewLayer links in.
 + * \param flag  Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
 + */
 +void BKE_view_layer_copy_data(
 +        ViewLayer *view_layer_dst, ViewLayer *view_layer_src, SceneCollection *mc_dst, SceneCollection *mc_src,
 +        const int flag)
 +{
 +      IDPropertyTemplate val = {0};
 +
 +      if (view_layer_dst->id_properties != NULL) {
 +              view_layer_dst->id_properties = IDP_CopyProperty_ex(view_layer_dst->id_properties, flag);
 +      }
 +      BKE_freestyle_config_copy(&view_layer_dst->freestyle_config, &view_layer_src->freestyle_config, flag);
 +
 +      view_layer_dst->stats = NULL;
 +      view_layer_dst->properties_evaluated = NULL;
 +      view_layer_dst->properties = IDP_New(IDP_GROUP, &val, ROOT_PROP);
 +      IDP_MergeGroup_ex(view_layer_dst->properties, view_layer_src->properties, true, flag);
 +
 +      /* we start fresh with no overrides and no visibility flags set
 +       * instead of syncing both trees we simply unlink and relink the scene collection */
 +      BLI_listbase_clear(&view_layer_dst->layer_collections);
 +      BLI_listbase_clear(&view_layer_dst->object_bases);
 +      BLI_listbase_clear(&view_layer_dst->drawdata);
 +
 +      layer_collections_recreate(view_layer_dst, &view_layer_src->layer_collections, mc_dst, mc_src);
 +
 +      /* Now we handle the syncing for visibility, selectability, ... */
 +      layer_collections_sync_flags(&view_layer_dst->layer_collections, &view_layer_src->layer_collections);
 +
 +      Object *active_ob = OBACT(view_layer_src);
 +      for (Base *base_src = view_layer_src->object_bases.first, *base_dst = view_layer_dst->object_bases.first;
 +           base_src;
 +           base_src = base_src->next, base_dst = base_dst->next)
 +      {
 +              base_dst->flag = base_src->flag;
 +              base_dst->flag_legacy = base_src->flag_legacy;
 +
 +              if (base_dst->object == active_ob) {
 +                      view_layer_dst->basact = base_dst;
 +              }
 +      }
 +}
 +
 +/**
 + * Find and return the ListBase of LayerCollection that has \a lc_child as one of its directly
 + * nested LayerCollection.
 + *
 + * \param lb_parent Initial ListBase of LayerCollection to look into recursively
 + * usually the view layer's collection list
 + */
 +static ListBase *find_layer_collection_parent_list_base(ListBase *lb_parent, const LayerCollection *lc_child)
 +{
 +      for (LayerCollection *lc_nested = lb_parent->first; lc_nested; lc_nested = lc_nested->next) {
 +              if (lc_nested == lc_child) {
 +                      return lb_parent;
 +              }
 +
 +              ListBase *found = find_layer_collection_parent_list_base(&lc_nested->layer_collections, lc_child);
 +              if (found != NULL) {
 +                      return found;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +/**
 + * Makes a shallow copy of a LayerCollection
 + *
 + * Add a new collection in the same level as the old one (linking if necessary),
 + * and copy all the collection data across them.
 + */
 +struct LayerCollection *BKE_layer_collection_duplicate(struct ID *owner_id, struct LayerCollection *layer_collection)
 +{
 +      SceneCollection *scene_collection, *scene_collection_new;
 +
 +      scene_collection = layer_collection->scene_collection;
 +      scene_collection_new = BKE_collection_duplicate(owner_id, scene_collection);
 +
 +      LayerCollection *layer_collection_new = NULL;
 +
 +      /* If the original layer_collection was directly linked to the view layer
 +         we need to link the new scene collection here as well. */
 +      for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
 +              if (BLI_findindex(&view_layer->layer_collections, layer_collection) != -1) {
 +                      layer_collection_new = BKE_collection_link(view_layer, scene_collection_new);
 +                      layer_collection_sync_flags(layer_collection_new, layer_collection);
 +
 +                      if (layer_collection_new != layer_collection->next) {
 +                              BLI_remlink(&view_layer->layer_collections, layer_collection_new);
 +                              BLI_insertlinkafter(&view_layer->layer_collections, layer_collection, layer_collection_new);
 +                      }
 +                      break;
 +              }
 +      }
 +
 +      /* Otherwise just try to find the corresponding layer collection to return it back. */
 +      if (layer_collection_new == NULL) {
 +              for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
 +                      ListBase *layer_collections_parent;
 +                      layer_collections_parent = find_layer_collection_parent_list_base(
 +                                                     &view_layer->layer_collections,
 +                                                     layer_collection);
 +                      if (layer_collections_parent != NULL) {
 +                              layer_collection_new = BLI_findptr(
 +                                      layer_collections_parent,
 +                                      scene_collection_new,
 +                                      offsetof(LayerCollection, scene_collection));
 +                              break;
 +                      }
 +              }
 +      }
 +      return layer_collection_new;
 +}
 +
 +static void view_layer_object_base_unref(ViewLayer *view_layer, Base *base)
 +{
 +      base->refcount--;
 +
 +      /* It only exists in the RenderLayer */
 +      if (base->refcount == 0) {
 +              if (view_layer->basact == base) {
 +                      view_layer->basact = NULL;
 +              }
 +
 +              if (base->collection_properties) {
 +                      IDP_FreeProperty(base->collection_properties);
 +                      MEM_freeN(base->collection_properties);
 +              }
 +
 +              BLI_remlink(&view_layer->object_bases, base);
 +              MEM_freeN(base);
 +      }
 +}
 +
 +/**
 + * Return the base if existent, or create it if necessary
 + * Always bump the refcount
 + */
 +static Base *object_base_add(ViewLayer *view_layer, Object *ob)
 +{
 +      Base *base;
 +      base = BKE_view_layer_base_find(view_layer, ob);
 +
 +      if (base == NULL) {
 +              base = MEM_callocN(sizeof(Base), "Object Base");
 +
 +              /* Do not bump user count, leave it for SceneCollections. */
 +              base->object = ob;
 +              BLI_addtail(&view_layer->object_bases, base);
 +
 +              IDPropertyTemplate val = {0};
 +              base->collection_properties = IDP_New(IDP_GROUP, &val, ROOT_PROP);
 +      }
 +
 +      base->refcount++;
 +      return base;
 +}
 +
 +/* LayerCollection */
 +
 +static void layer_collection_objects_unpopulate(ViewLayer *view_layer, LayerCollection *lc)
 +{
 +      if (view_layer) {
 +              for (LinkData *link = lc->object_bases.first; link; link = link->next) {
 +                      view_layer_object_base_unref(view_layer, link->data);
 +              }
 +      }
 +
 +      BLI_freelistN(&lc->object_bases);
 +}
 +
 +/**
 + * When freeing the entire ViewLayer at once we don't bother with unref
 + * otherwise ViewLayer is passed to keep the syncing of the LayerCollection tree
 + */
 +static void layer_collection_free(ViewLayer *view_layer, LayerCollection *lc)
 +{
 +      layer_collection_objects_unpopulate(view_layer, lc);
 +      BLI_freelistN(&lc->overrides);
 +
 +      if (lc->properties) {
 +              IDP_FreeProperty(lc->properties);
 +              MEM_freeN(lc->properties);
 +      }
 +
 +      if (lc->properties_evaluated) {
 +              IDP_FreeProperty(lc->properties_evaluated);
 +              MEM_freeN(lc->properties_evaluated);
 +      }
 +
 +      for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) {
 +              layer_collection_free(view_layer, nlc);
 +      }
 +
 +      BLI_freelistN(&lc->layer_collections);
 +}
 +
 +/**
 + * Free (or release) LayerCollection from ViewLayer
 + * (does not free the LayerCollection itself).
 + */
 +void BKE_layer_collection_free(ViewLayer *view_layer, LayerCollection *lc)
 +{
 +      layer_collection_free(view_layer, lc);
 +}
 +
 +/* LayerCollection */
 +
 +/**
 + * Recursively get the collection for a given index
 + */
 +static LayerCollection *collection_from_index(ListBase *lb, const int number, int *i)
 +{
 +      for (LayerCollection *lc = lb->first; lc; lc = lc->next) {
 +              if (*i == number) {
 +                      return lc;
 +              }
 +
 +              (*i)++;
 +
 +              LayerCollection *lc_nested = collection_from_index(&lc->layer_collections, number, i);
 +              if (lc_nested) {
 +                      return lc_nested;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +/**
 + * Get the collection for a given index
 + */
 +LayerCollection *BKE_layer_collection_from_index(ViewLayer *view_layer, const int index)
 +{
 +      int i = 0;
 +      return collection_from_index(&view_layer->layer_collections, index, &i);
 +}
 +
 +/**
 + * Get the active collection
 + */
 +LayerCollection *BKE_layer_collection_get_active(ViewLayer *view_layer)
 +{
 +      int i = 0;
 +      return collection_from_index(&view_layer->layer_collections, view_layer->active_collection, &i);
 +}
 +
 +
 +/**
 + * Return layer collection to add new object(s).
 + * Create one if none exists.
 + */
 +LayerCollection *BKE_layer_collection_get_active_ensure(Scene *scene, ViewLayer *view_layer)
 +{
 +      LayerCollection *lc = BKE_layer_collection_get_active(view_layer);
 +
 +      if (lc == NULL) {
 +              BLI_assert(BLI_listbase_is_empty(&view_layer->layer_collections));
 +              /* When there is no collection linked to this ViewLayer, create one. */
 +              SceneCollection *sc = BKE_collection_add(&scene->id, NULL, COLLECTION_TYPE_NONE, NULL);
 +              lc = BKE_collection_link(view_layer, sc);
 +              /* New collection has to be the active one. */
 +              BLI_assert(lc == BKE_layer_collection_get_active(view_layer));
 +      }
 +
 +      return lc;
 +}
 +
 +/**
 + * Recursively get the count of collections
 + */
 +static int collection_count(ListBase *lb)
 +{
 +      int i = 0;
 +      for (LayerCollection *lc = lb->first; lc; lc = lc->next) {
 +              i += collection_count(&lc->layer_collections) + 1;
 +      }
 +      return i;
 +}
 +
 +/**
 + * Get the total number of collections
 + * (including all the nested collections)
 + */
 +int BKE_layer_collection_count(ViewLayer *view_layer)
 +{
 +      return collection_count(&view_layer->layer_collections);
 +}
 +
 +/**
 + * Recursively get the index for a given collection
 + */
 +static int index_from_collection(ListBase *lb, const LayerCollection *lc, int *i)
 +{
 +      for (LayerCollection *lcol = lb->first; lcol; lcol = lcol->next) {
 +              if (lcol == lc) {
 +                      return *i;
 +              }
 +
 +              (*i)++;
 +
 +              int i_nested = index_from_collection(&lcol->layer_collections, lc, i);
 +              if (i_nested != -1) {
 +                      return i_nested;
 +              }
 +      }
 +      return -1;
 +}
 +
 +/**
 + * Return -1 if not found
 + */
 +int BKE_layer_collection_findindex(ViewLayer *view_layer, const LayerCollection *lc)
 +{
 +      int i = 0;
 +      return index_from_collection(&view_layer->layer_collections, lc, &i);
 +}
 +
 +/**
 + * Lookup the listbase that contains \a lc.
 + */
 +static ListBase *layer_collection_listbase_find(ListBase *lb, LayerCollection *lc)
 +{
 +      for (LayerCollection *lc_iter = lb->first; lc_iter; lc_iter = lc_iter->next) {
 +              if (lc_iter == lc) {
 +                      return lb;
 +              }
 +
 +              ListBase *lb_child_result;
 +              if ((lb_child_result = layer_collection_listbase_find(&lc_iter->layer_collections, lc))) {
 +                      return lb_child_result;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +#if 0
 +/**
 + * Lookup the listbase that contains \a sc.
 + */
 +static ListBase *scene_collection_listbase_find(ListBase *lb, SceneCollection *sc)
 +{
 +      for (SceneCollection *sc_iter = lb->first; sc_iter; sc_iter = sc_iter->next) {
 +              if (sc_iter == sc) {
 +                      return lb;
 +              }
 +
 +              ListBase *lb_child_result;
 +              if ((lb_child_result = scene_collection_listbase_find(&sc_iter->scene_collections, sc))) {
 +                      return lb_child_result;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +#endif
 +
 +/* ---------------------------------------------------------------------- */
 +/* Outliner drag and drop */
 +
 +/**
 + * Nest a LayerCollection into another one
 + * Both collections must be from the same ViewLayer, return true if succeded.
 + *
 + * The LayerCollection will effectively be moved into the
 + * new (nested) position. So all the settings, overrides, ... go with it, and
 + * if the collection was directly linked to the ViewLayer it's then unlinked.
 + *
 + * For the other ViewLayers we simply resync the tree, without changing directly
 + * linked collections (even if they link to the same SceneCollection)
 + *
 + * \param lc_src LayerCollection to nest into \a lc_dst
 + * \param lc_dst LayerCollection to have \a lc_src inserted into
 + */
 +
 +static void layer_collection_swap(
 +        ViewLayer *view_layer, ListBase *lb_a, ListBase *lb_b,
 +        LayerCollection *lc_a, LayerCollection *lc_b)
 +{
 +      if (lb_a == NULL) {
 +              lb_a = layer_collection_listbase_find(&view_layer->layer_collections, lc_a);
 +      }
 +
 +      if (lb_b == NULL) {
 +              lb_b = layer_collection_listbase_find(&view_layer->layer_collections, lc_b);
 +      }
 +
 +      BLI_assert(lb_a);
 +      BLI_assert(lb_b);
 +
 +      BLI_listbases_swaplinks(lb_a, lb_b, lc_a, lc_b);
 +}
 +
 +/**
 + * Move \a lc_src into \a lc_dst. Both have to be stored in \a view_layer.
 + * If \a lc_src is directly linked to the ViewLayer it's unlinked
 + */
 +bool BKE_layer_collection_move_into(const ID *owner_id, LayerCollection *lc_dst, LayerCollection *lc_src)
 +{
 +      ViewLayer *view_layer = BKE_view_layer_find_from_collection(owner_id, lc_src);
 +      bool is_directly_linked = false;
 +
 +      if ((!view_layer) || (view_layer != BKE_view_layer_find_from_collection(owner_id, lc_dst))) {
 +              return false;
 +      }
 +
 +      /* We can't nest the collection into itself */
 +      if (lc_src->scene_collection == lc_dst->scene_collection) {
 +              return false;
 +      }
 +
 +      /* Collection is already where we wanted it to be */
 +      if (lc_dst->layer_collections.last == lc_src) {
 +              return false;
 +      }
 +
 +      /* Collection is already where we want it to be in the scene tree
 +       * but we want to swap it in the layer tree still */
 +      if (lc_dst->scene_collection->scene_collections.last == lc_src->scene_collection) {
 +              LayerCollection *lc_swap = lc_dst->layer_collections.last;
 +              layer_collection_swap(view_layer, &lc_dst->layer_collections, NULL, lc_dst->layer_collections.last, lc_src);
 +
 +              if (BLI_findindex(&view_layer->layer_collections, lc_swap) != -1) {
 +                      BKE_collection_unlink(view_layer, lc_swap);
 +              }
 +              return true;
 +      }
 +      else {
 +              LayerCollection *lc_temp;
 +              is_directly_linked = BLI_findindex(&view_layer->layer_collections, lc_src) != -1;
 +
 +              if (!is_directly_linked) {
 +                      /* lc_src will be invalid after BKE_collection_move_into!
 +                       * so we swap it with lc_temp to preserve its settings */
 +                      lc_temp = BKE_collection_link(view_layer, lc_src->scene_collection);
 +                      layer_collection_swap(view_layer, &view_layer->layer_collections, NULL, lc_temp, lc_src);
 +              }
 +
 +              if (!BKE_collection_move_into(owner_id, lc_dst->scene_collection, lc_src->scene_collection)) {
 +                      if (!is_directly_linked) {
 +                              /* Swap back and remove */
 +                              layer_collection_swap(view_layer, NULL, NULL, lc_temp, lc_src);
 +                              BKE_collection_unlink(view_layer, lc_temp);
 +                      }
 +                      return false;
 +              }
 +      }
 +
 +      LayerCollection *lc_new = BLI_findptr(
 +              &lc_dst->layer_collections, lc_src->scene_collection, offsetof(LayerCollection, scene_collection));
 +      BLI_assert(lc_new);
 +      layer_collection_swap(view_layer, &lc_dst->layer_collections, NULL, lc_new, lc_src);
 +
 +      /* If it's directly linked, unlink it after the swap */
 +      if (BLI_findindex(&view_layer->layer_collections, lc_new) != -1) {
 +              BKE_collection_unlink(view_layer, lc_new);
 +      }
 +
 +      return true;
 +}
 +
 +/**
 + * Move \a lc_src above \a lc_dst. Both have to be stored in \a view_layer.
 + * If \a lc_src is directly linked to the ViewLayer it's unlinked
 + */
 +bool BKE_layer_collection_move_above(const ID *owner_id, LayerCollection *lc_dst, LayerCollection *lc_src)
 +{
 +      ViewLayer *view_layer = BKE_view_layer_find_from_collection(owner_id, lc_src);
 +      const bool is_directly_linked_src = BLI_findindex(&view_layer->layer_collections, lc_src) != -1;
 +      const bool is_directly_linked_dst = BLI_findindex(&view_layer->layer_collections, lc_dst) != -1;
 +
 +      if ((!view_layer) || (view_layer != BKE_view_layer_find_from_collection(owner_id, lc_dst))) {
 +              return false;
 +      }
 +
 +      /* Collection is already where we wanted it to be */
 +      if (lc_dst->prev == lc_src) {
 +              return false;
 +      }
 +
 +      /* Collection is already where we want it to be in the scene tree
 +       * but we want to swap it in the layer tree still */
 +      if (lc_dst->prev && lc_dst->prev->scene_collection == lc_src->scene_collection) {
 +              LayerCollection *lc_swap = lc_dst->prev;
 +              layer_collection_swap(view_layer, NULL, NULL, lc_dst->prev, lc_src);
 +
 +              if (BLI_findindex(&view_layer->layer_collections, lc_swap) != -1) {
 +                      BKE_collection_unlink(view_layer, lc_swap);
 +              }
 +              return true;
 +      }
 +      /* We don't allow to move above/below a directly linked collection
 +       * unless the source collection is also directly linked */
 +      else if (is_directly_linked_dst) {
 +              /* Both directly linked to the ViewLayer, just need to swap */
 +              if (is_directly_linked_src) {
 +                      BLI_remlink(&view_layer->layer_collections, lc_src);
 +                      BLI_insertlinkbefore(&view_layer->layer_collections, lc_dst, lc_src);
 +                      return true;
 +              }
 +              else {
 +                      return false;
 +              }
 +      }
 +      else {
 +              LayerCollection *lc_temp;
 +
 +              if (!is_directly_linked_src) {
 +                      /* lc_src will be invalid after BKE_collection_move_into!
 +                       * so we swap it with lc_temp to preserve its settings */
 +                      lc_temp = BKE_collection_link(view_layer, lc_src->scene_collection);
 +                      layer_collection_swap(view_layer, &view_layer->layer_collections, NULL, lc_temp, lc_src);
 +              }
 +
 +              if (!BKE_collection_move_above(owner_id, lc_dst->scene_collection, lc_src->scene_collection)) {
 +                      if (!is_directly_linked_src) {
 +                              /* Swap back and remove */
 +                              layer_collection_swap(view_layer, NULL, NULL, lc_temp, lc_src);
 +                              BKE_collection_unlink(view_layer, lc_temp);
 +                      }
 +                      return false;
 +              }
 +      }
 +
 +      LayerCollection *lc_new = lc_dst->prev;
 +      BLI_assert(lc_new);
 +      layer_collection_swap(view_layer, NULL, NULL, lc_new, lc_src);
 +
 +      /* If it's directly linked, unlink it after the swap */
 +      if (BLI_findindex(&view_layer->layer_collections, lc_new) != -1) {
 +              BKE_collection_unlink(view_layer, lc_new);
 +      }
 +
 +      return true;
 +}
 +
 +/**
 + * Move \a lc_src below \a lc_dst. Both have to be stored in \a view_layer.
 + * If \a lc_src is directly linked to the ViewLayer it's unlinked
 + */
 +bool BKE_layer_collection_move_below(const ID *owner_id, LayerCollection *lc_dst, LayerCollection *lc_src)
 +{
 +      ViewLayer *view_layer = BKE_view_layer_find_from_collection(owner_id, lc_src);
 +      const bool is_directly_linked_src = BLI_findindex(&view_layer->layer_collections, lc_src) != -1;
 +      const bool is_directly_linked_dst = BLI_findindex(&view_layer->layer_collections, lc_dst) != -1;
 +
 +      if ((!view_layer) || (view_layer != BKE_view_layer_find_from_collection(owner_id, lc_dst))) {
 +              return false;
 +      }
 +
 +      /* Collection is already where we wanted it to be */
 +      if (lc_dst->next == lc_src) {
 +              return false;
 +      }
 +
 +      /* Collection is already where we want it to be in the scene tree
 +       * but we want to swap it in the layer tree still */
 +      if (lc_dst->next && lc_dst->next->scene_collection == lc_src->scene_collection) {
 +              LayerCollection *lc_swap = lc_dst->next;
 +              layer_collection_swap(view_layer, NULL, NULL, lc_dst->next, lc_src);
 +
 +              if (BLI_findindex(&view_layer->layer_collections, lc_swap) != -1) {
 +                      BKE_collection_unlink(view_layer, lc_swap);
 +              }
 +              return true;
 +      }
 +      /* We don't allow to move above/below a directly linked collection
 +       * unless the source collection is also directly linked */
 +      else if (is_directly_linked_dst) {
 +              /* Both directly linked to the ViewLayer, just need to swap */
 +              if (is_directly_linked_src) {
 +                      BLI_remlink(&view_layer->layer_collections, lc_src);
 +                      BLI_insertlinkafter(&view_layer->layer_collections, lc_dst, lc_src);
 +                      return true;
 +              }
 +              else {
 +                      return false;
 +              }
 +      }
 +      else {
 +              LayerCollection *lc_temp;
 +
 +              if (!is_directly_linked_src) {
 +                      /* lc_src will be invalid after BKE_collection_move_into!
 +                       * so we swap it with lc_temp to preserve its settings */
 +                      lc_temp = BKE_collection_link(view_layer, lc_src->scene_collection);
 +                      layer_collection_swap(view_layer, &view_layer->layer_collections, NULL, lc_temp, lc_src);
 +              }
 +
 +              if (!BKE_collection_move_below(owner_id, lc_dst->scene_collection, lc_src->scene_collection)) {
 +                      if (!is_directly_linked_src) {
 +                              /* Swap back and remove */
 +                              layer_collection_swap(view_layer, NULL, NULL, lc_temp, lc_src);
 +                              BKE_collection_unlink(view_layer, lc_temp);
 +                      }
 +                      return false;
 +              }
 +      }
 +
 +      LayerCollection *lc_new = lc_dst->next;
 +      BLI_assert(lc_new);
 +      layer_collection_swap(view_layer, NULL, NULL, lc_new, lc_src);
 +
 +      /* If it's directly linked, unlink it after the swap */
 +      if (BLI_findindex(&view_layer->layer_collections, lc_new) != -1) {
 +              BKE_collection_unlink(view_layer, lc_new);
 +      }
 +
 +      return true;
 +}
 +
 +static bool layer_collection_resync(ViewLayer *view_layer, LayerCollection *lc, const SceneCollection *sc)
 +{
 +      if (lc->scene_collection == sc) {
 +              ListBase collections = {NULL};
 +              BLI_movelisttolist(&collections, &lc->layer_collections);
 +
 +              for (SceneCollection *sc_nested = sc->scene_collections.first; sc_nested; sc_nested = sc_nested->next) {
 +                      LayerCollection *lc_nested = BLI_findptr(&collections, sc_nested, offsetof(LayerCollection, scene_collection));
 +                      if (lc_nested) {
 +                              BLI_remlink(&collections, lc_nested);
 +                              BLI_addtail(&lc->layer_collections, lc_nested);
 +                      }
 +                      else {
 +                              layer_collection_add(view_layer, lc, sc_nested);
 +                      }
 +              }
 +
 +              for (LayerCollection *lc_nested = collections.first; lc_nested; lc_nested = lc_nested->next) {
 +                      layer_collection_free(view_layer, lc_nested);
 +              }
 +              BLI_freelistN(&collections);
 +
 +              BLI_assert(BLI_listbase_count(&lc->layer_collections) ==
 +                         BLI_listbase_count(&sc->scene_collections));
 +
 +              return true;
 +      }
 +
 +      for (LayerCollection *lc_nested = lc->layer_collections.first; lc_nested; lc_nested = lc_nested->next) {
 +              if (layer_collection_resync(view_layer, lc_nested, sc)) {
 +                      return true;
 +              }
 +      }
 +
 +      return false;
 +}
 +
 +/**
 + * Update the scene layers so that any LayerCollection that points
 + * to \a sc is re-synced again
 + */
 +void BKE_layer_collection_resync(const ID *owner_id, const SceneCollection *sc)
 +{
 +      for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
 +              for (LayerCollection *lc = view_layer->layer_collections.first; lc; lc = lc->next) {
 +                      layer_collection_resync(view_layer, lc, sc);
 +              }
 +      }
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/**
 + * Select all the objects of this layer collection
 + *
 + * It also select the objects that are in nested collections.
 + * \note Recursive
 + */
 +void BKE_layer_collection_objects_select(struct LayerCollection *layer_collection)
 +{
 +      if ((layer_collection->flag & COLLECTION_DISABLED) ||
 +          ((layer_collection->flag & COLLECTION_SELECTABLE) == 0))
 +      {
 +              return;
 +      }
 +
 +      for (LinkData *link = layer_collection->object_bases.first; link; link = link->next) {
 +              Base *base = link->data;
 +              if (base->flag & BASE_SELECTABLED) {
 +                      base->flag |= BASE_SELECTED;
 +              }
 +      }
 +
 +      for (LayerCollection *iter = layer_collection->layer_collections.first; iter; iter = iter->next) {
 +              BKE_layer_collection_objects_select(iter);
 +      }
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/**
 + * Link a collection to a renderlayer
 + * The collection needs to be created separately
 + */
 +LayerCollection *BKE_collection_link(ViewLayer *view_layer, SceneCollection *sc)
 +{
 +      LayerCollection *lc = layer_collection_add(view_layer, NULL, sc);
 +      view_layer->active_collection = BKE_layer_collection_findindex(view_layer, lc);
 +      return lc;
 +}
 +
 +/**
 + * Unlink a collection base from a renderlayer
 + * The corresponding collection is not removed from the master collection
 + */
 +void BKE_collection_unlink(ViewLayer *view_layer, LayerCollection *lc)
 +{
 +      BKE_layer_collection_free(view_layer, lc);
 +      BLI_remlink(&view_layer->layer_collections, lc);
 +      MEM_freeN(lc);
 +      view_layer->active_collection = 0;
 +}
 +
 +/**
 + * Recursively enable nested collections
 + */
 +static void layer_collection_enable(ViewLayer *view_layer, LayerCollection *lc)
 +{
 +      layer_collection_objects_populate(view_layer, lc, &lc->scene_collection->objects);
 +
 +      for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) {
 +              layer_collection_enable(view_layer, nlc);
 +      }
 +}
 +
 +/**
 + * Enable collection
 + * Add its objects bases to ViewLayer
 + *
 + * Only around for doversion.
 + */
 +void BKE_collection_enable(ViewLayer *view_layer, LayerCollection *lc)
 +{
 +      if ((lc->flag & COLLECTION_DISABLED) == 0) {
 +              return;
 +      }
 +
 +      lc->flag &= ~COLLECTION_DISABLED;
 +      layer_collection_enable(view_layer, lc);
 +}
 +
 +static void layer_collection_object_add(ViewLayer *view_layer, LayerCollection *lc, Object *ob)
 +{
 +      Base *base = object_base_add(view_layer, ob);
 +
 +      /* Only add an object once. */
 +      if (BLI_findptr(&lc->object_bases, base, offsetof(LinkData, data))) {
 +              return;
 +      }
 +
 +      bool is_visible = ((lc->flag & COLLECTION_VIEWPORT) != 0) && ((lc->flag & COLLECTION_DISABLED) == 0);
 +      bool is_selectable = is_visible && ((lc->flag & COLLECTION_SELECTABLE) != 0);
 +
 +      if (is_visible) {
 +              base->flag |= BASE_VISIBLED;
 +      }
 +
 +      if (is_selectable) {
 +              base->flag |= BASE_SELECTABLED;
 +      }
 +
 +      BLI_addtail(&lc->object_bases, BLI_genericNodeN(base));
 +}
 +
 +static void layer_collection_object_remove(ViewLayer *view_layer, LayerCollection *lc, Object *ob)
 +{
 +      Base *base;
 +      base = BKE_view_layer_base_find(view_layer, ob);
 +
 +      LinkData *link = BLI_findptr(&lc->object_bases, base, offsetof(LinkData, data));
 +      BLI_remlink(&lc->object_bases, link);
 +      MEM_freeN(link);
 +
 +      view_layer_object_base_unref(view_layer, base);
 +}
 +
 +static void layer_collection_objects_populate(ViewLayer *view_layer, LayerCollection *lc, ListBase *objects)
 +{
 +      for (LinkData *link = objects->first; link; link = link->next) {
 +              layer_collection_object_add(view_layer, lc, link->data);
 +      }
 +}
 +
 +static void layer_collection_populate(ViewLayer *view_layer, LayerCollection *lc, SceneCollection *sc)
 +{
 +      layer_collection_objects_populate(view_layer, lc, &sc->objects);
 +
 +      for (SceneCollection *nsc = sc->scene_collections.first; nsc; nsc = nsc->next) {
 +              layer_collection_add(view_layer, lc, nsc);
 +      }
 +}
 +
 +static LayerCollection *layer_collection_add(ViewLayer *view_layer, LayerCollection *parent, SceneCollection *sc)
 +{
 +      IDPropertyTemplate val = {0};
 +      LayerCollection *lc = MEM_callocN(sizeof(LayerCollection), "Collection Base");
 +
 +      lc->scene_collection = sc;
 +      lc->flag = COLLECTION_SELECTABLE | COLLECTION_VIEWPORT | COLLECTION_RENDER;
 +
 +      lc->properties = IDP_New(IDP_GROUP, &val, ROOT_PROP);
 +      collection_engine_settings_init(lc->properties, false);
 +
 +      if (parent != NULL) {
 +              BLI_addtail(&parent->layer_collections, lc);
 +      }
 +      else {
 +              BLI_addtail(&view_layer->layer_collections, lc);
 +      }
 +
 +      layer_collection_populate(view_layer, lc, sc);
 +
 +      return lc;
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/**
 + * See if render layer has the scene collection linked directly, or indirectly (nested)
 + */
 +bool BKE_view_layer_has_collection(ViewLayer *view_layer, const SceneCollection *sc)
 +{
 +      for (LayerCollection *lc = view_layer->layer_collections.first; lc; lc = lc->next) {
 +              if (find_layer_collection_by_scene_collection(lc, sc) != NULL) {
 +                      return true;
 +              }
 +      }
 +      return false;
 +}
 +
 +/**
 + * See if the object is in any of the scene layers of the scene
 + */
 +bool BKE_scene_has_object(Scene *scene, Object *ob)
 +{
 +      for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +              Base *base = BKE_view_layer_base_find(view_layer, ob);
 +              if (base) {
 +                      return true;
 +              }
 +      }
 +      return false;
 +}
 +
 +
 +/* ---------------------------------------------------------------------- */
 +/* Syncing */
 +
 +static LayerCollection *find_layer_collection_by_scene_collection(LayerCollection *lc, const SceneCollection *sc)
 +{
 +      if (lc->scene_collection == sc) {
 +              return lc;
 +      }
 +
 +      for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) {
 +              LayerCollection *found = find_layer_collection_by_scene_collection(nlc, sc);
 +              if (found) {
 +                      return found;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +/**
 + * Add a new LayerCollection for all the ViewLayers that have sc_parent
 + */
 +void BKE_layer_sync_new_scene_collection(ID *owner_id, const SceneCollection *sc_parent, SceneCollection *sc)
 +{
 +      for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
 +              for (LayerCollection *lc = view_layer->layer_collections.first; lc; lc = lc->next) {
 +                      LayerCollection *lc_parent = find_layer_collection_by_scene_collection(lc, sc_parent);
 +                      if (lc_parent) {
 +                              layer_collection_add(view_layer, lc_parent, sc);
 +                      }
 +              }
 +      }
 +}
 +
 +/**
 + * Add a corresponding ObjectBase to all the equivalent LayerCollection
 + */
 +void BKE_layer_sync_object_link(const ID *owner_id, SceneCollection *sc, Object *ob)
 +{
 +      for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
 +              for (LayerCollection *lc = view_layer->layer_collections.first; lc; lc = lc->next) {
 +                      LayerCollection *found = find_layer_collection_by_scene_collection(lc, sc);
 +                      if (found) {
 +                              layer_collection_object_add(view_layer, found, ob);
 +                      }
 +              }
 +      }
 +}
 +
 +/**
 + * Remove the equivalent object base to all layers that have this collection
 + */
 +void BKE_layer_sync_object_unlink(const ID *owner_id, SceneCollection *sc, Object *ob)
 +{
 +      for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
 +              for (LayerCollection *lc = view_layer->layer_collections.first; lc; lc = lc->next) {
 +                      LayerCollection *found = find_layer_collection_by_scene_collection(lc, sc);
 +                      if (found) {
 +                              layer_collection_object_remove(view_layer, found, ob);
 +                      }
 +              }
 +      }
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +/* Override */
 +
 +/**
 + * Add a new datablock override
 + */
 +void BKE_override_view_layer_datablock_add(ViewLayer *view_layer, int id_type, const char *data_path, const ID *owner_id)
 +{
 +      UNUSED_VARS(view_layer, id_type, data_path, owner_id);
 +      TODO_LAYER_OVERRIDE;
 +}
 +
 +/**
 + * Add a new int override
 + */
 +void BKE_override_view_layer_int_add(ViewLayer *view_layer, int id_type, const char *data_path, const int value)
 +{
 +      UNUSED_VARS(view_layer, id_type, data_path, value);
 +      TODO_LAYER_OVERRIDE;
 +}
 +
 +/**
 + * Add a new boolean override
 + */
 +void BKE_override_layer_collection_boolean_add(struct LayerCollection *layer_collection, int id_type, const char *data_path, const bool value)
 +{
 +      UNUSED_VARS(layer_collection, id_type, data_path, value);
 +      TODO_LAYER_OVERRIDE;
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +/* Engine Settings */
 +
 +ListBase R_layer_collection_engines_settings_callbacks = {NULL, NULL};
 +ListBase R_view_layer_engines_settings_callbacks = {NULL, NULL};
 +
 +typedef struct EngineSettingsCB_Type {
 +      struct EngineSettingsCB_Type *next, *prev;
 +
 +      char name[MAX_NAME]; /* engine name */
 +
 +      EngineSettingsCB callback;
 +
 +} EngineSettingsCB_Type;
 +
 +static void create_engine_settings_scene(IDProperty *root, EngineSettingsCB_Type *es_type)
 +{
 +      if (collection_engine_get(root, COLLECTION_MODE_NONE, es_type->name)) {
 +              return;
 +      }
 +
 +      IDProperty *props = collection_engine_settings_create(es_type, true);
 +      IDP_AddToGroup(root, props);
 +}
 +
 +static void create_layer_collection_engine_settings_scene(Scene *scene, EngineSettingsCB_Type *es_type)
 +{
 +      create_engine_settings_scene(scene->collection_properties, es_type);
 +}
 +
 +static void create_view_layer_engine_settings_scene(Scene *scene, EngineSettingsCB_Type *es_type)
 +{
 +      create_engine_settings_scene(scene->layer_properties, es_type);
 +}
 +
 +static void create_layer_collection_engine_settings_collection(LayerCollection *lc, EngineSettingsCB_Type *es_type)
 +{
 +      if (BKE_layer_collection_engine_collection_get(lc, COLLECTION_MODE_NONE, es_type->name)) {
 +              return;
 +      }
 +
 +      IDProperty *props = collection_engine_settings_create(es_type, false);
 +      IDP_AddToGroup(lc->properties, props);
 +
 +      for (LayerCollection *lcn = lc->layer_collections.first; lcn; lcn = lcn->next) {
 +              create_layer_collection_engine_settings_collection(lcn, es_type);
 +      }
 +}
 +
 +static void create_layer_collection_engines_settings_scene(Scene *scene, EngineSettingsCB_Type *es_type)
 +{
 +      /* Populate the scene with the new settings. */
 +      create_layer_collection_engine_settings_scene(scene, es_type);
 +
 +      for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +              for (LayerCollection *lc = view_layer->layer_collections.first; lc; lc = lc->next) {
 +                      create_layer_collection_engine_settings_collection(lc, es_type);
 +              }
 +      }
 +}
 +
 +static void create_view_layer_engines_settings_scene(Scene *scene, EngineSettingsCB_Type *es_type)
 +{
 +      /* Populate the scene with the new settings. */
 +      create_view_layer_engine_settings_scene(scene, es_type);
 +}
 +
 +static void create_view_layer_engines_settings_layer(ViewLayer *view_layer, EngineSettingsCB_Type *es_type)
 +{
 +      if (BKE_view_layer_engine_layer_get(view_layer, COLLECTION_MODE_NONE, es_type->name)) {
 +              return;
 +      }
 +
 +      IDProperty *props = collection_engine_settings_create(es_type, false);
 +      IDP_AddToGroup(view_layer->properties, props);
 +}
 +
 +static EngineSettingsCB_Type *engine_settings_callback_register(const char *engine_name, EngineSettingsCB func, ListBase *lb)
 +{
 +      EngineSettingsCB_Type *es_type;
 +
 +      /* Cleanup in case it existed. */
 +      es_type = BLI_findstring(lb, engine_name, offsetof(EngineSettingsCB_Type, name));
 +
 +      if (es_type) {
 +              BLI_remlink(lb, es_type);
 +              MEM_freeN(es_type);
 +      }
 +
 +      es_type = MEM_callocN(sizeof(EngineSettingsCB_Type), __func__);
 +      BLI_strncpy_utf8(es_type->name, engine_name, sizeof(es_type->name));
 +      es_type->callback = func;
 +      BLI_addtail(lb, es_type);
 +
 +      return es_type;
 +}
 +
 +void BKE_layer_collection_engine_settings_callback_register(
 +        Main *bmain, const char *engine_name, EngineSettingsCB func)
 +{
 +      EngineSettingsCB_Type *es_type =
 +              engine_settings_callback_register(engine_name, func, &R_layer_collection_engines_settings_callbacks);
 +
 +      if (bmain) {
 +              /* Populate all of the collections of the scene with those settings. */
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      create_layer_collection_engines_settings_scene(scene, es_type);
 +              }
 +      }
 +}
 +
 +void BKE_view_layer_engine_settings_callback_register(
 +        Main *bmain, const char *engine_name, EngineSettingsCB func)
 +{
 +      EngineSettingsCB_Type *es_type =
 +              engine_settings_callback_register(engine_name, func, &R_view_layer_engines_settings_callbacks);
 +
 +      if (bmain) {
 +              /* Populate all of the collections of the scene with those settings. */
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      create_view_layer_engines_settings_scene(scene, es_type);
 +
 +                      for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +                              create_view_layer_engines_settings_layer(view_layer, es_type);
 +                      }
 +              }
 +      }
 +}
 +
 +void BKE_layer_collection_engine_settings_callback_free(void)
 +{
 +      BLI_freelistN(&R_layer_collection_engines_settings_callbacks);
 +}
 +
 +void BKE_view_layer_engine_settings_callback_free(void)
 +{
 +      BLI_freelistN(&R_view_layer_engines_settings_callbacks);
 +}
 +
 +/**
 + * Create a root IDProperty for this engine
 + *
 + * \param populate whether we want to pre-fill the collection with the default properties
 + */
 +static IDProperty *collection_engine_settings_create(EngineSettingsCB_Type *es_type, const bool populate)
 +{
 +      IDProperty *props;
 +      IDPropertyTemplate val = {0};
 +
 +      props = IDP_New(IDP_GROUP, &val, es_type->name);
 +      props->subtype = IDP_GROUP_SUB_ENGINE_RENDER;
 +
 +      /* properties */
 +      if (populate) {
 +              es_type->callback(NULL, props);
 +      }
 +
 +      return props;
 +}
 +
 +static void layer_collection_create_mode_settings_object(IDProperty *root, const bool populate)
 +{
 +      IDProperty *props;
 +      IDPropertyTemplate val = {0};
 +
 +      props = IDP_New(IDP_GROUP, &val, "ObjectMode");
 +      props->subtype = IDP_GROUP_SUB_MODE_OBJECT;
 +
 +      /* properties */
 +      if (populate) {
 +              OBJECT_collection_settings_create(props);
 +      }
 +
 +      IDP_AddToGroup(root, props);
 +}
 +
 +static void layer_collection_create_mode_settings_edit(IDProperty *root, const bool populate)
 +{
 +      IDProperty *props;
 +      IDPropertyTemplate val = {0};
 +
 +      props = IDP_New(IDP_GROUP, &val, "EditMode");
 +      props->subtype = IDP_GROUP_SUB_MODE_EDIT;
 +
 +      /* properties */
 +      if (populate) {
 +              EDIT_MESH_collection_settings_create(props);
 +      }
 +
 +      IDP_AddToGroup(root, props);
 +}
 +
 +static void layer_collection_create_mode_settings_paint_weight(IDProperty *root, const bool populate)
 +{
 +      IDProperty *props;
 +      IDPropertyTemplate val = {0};
 +
 +      props = IDP_New(IDP_GROUP, &val, "WeightPaintMode");
 +      props->subtype = IDP_GROUP_SUB_MODE_PAINT_WEIGHT;
 +
 +      /* properties */
 +      if (populate) {
 +              PAINT_WEIGHT_collection_settings_create(props);
 +      }
 +
 +      IDP_AddToGroup(root, props);
 +}
 +
 +static void layer_collection_create_mode_settings_paint_vertex(IDProperty *root, const bool populate)
 +{
 +      IDProperty *props;
 +      IDPropertyTemplate val = {0};
 +
 +      props = IDP_New(IDP_GROUP, &val, "VertexPaintMode");
 +      props->subtype = IDP_GROUP_SUB_MODE_PAINT_VERTEX;
 +
 +      /* properties */
 +      if (populate) {
 +              PAINT_VERTEX_collection_settings_create(props);
 +      }
 +
 +      IDP_AddToGroup(root, props);
 +}
 +
 +static void layer_collection_create_render_settings(IDProperty *root, const bool populate)
 +{
 +      EngineSettingsCB_Type *es_type;
 +      for (es_type = R_layer_collection_engines_settings_callbacks.first; es_type; es_type = es_type->next) {
 +              IDProperty *props = collection_engine_settings_create(es_type, populate);
 +              IDP_AddToGroup(root, props);
 +      }
 +}
 +
 +static void view_layer_create_render_settings(IDProperty *root, const bool populate)
 +{
 +      EngineSettingsCB_Type *es_type;
 +      for (es_type = R_view_layer_engines_settings_callbacks.first; es_type; es_type = es_type->next) {
 +              IDProperty *props = collection_engine_settings_create(es_type, populate);
 +              IDP_AddToGroup(root, props);
 +      }
 +}
 +
 +static void collection_create_mode_settings(IDProperty *root, const bool populate)
 +{
 +      /* XXX TODO: put all those engines in the R_engines_settings_callbacks
 +       * and have IDP_AddToGroup outside the callbacks */
 +      layer_collection_create_mode_settings_object(root, populate);
 +      layer_collection_create_mode_settings_edit(root, populate);
 +      layer_collection_create_mode_settings_paint_weight(root, populate);
 +      layer_collection_create_mode_settings_paint_vertex(root, populate);
 +}
 +
 +static void layer_create_mode_settings(IDProperty *root, const bool populate)
 +{
 +      TODO_LAYER; /* XXX like collection_create_mode_settings */
 +      UNUSED_VARS(root, populate);
 +}
 +
 +static int idproperty_group_subtype(const int mode_type)
 +{
 +      int idgroup_type;
 +
 +      switch (mode_type) {
 +              case COLLECTION_MODE_OBJECT:
 +                      idgroup_type = IDP_GROUP_SUB_MODE_OBJECT;
 +                      break;
 +              case COLLECTION_MODE_EDIT:
 +                      idgroup_type = IDP_GROUP_SUB_MODE_EDIT;
 +                      break;
 +              case COLLECTION_MODE_PAINT_WEIGHT:
 +                      idgroup_type = IDP_GROUP_SUB_MODE_PAINT_WEIGHT;
 +                      break;
 +              case COLLECTION_MODE_PAINT_VERTEX:
 +                      idgroup_type = IDP_GROUP_SUB_MODE_PAINT_VERTEX;
 +                      break;
 +              default:
 +              case COLLECTION_MODE_NONE:
 +                      return IDP_GROUP_SUB_ENGINE_RENDER;
 +                      break;
 +      }
 +
 +      return idgroup_type;
 +}
 +
 +/**
 + * Return collection enginne settings for either Object s of LayerCollection s
 + */
 +static IDProperty *collection_engine_get(
 +        IDProperty *root, const int type, const char *engine_name)
 +{
 +      const int subtype = idproperty_group_subtype(type);
 +
 +      if (subtype == IDP_GROUP_SUB_ENGINE_RENDER) {
 +              return IDP_GetPropertyFromGroup(root, engine_name);
 +      }
 +      else {
 +              IDProperty *prop;
 +              for (prop = root->data.group.first; prop; prop = prop->next) {
 +                      if (prop->subtype == subtype) {
 +                              return prop;
 +                      }
 +              }
 +      }
 +
 +      BLI_assert(false);
 +      return NULL;
 +}
 +
 +/**
 + * Return collection engine settings from Object for specified engine of mode
 + */
 +IDProperty *BKE_layer_collection_engine_evaluated_get(Object *ob, const int type, const char *engine_name)
 +{
 +      return collection_engine_get(ob->base_collection_properties, type, engine_name);
 +}
 +/**
 + * Return layer collection engine settings for specified engine
 + */
 +IDProperty *BKE_layer_collection_engine_collection_get(LayerCollection *lc, const int type, const char *engine_name)
 +{
 +      return collection_engine_get(lc->properties, type, engine_name);
 +}
 +
 +/**
 + * Return layer collection engine settings for specified engine in the scene
 + */
 +IDProperty *BKE_layer_collection_engine_scene_get(Scene *scene, const int type, const char *engine_name)
 +{
 +      return collection_engine_get(scene->collection_properties, type, engine_name);
 +}
 +
 +/**
 + * Return scene layer engine settings for specified engine in the scene
 + */
 +IDProperty *BKE_view_layer_engine_scene_get(Scene *scene, const int type, const char *engine_name)
 +{
 +      return collection_engine_get(scene->layer_properties, type, engine_name);
 +}
 +
 +/**
 + * Return scene layer engine settings for specified engine
 + */
 +IDProperty *BKE_view_layer_engine_layer_get(ViewLayer *view_layer, const int type, const char *engine_name)
 +{
 +      return collection_engine_get(view_layer->properties, type, engine_name);
 +}
 +
 +/**
 + * Return scene layer evaluated engine settings for specified engine
 + */
 +IDProperty *BKE_view_layer_engine_evaluated_get(ViewLayer *view_layer, const int type, const char *engine_name)
 +{
 +      return collection_engine_get(view_layer->properties_evaluated, type, engine_name);
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +/* Engine Settings Properties */
 +
 +void BKE_collection_engine_property_add_float(IDProperty *props, const char *name, float value)
 +{
 +      IDPropertyTemplate val = {0};
 +      val.f = value;
 +      IDP_AddToGroup(props, IDP_New(IDP_FLOAT, &val, name));
 +}
 +
 +void BKE_collection_engine_property_add_float_array(
 +        IDProperty *props, const char *name, const float *values, const int array_length)
 +{
 +      IDPropertyTemplate val = {0};
 +      val.array.len = array_length;
 +      val.array.type = IDP_FLOAT;
 +
 +      IDProperty *idprop = IDP_New(IDP_ARRAY, &val, name);
 +      memcpy(IDP_Array(idprop), values, sizeof(float) * idprop->len);
 +      IDP_AddToGroup(props, idprop);
 +}
 +
 +void BKE_collection_engine_property_add_int(IDProperty *props, const char *name, int value)
 +{
 +      IDPropertyTemplate val = {0};
 +      val.i = value;
 +      IDP_AddToGroup(props, IDP_New(IDP_INT, &val, name));
 +}
 +
 +void BKE_collection_engine_property_add_bool(IDProperty *props, const char *name, bool value)
 +{
 +      IDPropertyTemplate val = {0};
 +      val.i = value;
 +      IDP_AddToGroup(props, IDP_New(IDP_INT, &val, name));
 +}
 +
 +int BKE_collection_engine_property_value_get_int(IDProperty *props, const char *name)
 +{
 +      IDProperty *idprop = IDP_GetPropertyFromGroup(props, name);
 +      return idprop ? IDP_Int(idprop) : 0;
 +}
 +
 +float BKE_collection_engine_property_value_get_float(IDProperty *props, const char *name)
 +{
 +      IDProperty *idprop = IDP_GetPropertyFromGroup(props, name);
 +      return idprop ? IDP_Float(idprop) : 0.0f;
 +}
 +
 +const float *BKE_collection_engine_property_value_get_float_array(IDProperty *props, const char *name)
 +{
 +      IDProperty *idprop = IDP_GetPropertyFromGroup(props, name);
 +      return idprop ? IDP_Array(idprop) : NULL;
 +}
 +
 +bool BKE_collection_engine_property_value_get_bool(IDProperty *props, const char *name)
 +{
 +      IDProperty *idprop = IDP_GetPropertyFromGroup(props, name);
 +      return idprop ? IDP_Int(idprop) : 0;
 +}
 +
 +void BKE_collection_engine_property_value_set_int(IDProperty *props, const char *name, int value)
 +{
 +      IDProperty *idprop = IDP_GetPropertyFromGroup(props, name);
 +      IDP_Int(idprop) = value;
 +}
 +
 +void BKE_collection_engine_property_value_set_float(IDProperty *props, const char *name, float value)
 +{
 +      IDProperty *idprop = IDP_GetPropertyFromGroup(props, name);
 +      IDP_Float(idprop) = value;
 +}
 +
 +void BKE_collection_engine_property_value_set_float_array(IDProperty *props, const char *name, const float *values)
 +{
 +      IDProperty *idprop = IDP_GetPropertyFromGroup(props, name);
 +      memcpy(IDP_Array(idprop), values, sizeof(float) * idprop->len);
 +}
 +
 +void BKE_collection_engine_property_value_set_bool(IDProperty *props, const char *name, bool value)
 +{
 +      IDProperty *idprop = IDP_GetPropertyFromGroup(props, name);
 +      IDP_Int(idprop) = value;
 +}
 +
 +/* Engine Settings recalculate  */
 +
 +/* get all the default settings defined in scene and merge them here */
 +static void collection_engine_settings_init(IDProperty *root, const bool populate)
 +{
 +      /* render engines */
 +      layer_collection_create_render_settings(root, populate);
 +
 +      /* mode engines */
 +      collection_create_mode_settings(root, populate);
 +}
 +
 +/* get all the default settings defined in scene and merge them here */
 +static void layer_engine_settings_init(IDProperty *root, const bool populate)
 +{
 +      /* render engines */
 +      view_layer_create_render_settings(root, populate);
 +
 +      /* mode engines */
 +      layer_create_mode_settings(root, populate);
 +}
 +
 +/**
 + * Initialize the layer collection render setings
 + * It's used mainly for scenes
 + */
 +void BKE_layer_collection_engine_settings_create(IDProperty *root)
 +{
 +      collection_engine_settings_init(root, true);
 +}
 +
 +/**
 + * Initialize the render setings
 + * It's used mainly for scenes
 + */
 +void BKE_view_layer_engine_settings_create(IDProperty *root)
 +{
 +      layer_engine_settings_init(root, true);
 +}
 +
 +/**
 + * Reference of IDProperty group scene collection settings
 + * Used when reading blendfiles, to see if there is any missing settings.
 + */
 +static struct {
 +      struct {
 +              IDProperty *collection_properties;
 +              IDProperty *render_settings;
 +      } scene;
 +      IDProperty *view_layer;
 +      IDProperty *layer_collection;
 +} root_reference = {
 +      .scene = {NULL, NULL},
 +      .view_layer = NULL,
 +      .layer_collection = NULL,
 +};
 +
 +/**
 + * Free the reference scene collection settings IDProperty group.
 + */
 +static void engine_settings_validate_init(void)
 +{
 +      IDPropertyTemplate val = {0};
 +
 +      /* LayerCollection engine settings. */
 +      if (root_reference.scene.collection_properties == NULL) {
 +              root_reference.scene.collection_properties = IDP_New(IDP_GROUP, &val, ROOT_PROP);
 +              collection_engine_settings_init(root_reference.scene.collection_properties, true);
 +      }
 +
 +      if (root_reference.layer_collection == NULL) {
 +              root_reference.layer_collection = IDP_New(IDP_GROUP, &val, ROOT_PROP);
 +              collection_engine_settings_init(root_reference.layer_collection, false);
 +      }
 +
 +      /* Render engine setting. */
 +      if (root_reference.scene.render_settings == NULL) {
 +              root_reference.scene.render_settings = IDP_New(IDP_GROUP, &val, ROOT_PROP);
 +              layer_engine_settings_init(root_reference.scene.render_settings, true);
 +      }
 +
 +      if (root_reference.view_layer == NULL) {
 +              root_reference.view_layer = IDP_New(IDP_GROUP, &val, ROOT_PROP);
 +              layer_engine_settings_init(root_reference.view_layer, false);
 +      }
 +}
 +
 +/**
 + * Free the reference scene collection settings IDProperty group.
 + */
 +static void layer_collection_engine_settings_validate_free(void)
 +{
 +      IDProperty *idprops[] = {
 +          root_reference.scene.render_settings,
 +          root_reference.scene.collection_properties,
 +          root_reference.view_layer,
 +          root_reference.layer_collection,
 +          NULL,
 +      };
 +
 +      IDProperty **idprop = &idprops[0];
 +      while (*idprop) {
 +              if (*idprop) {
 +                      IDP_FreeProperty(*idprop);
 +                      MEM_freeN(*idprop);
 +                      *idprop = NULL;
 +                      idprop++;
 +              }
 +      }
 +}
 +
 +/**
 + * Make sure Scene has all required collection settings.
 + */
 +void BKE_layer_collection_engine_settings_validate_scene(Scene *scene)
 +{
 +      if (root_reference.scene.collection_properties == NULL) {
 +              engine_settings_validate_init();
 +      }
 +
 +      if (scene->collection_properties == NULL) {
 +              IDPropertyTemplate val = {0};
 +              scene->collection_properties = IDP_New(IDP_GROUP, &val, ROOT_PROP);
 +              BKE_layer_collection_engine_settings_create(scene->collection_properties);
 +      }
 +      else {
 +              IDP_MergeGroup(scene->collection_properties, root_reference.scene.collection_properties, false);
 +      }
 +}
 +
 +/**
 + * Maker sure LayerCollection has all required collection settings.
 + */
 +void BKE_layer_collection_engine_settings_validate_collection(LayerCollection *lc)
 +{
 +      if (root_reference.layer_collection == NULL) {
 +              engine_settings_validate_init();
 +      }
 +
 +      BLI_assert(lc->properties != NULL);
 +      IDP_MergeGroup(lc->properties, root_reference.layer_collection, false);
 +}
 +
 +/**
 + * Make sure Scene has all required collection settings.
 + */
 +void BKE_view_layer_engine_settings_validate_scene(Scene *scene)
 +{
 +      if (root_reference.scene.render_settings == NULL) {
 +              engine_settings_validate_init();
 +      }
 +
 +      if (scene->layer_properties == NULL) {
 +              IDPropertyTemplate val = {0};
 +              scene->layer_properties = IDP_New(IDP_GROUP, &val, ROOT_PROP);
 +              BKE_view_layer_engine_settings_create(scene->layer_properties);
 +      }
 +      else {
 +              IDP_MergeGroup(scene->layer_properties, root_reference.scene.render_settings, false);
 +      }
 +}
 +
 +/**
 + * Make sure Scene has all required collection settings.
 + */
 +void BKE_view_layer_engine_settings_validate_layer(ViewLayer *view_layer)
 +{
 +      if (root_reference.view_layer == NULL) {
 +              engine_settings_validate_init();
 +      }
 +
 +      IDP_MergeGroup(view_layer->properties, root_reference.view_layer, false);
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +/* Iterators */
 +
 +static void object_bases_iterator_begin(BLI_Iterator *iter, void *data_in, const int flag)
 +{
 +      ViewLayer *view_layer = data_in;
 +      Base *base = view_layer->object_bases.first;
 +
 +      /* when there are no objects */
 +      if (base ==  NULL) {
 +              iter->valid = false;
 +              return;
 +      }
 +
 +      iter->data = base;
 +
 +      if ((base->flag & flag) == 0) {
 +              object_bases_iterator_next(iter, flag);
 +      }
 +      else {
 +              iter->current = base;
 +      }
 +}
 +
 +static void object_bases_iterator_next(BLI_Iterator *iter, const int flag)
 +{
 +      Base *base = ((Base *)iter->data)->next;
 +
 +      while (base) {
 +              if ((base->flag & flag) != 0) {
 +                      iter->current = base;
 +                      iter->data = base;
 +                      return;
 +              }
 +              base = base->next;
 +      }
 +
 +      iter->valid = false;
 +}
 +
 +static void objects_iterator_begin(BLI_Iterator *iter, void *data_in, const int flag)
 +{
 +      object_bases_iterator_begin(iter, data_in, flag);
 +
 +      if (iter->valid) {
 +              iter->current = ((Base *)iter->current)->object;
 +      }
 +}
 +
 +static void objects_iterator_next(BLI_Iterator *iter, const int flag)
 +{
 +      object_bases_iterator_next(iter, flag);
 +
 +      if (iter->valid) {
 +              iter->current = ((Base *)iter->current)->object;
 +      }
 +}
 +
 +void BKE_selected_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      objects_iterator_begin(iter, data_in, BASE_SELECTED);
 +}
 +
 +void BKE_selected_objects_iterator_next(BLI_Iterator *iter)
 +{
 +      objects_iterator_next(iter, BASE_SELECTED);
 +}
 +
 +void BKE_selected_objects_iterator_end(BLI_Iterator *UNUSED(iter))
 +{
 +      /* do nothing */
 +}
 +
 +void BKE_visible_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      objects_iterator_begin(iter, data_in, BASE_VISIBLED);
 +}
 +
 +void BKE_visible_objects_iterator_next(BLI_Iterator *iter)
 +{
 +      objects_iterator_next(iter, BASE_VISIBLED);
 +}
 +
 +void BKE_visible_objects_iterator_end(BLI_Iterator *UNUSED(iter))
 +{
 +      /* do nothing */
 +}
 +
 +void BKE_selected_bases_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      object_bases_iterator_begin(iter, data_in, BASE_SELECTED);
 +}
 +
 +void BKE_selected_bases_iterator_next(BLI_Iterator *iter)
 +{
 +      object_bases_iterator_next(iter, BASE_SELECTED);
 +}
 +
 +void BKE_selected_bases_iterator_end(BLI_Iterator *UNUSED(iter))
 +{
 +      /* do nothing */
 +}
 +
 +void BKE_visible_bases_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      object_bases_iterator_begin(iter, data_in, BASE_VISIBLED);
 +}
 +
 +void BKE_visible_bases_iterator_next(BLI_Iterator *iter)
 +{
 +      object_bases_iterator_next(iter, BASE_VISIBLED);
 +}
 +
 +void BKE_visible_bases_iterator_end(BLI_Iterator *UNUSED(iter))
 +{
 +      /* do nothing */
 +}
 +
 +void BKE_renderable_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      ObjectsRenderableIteratorData *data = data_in;
 +
 +      /* Tag objects to prevent going over the same object twice. */
 +      for (Scene *scene = data->scene; scene; scene = scene->set) {
 +              for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +                      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +                               base->object->id.flag |= LIB_TAG_DOIT;
 +                      }
 +              }
 +      }
 +
 +      ViewLayer *view_layer = data->scene->view_layers.first;
 +      data->iter.view_layer = view_layer;
 +
 +      data->base_temp.next = view_layer->object_bases.first;
 +      data->iter.base = &data->base_temp;
 +
 +      data->iter.set = NULL;
 +
 +      iter->data = data_in;
 +      BKE_renderable_objects_iterator_next(iter);
 +}
 +
 +void BKE_renderable_objects_iterator_next(BLI_Iterator *iter)
 +{
 +      /* Set it early in case we need to exit and we are running from within a loop. */
 +      iter->skip = true;
 +
 +      ObjectsRenderableIteratorData *data = iter->data;
 +      Base *base = data->iter.base->next;
 +
 +      /* There is still a base in the current scene layer. */
 +      if (base != NULL) {
 +              Object *ob = base->object;
 +
 +              /* We need to set the iter.base even if the rest fail otherwise
 +               * we keep checking the exactly same base over and over again. */
 +              data->iter.base = base;
 +
 +              if (ob->id.flag & LIB_TAG_DOIT) {
 +                      ob->id.flag &= ~LIB_TAG_DOIT;
 +
 +                      if ((base->flag & BASE_VISIBLED) != 0) {
 +                              iter->skip = false;
 +                              iter->current = ob;
 +                      }
 +              }
 +              return;
 +      }
 +
 +      /* Time to go to the next scene layer. */
 +      if (data->iter.set == NULL) {
 +              while ((data->iter.view_layer = data->iter.view_layer->next)) {
 +                      ViewLayer *view_layer = data->iter.view_layer;
 +                      if (view_layer->flag & VIEW_LAYER_RENDER) {
 +                              data->base_temp.next = view_layer->object_bases.first;
 +                              data->iter.base = &data->base_temp;
 +                              return;
 +                      }
 +              }
 +
 +              /* Setup the "set" for the next iteration. */
 +              data->scene_temp.set = data->scene;
 +              data->iter.set = &data->scene_temp;
 +              return;
 +      }
 +
 +      /* Look for an object in the next set. */
 +      while ((data->iter.set = data->iter.set->set)) {
 +              ViewLayer *view_layer = BKE_view_layer_from_scene_get(data->iter.set);
 +              data->base_temp.next = view_layer->object_bases.first;
 +              data->iter.base = &data->base_temp;
 +              return;
 +      }
 +
 +      iter->valid = false;
 +}
 +
 +void BKE_renderable_objects_iterator_end(BLI_Iterator *UNUSED(iter))
 +{
 +      /* Do nothing - iter->data was static allocated, we can't free it. */
 +}
 +
 +/* Evaluation  */
 +
 +/**
 + * Reset props
 + *
 + * If props_ref is pasted, copy props from it
 + */
 +static void idproperty_reset(IDProperty **props, IDProperty *props_ref)
 +{
 +      IDPropertyTemplate val = {0};
 +
 +      if (*props) {
 +              IDP_FreeProperty(*props);
 +              MEM_freeN(*props);
 +      }
 +      *props = IDP_New(IDP_GROUP, &val, ROOT_PROP);
 +
 +      if (props_ref) {
 +              IDP_MergeGroup(*props, props_ref, true);
 +      }
 +}
 +
 +void BKE_layer_eval_layer_collection_pre(const struct EvaluationContext *UNUSED(eval_ctx),
 +                                         ID *owner_id, ViewLayer *view_layer)
 +{
 +      DEBUG_PRINT("%s on %s (%p)\n", __func__, view_layer->name, view_layer);
 +      Scene *scene = (GS(owner_id->name) == ID_SCE) ? (Scene *)owner_id : NULL;
 +
 +      for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) {
 +              base->flag &= ~(BASE_VISIBLED | BASE_SELECTABLED);
 +              idproperty_reset(&base->collection_properties, scene ? scene->collection_properties : NULL);
 +      }
 +
 +      /* Sync properties from scene to scene layer. */
 +      idproperty_reset(&view_layer->properties_evaluated, scene ? scene->layer_properties : NULL);
 +      IDP_MergeGroup(view_layer->properties_evaluated, view_layer->properties, true);
 +
 +      /* TODO(sergey): Is it always required? */
 +      view_layer->flag |= VIEW_LAYER_ENGINE_DIRTY;
 +}
 +
 +static const char *collection_type_lookup[] =
 +{
 +    "None", /* COLLECTION_TYPE_NONE */
 +    "Group Internal", /* COLLECTION_TYPE_GROUP_INTERNAL */
 +};
 +
 +/**
 + * \note We can't use layer_collection->flag because of 3 level nesting (where parent is visible, but not grand-parent)
 + * So layer_collection->flag_evaluated is expected to be up to date with layer_collection->flag.
 + */
 +static bool layer_collection_visible_get(const EvaluationContext *eval_ctx, LayerCollection *layer_collection)
 +{
 +      if (layer_collection->flag_evaluated & COLLECTION_DISABLED) {
 +              return false;
 +      }
 +
 +      if (eval_ctx->mode == DAG_EVAL_VIEWPORT) {
 +              return (layer_collection->flag_evaluated & COLLECTION_VIEWPORT) != 0;
 +      }
 +      else {
 +              return (layer_collection->flag_evaluated & COLLECTION_RENDER) != 0;
 +      }
 +}
 +
 +void BKE_layer_eval_layer_collection(const EvaluationContext *eval_ctx,
 +                                     LayerCollection *layer_collection,
 +                                     LayerCollection *parent_layer_collection)
 +{
 +      DEBUG_PRINT("%s on %s (%p) [%s], parent %s (%p) [%s]\n",
 +                  __func__,
 +                  layer_collection->scene_collection->name,
 +                  layer_collection->scene_collection,
 +                  collection_type_lookup[layer_collection->scene_collection->type],
 +                  (parent_layer_collection != NULL) ? parent_layer_collection->scene_collection->name : "NONE",
 +                  (parent_layer_collection != NULL) ? parent_layer_collection->scene_collection : NULL,
 +                  (parent_layer_collection != NULL) ? collection_type_lookup[parent_layer_collection->scene_collection->type] : "");
 +      BLI_assert(layer_collection != parent_layer_collection);
 +
 +      /* visibility */
 +      layer_collection->flag_evaluated = layer_collection->flag;
 +
 +      if (parent_layer_collection != NULL) {
 +              if (layer_collection_visible_get(eval_ctx, parent_layer_collection) == false) {
 +                      layer_collection->flag_evaluated |= COLLECTION_DISABLED;
 +              }
 +
 +              if ((parent_layer_collection->flag_evaluated & COLLECTION_DISABLED) ||
 +                  (parent_layer_collection->flag_evaluated & COLLECTION_SELECTABLE) == 0)
 +              {
 +                      layer_collection->flag_evaluated &= ~COLLECTION_SELECTABLE;
 +              }
 +      }
 +
 +      const bool is_visible = layer_collection_visible_get(eval_ctx, layer_collection);
 +      const bool is_selectable = is_visible && ((layer_collection->flag_evaluated & COLLECTION_SELECTABLE) != 0);
 +
 +      /* overrides */
 +      if (is_visible) {
 +              if (parent_layer_collection == NULL) {
 +                      idproperty_reset(&layer_collection->properties_evaluated, layer_collection->properties);
 +              }
 +              else {
 +                      idproperty_reset(&layer_collection->properties_evaluated, parent_layer_collection->properties_evaluated);
 +                      IDP_MergeGroup(layer_collection->properties_evaluated, layer_collection->properties, true);
 +              }
 +      }
 +
 +      for (LinkData *link = layer_collection->object_bases.first; link != NULL; link = link->next) {
 +              Base *base = link->data;
 +
 +              if (is_visible) {
 +                      IDP_MergeGroup(base->collection_properties, layer_collection->properties_evaluated, true);
 +                      base->flag |= BASE_VISIBLED;
 +              }
 +
 +              if (is_selectable) {
 +                      base->flag |= BASE_SELECTABLED;
 +              }
 +      }
 +}
 +
 +void BKE_layer_eval_layer_collection_post(const struct EvaluationContext *UNUSED(eval_ctx),
 +                                          ViewLayer *view_layer)
 +{
 +      DEBUG_PRINT("%s on %s (%p)\n", __func__, view_layer->name, view_layer);
 +      /* if base is not selectabled, clear select */
 +      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +              if ((base->flag & BASE_SELECTABLED) == 0) {
 +                      base->flag &= ~BASE_SELECTED;
 +              }
 +      }
 +}
 +
 +/**
 + * Free any static allocated memory.
 + */
 +void BKE_layer_exit(void)
 +{
 +      layer_collection_engine_settings_validate_free();
 +}
index 7235aa0aaf6b4182114ef64bb33aafe763856108,0dd7047a376f355efcbf21de113b00ae3d208688..0117cfcc02209c56a793c0aacdcbeb90c380d98d
@@@ -1742,40 -1781,501 +1742,40 @@@ void paste_matcopybuf(Material *ma
        ma->nodetree = ntreeCopyTree_ex(matcopybuf.nodetree, G.main, false);
  }
  
 -
 -/*********************** texface to material convert functions **********************/
 -/* encode all the TF information into a single int */
 -static int encode_tfaceflag(MTFace *tf, int convertall)
 +struct Image *BKE_object_material_edit_image_get(Object *ob, short mat_nr)
  {
 -      /* calculate the flag */
 -      int flag = tf->mode;
 -
 -      /* options that change the material offline render */
 -      if (!convertall) {
 -              flag &= ~TF_OBCOL;
 -      }
 -
 -      /* clean flags that are not being converted */
 -      flag &= ~TF_TEX;
 -      flag &= ~TF_SHAREDVERT;
 -      flag &= ~TF_SHAREDCOL;
 -      flag &= ~TF_CONVERTED;
 -
 -      /* light tface flag is ignored in GLSL mode */
 -      flag &= ~TF_LIGHT;
 -      
 -      /* 15 is how big the flag can be - hardcoded here and in decode_tfaceflag() */
 -      flag |= tf->transp << 15;
 -      
 -      /* increase 1 so flag 0 is different than no flag yet */
 -      return flag + 1;
 +      Material *ma = give_current_material(ob, mat_nr + 1);
 +      return ma ? ma->edit_image : NULL;
  }
  
 -/* set the material options based in the tface flag */
 -static void decode_tfaceflag(Material *ma, int flag, int convertall)
 +struct Image **BKE_object_material_edit_image_get_array(Object *ob)
  {
 -      int alphablend;
 -      GameSettings *game = &ma->game;
 -
 -      /* flag is shifted in 1 to make 0 != no flag yet (see encode_tfaceflag) */
 -      flag -= 1;
 -
 -      alphablend = flag >> 15;  /* encoded in the encode_tfaceflag function */
 -      (*game).flag = 0;
 -      
 -      /* General Material Options */
 -      if ((flag & TF_DYNAMIC) == 0) (*game).flag    |= GEMAT_NOPHYSICS;
 -      
 -      /* Material Offline Rendering Properties */
 -      if (convertall) {
 -              if (flag & TF_OBCOL) ma->shade_flag |= MA_OBCOLOR;
 +      Image **image_array = MEM_mallocN(sizeof(Material *) * ob->totcol, __func__);
 +      for (int i = 0; i < ob->totcol; i++) {
 +              image_array[i] = BKE_object_material_edit_image_get(ob, i);
        }
 -      
 -      /* Special Face Properties */
 -      if ((flag & TF_TWOSIDE) == 0) (*game).flag |= GEMAT_BACKCULL;
 -      if (flag & TF_INVISIBLE) (*game).flag |= GEMAT_INVISIBLE;
 -      if (flag & TF_BMFONT) (*game).flag |= GEMAT_TEXT;
 -      
 -      /* Face Orientation */
 -      if (flag & TF_BILLBOARD) (*game).face_orientation |= GEMAT_HALO;
 -      else if (flag & TF_BILLBOARD2) (*game).face_orientation |= GEMAT_BILLBOARD;
 -      else if (flag & TF_SHADOW) (*game).face_orientation |= GEMAT_SHADOW;
 -      
 -      /* Alpha Blend */
 -      if (flag & TF_ALPHASORT && ELEM(alphablend, TF_ALPHA, TF_ADD)) (*game).alpha_blend = GEMAT_ALPHA_SORT;
 -      else if (alphablend & TF_ALPHA) (*game).alpha_blend = GEMAT_ALPHA;
 -      else if (alphablend & TF_ADD) (*game).alpha_blend = GEMAT_ADD;
 -      else if (alphablend & TF_CLIP) (*game).alpha_blend = GEMAT_CLIP;
 -}
 -
 -/* boolean check to see if the mesh needs a material */
 -static int check_tfaceneedmaterial(int flag)
 -{
 -      /* check if the flags we have are not deprecated != than default material options
 -       * also if only flags are visible and collision see if all objects using this mesh have this option in physics */
 -
 -      /* flag is shifted in 1 to make 0 != no flag yet (see encode_tfaceflag) */
 -      flag -= 1;
 -
 -      /* deprecated flags */
 -      flag &= ~TF_OBCOL;
 -      flag &= ~TF_SHAREDVERT;
 -      flag &= ~TF_SHAREDCOL;
 -
 -      /* light tface flag is ignored in GLSL mode */
 -      flag &= ~TF_LIGHT;
 -      
 -      /* automatic detected if tex image has alpha */
 -      flag &= ~(TF_ALPHA << 15);
 -      /* automatic detected if using texture */
 -      flag &= ~TF_TEX;
 -
 -      /* settings for the default NoMaterial */
 -      if (flag == TF_DYNAMIC)
 -              return 0;
 -
 -      else
 -              return 1;
 -}
 -
 -/* return number of digits of an integer */
 -/* XXX to be optmized or replaced by an equivalent blender internal function */
 -static int integer_getdigits(int number)
 -{
 -      int i = 0;
 -      if (number == 0) return 1;
 -
 -      while (number != 0) {
 -              number = (int)(number / 10);
 -              i++;
 -      }
 -      return i;
 -}
 -
 -static void calculate_tface_materialname(char *matname, char *newname, int flag)
 -{
 -      /* if flag has only light and collision and material matches those values
 -       * you can do strcpy(name, mat_name);
 -       * otherwise do: */
 -      int digits = integer_getdigits(flag);
 -      /* clamp the old name, remove the MA prefix and add the .TF.flag suffix
 -       * e.g. matname = "MALoooooooooooooongName"; newname = "Loooooooooooooon.TF.2" */
 -      BLI_snprintf(newname, MAX_ID_NAME, "%.*s.TF.%0*d", MAX_ID_NAME - (digits + 5), matname, digits, flag);
 -}
 -
 -/* returns -1 if no match */
 -static short mesh_getmaterialnumber(Mesh *me, Material *ma)
 -{
 -      short a;
 -
 -      for (a = 0; a < me->totcol; a++) {
 -              if (me->mat[a] == ma) {
 -                      return a;
 -              }
 -      }
 -
 -      return -1;
 -}
 -
 -/* append material */
 -static short mesh_addmaterial(Mesh *me, Material *ma)
 -{
 -      BKE_material_append_id(G.main, &me->id, NULL);
 -      me->mat[me->totcol - 1] = ma;
 -
 -      id_us_plus(&ma->id);
 -
 -      return me->totcol - 1;
 +      return image_array;
  }
  
 -static void set_facetexture_flags(Material *ma, Image *image)
 +bool BKE_object_material_edit_image_set(Object *ob, short mat_nr, Image *image)
  {
 -      if (image) {
 -              ma->mode |= MA_FACETEXTURE;
 -              /* we could check if the texture has alpha, but then more meshes sharing the same
 -               * material may need it. Let's make it simple. */
 -              if (BKE_image_has_alpha(image))
 -                      ma->mode |= MA_FACETEXTURE_ALPHA;
 -      }
 -}
 -
 -/* returns material number */
 -static short convert_tfacenomaterial(Main *main, Mesh *me, MTFace *tf, int flag)
 -{
 -      Material *ma;
 -      char idname[MAX_ID_NAME];
 -      short mat_nr = -1;
 -      
 -      /* new material, the name uses the flag*/
 -      BLI_snprintf(idname, sizeof(idname), "MAMaterial.TF.%0*d", integer_getdigits(flag), flag);
 -
 -      if ((ma = BLI_findstring(&main->mat, idname + 2, offsetof(ID, name) + 2))) {
 -              mat_nr = mesh_getmaterialnumber(me, ma);
 -              /* assign the material to the mesh */
 -              if (mat_nr == -1) mat_nr = mesh_addmaterial(me, ma);
 -
 -              /* if needed set "Face Textures [Alpha]" Material options */
 -              set_facetexture_flags(ma, tf->tpage);
 -      }
 -      /* create a new material */
 -      else {
 -              ma = BKE_material_add(main, idname + 2);
 -
 -              if (ma) {
 -                      printf("TexFace Convert: Material \"%s\" created.\n", idname + 2);
 -                      mat_nr = mesh_addmaterial(me, ma);
 -                      
 -                      /* if needed set "Face Textures [Alpha]" Material options */
 -                      set_facetexture_flags(ma, tf->tpage);
 -
 -                      decode_tfaceflag(ma, flag, 1);
 -                      /* the final decoding will happen after, outside the main loop
 -                       * for now store the flag into the material and change light/tex/collision
 -                       * store the flag as a negative number */
 -                      ma->game.flag = -flag;
 -                      id_us_min((ID *)ma);
 -              }
 -              else {
 -                      printf("Error: Unable to create Material \"%s\" for Mesh \"%s\".", idname + 2, me->id.name + 2);
 -              }
 -      }
 -
 -      /* set as converted, no need to go bad to this face */
 -      tf->mode |= TF_CONVERTED;
 -      return mat_nr;
 -}
 -
 -/* Function to fully convert materials */
 -static void convert_tfacematerial(Main *main, Material *ma)
 -{
 -      Mesh *me;
 -      Material *mat_new;
 -      MFace *mf;
 -      MTFace *tf;
 -      int flag, index;
 -      int a;
 -      short mat_nr;
 -      CustomDataLayer *cdl;
 -      char idname[MAX_ID_NAME];
 -
 -      for (me = main->mesh.first; me; me = me->id.next) {
 -              /* check if this mesh uses this material */
 -              for (a = 0; a < me->totcol; a++)
 -                      if (me->mat[a] == ma) break;
 -                      
 -              /* no material found */
 -              if (a == me->totcol) continue;
 -
 -              /* get the active tface layer */
 -              index = CustomData_get_active_layer_index(&me->fdata, CD_MTFACE);
 -              cdl = (index == -1) ? NULL : &me->fdata.layers[index];
 -              if (!cdl) continue;
 -
 -              /* loop over all the faces and stop at the ones that use the material*/
 -              for (a = 0, mf = me->mface; a < me->totface; a++, mf++) {
 -                      if (me->mat[mf->mat_nr] != ma) continue;
 -
 -                      /* texface data for this face */
 -                      tf = ((MTFace *)cdl->data) + a;
 -                      flag = encode_tfaceflag(tf, 1);
 -
 -                      /* the name of the new material */
 -                      calculate_tface_materialname(ma->id.name, (char *)&idname, flag);
 -
 -                      if ((mat_new = BLI_findstring(&main->mat, idname + 2, offsetof(ID, name) + 2))) {
 -                              /* material already existent, see if the mesh has it */
 -                              mat_nr = mesh_getmaterialnumber(me, mat_new);
 -                              /* material is not in the mesh, add it */
 -                              if (mat_nr == -1) mat_nr = mesh_addmaterial(me, mat_new);
 -                      }
 -                      /* create a new material */
 -                      else {
 -                              mat_new = BKE_material_copy(main, ma);
 -                              if (mat_new) {
 -                                      /* rename the material*/
 -                                      BLI_strncpy(mat_new->id.name, idname, sizeof(mat_new->id.name));
 -                                      id_us_min((ID *)mat_new);
 -
 -                                      mat_nr = mesh_addmaterial(me, mat_new);
 -                                      decode_tfaceflag(mat_new, flag, 1);
 -                              }
 -                              else {
 -                                      printf("Error: Unable to create Material \"%s\" for Mesh \"%s.", idname + 2, me->id.name + 2);
 -                                      mat_nr = mf->mat_nr;
 -                                      continue;
 -                              }
 -                      }
 -                      
 -                      /* if the material has a texture but no texture channel
 -                       * set "Face Textures [Alpha]" Material options 
 -                       * actually we need to run it always, because of old behavior
 -                       * of using face texture if any texture channel was present (multitex) */
 -                      //if ((!mat_new->mtex[0]) && (!mat_new->mtex[0]->tex))
 -                      set_facetexture_flags(mat_new, tf->tpage);
 -
 -                      /* set the material number to the face*/
 -                      mf->mat_nr = mat_nr;
 -              }
 -              /* remove material from mesh */
 -              for (a = 0; a < me->totcol; ) {
 -                      if (me->mat[a] == ma) {
 -                              BKE_material_pop_id(main, &me->id, a, true);
 -                      }
 -                      else {
 -                              a++;
 -                      }
 -              }
 +      Material *ma = give_current_material(ob, mat_nr + 1);
 +      if (ma) {
 +              /* both may be NULL */
 +              id_us_min((ID *)ma->edit_image);
 +              ma->edit_image = image;
 +              id_us_plus((ID *)ma->edit_image);
 +              return true;
        }
 +      return false;
  }
  
 -
 -#define MAT_BGE_DISPUTED -99999
 -
 -int do_version_tface(Main *main)
 +void BKE_material_eval(const struct EvaluationContext *UNUSED(eval_ctx), Material *material)
  {
-       if (G.debug & G_DEBUG_DEPSGRAPH) {
 -      Mesh *me;
 -      Material *ma;
 -      MFace *mf;
 -      MTFace *tf;
 -      CustomDataLayer *cdl;
 -      int a;
 -      int flag;
 -      int index;
 -      
 -      /* Operator in help menu has been removed for 2.7x */
 -      int fileload = 1;
 -
 -      /* sometimes mesh has no materials but will need a new one. In those
 -       * cases we need to ignore the mf->mat_nr and only look at the face
 -       * mode because it can be zero as uninitialized or the 1st created material
 -       */
 -      int nomaterialslots;
 -
 -      /* alert to user to check the console */
 -      int nowarning = 1;
 -
 -      /* mark all the materials to conversion with a flag
 -       * if there is tface create a complete flag for that storing in flag
 -       * if there is tface and flag > 0: creates a new flag based on this face
 -       * if flags are different set flag to -1  
 -       */
 -      
 -      /* 1st part: marking mesh materials to update */
 -      for (me = main->mesh.first; me; me = me->id.next) {
 -              if (ID_IS_LINKED(me)) continue;
 -
 -              /* get the active tface layer */
 -              index = CustomData_get_active_layer_index(&me->fdata, CD_MTFACE);
 -              cdl = (index == -1) ? NULL : &me->fdata.layers[index];
 -              if (!cdl) continue;
 -
 -              nomaterialslots = (me->totcol == 0 ? 1 : 0);
 -              
 -              /* loop over all the faces*/
 -              for (a = 0, mf = me->mface; a < me->totface; a++, mf++) {
 -                      /* texface data for this face */
 -                      tf = ((MTFace *)cdl->data) + a;
 -
 -                      /* conversion should happen only once */
 -                      if (fileload)
 -                              tf->mode &= ~TF_CONVERTED;
 -                      else {
 -                              if ((tf->mode & TF_CONVERTED)) continue;
 -                              else tf->mode |= TF_CONVERTED;
 -                      }
 -                      
 -                      /* no material slots */
 -                      if (nomaterialslots) {
 -                              flag = encode_tfaceflag(tf, 1);
 -                              
 -                              /* create/find a new material and assign to the face */
 -                              if (check_tfaceneedmaterial(flag)) {
 -                                      mf->mat_nr = convert_tfacenomaterial(main, me, tf, flag);
 -                              }
 -                              /* else mark them as no-material to be reverted to 0 later */
 -                              else {
 -                                      mf->mat_nr = -1;
 -                              }
 -                      }
 -                      else if (mf->mat_nr < me->totcol) {
 -                              ma = me->mat[mf->mat_nr];
 -                              
 -                              /* no material create one if necessary */
 -                              if (!ma) {
 -                                      /* find a new material and assign to the face */
 -                                      flag = encode_tfaceflag(tf, 1);
 -
 -                                      /* create/find a new material and assign to the face */
 -                                      if (check_tfaceneedmaterial(flag))
 -                                              mf->mat_nr = convert_tfacenomaterial(main, me, tf, flag);
 -
 -                                      continue;
 -                              }
 -
 -                              /* we can't read from this if it comes from a library,
 -                               * at doversion time: direct_link might not have happened on it,
 -                               * so ma->mtex is not pointing to valid memory yet.
 -                               * later we could, but it's better not */
 -                              else if (ID_IS_LINKED(ma))
 -                                      continue;
 -                              
 -                              /* material already marked as disputed */
 -                              else if (ma->game.flag == MAT_BGE_DISPUTED)
 -                                      continue;
 -
 -                              /* found a material */
 -                              else {
 -                                      flag = encode_tfaceflag(tf, ((fileload) ? 0 : 1));
 -
 -                                      /* first time changing this material */
 -                                      if (ma->game.flag == 0)
 -                                              ma->game.flag = -flag;
 -                      
 -                                      /* mark material as disputed */
 -                                      else if (ma->game.flag != -flag) {
 -                                              ma->game.flag = MAT_BGE_DISPUTED;
 -                                              continue;
 -                                      }
 -                      
 -                                      /* material ok so far */
 -                                      else {
 -                                              ma->game.flag = -flag;
 -                                              
 -                                              /* some people uses multitexture with TexFace by creating a texture
 -                                               * channel which not necessarily the tf->tpage image. But the game engine
 -                                               * was enabling it. Now it's required to set "Face Texture [Alpha] in the
 -                                               * material settings. */
 -                                              if (!fileload)
 -                                                      set_facetexture_flags(ma, tf->tpage);
 -                                      }
 -                              }
 -                      }
 -                      else {
 -                              continue;
 -                      }
 -              }
 -
 -              /* if we didn't have material slot and now we do, we need to
 -               * make sure the materials are correct */
 -              if (nomaterialslots) {
 -                      if (me->totcol > 0) {
 -                              for (a = 0, mf = me->mface; a < me->totface; a++, mf++) {
 -                                      if (mf->mat_nr == -1) {
 -                                              /* texface data for this face */
 -                                              tf = ((MTFace *)cdl->data) + a;
 -                                              mf->mat_nr = convert_tfacenomaterial(main, me, tf, encode_tfaceflag(tf, 1));
 -                                      }
 -                              }
 -                      }
 -                      else {
 -                              for (a = 0, mf = me->mface; a < me->totface; a++, mf++) {
 -                                      mf->mat_nr = 0;
 -                              }
 -                      }
 -              }
 -
++      if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) {
 +              printf("%s on %s (%p)\n", __func__, material->id.name, material);
        }
 -      
 -      /* 2nd part - conversion */
 -      /* skip library files */
 -
 -      /* we shouldn't loop through the materials created in the loop. make the loop stop at its original length) */
 -      for (ma = main->mat.first, a = 0; ma; ma = ma->id.next, a++) {
 -              if (ID_IS_LINKED(ma)) continue;
 -
 -              /* disputed material */
 -              if (ma->game.flag == MAT_BGE_DISPUTED) {
 -                      ma->game.flag = 0;
 -                      if (fileload) {
 -                              printf("Warning: material \"%s\" skipped.\n", ma->id.name + 2);
 -                              nowarning = 0;
 -                      }
 -                      else {
 -                              convert_tfacematerial(main, ma);
 -                      }
 -                      continue;
 -              }
 -      
 -              /* no conflicts in this material - 90% of cases
 -               * convert from tface system to material */
 -              else if (ma->game.flag < 0) {
 -                      decode_tfaceflag(ma, -(ma->game.flag), 1);
 -
 -                      /* material is good make sure all faces using
 -                       * this material are set to converted */
 -                      if (fileload) {
 -                              for (me = main->mesh.first; me; me = me->id.next) {
 -                                      /* check if this mesh uses this material */
 -                                      for (a = 0; a < me->totcol; a++)
 -                                              if (me->mat[a] == ma) break;
 -                                              
 -                                      /* no material found */
 -                                      if (a == me->totcol) continue;
 -                      
 -                                      /* get the active tface layer */
 -                                      index = CustomData_get_active_layer_index(&me->fdata, CD_MTFACE);
 -                                      cdl = (index == -1) ? NULL : &me->fdata.layers[index];
 -                                      if (!cdl) continue;
 -                      
 -                                      /* loop over all the faces and stop at the ones that use the material*/
 -                                      for (a = 0, mf = me->mface; a < me->totface; a++, mf++) {
 -                                              if (me->mat[mf->mat_nr] == ma) {
 -                                                      /* texface data for this face */
 -                                                      tf = ((MTFace *)cdl->data) + a;
 -                                                      tf->mode |= TF_CONVERTED;
 -                                              }
 -                                      }
 -                              }
 -                      }
 -              }
 -              /* material is not used by faces with texface
 -               * set the default flag - do it only once */
 -              else {
 -                      if (fileload) {
 -                              ma->game.flag = GEMAT_BACKCULL;
 -                      }
 -              }
 +      if ((BLI_listbase_is_empty(&material->gpumaterial) == false)) {
 +              GPU_material_uniform_buffer_tag_dirty(&material->gpumaterial);
        }
 -
 -      return nowarning;
  }
 -
index d3c74f71f78a80f5bb74210b55c82d8ba7fa9752,81a06ff17cc13fe40a2e7479a733834a2d9e506d..d1b8920d612b299f0bf0be19260678f1951af8b8
@@@ -2664,10 -2630,10 +2664,10 @@@ Mesh *BKE_mesh_new_from_object
  
  /* **** Depsgraph evaluation **** */
  
 -void BKE_mesh_eval_geometry(EvaluationContext *UNUSED(eval_ctx),
 +void BKE_mesh_eval_geometry(const EvaluationContext *UNUSED(eval_ctx),
                              Mesh *mesh)
  {
-       if (G.debug & G_DEBUG_DEPSGRAPH) {
+       if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) {
                printf("%s on %s\n", __func__, mesh->id.name);
        }
        if (mesh->bb == NULL || (mesh->bb->flag & BOUNDBOX_DIRTY)) {
index c2d74739beb1e733d4b8715ad9ef76a149c568b5,00f5f0f84b6ab41a79717a9dfce5e28d96d88d2f..97a26b23c17a1e6f9b49324ca6215c0084c4a92d
@@@ -3814,64 -3813,3 +3814,64 @@@ bool BKE_node_tree_iter_step(struct Nod
  
        return true;
  }
-       if (G.debug & G_DEBUG_DEPSGRAPH) {
 +
 +/* -------------------------------------------------------------------- */
 +/* NodeTree kernel functions */
 +
 +void BKE_nodetree_remove_layer_n(bNodeTree *ntree, Scene *scene, const int layer_index)
 +{
 +      BLI_assert(layer_index != -1);
 +      for (bNode *node = ntree->nodes.first; node; node = node->next) {
 +              if (node->type == CMP_NODE_R_LAYERS && (Scene *)node->id == scene) {
 +                      if (node->custom1 == layer_index) {
 +                              node->custom1 = 0;
 +                      }
 +                      else if (node->custom1 > layer_index) {
 +                              node->custom1--;
 +                      }
 +              }
 +      }
 +}
 +
 +static void node_copy_default_values_list(ListBase *sockets_dst,
 +                                          const ListBase *sockets_src)
 +{
 +      bNodeSocket *sock_dst = sockets_dst->first;
 +      const bNodeSocket *sock_src = sockets_src->first;
 +      while (sock_dst != NULL) {
 +              node_socket_copy_default_value(sock_dst, sock_src);
 +              sock_dst = sock_dst->next;
 +              sock_src = sock_src->next;
 +      }
 +}
 +
 +static void node_copy_default_values(bNode *node_dst, const bNode *node_src)
 +{
 +      node_copy_default_values_list(&node_dst->inputs, &node_src->inputs);
 +      node_copy_default_values_list(&node_dst->outputs, &node_src->outputs);
 +}
 +
 +void BKE_nodetree_copy_default_values(bNodeTree *ntree_dst,
 +                                      const bNodeTree *ntree_src)
 +{
 +      if (ntree_dst == ntree_src) {
 +              return;
 +      }
 +      bNode *node_dst = ntree_dst->nodes.first;
 +      const bNode *node_src = ntree_src->nodes.first;
 +      while (node_dst != NULL) {
 +              node_copy_default_values(node_dst, node_src);
 +              node_dst = node_dst->next;
 +              node_src = node_src->next;
 +      }
 +}
 +
 +void BKE_nodetree_shading_params_eval(const struct EvaluationContext *UNUSED(eval_ctx),
 +                                      bNodeTree *ntree_dst,
 +                                      const bNodeTree *ntree_src)
 +{
++      if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) {
 +              printf("%s on %s (%p)\n", __func__, ntree_src->id.name, ntree_dst);
 +      }
 +      BKE_nodetree_copy_default_values(ntree_dst, ntree_src);
 +}
index a0d2e7d76c8c46da2696c80a85c4ebe7718aa3ad,acc652fb1be0ad94ce32961a7ad2cba85261864e..3e577e58ae386377d5de0d1431542a357dc0bcd6
@@@ -2769,8 -2645,8 +2769,8 @@@ void BKE_object_handle_update_ex(const 
        /* XXX new animsys warning: depsgraph tag OB_RECALC_DATA should not skip drivers,
         * which is only in BKE_object_where_is_calc now */
        /* XXX: should this case be OB_RECALC_OB instead? */
 -      if (ob->recalc & OB_RECALC_ALL) {
 +      if (recalc_object || recalc_data) {
-               if (G.debug & G_DEBUG_DEPSGRAPH) {
+               if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) {
                        printf("recalcob %s\n", ob->id.name + 2);
                }
                /* Handle proxy copy for target. */
index 52c85011b6ac555e517de241049d3b944c2b0cdb,85cf7d8560dabfbbc65a915a3390acd68246c465..ada3838830e3dbce09bdababbe1c943429ac9c2a
  #include "BKE_pointcache.h"
  #include "BKE_scene.h"
  #include "BKE_material.h"
 +#include "BKE_mball.h"
 +#include "BKE_mesh.h"
  #include "BKE_image.h"
  
 +#include "MEM_guardedalloc.h"
  #include "DEG_depsgraph.h"
  
- #define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH) printf
 -#ifdef WITH_LEGACY_DEPSGRAPH
 -#  define DEBUG_PRINT if (!DEG_depsgraph_use_legacy() && G.debug & G_DEBUG_DEPSGRAPH_EVAL) printf
 -#else
 -#  define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) printf
 -#endif
 -
 -static ThreadMutex material_lock = BLI_MUTEX_INITIALIZER;
++#define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) printf
  
 -void BKE_object_eval_local_transform(EvaluationContext *UNUSED(eval_ctx),
 +void BKE_object_eval_local_transform(const EvaluationContext *UNUSED(eval_ctx),
                                       Object *ob)
  {
 -      DEBUG_PRINT("%s on %s\n", __func__, ob->id.name);
 +      DEBUG_PRINT("%s on %s (%p)\n", __func__, ob->id.name, ob);
  
        /* calculate local matrix */
        BKE_object_to_mat4(ob, ob->obmat);
index 9bc4da6f0d50fa4b6f57bfe287dc310bf3e0cf0c,2a1e0f559d7990d65c105ef063253fa490e51d04..2ee18df1331cec1d937de4c3b4905139c01d07e2
@@@ -4416,30 -4401,12 +4416,30 @@@ void BKE_particlesystem_id_loop(Particl
  
  /* **** Depsgraph evaluation **** */
  
 -void BKE_particle_system_eval_init(EvaluationContext *UNUSED(eval_ctx),
 +void BKE_particle_system_settings_eval(const struct EvaluationContext *UNUSED(eval_ctx),
 +                                       ParticleSystem *psys)
 +{
-       if (G.debug & G_DEBUG_DEPSGRAPH) {
++      if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) {
 +              printf("%s on %s (%p)\n", __func__, psys->name, psys);
 +      }
 +      psys->recalc |= psys->part->recalc;
 +}
 +
 +void BKE_particle_system_settings_recalc_clear(struct EvaluationContext *UNUSED(eval_ctx),
 +                                               ParticleSettings *particle_settings)
 +{
-       if (G.debug & G_DEBUG_DEPSGRAPH) {
++      if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) {
 +              printf("%s on %s (%p)\n", __func__, particle_settings->id.name, particle_settings);
 +      }
 +      particle_settings->recalc = 0;
 +}
 +
 +void BKE_particle_system_eval_init(const struct EvaluationContext *UNUSED(eval_ctx),
                                     Scene *scene,
                                     Object *ob)
  {
-       if (G.debug & G_DEBUG_DEPSGRAPH) {
+       if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) {
 -              printf("%s on %s\n", __func__, ob->id.name);
 +              printf("%s on %s (%p)\n", __func__, ob->id.name, ob);
        }
        BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_DEPSGRAPH);
  }
index 5da8dc563e248a57fc3837a24ea41c40a67f03d3,10fcfaa444f2dc0ec810253ac12f41bbf1cbd2c1..a57fc1fe027e9d6361876a2ef7063ca9197d0a32
@@@ -193,14 -193,3 +193,14 @@@ void BKE_world_make_local(Main *bmain, 
  {
        BKE_id_make_local_generic(bmain, &wrld->id, true, lib_local);
  }
-       if (G.debug & G_DEBUG_DEPSGRAPH) {
 +
 +void BKE_world_eval(const struct EvaluationContext *UNUSED(eval_ctx), World *world)
 +{
++      if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) {
 +              printf("%s on %s (%p)\n", __func__, world->id.name, world);
 +      }
 +      if (!BLI_listbase_is_empty(&world->gpumaterial)) {
 +              world->update_flag = 1;
 +              GPU_material_uniform_buffer_tag_dirty(&world->gpumaterial);
 +      }
 +}
index df5e51a39101658b461ae0e7a35c9621d14b20e7,87d3d5e0e0741390e9dd9a1b7e808d25128e5d79..256b52a78f3e2b80137d8fc30a8ff32e0cb65a34
@@@ -106,24 -105,15 +106,24 @@@ DepsNodeFactory *deg_type_get_factory(c
  
  /* Editors Integration -------------------------------------------------- */
  
 -void deg_editors_id_update(struct Main *bmain, struct ID *id);
 +void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx,
 +                           struct ID *id);
  
 -void deg_editors_scene_update(struct Main *bmain, struct Scene *scene, bool updated);
 +void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx,
 +                              bool updated);
 +
 +#define DEG_DEBUG_PRINTF(...)               \
 +      do {                                    \
-               if (G.debug & G_DEBUG_DEPSGRAPH) {  \
++              if (G.debug & G_DEBUG_DEPSGRAPH_BUILD) {  \
 +                      fprintf(stderr, __VA_ARGS__);   \
 +                      fflush(stderr);                 \
 +              }                                   \
 +      } while (0)
  
 -#define DEG_DEBUG_PRINTF(...) \
 -      do { \
 -              if (G.debug & G_DEBUG_DEPSGRAPH_BUILD) { \
 -                      fprintf(stderr, __VA_ARGS__); \
 -              } \
 +#define DEG_ERROR_PRINTF(...)               \
 +      do {                                    \
 +              fprintf(stderr, __VA_ARGS__);       \
 +              fflush(stderr);                     \
        } while (0)
  
  }  // namespace DEG
index b0b3cbe0f8c65c5a2c01534a656b1a3ad1517075,3ee7ef833209b49de263a4333e3f48511888f4d1..ea53b18f55bbde28e2a8ec9579d200e524df9b9b
@@@ -456,101 -282,76 +456,103 @@@ void deg_id_tag_update(Main *bmain, ID 
                        }
                }
        }
 -      else {
 -              /* All the layers for background render for now. */
 -              graph->layers = (1 << 20) - 1;
 -      }
 -      if (old_layers != graph->layers) {
 -              /* Tag all objects which becomes visible (or which becomes needed for dependencies)
 -               * for recalc.
 +}
 +
 +void deg_graph_on_visible_update(Main *bmain, Depsgraph *graph)
 +{
 +      /* Make sure objects are up to date. */
 +      foreach (DEG::IDDepsNode *id_node, graph->id_nodes) {
 +              const ID_Type id_type = GS(id_node->id_orig->name);
 +              int flag = 0;
 +              /* We only tag components which needs an update. Tagging everything is
 +               * not a good idea because that might reset particles cache (or any
 +               * other type of cache).
                 *
 -               * This is mainly needed on file load only, after that updates of invisible objects
 -               * will be stored in the pending list.
 +               * TODO(sergey): Need to generalize this somehow.
                 */
 -              foreach (DEG::IDDepsNode *id_node, graph->id_nodes) {
 -                      ID *id = id_node->id;
 -                      if ((id->recalc & ID_RECALC_ALL) != 0 ||
 -                          (id_node->layers & scene->lay_updated) == 0)
 -                      {
 -                              id_node->tag_update(graph);
 -                      }
 -                      /* A bit of magic: if object->recalc is set it means somebody tagged
 -                       * it for update. If corresponding ID recalc flags are zero it means
 -                       * graph has been evaluated after that and the recalc was skipped
 -                       * because of visibility check.
 -                       */
 -                      if (GS(id->name) == ID_OB) {
 -                              Object *object = (Object *)id;
 -                              if ((id->recalc & ID_RECALC_ALL) == 0 &&
 -                                  (object->recalc & OB_RECALC_ALL) != 0)
 -                              {
 -                                      id_node->tag_update(graph);
 -                                      DEG::ComponentDepsNode *anim_comp =
 -                                              id_node->find_component(DEG::DEG_NODE_TYPE_ANIMATION);
 -                                      if (anim_comp != NULL && object->recalc & OB_RECALC_TIME) {
 -                                              anim_comp->tag_update(graph);
 -                                      }
 -                              }
 -                      }
 +              if (id_type == ID_OB) {
 +                      flag |= OB_RECALC_OB | OB_RECALC_DATA | DEG_TAG_COPY_ON_WRITE;
                }
 +              deg_graph_id_tag_update(bmain, graph, id_node->id_orig, flag);
        }
 -      scene->lay_updated |= graph->layers;
 -      /* If graph is tagged for update, we don't need to bother with updates here,
 -       * nodes will be re-created.
 -       */
 -      if (graph->need_update) {
 +      /* Make sure collection properties are up to date. */
 +      for (Scene *scene_iter = graph->scene;
 +           scene_iter != NULL;
 +           scene_iter = scene_iter->set)
 +      {
 +              IDDepsNode *scene_id_node = graph->find_id_node(&scene_iter->id);
 +              if (scene_id_node != NULL) {
 +                      scene_id_node->tag_update(graph);
 +              }
 +              else {
 +                      BLI_assert(graph->need_update);
 +              }
 +      }
 +}
 +
 +}  /* namespace */
 +
 +}  // namespace DEG
 +
 +/* Data-Based Tagging  */
 +
 +/* Tag given ID for an update in all the dependency graphs. */
 +void DEG_id_tag_update(ID *id, int flag)
 +{
 +      DEG_id_tag_update_ex(G.main, id, flag);
 +}
 +
 +void DEG_id_tag_update_ex(Main *bmain, ID *id, int flag)
 +{
 +      if (id == NULL) {
 +              /* Ideally should not happen, but old depsgraph allowed this. */
                return;
        }
-       DEG_DEBUG_PRINTF("%s: id=%s flag=%d\n", __func__, id->name, flag);
 -      /* Special trick to get local view to work.  */
 -      LISTBASE_FOREACH (Base *, base, &scene->base) {
 -              Object *object = base->object;
 -              DEG::IDDepsNode *id_node = graph->find_id_node(&object->id);
 -              id_node->layers = 0;
++      if (G.debug & G_DEBUG_DEPSGRAPH_TAG) {
++              printf("%s: id=%s flag=%d\n", __func__, id->name, flag);
+       }
 -      LISTBASE_FOREACH (Base *, base, &scene->base) {
 -              Object *object = base->object;
 -              DEG::IDDepsNode *id_node = graph->find_id_node(&object->id);
 -              id_node->layers |= base->lay;
 -              if (object == scene->camera || object->type == OB_CAMERA) {
 -                      /* Camera should always be updated, it used directly by viewport. */
 -                      id_node->layers |= (unsigned int)(-1);
 -              }
 +      DEG::deg_id_tag_update(bmain, id, flag);
 +}
 +
 +void DEG_graph_id_tag_update(struct Main *bmain,
 +                             struct Depsgraph *depsgraph,
 +                             struct ID *id,
 +                             int flag)
 +{
 +      DEG::Depsgraph *graph = (DEG::Depsgraph *)depsgraph;
 +      DEG::deg_graph_id_tag_update(bmain, graph, id, flag);
 +}
 +
 +/* Mark a particular datablock type as having changing. */
 +void DEG_id_type_tag(Main *bmain, short id_type)
 +{
 +      if (id_type == ID_NT) {
 +              /* Stupid workaround so parent datablocks of nested nodetree get looped
 +               * over when we loop over tagged datablock types.
 +               */
 +              DEG_id_type_tag(bmain, ID_MA);
 +              DEG_id_type_tag(bmain, ID_TE);
 +              DEG_id_type_tag(bmain, ID_LA);
 +              DEG_id_type_tag(bmain, ID_WO);
 +              DEG_id_type_tag(bmain, ID_SCE);
        }
 -      DEG::deg_graph_build_flush_layers(graph);
 -      LISTBASE_FOREACH (Base *, base, &scene->base) {
 -              Object *object = base->object;
 -              DEG::IDDepsNode *id_node = graph->find_id_node(&object->id);
 -              GHASH_FOREACH_BEGIN(DEG::ComponentDepsNode *, comp, id_node->components)
 -              {
 -                      id_node->layers |= comp->layers;
 -              }
 -              GHASH_FOREACH_END();
 +
 +      bmain->id_tag_update[BKE_idcode_to_index(id_type)] = 1;
 +}
 +
 +void DEG_graph_flush_update(Main *bmain, Depsgraph *depsgraph)
 +{
 +      if (depsgraph == NULL) {
 +              return;
        }
 +      DEG::deg_graph_flush_updates(bmain, (DEG::Depsgraph *)depsgraph);
 +}
 +
 +/* Update dependency graph when visible scenes/layers changes. */
 +void DEG_graph_on_visible_update(Main *bmain, Depsgraph *depsgraph)
 +{
 +      DEG::Depsgraph *graph = (DEG::Depsgraph *)depsgraph;
 +      DEG::deg_graph_on_visible_update(bmain, graph);
  }
  
  void DEG_on_visible_update(Main *bmain, const bool UNUSED(do_time))
index 8bdc68d935191bbc78388eb9342a0996aebc3a68,0000000000000000000000000000000000000000..daace491a64b83ba5a550c6f26563d329ae56b3d
mode 100644,000000..100644
--- /dev/null
@@@ -1,1037 -1,0 +1,1037 @@@
- #define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH) printf
 +/*
 + * ***** BEGIN GPL LICENSE BLOCK *****
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * The Original Code is Copyright (C) 20137Blender Foundation.
 + * All rights reserved.
 + *
 + * Original Author: Sergey Sharybin
 + * Contributor(s): None Yet
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +
 +/** \file blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
 + *  \ingroup depsgraph
 + */
 +
 +/* Enable special; trickery to treat nested owned IDs (such as nodetree of
 + * material) to be handled in same way as "real" datablocks, even tho some
 + * internal BKE routines doesn't treat them like that.
 + *
 + * TODO(sergey): Re-evaluate that after new ID handling is in place.
 + */
 +#define NESTED_ID_NASTY_WORKAROUND
 +
 +#include "intern/eval/deg_eval_copy_on_write.h"
 +
 +#include <cstring>
 +
 +#include "BLI_utildefines.h"
 +#include "BLI_threads.h"
 +#include "BLI_string.h"
 +
 +#include "BKE_global.h"
 +#include "BKE_idprop.h"
 +#include "BKE_layer.h"
 +#include "BKE_library.h"
 +#include "BKE_main.h"
 +#include "BKE_scene.h"
 +
 +#include "DEG_depsgraph.h"
 +
 +#include "MEM_guardedalloc.h"
 +
 +extern "C" {
 +#include "DNA_ID.h"
 +#include "DNA_mesh_types.h"
 +#include "DNA_scene_types.h"
 +#include "DNA_object_types.h"
 +
 +#ifdef NESTED_ID_NASTY_WORKAROUND
 +#  include "DNA_curve_types.h"
 +#  include "DNA_key_types.h"
 +#  include "DNA_lamp_types.h"
 +#  include "DNA_lattice_types.h"
 +#  include "DNA_linestyle_types.h"
 +#  include "DNA_material_types.h"
 +#  include "DNA_node_types.h"
 +#  include "DNA_texture_types.h"
 +#  include "DNA_world_types.h"
 +#endif
 +
 +#include "BKE_action.h"
 +#include "BKE_editmesh.h"
 +#include "BKE_library_query.h"
 +#include "BKE_object.h"
 +}
 +
 +#include "intern/depsgraph.h"
 +#include "intern/builder/deg_builder_nodes.h"
 +#include "intern/nodes/deg_node.h"
 +#include "intern/nodes/deg_node_id.h"
 +
 +namespace DEG {
 +
++#define DEBUG_PRINT if (G.debug & G_DEBUG_DEPSGRAPH_EVAL) printf
 +
 +namespace {
 +
 +#ifdef NESTED_ID_NASTY_WORKAROUND
 +union NestedIDHackTempStorage {
 +      Curve curve;
 +      FreestyleLineStyle linestyle;
 +      Lamp lamp;
 +      Lattice lattice;
 +      Material material;
 +      Mesh mesh;
 +      Scene scene;
 +      Tex tex;
 +      World world;
 +};
 +
 +/* Set nested owned ID pointers to NULL. */
 +void nested_id_hack_discard_pointers(ID *id_cow)
 +{
 +      switch (GS(id_cow->name)) {
 +#  define SPECIAL_CASE(id_type, dna_type, field)  \
 +              case id_type:                             \
 +              {                                         \
 +                      ((dna_type *)id_cow)->field = NULL;   \
 +                      break;                                \
 +              }
 +
 +              SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree)
 +              SPECIAL_CASE(ID_LA, Lamp, nodetree)
 +              SPECIAL_CASE(ID_MA, Material, nodetree)
 +              SPECIAL_CASE(ID_SCE, Scene, nodetree)
 +              SPECIAL_CASE(ID_TE, Tex, nodetree)
 +              SPECIAL_CASE(ID_WO, World, nodetree)
 +
 +              SPECIAL_CASE(ID_CU, Curve, key)
 +              SPECIAL_CASE(ID_LT, Lattice, key)
 +              SPECIAL_CASE(ID_ME, Mesh, key)
 +
 +#  undef SPECIAL_CASE
 +
 +              default:
 +                      break;
 +      }
 +}
 +
 +/* Set ID pointer of nested owned IDs (nodetree, key) to NULL.
 + *
 + * Return pointer to a new ID to be used.
 + */
 +const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage,
 +                                                const ID *id)
 +{
 +      switch (GS(id->name)) {
 +#  define SPECIAL_CASE(id_type, dna_type, field, variable)  \
 +              case id_type:                                       \
 +              {                                                   \
 +                      storage->variable = *(dna_type *)id;            \
 +                      storage->variable.field = NULL;                 \
 +                      return &storage->variable.id;                   \
 +              }
 +
 +              SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree, linestyle)
 +              SPECIAL_CASE(ID_LA, Lamp, nodetree, lamp)
 +              SPECIAL_CASE(ID_MA, Material, nodetree, material)
 +              SPECIAL_CASE(ID_SCE, Scene, nodetree, scene)
 +              SPECIAL_CASE(ID_TE, Tex, nodetree, tex)
 +              SPECIAL_CASE(ID_WO, World, nodetree, world)
 +
 +              SPECIAL_CASE(ID_CU, Curve, key, curve)
 +              SPECIAL_CASE(ID_LT, Lattice, key, lattice)
 +              SPECIAL_CASE(ID_ME, Mesh, key, mesh)
 +
 +#  undef SPECIAL_CASE
 +
 +              default:
 +                      break;
 +      }
 +      return id;
 +}
 +
 +/* Set ID pointer of nested owned IDs (nodetree, key) to the original value. */
 +void nested_id_hack_restore_pointers(const ID *old_id, ID *new_id)
 +{
 +      if (new_id == NULL) {
 +              return;
 +      }
 +      switch (GS(old_id->name)) {
 +#  define SPECIAL_CASE(id_type, dna_type, field)    \
 +              case id_type:                               \
 +              {                                           \
 +                      ((dna_type *)(new_id))->field =         \
 +                              ((dna_type *)(old_id))->field;  \
 +                      break;                                  \
 +              }
 +
 +              SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree)
 +              SPECIAL_CASE(ID_LA, Lamp, nodetree)
 +              SPECIAL_CASE(ID_MA, Material, nodetree)
 +              SPECIAL_CASE(ID_SCE, Scene, nodetree)
 +              SPECIAL_CASE(ID_TE, Tex, nodetree)
 +              SPECIAL_CASE(ID_WO, World, nodetree)
 +
 +              SPECIAL_CASE(ID_CU, Curve, key)
 +              SPECIAL_CASE(ID_LT, Lattice, key)
 +              SPECIAL_CASE(ID_ME, Mesh, key)
 +
 +#undef SPECIAL_CASE
 +              default:
 +                      break;
 +      }
 +}
 +
 +/* Remap pointer of nested owned IDs (nodetree. key) to the new ID values. */
 +void ntree_hack_remap_pointers(const Depsgraph *depsgraph, ID *id_cow)
 +{
 +      switch (GS(id_cow->name)) {
 +#  define SPECIAL_CASE(id_type, dna_type, field, field_type)                   \
 +              case id_type:                                                          \
 +              {                                                                      \
 +                      dna_type *data = (dna_type *)id_cow;                               \
 +                      if (data->field != NULL) {                                         \
 +                              ID *ntree_id_cow = depsgraph->get_cow_id(&data->field->id);    \
 +                              if (ntree_id_cow != NULL) {                                    \
 +                                      DEG_COW_PRINT("    Remapping datablock for %s: id_orig=%p id_cow=%p\n", \
 +                                                    data->field->id.name,                        \
 +                                                    data->field,                                 \
 +                                                    ntree_id_cow);                               \
 +                                      data->field = (field_type *)ntree_id_cow;                  \
 +                              }                                                              \
 +                      }                                                                  \
 +                      break;                                                             \
 +              }
 +
 +              SPECIAL_CASE(ID_LS, FreestyleLineStyle, nodetree, bNodeTree)
 +              SPECIAL_CASE(ID_LA, Lamp, nodetree, bNodeTree)
 +              SPECIAL_CASE(ID_MA, Material, nodetree, bNodeTree)
 +              SPECIAL_CASE(ID_SCE, Scene, nodetree, bNodeTree)
 +              SPECIAL_CASE(ID_TE, Tex, nodetree, bNodeTree)
 +              SPECIAL_CASE(ID_WO, World, nodetree, bNodeTree)
 +
 +              SPECIAL_CASE(ID_CU, Curve, key, Key)
 +              SPECIAL_CASE(ID_LT, Lattice, key, Key)
 +              SPECIAL_CASE(ID_ME, Mesh, key, Key)
 +
 +#undef SPECIAL_CASE
 +              default:
 +                      break;
 +      }
 +}
 +#endif  /* NODETREE_NASTY_WORKAROUND */
 +
 +struct ValidateData {
 +      bool is_valid;
 +};
 +
 +/* Similar to generic id_copy() but does not require main and assumes pointer
 + * is already allocated,
 + */
 +bool id_copy_inplace_no_main(const ID *id, ID *newid)
 +{
 +      const ID *id_for_copy = id;
 +
 +#ifdef NESTED_ID_NASTY_WORKAROUND
 +      NestedIDHackTempStorage id_hack_storage;
 +      id_for_copy = nested_id_hack_get_discarded_pointers(&id_hack_storage, id);
 +#endif
 +
 +      bool result = BKE_id_copy_ex(NULL,
 +                                   (ID *)id_for_copy,
 +                                   &newid,
 +                                   LIB_ID_CREATE_NO_MAIN |
 +                                   LIB_ID_CREATE_NO_USER_REFCOUNT |
 +                                   LIB_ID_CREATE_NO_ALLOCATE |
 +                                   LIB_ID_CREATE_NO_DEG_TAG,
 +                                   false);
 +
 +#ifdef NESTED_ID_NASTY_WORKAROUND
 +      if (result) {
 +              nested_id_hack_restore_pointers(id, newid);
 +      }
 +#endif
 +
 +      return result;
 +}
 +
 +/* Similar to BKE_scene_copy() but does not require main and assumes pointer
 + * is already allocated.
 + */
 +bool scene_copy_inplace_no_main(const Scene *scene, Scene *new_scene)
 +{
 +      const ID *id_for_copy = &scene->id;
 +
 +#ifdef NESTED_ID_NASTY_WORKAROUND
 +      NestedIDHackTempStorage id_hack_storage;
 +      id_for_copy = nested_id_hack_get_discarded_pointers(&id_hack_storage,
 +                                                          &scene->id);
 +#endif
 +
 +      bool result = BKE_id_copy_ex(NULL,
 +                                   id_for_copy,
 +                                   (ID **)&new_scene,
 +                                   LIB_ID_COPY_ACTIONS |
 +                                   LIB_ID_CREATE_NO_MAIN |
 +                                   LIB_ID_CREATE_NO_USER_REFCOUNT |
 +                                   LIB_ID_CREATE_NO_ALLOCATE |
 +                                   LIB_ID_CREATE_NO_DEG_TAG,
 +                                   false);
 +
 +#ifdef NESTED_ID_NASTY_WORKAROUND
 +      if (result) {
 +              nested_id_hack_restore_pointers(&scene->id, &new_scene->id);
 +      }
 +#endif
 +
 +      return result;
 +}
 +
 +/* Check whether given ID is expanded or still a shallow copy. */
 +BLI_INLINE bool check_datablock_expanded(const ID *id_cow)
 +{
 +      return (id_cow->name[0] != '\0');
 +}
 +
 +/* Check whether datablock was already expanded during depsgraph
 + * construction.
 + */
 +static bool check_datablock_expanded_at_construction(const ID *id_orig)
 +{
 +      const ID_Type id_type = GS(id_orig->name);
 +      return (id_type == ID_SCE) ||
 +             (id_type == ID_OB && ((Object *)id_orig)->type == OB_ARMATURE) ||
 +             (id_type == ID_AR);
 +}
 +
 +/* Those are datablocks which are not covered by dependency graph and hence
 + * does not need any remapping or anything.
 + *
 + * TODO(sergey): How to make it more robust for the future, so we don't have
 + * to maintain exception lists all over the code?
 + */
 +static bool check_datablocks_copy_on_writable(const ID *id_orig)
 +{
 +      const ID_Type id_type = GS(id_orig->name);
 +      /* We shouldn't bother if copied ID is same as original one. */
 +      if (!deg_copy_on_write_is_needed(id_orig)) {
 +              return false;
 +      }
 +      return !ELEM(id_type, ID_BR,
 +                            ID_LS,
 +                            ID_AC,
 +                            ID_PAL);
 +}
 +
 +/* Callback for BKE_library_foreach_ID_link which remaps original ID pointer
 + * with the one created by CoW system.
 + */
 +
 +struct RemapCallbackUserData {
 +      /* Dependency graph for which remapping is happening. */
 +      const Depsgraph *depsgraph;
 +      /* Temporarily allocated memory for copying purposes. This ID will
 +       * be discarded after expanding is done, so need to make sure temp_id
 +       * is replaced with proper real_id.
 +       *
 +       * NOTE: This is due to our logic of "inplace" duplication, where we
 +       * use generic duplication routines (which gives us new ID) which then
 +       * is followed with copying data to a placeholder we prepared before and
 +       * discarding pointer returned by duplication routines.
 +       */
 +      const ID *temp_id;
 +      ID *real_id;
 +      /* Create placeholder for ID nodes for cases when we need to remap original
 +       * ID to it[s CoW version but we don't have required ID node yet.
 +       *
 +       * This happens when expansion happens a ta construction time.
 +       */
 +      DepsgraphNodeBuilder *node_builder;
 +      bool create_placeholders;
 +};
 +
 +int foreach_libblock_remap_callback(void *user_data_v,
 +                                    ID *id_self,
 +                                    ID **id_p,
 +                                    int /*cb_flag*/)
 +{
 +      RemapCallbackUserData *user_data = (RemapCallbackUserData *)user_data_v;
 +      const Depsgraph *depsgraph = user_data->depsgraph;
 +      if (*id_p != NULL) {
 +              ID *id_orig = *id_p;
 +              if (id_orig == user_data->temp_id) {
 +                      DEG_COW_PRINT("    Remapping datablock for %s: id_temp=%p id_cow=%p\n",
 +                                    id_orig->name, id_orig, user_data->real_id);
 +                      *id_p = user_data->real_id;
 +              }
 +              else if (check_datablocks_copy_on_writable(id_orig)) {
 +                      ID *id_cow;
 +                      if (user_data->create_placeholders) {
 +                              /* Special workaround to stop creating temp datablocks for
 +                               * objects which are coming from scene's collection and which
 +                               * are never linked to any of layers.
 +                               *
 +                               * TODO(sergey): Ideally we need to tell ID looper to ignore
 +                               * those or at least make it more reliable check where the
 +                               * pointer is coming from.
 +                               */
 +                              const ID_Type id_type = GS(id_orig->name);
 +                              const ID_Type id_type_self = GS(id_self->name);
 +                              if (id_type == ID_OB && id_type_self == ID_SCE) {
 +                                      IDDepsNode *id_node = depsgraph->find_id_node(id_orig);
 +                                      if (id_node == NULL) {
 +                                              id_cow = id_orig;
 +                                      }
 +                                      else {
 +                                              id_cow = id_node->id_cow;
 +                                      }
 +                              }
 +                              else {
 +                                      id_cow = user_data->node_builder->ensure_cow_id(id_orig);
 +                              }
 +                      }
 +                      else {
 +                              id_cow = depsgraph->get_cow_id(id_orig);
 +                      }
 +                      BLI_assert(id_cow != NULL);
 +                      DEG_COW_PRINT("    Remapping datablock for %s: id_orig=%p id_cow=%p\n",
 +                                    id_orig->name, id_orig, id_cow);
 +                      *id_p = id_cow;
 +              }
 +      }
 +      return IDWALK_RET_NOP;
 +}
 +
 +/* Do some special treatment of data transfer from original ID to it's
 + * CoW complementary part.
 + *
 + * Only use for the newly created CoW datablocks.
 + */
 +void update_special_pointers(const Depsgraph *depsgraph,
 +                             const ID *id_orig, ID *id_cow)
 +{
 +      const ID_Type type = GS(id_orig->name);
 +      switch (type) {
 +              case ID_OB:
 +              {
 +                      /* Ensure we don't drag someone's else derived mesh to the
 +                       * new copy of the object.
 +                       */
 +                      Object *object_cow = (Object *)id_cow;
 +                      (void) object_cow;  /* Ignored for release builds. */
 +                      BLI_assert(object_cow->derivedFinal == NULL);
 +                      BLI_assert(object_cow->derivedDeform == NULL);
 +                      break;
 +              }
 +              case ID_ME:
 +              {
 +                      /* For meshes we need to update edit_btmesh to make it to point
 +                       * to the CoW version of object.
 +                       *
 +                       * This is kind of confusing, because actual bmesh is not owned by
 +                       * the CoW object, so need to be accurate about using link from
 +                       * edit_btmesh to object.
 +                       */
 +                      const Mesh *mesh_orig = (const Mesh *)id_orig;
 +                      Mesh *mesh_cow = (Mesh *)id_cow;
 +                      if (mesh_orig->edit_btmesh != NULL) {
 +                              mesh_cow->edit_btmesh = (BMEditMesh *)MEM_dupallocN(mesh_orig->edit_btmesh);
 +                              mesh_cow->edit_btmesh->ob =
 +                                      (Object *)depsgraph->get_cow_id(&mesh_orig->edit_btmesh->ob->id);
 +                              mesh_cow->edit_btmesh->derivedFinal = NULL;
 +                              mesh_cow->edit_btmesh->derivedCage = NULL;
 +                      }
 +                      break;
 +              }
 +              default:
 +                      break;
 +      }
 +}
 +
 +void update_copy_on_write_layer_collections(
 +        ListBase *layer_collections_cow,
 +        const ListBase *layer_collections_orig);
 +
 +void update_copy_on_write_layer_collection(
 +        LayerCollection *layer_collection_cow,
 +        const LayerCollection *layer_collection_orig)
 +{
 +      // Make a local copy of original layer collection, so we can start
 +      // modifying it.
 +      LayerCollection local = *layer_collection_orig;
 +      // Copy all pointer data from original CoW version of layer collection.
 +      local.next = layer_collection_cow->next;
 +      local.prev = layer_collection_cow->prev;
 +      local.scene_collection = layer_collection_cow->scene_collection;
 +      local.object_bases = layer_collection_cow->object_bases;
 +      local.overrides = layer_collection_cow->overrides;
 +      local.layer_collections = layer_collection_cow->layer_collections;
 +      local.properties = layer_collection_cow->properties;
 +      local.properties_evaluated = layer_collection_cow->properties_evaluated;
 +      // Synchronize pointer-related data.
 +      IDP_Reset(local.properties, layer_collection_orig->properties);
 +      // Copy synchronized version back.
 +      *layer_collection_cow = local;
 +      // Recurs into nested layer collections.
 +      update_copy_on_write_layer_collections(
 +              &layer_collection_cow->layer_collections,
 +              &layer_collection_orig->layer_collections);
 +}
 +
 +void update_copy_on_write_layer_collections(
 +        ListBase *layer_collections_cow,
 +        const ListBase *layer_collections_orig)
 +{
 +      const LayerCollection *layer_collection_orig =
 +              (const LayerCollection *)layer_collections_orig->first;
 +      LayerCollection *layer_collection_cow =
 +              (LayerCollection *)layer_collections_cow->first;
 +      while (layer_collection_orig != NULL) {
 +              update_copy_on_write_layer_collection(layer_collection_cow,
 +                                                    layer_collection_orig);
 +              layer_collection_orig = layer_collection_orig->next;
 +              layer_collection_cow = layer_collection_cow->next;
 +      }
 +}
 +
 +void update_copy_on_write_view_layer(const Depsgraph *depsgraph,
 +                                     ViewLayer *view_layer_cow,
 +                                     const ViewLayer *view_layer_orig)
 +{
 +      // Update pointers to active base.
 +      if (view_layer_orig->basact == NULL) {
 +              view_layer_cow->basact = NULL;
 +      }
 +      else {
 +              const Object *obact_orig = view_layer_orig->basact->object;
 +              Object *obact_cow = (Object *)depsgraph->get_cow_id(&obact_orig->id);
 +              view_layer_cow->basact = BKE_view_layer_base_find(view_layer_cow, obact_cow);
 +      }
 +      // Update base flags.
 +      //
 +      // TODO(sergey): We should probably check visibled/selectabled.
 +      // flag here?
 +      const Base *base_orig = (Base *)view_layer_orig->object_bases.first;
 +      Base *base_cow = (Base *)view_layer_cow->object_bases.first;;
 +      while (base_orig != NULL) {
 +              base_cow->flag = base_orig->flag;
 +              base_orig = base_orig->next;
 +              base_cow = base_cow->next;
 +      }
 +      // Synchronize settings.
 +      view_layer_cow->active_collection = view_layer_orig->active_collection;
 +      view_layer_cow->flag = view_layer_orig->flag;
 +      view_layer_cow->layflag = view_layer_orig->layflag;
 +      view_layer_cow->passflag = view_layer_orig->passflag;
 +      view_layer_cow->pass_alpha_threshold = view_layer_orig->pass_alpha_threshold;
 +      // Synchronize ID properties.
 +      IDP_Reset(view_layer_cow->properties, view_layer_orig->properties);
 +      IDP_Reset(view_layer_cow->id_properties, view_layer_orig->id_properties);
 +      // Synchronize layer collections.
 +      update_copy_on_write_layer_collections(
 +              &view_layer_cow->layer_collections,
 +              &view_layer_orig->layer_collections);
 +}
 +
 +void update_copy_on_write_view_layers(const Depsgraph *depsgraph,
 +                                      Scene *scene_cow,
 +                                      const Scene *scene_orig)
 +{
 +      const ViewLayer *view_layer_orig = (const ViewLayer *)scene_orig->view_layers.first;
 +      ViewLayer *view_layer_cow = (ViewLayer *)scene_cow->view_layers.first;
 +      while (view_layer_orig != NULL) {
 +              update_copy_on_write_view_layer(depsgraph,
 +                                              view_layer_cow,
 +                                              view_layer_orig);
 +              view_layer_orig = view_layer_orig->next;
 +              view_layer_cow = view_layer_cow->next;
 +      }
 +}
 +
 +void update_copy_on_write_scene_collections(
 +        ListBase *collections_cow,
 +        const ListBase *collections_orig);
 +
 +void update_copy_on_write_scene_collection(
 +        SceneCollection *collection_cow,
 +        const SceneCollection *collection_orig)
 +{
 +      collection_cow->active_object_index = collection_orig->active_object_index;
 +      update_copy_on_write_scene_collections(
 +              &collection_cow->scene_collections,
 +              &collection_orig->scene_collections);
 +}
 +
 +void update_copy_on_write_scene_collections(
 +        ListBase *collections_cow,
 +        const ListBase *collections_orig)
 +{
 +      const SceneCollection *nested_collection_orig =
 +              (const SceneCollection *)collections_orig->first;
 +      SceneCollection *nested_collection_cow =
 +              (SceneCollection *)collections_cow->first;
 +      while (nested_collection_orig != NULL) {
 +              update_copy_on_write_scene_collection(
 +                      nested_collection_cow,
 +                      nested_collection_orig);
 +              nested_collection_orig = nested_collection_orig->next;
 +              nested_collection_cow = nested_collection_cow->next;
 +      }
 +}
 +
 +/* Update copy-on-write version of scene from original scene. */
 +void update_copy_on_write_scene(const Depsgraph *depsgraph,
 +                                Scene *scene_cow,
 +                                const Scene *scene_orig)
 +{
 +      // Some non-pointer data sync, current frame for now.
 +      // TODO(sergey): Are we missing something here?
 +      scene_cow->r.cfra = scene_orig->r.cfra;
 +      scene_cow->r.subframe = scene_orig->r.subframe;
 +      // Update view layers and collections.
 +      update_copy_on_write_view_layers(depsgraph, scene_cow, scene_orig);
 +      update_copy_on_write_scene_collection(scene_cow->collection,
 +                                            scene_orig->collection);
 +      /* Synchronize active render engine. */
 +      BLI_strncpy(scene_cow->view_render.engine_id,
 +                  scene_orig->view_render.engine_id,
 +                  sizeof(scene_cow->view_render.engine_id));
 +      /* TODO(sergey): What else do we need here? */
 +}
 +
 +/* Update copy-on-write version of armature object from original scene. */
 +void update_copy_on_write_object(const Depsgraph * /*depsgraph*/,
 +                                 Object *object_cow,
 +                                 const Object *object_orig)
 +{
 +      /* TODO(sergey): This function might be split into a smaller ones,
 +       * reused for different updates. And maybe even moved to BKE.
 +       */
 +      /* Update armature/pose related flags. */
 +      bPose *pose_cow = object_cow->pose;
 +      const bPose *pose_orig = object_orig->pose;
 +      extract_pose_from_pose(pose_cow, pose_orig);
 +      /* Update object itself. */
 +      BKE_object_transform_copy(object_cow, object_orig);
 +}
 +
 +/* Update copy-on-write version of datablock from it's original ID without re-building
 + * the whole datablock from scratch.
 + *
 + * Used for such special cases as scene collections and armatures, which can not use full
 + * re-alloc due to pointers used as function bindings.
 + */
 +void update_copy_on_write_datablock(const Depsgraph *depsgraph,
 +                                    const ID *id_orig, ID *id_cow)
 +{
 +      bool ok = false;
 +      const ID_Type id_type = GS(id_orig->name);
 +      switch (id_type) {
 +              case ID_SCE: {
 +                      const Scene *scene_orig = (const Scene *)id_orig;
 +                      Scene *scene_cow = (Scene *)id_cow;
 +                      update_copy_on_write_scene(depsgraph, scene_cow, scene_orig);
 +                      ok = true;
 +                      break;
 +              }
 +              case ID_OB: {
 +                      const Object *object_orig = (const Object *)id_orig;
 +                      Object *object_cow = (Object *)id_cow;
 +                      if (object_orig->type == OB_ARMATURE) {
 +                              update_copy_on_write_object(depsgraph,
 +                                                          object_cow,
 +                                                          object_orig);
 +                              ok = true;
 +                      }
 +                      break;
 +              }
 +              case ID_AR:
 +                      /* Nothing to do currently. */
 +                      ok = true;
 +                      break;
 +              default:
 +                      break;
 +      }
 +      // TODO(sergey): Other ID types here.
 +      if (!ok) {
 +              BLI_assert(!"Missing update logic of expanded datablock");
 +      }
 +}
 +
 +/* This callback is used to validate that all nested ID datablocks are
 + * properly expanded.
 + */
 +int foreach_libblock_validate_callback(void *user_data,
 +                                       ID * /*id_self*/,
 +                                       ID **id_p,
 +                                       int /*cb_flag*/)
 +{
 +      ValidateData *data = (ValidateData *)user_data;
 +      if (*id_p != NULL) {
 +              if (!check_datablock_expanded(*id_p)) {
 +                      data->is_valid = false;
 +                      /* TODO(sergey): Store which is is not valid? */
 +              }
 +      }
 +      return IDWALK_RET_NOP;
 +}
 +
 +}  // namespace
 +
 +/* Actual implementation of logic which "expands" all the data which was not
 + * yet copied-on-write.
 + *
 + * NOTE: Expects that CoW datablock is empty.
 + */
 +ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph,
 +                                       const IDDepsNode *id_node,
 +                                       DepsgraphNodeBuilder *node_builder,
 +                                       bool create_placeholders)
 +{
 +      BLI_assert(!create_placeholders ||
 +                 check_datablock_expanded_at_construction(id_node->id_orig));
 +      const ID *id_orig = id_node->id_orig;
 +      ID *id_cow = id_node->id_cow;
 +      /* No need to expand such datablocks, their copied ID is same as original
 +       * one already.
 +       */
 +      if (!deg_copy_on_write_is_needed(id_orig)) {
 +              return id_cow;
 +      }
 +      DEG_COW_PRINT("Expanding datablock for %s: id_orig=%p id_cow=%p\n",
 +                    id_orig->name, id_orig, id_cow);
 +      /* Sanity checks. */
 +      /* NOTE: Disabled for now, conflicts when re-using evaluated datablock when
 +       * rebuilding dependencies.
 +       */
 +      if (check_datablock_expanded(id_cow) && create_placeholders) {
 +              deg_free_copy_on_write_datablock(id_cow);
 +      }
 +      // BLI_assert(check_datablock_expanded(id_cow) == false);
 +      /* Copy data from original ID to a copied version. */
 +      /* TODO(sergey): Avoid doing full ID copy somehow, make Mesh to reference
 +       * original geometry arrays for until those are modified.
 +       */
 +      /* TODO(sergey): We do some trickery with temp bmain and extra ID pointer
 +       * just to be able to use existing API. Ideally we need to replace this with
 +       * in-place copy from existing datablock to a prepared memory.
 +       *
 +       * NOTE: We don't use BKE_main_{new,free} because:
 +       * - We don't want heap-allocations here.
 +       * - We don't want bmain's content to be freed when main is freed.
 +       */
 +      bool done = false;
 +      /* Need to make sure the possibly temporary allocated memory is correct for
 +       * until we are fully done with remapping original pointers with copied on
 +       * write ones.
 +       */
 +      ID *newid = NULL;
 +      /* First we handle special cases which are not covered by id_copy() yet.
 +       * or cases where we want to do something smarter than simple datablock
 +       * copy.
 +       */
 +      const ID_Type id_type = GS(id_orig->name);
 +      switch (id_type) {
 +              case ID_SCE:
 +              {
 +                      done = scene_copy_inplace_no_main((Scene *)id_orig, (Scene *)id_cow);
 +                      break;
 +              }
 +              case ID_ME:
 +              {
 +                      /* TODO(sergey): Ideally we want to handle meshes in a special
 +                       * manner here to avoid initial copy of all the geometry arrays.
 +                       */
 +                      break;
 +              }
 +              default:
 +                      break;
 +      }
 +      if (!done) {
 +              done = id_copy_inplace_no_main(id_orig, id_cow);
 +      }
 +      if (!done) {
 +              BLI_assert(!"No idea how to perform CoW on datablock");
 +      }
 +      /* Update pointers to nested ID datablocks. */
 +      DEG_COW_PRINT("  Remapping ID links for %s: id_orig=%p id_cow=%p\n",
 +                    id_orig->name, id_orig, id_cow);
 +
 +#ifdef NESTED_ID_NASTY_WORKAROUND
 +      ntree_hack_remap_pointers(depsgraph, id_cow);
 +#endif
 +      /* Do it now, so remapping will understand that possibly remapped self ID
 +       * is not to be remapped again.
 +       */
 +      deg_tag_copy_on_write_id(id_cow, id_orig);
 +      /* Perform remapping of the nodes. */
 +      RemapCallbackUserData user_data;
 +      user_data.depsgraph = depsgraph;
 +      user_data.temp_id = newid;
 +      user_data.real_id = id_cow;
 +      user_data.node_builder = node_builder;
 +      user_data.create_placeholders = create_placeholders;
 +      BKE_library_foreach_ID_link(NULL,
 +                                  id_cow,
 +                                  foreach_libblock_remap_callback,
 +                                  (void *)&user_data,
 +                                  IDWALK_NOP);
 +      /* Correct or tweak some pointers which are not taken care by foreach
 +       * from above.
 +       */
 +      update_special_pointers(depsgraph, id_orig, id_cow);
 +      /* Now we can safely discard temporary memory used for copying. */
 +      if (newid != NULL) {
 +              MEM_freeN(newid);
 +      }
 +      return id_cow;
 +}
 +
 +/* NOTE: Depsgraph is supposed to have ID node already. */
 +ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph,
 +                                       ID *id_orig,
 +                                       DepsgraphNodeBuilder *node_builder,
 +                                       bool create_placeholders)
 +{
 +      DEG::IDDepsNode *id_node = depsgraph->find_id_node(id_orig);
 +      BLI_assert(id_node != NULL);
 +      return deg_expand_copy_on_write_datablock(depsgraph,
 +                                                id_node,
 +                                                node_builder,
 +                                                create_placeholders);
 +}
 +
 +ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph,
 +                                       const IDDepsNode *id_node)
 +{
 +      const ID *id_orig = id_node->id_orig;
 +      const ID_Type id_type = GS(id_orig->name);
 +      ID *id_cow = id_node->id_cow;
 +      /* Similar to expansion, no need to do anything here. */
 +      if (!deg_copy_on_write_is_needed(id_orig)) {
 +              return id_cow;
 +      }
 +      /* Special case for datablocks which are expanded at the dependency graph
 +       * construction time. This datablocks must never change pointers of their
 +       * nested data since it is used for function bindings.
 +       */
 +      if (check_datablock_expanded_at_construction(id_orig)) {
 +              BLI_assert(check_datablock_expanded(id_cow) == true);
 +              update_copy_on_write_datablock(depsgraph, id_orig, id_cow);
 +              return id_cow;
 +      }
 +      /* For the rest if datablock types we use simple logic:
 +       * - Free previously expanded data, if any.
 +       * - Perform full datablock copy.
 +       *
 +       * Note that we never free GPU materials from here since that's not
 +       * safe for threading and GPU materials are likely to be re-used.
 +       */
 +      /* TODO(sergey): Either move this to an utility function or redesign
 +       * Copy-on-Write components in a way that only needed parts are being
 +       * copied over.
 +       */
 +      ListBase gpumaterial_backup;
 +      ListBase *gpumaterial_ptr = NULL;
 +      Mesh *mesh_evaluated = NULL;
 +      IDProperty *base_collection_properties = NULL;
 +      short base_flag = 0;
 +      if (check_datablock_expanded(id_cow)) {
 +              switch (id_type) {
 +                      case ID_MA:
 +                      {
 +                              Material *material = (Material *)id_cow;
 +                              gpumaterial_ptr = &material->gpumaterial;
 +                              break;
 +                      }
 +                      case ID_WO:
 +                      {
 +                              World *world = (World *)id_cow;
 +                              gpumaterial_ptr = &world->gpumaterial;
 +                              break;
 +                      }
 +                      case ID_OB:
 +                      {
 +                              Object *object = (Object *)id_cow;
 +                              /* Store evaluated mesh, make sure we don't free it. */
 +                              mesh_evaluated = object->mesh_evaluated;
 +                              object->mesh_evaluated = NULL;
 +                              /* Currently object update will override actual object->data
 +                               * to an evaluated version. Need to make sure we don't have
 +                               * data set to evaluated one before free anything.
 +                               */
 +                              if (mesh_evaluated != NULL) {
 +                                      if (object->data == mesh_evaluated) {
 +                                              object->data = mesh_evaluated->id.orig_id;
 +                                      }
 +                              }
 +                              /* Make a backup of base flags. */
 +                              base_collection_properties = object->base_collection_properties;
 +                              base_flag = object->base_flag;
 +                              break;
 +                      }
 +                      default:
 +                              break;
 +              }
 +              if (gpumaterial_ptr != NULL) {
 +                      gpumaterial_backup = *gpumaterial_ptr;
 +                      gpumaterial_ptr->first = gpumaterial_ptr->last = NULL;
 +              }
 +      }
 +      deg_free_copy_on_write_datablock(id_cow);
 +      deg_expand_copy_on_write_datablock(depsgraph, id_node);
 +      /* Restore GPU materials. */
 +      if (gpumaterial_ptr != NULL) {
 +              *gpumaterial_ptr = gpumaterial_backup;
 +      }
 +      if (id_type == ID_OB) {
 +              Object *object = (Object *)id_cow;
 +              if (mesh_evaluated != NULL) {
 +                      object->mesh_evaluated = mesh_evaluated;
 +                      /* Do same thing as object update: override actual object data
 +                       * pointer with evaluated datablock.
 +                       */
 +                      if (object->type == OB_MESH) {
 +                              object->data = mesh_evaluated;
 +                              /* Evaluated mesh simply copied edit_btmesh pointer from
 +                               * original mesh during update, need to make sure no dead
 +                               * pointers are left behind.
 +                               */
 +                              mesh_evaluated->edit_btmesh =
 +                                      ((Mesh *)mesh_evaluated->id.orig_id)->edit_btmesh;
 +                      }
 +              }
 +              if (base_collection_properties != NULL) {
 +                      object->base_collection_properties = base_collection_properties;
 +                      object->base_flag = base_flag;
 +              }
 +      }
 +      return id_cow;
 +}
 +
 +/* NOTE: Depsgraph is supposed to have ID node already. */
 +ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph,
 +                                       ID *id_orig)
 +{
 +      DEG::IDDepsNode *id_node = depsgraph->find_id_node(id_orig);
 +      BLI_assert(id_node != NULL);
 +      return deg_update_copy_on_write_datablock(depsgraph, id_node);
 +}
 +
 +/* Free content of the CoW datablock
 + * Notes:
 + * - Does not recurs into nested ID datablocks.
 + * - Does not free datablock itself.
 + */
 +void deg_free_copy_on_write_datablock(ID *id_cow)
 +{
 +      if (!check_datablock_expanded(id_cow)) {
 +              /* Actual content was never copied on top of CoW block, we have
 +               * nothing to free.
 +               */
 +              return;
 +      }
 +      const ID_Type type = GS(id_cow->name);
 +#ifdef NESTED_ID_NASTY_WORKAROUND
 +      nested_id_hack_discard_pointers(id_cow);
 +#endif
 +      switch (type) {
 +              case ID_OB:
 +              {
 +                      /* TODO(sergey): This workaround is only to prevent free derived
 +                       * caches from modifying object->data. This is currently happening
 +                       * due to mesh/curve datablock boundbox tagging dirty.
 +                       */
 +                      Object *ob_cow = (Object *)id_cow;
 +                      ob_cow->data = NULL;
 +                      break;
 +              }
 +              case ID_ME:
 +              {
 +                      Mesh *mesh_cow = (Mesh *)id_cow;
 +                      if (mesh_cow->edit_btmesh != NULL) {
 +                              BKE_editmesh_free_derivedmesh(mesh_cow->edit_btmesh);
 +                              MEM_freeN(mesh_cow->edit_btmesh);
 +                              mesh_cow->edit_btmesh = NULL;
 +                      }
 +                      break;
 +              }
 +              case ID_SCE:
 +              {
 +                      /* Special case for scene: we use explicit function call which
 +                       * ensures no access to other datablocks is done.
 +                       */
 +                      Scene *scene = (Scene *)id_cow;
 +                      BKE_scene_free_ex(scene, false);
 +                      BKE_libblock_free_data(id_cow, false);
 +                      id_cow->name[0] = '\0';
 +                      return;
 +              }
 +              default:
 +                      break;
 +      }
 +      BKE_libblock_free_datablock(id_cow, 0);
 +      BKE_libblock_free_data(id_cow, false);
 +      /* Signal datablock as not being expanded. */
 +      id_cow->name[0] = '\0';
 +}
 +
 +void deg_evaluate_copy_on_write(const EvaluationContext * /*eval_ctx*/,
 +                                const Depsgraph *depsgraph,
 +                                const IDDepsNode *id_node)
 +{
 +      DEBUG_PRINT("%s on %s\n", __func__, id_node->id_orig->name);
 +      deg_update_copy_on_write_datablock(depsgraph, id_node);
 +}
 +
 +bool deg_validate_copy_on_write_datablock(ID *id_cow)
 +{
 +      if (id_cow == NULL) {
 +              return false;
 +      }
 +      ValidateData data;
 +      data.is_valid = true;
 +      BKE_library_foreach_ID_link(NULL,
 +                                  id_cow,
 +                                  foreach_libblock_validate_callback,
 +                                  &data,
 +                                  IDWALK_NOP);
 +      return data.is_valid;
 +}
 +
 +void deg_tag_copy_on_write_id(ID *id_cow, const ID *id_orig)
 +{
 +      id_cow->tag |= LIB_TAG_COPY_ON_WRITE;
 +      id_cow->orig_id = (ID *)id_orig;
 +}
 +
 +bool deg_copy_on_write_is_expanded(const ID *id_cow)
 +{
 +      return check_datablock_expanded(id_cow);
 +}
 +
 +bool deg_copy_on_write_is_needed(const ID *id_orig)
 +{
 +      const ID_Type id_type = GS(id_orig->name);
 +      return !ELEM(id_type, ID_IM);
 +}
 +
 +}  // namespace DEG
Simple merge