Merge branch 'master' into blender2.8
authorSergey Sharybin <sergey.vfx@gmail.com>
Wed, 20 Dec 2017 15:40:49 +0000 (16:40 +0100)
committerSergey Sharybin <sergey.vfx@gmail.com>
Wed, 20 Dec 2017 15:40:49 +0000 (16:40 +0100)
24 files changed:
1  2 
source/blender/depsgraph/CMakeLists.txt
source/blender/depsgraph/intern/builder/deg_builder.cc
source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
source/blender/depsgraph/intern/builder/deg_builder_relations.cc
source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc
source/blender/depsgraph/intern/depsgraph.cc
source/blender/depsgraph/intern/depsgraph.h
source/blender/depsgraph/intern/depsgraph_build.cc
source/blender/depsgraph/intern/depsgraph_debug.cc
source/blender/depsgraph/intern/depsgraph_eval.cc
source/blender/depsgraph/intern/depsgraph_query.cc
source/blender/depsgraph/intern/depsgraph_query_foreach.cc
source/blender/depsgraph/intern/depsgraph_query_iter.cc
source/blender/depsgraph/intern/depsgraph_tag.cc
source/blender/depsgraph/intern/eval/deg_eval.cc
source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
source/blender/depsgraph/intern/eval/deg_eval_flush.cc
source/blender/depsgraph/intern/nodes/deg_node.cc
source/blender/depsgraph/intern/nodes/deg_node_component.cc
source/blender/depsgraph/intern/nodes/deg_node_component.h
source/blender/depsgraph/intern/nodes/deg_node_id.cc
source/blender/depsgraph/intern/nodes/deg_node_id.h
source/blender/editors/interface/interface_region_tooltip.c

index 1e906d454acceb6d15dae3d2f427751a2d1a546d,5713297d65864054692d05e3824f93664468b76f..2fcad233044b39f5177a322010e62e0079e75766
  #include "intern/depsgraph.h"
  #include "intern/depsgraph_types.h"
  #include "intern/nodes/deg_node.h"
 -#include "intern/nodes/deg_node_component.h"
+ #include "intern/nodes/deg_node_id.h"
 -#include "intern/nodes/deg_node_operation.h"
  
  #include "util/deg_util_foreach.h"
  
index eb2976823081f75632a1a9421ee7e3729176c4e7,85e80f80d262d482f87d6563568048158164acae..60562641c935a1b8846ee0179e040236a0be764c
@@@ -103,9 -103,9 +103,10 @@@ extern "C" 
  #include "DEG_depsgraph_build.h"
  
  #include "intern/builder/deg_builder.h"
 +#include "intern/eval/deg_eval_copy_on_write.h"
  #include "intern/nodes/deg_node.h"
  #include "intern/nodes/deg_node_component.h"
+ #include "intern/nodes/deg_node_id.h"
  #include "intern/nodes/deg_node_operation.h"
  #include "intern/depsgraph_types.h"
  #include "intern/depsgraph_intern.h"
index 29cff0cb28d88f179b9296001a4387dfa58805ea,0000000000000000000000000000000000000000..9b3e46df69a228c280d4c7ba4a26efd7c01c048a
mode 100644,000000..100644
--- /dev/null
@@@ -1,135 -1,0 +1,136 @@@
 +/*
 + * ***** 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) 2013 Blender Foundation.
 + * All rights reserved.
 + *
 + * Original Author: Joshua Leung
 + * Contributor(s): Based on original depsgraph.c code - Blender Foundation (2005-2013)
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
 + *  \ingroup depsgraph
 + *
 + * Methods for constructing depsgraph
 + */
 +
 +#include "intern/builder/deg_builder_relations.h"
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <cstring>  /* required for STREQ later on. */
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "BLI_utildefines.h"
 +#include "BLI_blenlib.h"
 +
 +extern "C" {
 +#include "DNA_node_types.h"
 +#include "DNA_object_types.h"
 +#include "DNA_scene_types.h"
 +
 +#include "BKE_layer.h"
 +#include "BKE_main.h"
 +#include "BKE_node.h"
 +} /* extern "C" */
 +
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_build.h"
 +
 +#include "intern/builder/deg_builder.h"
 +#include "intern/builder/deg_builder_pchanmap.h"
 +
 +#include "intern/nodes/deg_node.h"
 +#include "intern/nodes/deg_node_component.h"
++#include "intern/nodes/deg_node_id.h"
 +#include "intern/nodes/deg_node_operation.h"
 +
 +#include "intern/depsgraph_intern.h"
 +#include "intern/depsgraph_types.h"
 +
 +#include "util/deg_util_foreach.h"
 +
 +namespace DEG {
 +
 +void DepsgraphRelationBuilder::build_view_layer(Scene *scene, ViewLayer *view_layer)
 +{
 +      /* Setup currently building context. */
 +      scene_ = scene;
 +      /* Scene objects. */
 +      /* NOTE: Nodes builder requires us to pass CoW base because it's being
 +       * passed to the evaluation functions. During relations builder we only
 +       * do NULL-pointer check of the base, so it's fine to pass original one.
 +       */
 +      LINKLIST_FOREACH(Base *, base, &view_layer->object_bases) {
 +              build_object(base, base->object);
 +      }
 +      if (scene->camera != NULL) {
 +              build_object(NULL, scene->camera);
 +      }
 +      /* Rigidbody. */
 +      if (scene->rigidbody_world != NULL) {
 +              build_rigidbody(scene);
 +      }
 +      /* Scene's animation and drivers. */
 +      if (scene->adt != NULL) {
 +              build_animdata(&scene->id);
 +      }
 +      /* World. */
 +      if (scene->world != NULL) {
 +              build_world(scene->world);
 +      }
 +      /* Compositor nodes. */
 +      if (scene->nodetree != NULL) {
 +              build_compositor(scene);
 +      }
 +      /* Grease pencil. */
 +      if (scene->gpd != NULL) {
 +              build_gpencil(scene->gpd);
 +      }
 +      /* Masks. */
 +      LINKLIST_FOREACH (Mask *, mask, &bmain_->mask) {
 +              build_mask(mask);
 +      }
 +      /* Movie clips. */
 +      LINKLIST_FOREACH (MovieClip *, clip, &bmain_->movieclip) {
 +              build_movieclip(clip);
 +      }
 +      /* Collections. */
 +      build_view_layer_collections(&scene_->id, view_layer);
 +      /* TODO(sergey): Do this flush on CoW object? */
 +      foreach (OperationDepsNode *node, graph_->operations) {
 +              IDDepsNode *id_node = node->owner->owner;
 +              ID *id = id_node->id_orig;
 +              if (GS(id->name) == ID_OB) {
 +                      Object *object = (Object *)id;
 +                      object->customdata_mask |= node->customdata_mask;
 +              }
 +      }
 +      /* Build all set scenes. */
 +      if (scene->set != NULL) {
 +              ViewLayer *set_view_layer = BKE_view_layer_from_scene_get(scene->set);
 +              build_view_layer(scene->set, set_view_layer);
 +      }
 +
 +      graph_->scene = scene;
 +      graph_->view_layer = view_layer;
 +}
 +
 +}  // namespace DEG
index 995157f5c9a6c2045f689ec0f7608841c0d75c3c,c4841d6789eacf90dd3a7ca9aea9a880d9ddb0d2..45013bb1bcde9ffd8d5443874ed4aa8591333c3e
@@@ -53,11 -56,11 +54,13 @@@ extern "C" 
  
  #include "DEG_depsgraph.h"
  
 +#include "intern/eval/deg_eval_copy_on_write.h"
 +
  #include "intern/nodes/deg_node.h"
  #include "intern/nodes/deg_node_component.h"
+ #include "intern/nodes/deg_node_id.h"
  #include "intern/nodes/deg_node_operation.h"
+ #include "intern/nodes/deg_node_time.h"
  
  #include "intern/depsgraph_intern.h"
  #include "util/deg_util_foreach.h"
@@@ -78,12 -69,20 +81,20 @@@ namespace DEG 
  
  static DEG_EditorUpdateIDCb deg_editor_update_id_cb = NULL;
  static DEG_EditorUpdateSceneCb deg_editor_update_scene_cb = NULL;
 -static DEG_EditorUpdateScenePreCb deg_editor_update_scene_pre_cb = NULL;
  
+ /* TODO(sergey): Find a better place for this. */
+ template <typename T>
+ static void remove_from_vector(vector<T> *vector, const T& value)
+ {
+       vector->erase(std::remove(vector->begin(), vector->end(), value),
+                     vector->end());
+ }
  Depsgraph::Depsgraph()
    : time_source(NULL),
 -    need_update(false),
 -    layers(0)
 +    need_update(true),
 +    scene(NULL),
 +    view_layer(NULL)
  {
        BLI_spin_init(&lock);
        id_hash = BLI_ghash_ptr_new("Depsgraph id hash");
index 6892bdaa178147b2a658b24a472db6e0c72d689f,9b1961baa48e5f2d540569e6b48fb6b197675035..98bf335f89e6811d912ffda7887e60d9fa621f12
@@@ -46,10 -40,11 +46,11 @@@ extern "C" 
  #include "DEG_depsgraph_query.h"
  
  #include "intern/depsgraph_intern.h"
+ #include "intern/nodes/deg_node_id.h"
  
 -bool DEG_id_type_tagged(Main *bmain, short idtype)
 +bool DEG_id_type_tagged(Main *bmain, short id_type)
  {
 -      return bmain->id_tag_update[BKE_idcode_to_index(idtype)] != 0;
 +      return bmain->id_tag_update[BKE_idcode_to_index(id_type)] != 0;
  }
  
  short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id)
index a7f37575ff66a52ce5172b9f5b2b902d7deb1571,0000000000000000000000000000000000000000..2a323fe63bd2146da8b766b0edf5160f03b1f07e
mode 100644,000000..100644
--- /dev/null
@@@ -1,227 -1,0 +1,229 @@@
 +/*
 + * ***** 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) 2017 Blender Foundation.
 + * All rights reserved.
 + *
 + * Original Author: Dalai Felinto
 + * Contributor(s): Sergey Sharybin
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/depsgraph/intern/depsgraph_query_iter.cc
 + *  \ingroup depsgraph
 + *
 + * Implementation of Querying and Filtering API's
 + */
 +
 +#include "MEM_guardedalloc.h"
 +
 +extern "C" {
 +#include "BLI_utildefines.h"
 +#include "BLI_math.h"
 +#include "BKE_anim.h"
 +#include "BKE_idprop.h"
 +#include "BKE_layer.h"
 +#include "BKE_object.h"
 +} /* extern "C" */
 +
 +#include "DNA_object_types.h"
 +#include "DNA_scene_types.h"
 +
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_query.h"
 +
 +#include "intern/depsgraph_intern.h"
 +#include "util/deg_util_foreach.h"
 +
++#include "intern/nodes/deg_node_id.h"
++
 +#ifndef NDEBUG
 +#  include "intern/eval/deg_eval_copy_on_write.h"
 +#endif
 +
 +/* ************************ DEG ITERATORS ********************* */
 +
 +static bool deg_objects_dupli_iterator_next(BLI_Iterator *iter)
 +{
 +      DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
 +      while (data->dupli_object_next != NULL) {
 +              DupliObject *dob = data->dupli_object_next;
 +              Object *obd = dob->ob;
 +
 +              data->dupli_object_next = data->dupli_object_next->next;
 +
 +              /* Group duplis need to set ob matrices correct, for deform. so no_draw
 +               * is part handled.
 +               */
 +              if ((obd->transflag & OB_RENDER_DUPLI) == 0 && dob->no_draw) {
 +                      continue;
 +              }
 +
 +              if (obd->type == OB_MBALL) {
 +                      continue;
 +              }
 +
 +              data->dupli_object_current = dob;
 +
 +              /* Temporary object to evaluate. */
 +              Object *dupli_parent = data->dupli_parent;
 +              Object *temp_dupli_object = &data->temp_dupli_object;
 +              *temp_dupli_object = *dob->ob;
 +              temp_dupli_object->select_color = dupli_parent->select_color;
 +              temp_dupli_object->base_flag = dupli_parent->base_flag | BASE_FROMDUPLI;
 +
 +              if (dob->collection_properties != NULL) {
 +                      temp_dupli_object->base_collection_properties = dob->collection_properties;
 +                      IDP_MergeGroup(temp_dupli_object->base_collection_properties, dupli_parent->base_collection_properties, false);
 +              }
 +              else {
 +                      temp_dupli_object->base_collection_properties = dupli_parent->base_collection_properties;
 +              }
 +
 +              copy_m4_m4(data->temp_dupli_object.obmat, dob->mat);
 +              iter->current = &data->temp_dupli_object;
 +              BLI_assert(
 +                      DEG::deg_validate_copy_on_write_datablock(
 +                              &data->temp_dupli_object.id));
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
 +static void DEG_iterator_objects_step(BLI_Iterator *iter, DEG::IDDepsNode *id_node)
 +{
 +      /* Set it early in case we need to exit and we are running from within a loop. */
 +      iter->skip = true;
 +
 +      DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
 +      const ID_Type id_type = GS(id_node->id_orig->name);
 +
 +      if (id_type != ID_OB) {
 +              return;
 +      }
 +
 +      switch (id_node->linked_state) {
 +              case DEG::DEG_ID_LINKED_DIRECTLY:
 +                      if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY) == 0) {
 +                              return;
 +                      }
 +                      break;
 +              case DEG::DEG_ID_LINKED_VIA_SET:
 +                      if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) == 0) {
 +                              return;
 +                      }
 +                      break;
 +              case DEG::DEG_ID_LINKED_INDIRECTLY:
 +                      if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY) == 0) {
 +                              return;
 +                      }
 +                      break;
 +      }
 +
 +      Object *object = (Object *)id_node->id_cow;
 +      BLI_assert(DEG::deg_validate_copy_on_write_datablock(&object->id));
 +
 +      if ((BKE_object_is_visible(object) == false) &&
 +          ((data->flag & DEG_ITER_OBJECT_FLAG_VISIBLE) != 0))
 +      {
 +              return;
 +      }
 +
 +      if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) && (object->transflag & OB_DUPLI)) {
 +              data->dupli_parent = object;
 +              data->dupli_list = object_duplilist(&data->eval_ctx, data->scene, object);
 +              data->dupli_object_next = (DupliObject *)data->dupli_list->first;
 +      }
 +
 +      iter->current = object;
 +      iter->skip = false;
 +}
 +
 +void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data)
 +{
 +      Depsgraph *depsgraph = data->graph;
 +      DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
 +      const size_t num_id_nodes = deg_graph->id_nodes.size();
 +
 +      if (num_id_nodes == 0) {
 +              iter->valid = false;
 +              return;
 +      }
 +
 +      /* TODO(sergey): What evaluation type we want here? */
 +      DEG_evaluation_context_init(&data->eval_ctx, DAG_EVAL_RENDER);
 +      data->eval_ctx.view_layer = DEG_get_evaluated_view_layer(depsgraph);
 +
 +      iter->data = data;
 +      data->dupli_parent = NULL;
 +      data->dupli_list = NULL;
 +      data->dupli_object_next = NULL;
 +      data->dupli_object_current = NULL;
 +      data->scene = DEG_get_evaluated_scene(depsgraph);
 +      data->id_node_index = 0;
 +      data->num_id_nodes = num_id_nodes;
 +
 +      DEG::IDDepsNode *id_node = deg_graph->id_nodes[data->id_node_index];
 +      DEG_iterator_objects_step(iter, id_node);
 +
 +      if (iter->skip) {
 +              DEG_iterator_objects_next(iter);
 +      }
 +}
 +
 +void DEG_iterator_objects_next(BLI_Iterator *iter)
 +{
 +      DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
 +      Depsgraph *depsgraph = data->graph;
 +      DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph);
 +      do {
 +              if (data->dupli_list) {
 +                      if (deg_objects_dupli_iterator_next(iter)) {
 +                              return;
 +                      }
 +                      else {
 +                              free_object_duplilist(data->dupli_list);
 +                              data->dupli_parent = NULL;
 +                              data->dupli_list = NULL;
 +                              data->dupli_object_next = NULL;
 +                              data->dupli_object_current = NULL;
 +                      }
 +              }
 +
 +              ++data->id_node_index;
 +              if (data->id_node_index == data->num_id_nodes) {
 +                      iter->valid = false;
 +                      return;
 +              }
 +
 +              DEG::IDDepsNode *id_node = deg_graph->id_nodes[data->id_node_index];
 +              DEG_iterator_objects_step(iter, id_node);
 +      } while (iter->skip);
 +}
 +
 +void DEG_iterator_objects_end(BLI_Iterator *iter)
 +{
 +#ifndef NDEBUG
 +      DEGObjectIterData *data = (DEGObjectIterData *)iter->data;
 +      /* Force crash in case the iterator data is referenced and accessed down the line. (T51718) */
 +      memset(&data->temp_dupli_object, 0xff, sizeof(data->temp_dupli_object));
 +#else
 +      (void) iter;
 +#endif
 +}
index 5aebd6814a025f49894fe999a72a79a2b2c9e2ea,16e5fc9b4a52875d3861fd13a035b58508d117fb..583261024ee2a8e32e9ab147e7518a576c39d452
@@@ -307,14 -287,7 +261,7 @@@ void deg_evaluate_on_refresh(Evaluation
                node->done = 0;
        }
  
-       /* Calculate priority for operation nodes. */
- #ifdef USE_EVAL_PRIORITY
-       foreach (OperationDepsNode *node, graph->operations) {
-               calculate_eval_priority(node);
-       }
- #endif
 -      schedule_graph(task_pool, graph, layers);
 +      schedule_graph(task_pool, graph);
  
        BLI_task_pool_work_and_wait(task_pool);
        BLI_task_pool_free(task_pool);
index 00406cc8342984bf501c02111f51b09d808d82c2,0000000000000000000000000000000000000000..abd1761658435b591e57a247ca8b710d8efcfab7
mode 100644,000000..100644
--- /dev/null
@@@ -1,938 -1,0 +1,939 @@@
 +/*
 + * ***** 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_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_key_types.h"
 +#  include "DNA_lamp_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) printf
 +
 +namespace {
 +
 +#ifdef NESTED_ID_NASTY_WORKAROUND
 +union NestedIDHackTempStorage {
 +      FreestyleLineStyle linestyle;
 +      Lamp lamp;
 +      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_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_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_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_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;
 +                      const Object *object_orig = (const Object *)id_orig;
 +                      (void) object_cow;  /* Ignored for release builds. */
 +                      BLI_assert(object_cow->derivedFinal == NULL);
 +                      BLI_assert(object_cow->derivedDeform == NULL);
 +                      object_cow->mode = object_orig->mode;
 +                      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;
 +              }
 +              case ID_SCE:
 +              {
 +                      const Scene *scene_orig = (const Scene *)id_orig;
 +                      Scene *scene_cow = (Scene *)id_cow;
 +                      if (scene_orig->obedit != NULL) {
 +                              scene_cow->obedit = (Object *)depsgraph->get_cow_id(&scene_orig->obedit->id);
 +                      }
 +                      else {
 +                              scene_cow->obedit = NULL;
 +                      }
 +                      break;
 +              }
 +              default:
 +                      break;
 +      }
 +}
 +
 +/* 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 bases.
 +      const ViewLayer *view_layer_orig = (ViewLayer *)scene_orig->view_layers.first;
 +      ViewLayer *view_layer_cow = (ViewLayer *)scene_cow->view_layers.first;
 +      while (view_layer_orig != NULL) {
 +              // 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;
 +              }
 +              view_layer_orig = view_layer_orig->next;
 +              view_layer_cow = view_layer_cow->next;
 +      }
 +      // Update edit object pointer.
 +      if (scene_orig->obedit != NULL) {
 +              scene_cow->obedit = (Object *)depsgraph->get_cow_id(&scene_orig->obedit->id);
 +      }
 +      else {
 +              scene_cow->obedit = NULL;
 +      }
 +      /* 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);
 +      object_cow->mode = object_orig->mode;
 +}
 +
 +/* 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.newid;
 +                                      }
 +                              }
 +                              /* 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.newid)->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;
 +      /* TODO(sergey): Is it safe to re-use newid for original ID link? */
 +      id_cow->newid = (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
index de340907114d5bccb111d197c399fa294f759af7,c558777aa67ac12975fe95660d595b45cc1654ca..8a9eb0b11690376c1762f033392c03dbfbc47bb4
@@@ -47,12 -45,13 +47,15 @@@ extern "C" 
  
  #include "DEG_depsgraph.h"
  
 +#include "intern/eval/deg_eval_copy_on_write.h"
  #include "intern/nodes/deg_node_component.h"
+ #include "intern/nodes/deg_node_id.h"
  #include "intern/nodes/deg_node_operation.h"
+ #include "intern/nodes/deg_node_time.h"
  #include "intern/depsgraph_intern.h"
  #include "util/deg_util_foreach.h"
 +#include "util/deg_util_function.h"
  
  namespace DEG {
  
index 0000000000000000000000000000000000000000,fc77879ec331568f309c9f9dd039344fa8b5de50..572b421bdeed3a217a8c6241449bea4a03a81ddd
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,183 +1,219 @@@
 -    return type == other.type &&
 -           STREQ(name, other.name);
+ /*
+  * ***** 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) 2013 Blender Foundation.
+  * All rights reserved.
+  *
+  * Original Author: Joshua Leung
+  * Contributor(s): None Yet
+  *
+  * ***** END GPL LICENSE BLOCK *****
+  */
+ /** \file blender/depsgraph/intern/nodes/deg_node_id.cc
+  *  \ingroup depsgraph
+  */
+ #include "intern/nodes/deg_node_id.h"
+ #include <stdio.h>
+ #include <cstring>  /* required for STREQ later on. */
+ #include "BLI_utildefines.h"
+ #include "BLI_ghash.h"
+ extern "C" {
+ #include "DNA_ID.h"
+ #include "DNA_anim_types.h"
+ #include "BKE_animsys.h"
++#include "BKE_library.h"
+ }
+ #include "DEG_depsgraph.h"
++#include "intern/eval/deg_eval_copy_on_write.h"
+ #include "intern/nodes/deg_node_component.h"
+ #include "intern/nodes/deg_node_operation.h"
+ #include "intern/nodes/deg_node_time.h"
+ #include "intern/depsgraph_intern.h"
+ #include "util/deg_util_foreach.h"
+ namespace DEG {
+ IDDepsNode::ComponentIDKey::ComponentIDKey(eDepsNode_Type type,
+                                            const char *name)
+         : type(type), name(name)
+ {
+ }
+ bool IDDepsNode::ComponentIDKey::operator== (const ComponentIDKey &other) const
+ {
 -      /* Store ID-pointer. */
++      return type == other.type &&
++              STREQ(name, other.name);
+ }
+ static unsigned int id_deps_node_hash_key(const void *key_v)
+ {
+       const IDDepsNode::ComponentIDKey *key =
+               reinterpret_cast<const IDDepsNode::ComponentIDKey *>(key_v);
+       return BLI_ghashutil_combine_hash(BLI_ghashutil_uinthash(key->type),
+                                         BLI_ghashutil_strhash_p(key->name));
+ }
+ static bool id_deps_node_hash_key_cmp(const void *a, const void *b)
+ {
+       const IDDepsNode::ComponentIDKey *key_a =
+               reinterpret_cast<const IDDepsNode::ComponentIDKey *>(a);
+       const IDDepsNode::ComponentIDKey *key_b =
+               reinterpret_cast<const IDDepsNode::ComponentIDKey *>(b);
+       return !(*key_a == *key_b);
+ }
+ static void id_deps_node_hash_key_free(void *key_v)
+ {
+       typedef IDDepsNode::ComponentIDKey ComponentIDKey;
+       ComponentIDKey *key = reinterpret_cast<ComponentIDKey *>(key_v);
+       OBJECT_GUARDED_DELETE(key, ComponentIDKey);
+ }
+ static void id_deps_node_hash_value_free(void *value_v)
+ {
+       ComponentDepsNode *comp_node = reinterpret_cast<ComponentDepsNode *>(value_v);
+       OBJECT_GUARDED_DELETE(comp_node, ComponentDepsNode);
+ }
+ /* Initialize 'id' node - from pointer data given. */
+ void IDDepsNode::init(const ID *id, const char *UNUSED(subdata))
+ {
 -      this->id = (ID *)id;
 -      this->layers = (1 << 20) - 1;
 -      this->eval_flags = 0;
 -
 -      /* For object we initialize layers to layer from base. */
 -      if (GS(id->name) == ID_OB) {
 -              this->layers = 0;
 -      }
+       BLI_assert(id != NULL);
 -      /* NOTE: components themselves are created if/when needed.
 -       * This prevents problems with components getting added
 -       * twice if an ID-Ref needs to be created to house it...
++      /* Store ID-pointer. */
++      id_orig = (ID *)id;
++      eval_flags = 0;
++      linked_state = DEG_ID_LINKED_INDIRECTLY;
+       components = BLI_ghash_new(id_deps_node_hash_key,
+                                  id_deps_node_hash_key_cmp,
+                                  "Depsgraph id components hash");
++}
 -              comp_node = (ComponentDepsNode *)factory->create_node(this->id, "", name);
++void IDDepsNode::init_copy_on_write(ID *id_cow_hint)
++{
++      /* Early output for non-copy-on-write case: we keep CoW pointer same as
++       * an original one.
+        */
++      if (!DEG_depsgraph_use_copy_on_write()) {
++              UNUSED_VARS(id_cow_hint);
++              id_cow = id_orig;
++              return;
++      }
++      /* Create pointer as early as possible, so we can use it for function
++       * bindings. Rest of data we'll be copying to the new datablock when
++       * it is actually needed.
++       */
++      if (id_cow_hint != NULL) {
++              // BLI_assert(deg_copy_on_write_is_needed(id_orig));
++              if (deg_copy_on_write_is_needed(id_orig)) {
++                      id_cow = id_cow_hint;
++              }
++              else {
++                      id_cow = id_orig;
++              }
++      }
++      else if (deg_copy_on_write_is_needed(id_orig)) {
++              id_cow = (ID *)BKE_libblock_alloc_notest(GS(id_orig->name));
++              DEG_COW_PRINT("Create shallow copy for %s: id_orig=%p id_cow=%p\n",
++                            id_orig->name, id_orig, id_cow);
++              deg_tag_copy_on_write_id(id_cow, id_orig);
++      }
++      else {
++              id_cow = id_orig;
++      }
+ }
+ /* Free 'id' node. */
+ IDDepsNode::~IDDepsNode()
+ {
++      destroy();
++}
++
++void IDDepsNode::destroy()
++{
++      if (id_orig == NULL) {
++              return;
++      }
++
+       BLI_ghash_free(components,
+                      id_deps_node_hash_key_free,
+                      id_deps_node_hash_value_free);
++
++      /* Free memory used by this CoW ID. */
++      if (id_cow != id_orig && id_cow != NULL) {
++              deg_free_copy_on_write_datablock(id_cow);
++              MEM_freeN(id_cow);
++              DEG_COW_PRINT("Destroy CoW for %s: id_orig=%p id_cow=%p\n",
++                            id_orig->name, id_orig, id_cow);
++      }
++
++      /* Tag that the node is freed. */
++      id_orig = NULL;
+ }
+ ComponentDepsNode *IDDepsNode::find_component(eDepsNode_Type type,
+                                               const char *name) const
+ {
+       ComponentIDKey key(type, name);
+       return reinterpret_cast<ComponentDepsNode *>(BLI_ghash_lookup(components, &key));
+ }
+ ComponentDepsNode *IDDepsNode::add_component(eDepsNode_Type type,
+                                              const char *name)
+ {
+       ComponentDepsNode *comp_node = find_component(type, name);
+       if (!comp_node) {
+               DepsNodeFactory *factory = deg_type_get_factory(type);
 -              /* TODO(sergey): What about drievrs? */
 -              bool do_component_tag = comp_node->type != DEG_NODE_TYPE_ANIMATION;
 -              if (comp_node->type == DEG_NODE_TYPE_ANIMATION) {
 -                      AnimData *adt = BKE_animdata_from_id(id);
 -                      /* Animation data might be null if relations are tagged for update. */
 -                      if (adt != NULL && (adt->recalc & ADT_RECALC_ANIM)) {
 -                              do_component_tag = true;
 -                      }
 -              }
 -              if (do_component_tag) {
 -                      comp_node->tag_update(graph);
 -              }
++              comp_node = (ComponentDepsNode *)factory->create_node(this->id_orig, "", name);
+               /* Register. */
+               ComponentIDKey *key = OBJECT_GUARDED_NEW(ComponentIDKey, type, name);
+               BLI_ghash_insert(components, key, comp_node);
+               comp_node->owner = this;
+       }
+       return comp_node;
+ }
+ void IDDepsNode::tag_update(Depsgraph *graph)
+ {
+       GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, components)
+       {
 -void IDDepsNode::finalize_build()
++              comp_node->tag_update(graph);
+       }
+       GHASH_FOREACH_END();
+ }
 -              comp_node->finalize_build();
++void IDDepsNode::finalize_build(Depsgraph *graph)
+ {
++      /* Finalize build of all components. */
+       GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, components)
+       {
++              comp_node->finalize_build(graph);
+       }
+       GHASH_FOREACH_END();
+ }
+ }  // namespace DEG
index 0000000000000000000000000000000000000000,55022916c4da7047a894d2a1e4130fd53eb60f2f..505a11291929c0a2448dda3b7368ca7ffb4b851a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,79 +1,81 @@@
 -      void finalize_build();
+ /*
+  * ***** 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) 2013 Blender Foundation.
+  * All rights reserved.
+  *
+  * Original Author: Joshua Leung
+  * Contributor(s): None Yet
+  *
+  * ***** END GPL LICENSE BLOCK *****
+  */
+ /** \file depsgraph/intern/nodes/deg_node_id.h
+  *  \ingroup depsgraph
+  */
+ #pragma once
+ #include "intern/nodes/deg_node.h"
+ namespace DEG {
+ struct ComponentDepsNode;
+ /* ID-Block Reference */
+ struct IDDepsNode : public DepsNode {
+       struct ComponentIDKey {
+               ComponentIDKey(eDepsNode_Type type, const char *name = "");
+               bool operator==(const ComponentIDKey &other) const;
+               eDepsNode_Type type;
+               const char *name;
+       };
+       void init(const ID *id, const char *subdata);
++      void init_copy_on_write(ID *id_cow_hint = NULL);
+       ~IDDepsNode();
++      void destroy();
+       ComponentDepsNode *find_component(eDepsNode_Type type,
+                                         const char *name = "") const;
+       ComponentDepsNode *add_component(eDepsNode_Type type,
+                                        const char *name = "");
+       void tag_update(Depsgraph *graph);
 -      ID *id;
++      void finalize_build(Depsgraph *graph);
+       /* ID Block referenced. */
 -      /* Layers of this node with accumulated layers of it's output relations. */
 -      unsigned int layers;
 -
++      ID *id_orig;
++      ID *id_cow;
+       /* Hash to make it faster to look up components. */
+       GHash *components;
+       /* Additional flags needed for scene evaluation.
+        * TODO(sergey): Only needed for until really granular updates
+        * of all the entities.
+        */
+       int eval_flags;
++      eDepsNode_LinkedState_Type linked_state;
++
+       DEG_DEPSNODE_DECLARE;
+ };
+ }  // namespace DEG