Merge branch 'master' into blender2.8
authorSergey Sharybin <sergey.vfx@gmail.com>
Thu, 15 Feb 2018 13:15:55 +0000 (14:15 +0100)
committerSergey Sharybin <sergey.vfx@gmail.com>
Thu, 15 Feb 2018 13:15:55 +0000 (14:15 +0100)
45 files changed:
1  2 
source/blender/blenkernel/intern/cdderivedmesh.c
source/blender/blenkernel/intern/group.c
source/blender/blenkernel/intern/library.c
source/blender/blenkernel/intern/mesh.c
source/blender/blenkernel/intern/mesh_validate.c
source/blender/blenkernel/intern/node.c
source/blender/blenkernel/intern/object_deform.c
source/blender/blenkernel/intern/outliner_treehash.c
source/blender/blenkernel/intern/pbvh.c
source/blender/blenkernel/intern/workspace.c
source/blender/blenloader/intern/writefile.c
source/blender/bmesh/tools/bmesh_intersect.c
source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
source/blender/depsgraph/intern/builder/deg_builder_nodes_layer_collection.cc
source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
source/blender/depsgraph/intern/builder/deg_builder_relations.cc
source/blender/depsgraph/intern/builder/deg_builder_relations_layer_collection.cc
source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc
source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc
source/blender/depsgraph/intern/depsgraph.cc
source/blender/depsgraph/intern/depsgraph_build.cc
source/blender/depsgraph/intern/depsgraph_eval.cc
source/blender/depsgraph/intern/depsgraph_tag.cc
source/blender/depsgraph/intern/eval/deg_eval.cc
source/blender/depsgraph/intern/eval/deg_eval_flush.cc
source/blender/depsgraph/intern/nodes/deg_node_component.cc
source/blender/draw/intern/draw_manager.c
source/blender/editors/animation/anim_markers.c
source/blender/editors/armature/editarmature_sketch.c
source/blender/editors/armature/reeb.c
source/blender/editors/curve/editcurve.c
source/blender/editors/curve/editcurve_paint.c
source/blender/editors/mesh/editmesh_knife.c
source/blender/editors/render/render_update.c
source/blender/editors/sculpt_paint/sculpt_uv.c
source/blender/editors/space_outliner/outliner_collections.c
source/blender/editors/space_outliner/outliner_tree.c
source/blender/editors/uvedit/uvedit_smart_stitch.c
source/blender/gpu/intern/gpu_buffers.c
source/blender/modifiers/intern/MOD_build.c
source/blender/modifiers/intern/MOD_skin.c
source/blender/python/intern/bpy_rna.c
source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c

index ca6d92efa80cfa6ce35876d58688e1ae78489893,6dc8cc5aff1c30ddfaa8049e276232ff3c6576bf..c615f67fe42d2d8678df874e89dccc416939540e
@@@ -369,45 -367,12 +369,45 @@@ void BKE_group_handle_recalc_and_update
  #endif
        {
                /* only do existing tags, as set by regular depsgraph */
 -              for (go = group->gobject.first; go; go = go->next) {
 -                      if (go->ob) {
 -                              if (go->ob->recalc) {
 -                                      BKE_object_handle_update(eval_ctx, scene, go->ob);
 -                              }
 +              FOREACH_GROUP_OBJECT(group, object)
 +              {
 +                      if (object->id.recalc & ID_RECALC_ALL) {
 +                              BKE_object_handle_update(eval_ctx, scene, object);
                        }
                }
-       BLI_LISTBASE_FOREACH (LayerCollection *, layer_collection, layer_collections) {
 +              FOREACH_GROUP_OBJECT_END
 +      }
 +}
 +
 +/* ******** Dependency graph evaluation ******** */
 +
 +static void group_eval_layer_collections(
 +        const struct EvaluationContext *eval_ctx,
 +        Group *group,
 +        ListBase *layer_collections,
 +        LayerCollection *parent_layer_collection)
 +{
++      LISTBASE_FOREACH (LayerCollection *, layer_collection, layer_collections) {
 +              /* Evaluate layer collection itself. */
 +              BKE_layer_eval_layer_collection(eval_ctx,
 +                                              layer_collection,
 +                                              parent_layer_collection);
 +              /* Evaluate nested collections. */
 +              group_eval_layer_collections(eval_ctx,
 +                                           group,
 +                                           &layer_collection->layer_collections,
 +                                           layer_collection);
        }
  }
 +
 +void BKE_group_eval_view_layers(const struct EvaluationContext *eval_ctx,
 +                                Group *group)
 +{
 +      DEBUG_PRINT("%s on %s (%p)\n", __func__, group->id.name, group);
 +      BKE_layer_eval_layer_collection_pre(eval_ctx, &group->id, group->view_layer);
 +      group_eval_layer_collections(eval_ctx,
 +                                   group,
 +                                   &group->view_layer->layer_collections,
 +                                   NULL);
 +      BKE_layer_eval_layer_collection_post(eval_ctx, group->view_layer);
 +}
index bc5731b957425c85978e4961f900f811f85c30f7,0000000000000000000000000000000000000000..9251c6630a5a38a4256c5de7155031393d949fe1
mode 100644,000000..100644
--- /dev/null
@@@ -1,537 -1,0 +1,537 @@@
-               BLI_LISTBASE_CIRCULAR_BACKWARD_BEGIN(&workspace->layouts, iter_layout, start)
 +/*
 + * ***** 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.
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/blenkernel/intern/workspace.c
 + *  \ingroup bke
 + */
 +
 +/* allow accessing private members of DNA_workspace_types.h */
 +#define DNA_PRIVATE_WORKSPACE_ALLOW
 +
 +#include <stdlib.h>
 +
 +#include "BLI_utildefines.h"
 +#include "BLI_string.h"
 +#include "BLI_string_utf8.h"
 +#include "BLI_string_utils.h"
 +#include "BLI_listbase.h"
 +
 +#include "BKE_global.h"
 +#include "BKE_library.h"
 +#include "BKE_main.h"
 +#include "BKE_scene.h"
 +#include "BKE_screen.h"
 +#include "BKE_object.h"
 +#include "BKE_workspace.h"
 +
 +#include "DNA_object_types.h"
 +#include "DNA_scene_types.h"
 +#include "DNA_screen_types.h"
 +#include "DNA_workspace_types.h"
 +
 +#include "DEG_depsgraph.h"
 +
 +#include "MEM_guardedalloc.h"
 +
 +
 +/* -------------------------------------------------------------------- */
 +/* Internal utils */
 +
 +static void workspace_layout_name_set(
 +        WorkSpace *workspace, WorkSpaceLayout *layout, const char *new_name)
 +{
 +      BLI_strncpy(layout->name, new_name, sizeof(layout->name));
 +      BLI_uniquename(&workspace->layouts, layout, "Layout", '.', offsetof(WorkSpaceLayout, name), sizeof(layout->name));
 +}
 +
 +/**
 + * This should only be used directly when it is to be expected that there isn't
 + * a layout within \a workspace that wraps \a screen. Usually - especially outside
 + * of BKE_workspace - #BKE_workspace_layout_find should be used!
 + */
 +static WorkSpaceLayout *workspace_layout_find_exec(
 +        const WorkSpace *workspace, const bScreen *screen)
 +{
 +      return BLI_findptr(&workspace->layouts, screen, offsetof(WorkSpaceLayout, screen));
 +}
 +
 +static void workspace_relation_add(
 +        ListBase *relation_list, void *parent, void *data)
 +{
 +      WorkSpaceDataRelation *relation = MEM_callocN(sizeof(*relation), __func__);
 +      relation->parent = parent;
 +      relation->value = data;
 +      /* add to head, if we switch back to it soon we find it faster. */
 +      BLI_addhead(relation_list, relation);
 +}
 +static void workspace_relation_remove(
 +        ListBase *relation_list, WorkSpaceDataRelation *relation)
 +{
 +      BLI_remlink(relation_list, relation);
 +      MEM_freeN(relation);
 +}
 +
 +static void workspace_relation_ensure_updated(
 +        ListBase *relation_list, void *parent, void *data)
 +{
 +      WorkSpaceDataRelation *relation = BLI_findptr(relation_list, parent, offsetof(WorkSpaceDataRelation, parent));
 +      if (relation != NULL) {
 +              relation->value = data;
 +              /* reinsert at the head of the list, so that more commonly used relations are found faster. */
 +              BLI_remlink(relation_list, relation);
 +              BLI_addhead(relation_list, relation);
 +      }
 +      else {
 +              /* no matching relation found, add new one */
 +              workspace_relation_add(relation_list, parent, data);
 +      }
 +}
 +
 +static void *workspace_relation_get_data_matching_parent(
 +        const ListBase *relation_list, const void *parent)
 +{
 +      WorkSpaceDataRelation *relation = BLI_findptr(relation_list, parent, offsetof(WorkSpaceDataRelation, parent));
 +      if (relation != NULL) {
 +              return relation->value;
 +      }
 +      else {
 +              return NULL;
 +      }
 +}
 +
 +static void workspace_relation_remove_from_value(
 +        ListBase *relation_list, const void *value)
 +{
 +      for (WorkSpaceDataRelation *relation = relation_list->first, *relation_next; relation; relation = relation_next) {
 +              relation_next = relation->next;
 +
 +              if (relation->value == value) {
 +                      workspace_relation_remove(relation_list, relation);
 +              }
 +      }
 +}
 +
 +/**
 + * Checks if \a screen is already used within any workspace. A screen should never be assigned to multiple
 + * WorkSpaceLayouts, but that should be ensured outside of the BKE_workspace module and without such checks.
 + * Hence, this should only be used as assert check before assigining a screen to a workspace.
 + */
 +#ifndef NDEBUG
 +static bool workspaces_is_screen_used
 +#else
 +static bool UNUSED_FUNCTION(workspaces_is_screen_used)
 +#endif
 +        (const Main *bmain, bScreen *screen)
 +{
 +      for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
 +              if (workspace_layout_find_exec(workspace, screen)) {
 +                      return true;
 +              }
 +      }
 +
 +      return false;
 +}
 +
 +/* -------------------------------------------------------------------- */
 +/* Create, delete, init */
 +
 +WorkSpace *BKE_workspace_add(Main *bmain, const char *name)
 +{
 +      WorkSpace *new_workspace = BKE_libblock_alloc(bmain, ID_WS, name, 0);
 +      return new_workspace;
 +}
 +
 +/**
 + * The function that actually frees the workspace data (not workspace itself). It shouldn't be called
 + * directly, instead #BKE_workspace_remove should be, which calls this through #BKE_libblock_free then.
 + *
 + * Should something like a bke_internal.h be added, this should go there!
 + */
 +void BKE_workspace_free(WorkSpace *workspace)
 +{
 +      BKE_workspace_relations_free(&workspace->hook_layout_relations);
 +      BKE_workspace_relations_free(&workspace->scene_viewlayer_relations);
 +
 +      BLI_freelistN(&workspace->layouts);
 +      BLI_freelistN(&workspace->transform_orientations);
 +
 +      BKE_viewrender_free(&workspace->view_render);
 +}
 +
 +/**
 + * Remove \a workspace by freeing itself and its data. This is a higher-level wrapper that
 + * calls #BKE_workspace_free (through #BKE_libblock_free) to free the workspace data, and frees
 + * other data-blocks owned by \a workspace and its layouts (currently that is screens only).
 + *
 + * Always use this to remove (and free) workspaces. Don't free non-ID workspace members here.
 + */
 +void BKE_workspace_remove(Main *bmain, WorkSpace *workspace)
 +{
 +      for (WorkSpaceLayout *layout = workspace->layouts.first, *layout_next; layout; layout = layout_next) {
 +              layout_next = layout->next;
 +              BKE_workspace_layout_remove(bmain, workspace, layout);
 +      }
 +      BKE_libblock_free(bmain, workspace);
 +}
 +
 +WorkSpaceInstanceHook *BKE_workspace_instance_hook_create(const Main *bmain)
 +{
 +      WorkSpaceInstanceHook *hook = MEM_callocN(sizeof(WorkSpaceInstanceHook), __func__);
 +
 +      /* set an active screen-layout for each possible window/workspace combination */
 +      for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
 +              BKE_workspace_hook_layout_for_workspace_set(hook, workspace, workspace->layouts.first);
 +      }
 +
 +      return hook;
 +}
 +void BKE_workspace_instance_hook_free(const Main *bmain, WorkSpaceInstanceHook *hook)
 +{
 +      /* workspaces should never be freed before wm (during which we call this function) */
 +      BLI_assert(!BLI_listbase_is_empty(&bmain->workspaces));
 +
 +      /* Free relations for this hook */
 +      for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
 +              for (WorkSpaceDataRelation *relation = workspace->hook_layout_relations.first, *relation_next;
 +                   relation;
 +                   relation = relation_next)
 +              {
 +                      relation_next = relation->next;
 +                      if (relation->parent == hook) {
 +                              workspace_relation_remove(&workspace->hook_layout_relations, relation);
 +                      }
 +              }
 +      }
 +
 +      MEM_freeN(hook);
 +}
 +
 +/**
 + * Add a new layout to \a workspace for \a screen.
 + */
 +WorkSpaceLayout *BKE_workspace_layout_add(
 +        WorkSpace *workspace,
 +        bScreen *screen,
 +        const char *name)
 +{
 +      WorkSpaceLayout *layout = MEM_callocN(sizeof(*layout), __func__);
 +
 +      BLI_assert(!workspaces_is_screen_used(G.main, screen));
 +      layout->screen = screen;
 +      workspace_layout_name_set(workspace, layout, name);
 +      BLI_addtail(&workspace->layouts, layout);
 +
 +      return layout;
 +}
 +
 +void BKE_workspace_layout_remove(
 +        Main *bmain,
 +        WorkSpace *workspace, WorkSpaceLayout *layout)
 +{
 +      BKE_libblock_free(bmain, BKE_workspace_layout_screen_get(layout));
 +      BLI_freelinkN(&workspace->layouts, layout);
 +}
 +
 +void BKE_workspace_relations_free(
 +        ListBase *relation_list)
 +{
 +      for (WorkSpaceDataRelation *relation = relation_list->first, *relation_next; relation; relation = relation_next) {
 +              relation_next = relation->next;
 +              workspace_relation_remove(relation_list, relation);
 +      }
 +}
 +
 +
 +/* -------------------------------------------------------------------- */
 +/* General Utils */
 +
 +void BKE_workspace_view_layer_remove_references(
 +        const Main *bmain,
 +        const ViewLayer *view_layer)
 +{
 +      for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
 +              workspace_relation_remove_from_value(&workspace->scene_viewlayer_relations, view_layer);
 +      }
 +}
 +
 +void BKE_workspace_transform_orientation_remove(
 +        WorkSpace *workspace, TransformOrientation *orientation)
 +{
 +      for (WorkSpaceLayout *layout = workspace->layouts.first; layout; layout = layout->next) {
 +              BKE_screen_transform_orientation_remove(BKE_workspace_layout_screen_get(layout), workspace, orientation);
 +      }
 +
 +      BLI_freelinkN(&workspace->transform_orientations, orientation);
 +}
 +
 +TransformOrientation *BKE_workspace_transform_orientation_find(
 +        const WorkSpace *workspace, const int index)
 +{
 +      return BLI_findlink(&workspace->transform_orientations, index);
 +}
 +
 +/**
 + * \return the index that \a orientation has within \a workspace's transform-orientation list or -1 if not found.
 + */
 +int BKE_workspace_transform_orientation_get_index(
 +        const WorkSpace *workspace, const TransformOrientation *orientation)
 +{
 +      return BLI_findindex(&workspace->transform_orientations, orientation);
 +}
 +
 +WorkSpaceLayout *BKE_workspace_layout_find(
 +        const WorkSpace *workspace, const bScreen *screen)
 +{
 +      WorkSpaceLayout *layout = workspace_layout_find_exec(workspace, screen);
 +      if (layout) {
 +              return layout;
 +      }
 +
 +      printf("%s: Couldn't find layout in this workspace: '%s' screen: '%s'. "
 +             "This should not happen!\n",
 +             __func__, workspace->id.name + 2, screen->id.name + 2);
 +
 +      return NULL;
 +}
 +
 +/**
 + * Find the layout for \a screen without knowing which workspace to look in.
 + * Can also be used to find the workspace that contains \a screen.
 + *
 + * \param r_workspace: Optionally return the workspace that contains the looked up layout (if found).
 + */
 +WorkSpaceLayout *BKE_workspace_layout_find_global(
 +        const Main *bmain, const bScreen *screen,
 +        WorkSpace **r_workspace)
 +{
 +      WorkSpaceLayout *layout;
 +
 +      if (r_workspace) {
 +              *r_workspace = NULL;
 +      }
 +
 +      for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
 +              if ((layout = workspace_layout_find_exec(workspace, screen))) {
 +                      if (r_workspace) {
 +                              *r_workspace = workspace;
 +                      }
 +
 +                      return layout;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +/**
 + * Circular workspace layout iterator.
 + *
 + * \param callback: Custom function which gets executed for each layout. Can return false to stop iterating.
 + * \param arg: Custom data passed to each \a callback call.
 + *
 + * \return the layout at which \a callback returned false.
 + */
 +WorkSpaceLayout *BKE_workspace_layout_iter_circular(
 +        const WorkSpace *workspace, WorkSpaceLayout *start,
 +        bool (*callback)(const WorkSpaceLayout *layout, void *arg),
 +        void *arg, const bool iter_backward)
 +{
 +      WorkSpaceLayout *iter_layout;
 +
 +      if (iter_backward) {
-               BLI_LISTBASE_CIRCULAR_BACKWARD_END(&workspace->layouts, iter_layout, start);
++              LISTBASE_CIRCULAR_BACKWARD_BEGIN(&workspace->layouts, iter_layout, start)
 +              {
 +                      if (!callback(iter_layout, arg)) {
 +                              return iter_layout;
 +                      }
 +              }
-               BLI_LISTBASE_CIRCULAR_FORWARD_BEGIN(&workspace->layouts, iter_layout, start)
++              LISTBASE_CIRCULAR_BACKWARD_END(&workspace->layouts, iter_layout, start);
 +      }
 +      else {
-               BLI_LISTBASE_CIRCULAR_FORWARD_END(&workspace->layouts, iter_layout, start)
++              LISTBASE_CIRCULAR_FORWARD_BEGIN(&workspace->layouts, iter_layout, start)
 +              {
 +                      if (!callback(iter_layout, arg)) {
 +                              return iter_layout;
 +                      }
 +              }
++              LISTBASE_CIRCULAR_FORWARD_END(&workspace->layouts, iter_layout, start)
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +/* -------------------------------------------------------------------- */
 +/* Getters/Setters */
 +
 +WorkSpace *BKE_workspace_active_get(WorkSpaceInstanceHook *hook)
 +{
 +      return hook->active;
 +}
 +void BKE_workspace_active_set(WorkSpaceInstanceHook *hook, WorkSpace *workspace)
 +{
 +      hook->active = workspace;
 +      if (workspace) {
 +              WorkSpaceLayout *layout = workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook);
 +              if (layout) {
 +                      hook->act_layout = layout;
 +              }
 +      }
 +}
 +
 +WorkSpaceLayout *BKE_workspace_active_layout_get(const WorkSpaceInstanceHook *hook)
 +{
 +      return hook->act_layout;
 +}
 +void BKE_workspace_active_layout_set(WorkSpaceInstanceHook *hook, WorkSpaceLayout *layout)
 +{
 +      hook->act_layout = layout;
 +}
 +
 +bScreen *BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook)
 +{
 +      return hook->act_layout->screen;
 +}
 +void BKE_workspace_active_screen_set(WorkSpaceInstanceHook *hook, WorkSpace *workspace, bScreen *screen)
 +{
 +      /* we need to find the WorkspaceLayout that wraps this screen */
 +      WorkSpaceLayout *layout = BKE_workspace_layout_find(hook->active, screen);
 +      BKE_workspace_hook_layout_for_workspace_set(hook, workspace, layout);
 +}
 +
 +Base *BKE_workspace_active_base_get(const WorkSpace *workspace, const Scene *scene)
 +{
 +      ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene);
 +      return view_layer->basact;
 +}
 +
 +ListBase *BKE_workspace_transform_orientations_get(WorkSpace *workspace)
 +{
 +      return &workspace->transform_orientations;
 +}
 +
 +ViewLayer *BKE_workspace_view_layer_get(const WorkSpace *workspace, const Scene *scene)
 +{
 +      return workspace_relation_get_data_matching_parent(&workspace->scene_viewlayer_relations, scene);
 +}
 +void BKE_workspace_view_layer_set(WorkSpace *workspace, ViewLayer *layer, Scene *scene)
 +{
 +      workspace_relation_ensure_updated(&workspace->scene_viewlayer_relations, scene, layer);
 +}
 +
 +ListBase *BKE_workspace_layouts_get(WorkSpace *workspace)
 +{
 +      return &workspace->layouts;
 +}
 +
 +
 +const char *BKE_workspace_layout_name_get(const WorkSpaceLayout *layout)
 +{
 +      return layout->name;
 +}
 +void BKE_workspace_layout_name_set(WorkSpace *workspace, WorkSpaceLayout *layout, const char *new_name)
 +{
 +      workspace_layout_name_set(workspace, layout, new_name);
 +}
 +
 +bScreen *BKE_workspace_layout_screen_get(const WorkSpaceLayout *layout)
 +{
 +      return layout->screen;
 +}
 +void BKE_workspace_layout_screen_set(WorkSpaceLayout *layout, bScreen *screen)
 +{
 +      layout->screen = screen;
 +}
 +
 +WorkSpaceLayout *BKE_workspace_hook_layout_for_workspace_get(
 +        const WorkSpaceInstanceHook *hook, const WorkSpace *workspace)
 +{
 +      return workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook);
 +}
 +void BKE_workspace_hook_layout_for_workspace_set(
 +        WorkSpaceInstanceHook *hook, WorkSpace *workspace, WorkSpaceLayout *layout)
 +{
 +      hook->act_layout = layout;
 +      workspace_relation_ensure_updated(&workspace->hook_layout_relations, hook, layout);
 +}
 +
 +/**
 + * Get the render engine of a workspace, to be used in the viewport.
 + */
 +ViewRender *BKE_workspace_view_render_get(WorkSpace *workspace)
 +{
 +      return &workspace->view_render;
 +}
 +
 +/* Flags */
 +bool BKE_workspace_use_scene_settings_get(const WorkSpace *workspace)
 +{
 +      return (workspace->flags & WORKSPACE_USE_SCENE_SETTINGS) != 0;
 +}
 +
 +void BKE_workspace_use_scene_settings_set(WorkSpace *workspace, bool value)
 +{
 +      if (value) {
 +              workspace->flags |= WORKSPACE_USE_SCENE_SETTINGS;
 +      }
 +      else {
 +              workspace->flags &= ~WORKSPACE_USE_SCENE_SETTINGS;
 +      }
 +}
 +
 +/* Update / evaluate */
 +
 +void BKE_workspace_update_tagged(struct EvaluationContext *eval_ctx,
 +                                 Main *bmain,
 +                                 WorkSpace *workspace,
 +                                 Scene *scene)
 +{
 +      ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene);
 +      struct Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene,
 +                                                            view_layer,
 +                                                            true);
 +      BKE_scene_graph_update_tagged(eval_ctx, depsgraph, bmain, scene, view_layer);
 +}
 +
 +void BKE_workspace_update_object_mode(
 +        struct EvaluationContext *eval_ctx,
 +        WorkSpace *workspace)
 +{
 +      /* TODO(campbell): Investigate how this should work exactly,
 +       * for now without this 'bmain->eval_ctx' is never set. */
 +
 +      eval_ctx->object_mode = workspace->object_mode;
 +}
 +
 +Object *BKE_workspace_edit_object(WorkSpace *workspace, Scene *scene)
 +{
 +      if (workspace->object_mode & OB_MODE_EDIT) {
 +              ViewLayer *view_layer = BKE_workspace_view_layer_get(workspace, scene);
 +              if (view_layer) {
 +                      Object *obedit = OBACT(view_layer);
 +                      if (obedit) {
 +                              BLI_assert(BKE_object_is_in_editmode(obedit));
 +                              return obedit;
 +                      }
 +              }
 +      }
 +      return NULL;
 +}
 +
index db1e8aeb9bdc072ffe89b1bf8bd20d322d3bdf4e,27bcc224ef5f7d551da573ade1c74e319e1d16f2..b65c16591dc4eea2b010ed71e6f93481b22556a6
@@@ -174,44 -161,11 +174,44 @@@ DepsgraphNodeBuilder::DepsgraphNodeBuil
  
  DepsgraphNodeBuilder::~DepsgraphNodeBuilder()
  {
-       if (BLI_ghash_size(id_node->components) == 0) {
 +      if (cow_id_hash_ != NULL) {
 +              BLI_ghash_free(cow_id_hash_, NULL, free_copy_on_write_datablock);
 +      }
 +}
 +
 +IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id, bool do_tag)
 +{
 +      if (!DEG_depsgraph_use_copy_on_write()) {
 +              return graph_->add_id_node(id, do_tag);
 +      }
 +      IDDepsNode *id_node = NULL;
 +      ID *id_cow = (ID *)BLI_ghash_lookup(cow_id_hash_, id);
 +      if (id_cow != NULL) {
 +              /* TODO(sergey): Is it possible to lookup and pop element from GHash
 +               * at the same time?
 +               */
 +              BLI_ghash_remove(cow_id_hash_, id, NULL, NULL);
 +      }
 +      id_node = graph_->add_id_node(id, do_tag, id_cow);
 +      /* Currently all ID nodes are supposed to have copy-on-write logic.
 +       *
 +       * NOTE: Zero number of components indicates that ID node was just created.
 +       */
++      if (BLI_ghash_len(id_node->components) == 0) {
 +              ComponentDepsNode *comp_cow =
 +                      id_node->add_component(DEG_NODE_TYPE_COPY_ON_WRITE);
 +              OperationDepsNode *op_cow = comp_cow->add_operation(
 +                      function_bind(deg_evaluate_copy_on_write, _1, graph_, id_node),
 +                      DEG_OPCODE_COPY_ON_WRITE,
 +                      "", -1);
 +              graph_->operations.push_back(op_cow);
 +      }
 +      return id_node;
  }
  
 -IDDepsNode *DepsgraphNodeBuilder::add_id_node(ID *id)
 +IDDepsNode *DepsgraphNodeBuilder::find_id_node(ID *id)
  {
 -      return graph_->add_id_node(id, id->name);
 +      return graph_->find_id_node(id);
  }
  
  TimeSourceDepsNode *DepsgraphNodeBuilder::add_time_source()
@@@ -429,40 -304,34 +429,40 @@@ void DepsgraphNodeBuilder::build_group(
                return;
        }
        group_id->tag |= LIB_TAG_DOIT;
 -
 -      LISTBASE_FOREACH (GroupObject *, go, &group->gobject) {
 -              build_object(base, go->ob);
 +      /* Build group objects. */
-       BLI_LISTBASE_FOREACH (Base *, base, &group->view_layer->object_bases) {
++      LISTBASE_FOREACH (Base *, base, &group->view_layer->object_bases) {
 +              build_object(NULL, base->object, DEG_ID_LINKED_INDIRECTLY);
        }
 +      /* Operation to evaluate the whole view layer.
 +       *
 +       * NOTE: We re-use DONE opcode even though the function does everything.
 +       * This way we wouldn't need to worry about possible relations from DONE,
 +       * regardless whether it's a group or scene or something else.
 +       */
 +      add_id_node(group_id);
 +      Group *group_cow = get_cow_datablock(group);
 +      add_operation_node(group_id,
 +                         DEG_NODE_TYPE_LAYER_COLLECTIONS,
 +                         function_bind(BKE_group_eval_view_layers,
 +                                       _1,
 +                                       group_cow),
 +                         DEG_OPCODE_VIEW_LAYER_DONE);
  }
  
 -void DepsgraphNodeBuilder::build_object(Base *base, Object *object)
 +void DepsgraphNodeBuilder::build_object(Base *base,
 +                                        Object *object,
 +                                        eDepsNode_LinkedState_Type linked_state)
  {
 -      const bool has_object = (object->id.tag & LIB_TAG_DOIT);
 -      IDDepsNode *id_node = (has_object)
 -              ? graph_->find_id_node(&object->id)
 -              : add_id_node(&object->id);
 -      /* Update node layers.
 -       * Do it for both new and existing ID nodes. This is so because several
 -       * bases might be sharing same object.
 -       */
 -      if (base != NULL) {
 -              id_node->layers |= base->lay;
 -      }
 -      if (object->type == OB_CAMERA) {
 -              /* Camera should always be updated, it used directly by viewport.
 -               *
 -               * TODO(sergey): Make it only for active scene camera.
 -               */
 -              id_node->layers |= (unsigned int)(-1);
 -      }
        /* Skip rest of components if the ID node was already there. */
 -      if (has_object) {
 +      if (object->id.tag & LIB_TAG_DOIT) {
 +              IDDepsNode *id_node = find_id_node(&object->id);
 +              /* We need to build some extra stuff if object becomes linked
 +               * directly.
 +               */
 +              if (id_node->linked_state == DEG_ID_LINKED_INDIRECTLY) {
 +                      build_object_flags(base, object, linked_state);
 +              }
 +              id_node->linked_state = max(id_node->linked_state, linked_state);
                return;
        }
        object->id.tag |= LIB_TAG_DOIT;
@@@ -816,10 -627,11 +816,10 @@@ void DepsgraphNodeBuilder::build_rigidb
        sim_node->owner->entry_operation = sim_node;
        sim_node->owner->exit_operation  = sim_node;
  
 -
        /* objects - simulation participants */
        if (rbw->group) {
-               BLI_LISTBASE_FOREACH (Base *, base, &rbw->group->view_layer->object_bases) {
 -              LISTBASE_FOREACH (GroupObject *, go, &rbw->group->gobject) {
 -                      Object *object = go->ob;
++              LISTBASE_FOREACH (Base *, base, &rbw->group->view_layer->object_bases) {
 +                      Object *object = base->object;
  
                        if (!object || (object->type != OB_MESH))
                                continue;
@@@ -865,25 -667,17 +865,25 @@@ void DepsgraphNodeBuilder::build_partic
        add_operation_node(psys_comp,
                           function_bind(BKE_particle_system_eval_init,
                                         _1,
 -                                       scene_,
 -                                       object),
 +                                       scene_cow,
 +                                       ob_cow),
                           DEG_OPCODE_PARTICLE_SYSTEM_EVAL_INIT);
        /* Build all particle systems. */
-       BLI_LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) {
+       LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) {
                ParticleSettings *part = psys->part;
 -              /* Particle settings. */
 -              // XXX: what if this is used more than once!
 -              build_animdata(&part->id);
 -              /* This particle system evaluation. */
 -              // TODO: for now, this will just be a placeholder "ubereval" node
 +              /* Build particle settings operations.
 +               *
 +               * NOTE: The call itself ensures settings are only build once.
 +               */
 +              build_particle_settings(part);
 +              /* Update on particle settings change. */
 +              add_operation_node(psys_comp,
 +                                 function_bind(BKE_particle_system_settings_eval,
 +                                               _1,
 +                                               psys),
 +                                 DEG_OPCODE_PARTICLE_SETTINGS_EVAL,
 +                                 psys->name);
 +              /* Particle system evaluation. */
                add_operation_node(psys_comp,
                                   NULL,
                                   DEG_OPCODE_PARTICLE_SYSTEM_EVAL,
@@@ -1224,32 -953,24 +1224,32 @@@ void DepsgraphNodeBuilder::build_lamp(O
  
  void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
  {
 -      if (!ntree)
 +      if (ntree == NULL) {
                return;
 -
 +      }
        /* nodetree itself */
        ID *ntree_id = &ntree->id;
 -      OperationDepsNode *op_node;
 -
 +      add_id_node(ntree_id);
 +      bNodeTree *ntree_cow = get_cow_datablock(ntree);
 +      /* Animation, */
        build_animdata(ntree_id);
 -
 -      /* Parameters for drivers. */
 -      op_node = add_operation_node(ntree_id,
 -                                   DEG_NODE_TYPE_PARAMETERS,
 -                                   NULL,
 -                                   DEG_OPCODE_PARAMETERS_EVAL);
 -      op_node->set_as_exit();
 -
 +      /* Shading update. */
 +      add_operation_node(ntree_id,
 +                         DEG_NODE_TYPE_SHADING,
 +                         NULL,
 +                         DEG_OPCODE_MATERIAL_UPDATE);
 +      /* NOTE: We really pass original and CoW node trees here, this is how the
 +       * callback works. Ideally we need to find a better way for that.
 +       */
 +      add_operation_node(ntree_id,
 +                         DEG_NODE_TYPE_SHADING_PARAMETERS,
 +                         function_bind(BKE_nodetree_shading_params_eval,
 +                                       _1,
 +                                       ntree_cow,
 +                                       ntree),
 +                         DEG_OPCODE_MATERIAL_UPDATE);
        /* nodetree's nodes... */
-       BLI_LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) {
+       LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) {
                ID *id = bnode->id;
                if (id == NULL) {
                        continue;
index 137a79e7276c46d3b1383de7cc4e86ecf45bace5,0000000000000000000000000000000000000000..2ee526b71283d54096a513aad30d708c76008006
mode 100644,000000..100644
--- /dev/null
@@@ -1,126 -1,0 +1,126 @@@
-       BLI_LISTBASE_FOREACH (LayerCollection *, layer_collection, layer_collections) {
 +/*
 + * ***** 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_nodes_layer_collection.cc
 + *  \ingroup depsgraph
 + *
 + * Methods for constructing depsgraph's nodes
 + */
 +
 +#include "intern/builder/deg_builder_nodes.h"
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include "MEM_guardedalloc.h"
 +
 +extern "C" {
 +#include "BLI_utildefines.h"
 +#include "BLI_listbase.h"
 +#include "BLI_string.h"
 +
 +#include "BKE_layer.h"
 +
 +#include "DNA_scene_types.h"
 +
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_build.h"
 +} /* extern "C" */
 +
 +#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_operation.h"
 +#include "intern/depsgraph_types.h"
 +#include "intern/depsgraph_intern.h"
 +#include "util/deg_util_foreach.h"
 +
 +namespace DEG {
 +
 +void DepsgraphNodeBuilder::build_layer_collection(
 +        ID *owner_id,
 +        LayerCollection *layer_collection,
 +        LayerCollectionState *state)
 +{
 +      /* TODO(sergey): This will attempt to create component for each collection.
 +       * Harmless but could be optimized.
 +       */
 +      ComponentDepsNode *comp = add_component_node(
 +              owner_id,
 +              DEG_NODE_TYPE_LAYER_COLLECTIONS);
 +
 +      add_operation_node(comp,
 +                         function_bind(BKE_layer_eval_layer_collection,
 +                                       _1,
 +                                       layer_collection,
 +                                       state->parent),
 +                         DEG_OPCODE_VIEW_LAYER_EVAL,
 +                         layer_collection->scene_collection->name,
 +                         state->index);
 +      ++state->index;
 +
 +      /* Recurs into nested layer collections. */
 +      LayerCollection *parent = state->parent;
 +      state->parent = layer_collection;
 +      build_layer_collections(owner_id, &layer_collection->layer_collections, state);
 +      state->parent = parent;
 +}
 +
 +void DepsgraphNodeBuilder::build_layer_collections(ID *owner_id,
 +                                                   ListBase *layer_collections,
 +                                                   LayerCollectionState *state)
 +{
++      LISTBASE_FOREACH (LayerCollection *, layer_collection, layer_collections) {
 +              build_layer_collection(owner_id, layer_collection, state);
 +      }
 +}
 +
 +void DepsgraphNodeBuilder::build_view_layer_collections(
 +        ID *owner_id,
 +        ViewLayer *view_layer)
 +{
 +      LayerCollectionState state;
 +      state.index = 0;
 +      ComponentDepsNode *comp = add_component_node(
 +              owner_id,
 +              DEG_NODE_TYPE_LAYER_COLLECTIONS);
 +      add_operation_node(comp,
 +                         function_bind(BKE_layer_eval_layer_collection_pre,
 +                                       _1,
 +                                       owner_id,
 +                                       view_layer),
 +                         DEG_OPCODE_VIEW_LAYER_INIT);
 +      add_operation_node(comp,
 +                         function_bind(BKE_layer_eval_layer_collection_post,
 +                                       _1,
 +                                       view_layer),
 +                         DEG_OPCODE_VIEW_LAYER_DONE);
 +      state.parent = NULL;
 +      build_layer_collections(owner_id, &view_layer->layer_collections, &state);
 +}
 +
 +}  // namespace DEG
index 8be974aae3954f16865fe33142e32e08c82f8d8b,1a6b3f89f2642b20d2d5e82aeb043ac9e1a94206..c3116db6146db930333e2b938449b797deb5c990
@@@ -247,7 -195,7 +247,7 @@@ void DepsgraphNodeBuilder::build_rig(Ob
        op_node->set_as_exit();
  
        /* bones */
-       BLI_LISTBASE_FOREACH (bPoseChannel *, pchan, &object_cow->pose->chanbase) {
 -      LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
++      LISTBASE_FOREACH (bPoseChannel *, pchan, &object_cow->pose->chanbase) {
                /* Node for bone evaluation. */
                op_node = add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name, NULL,
                                             DEG_OPCODE_BONE_LOCAL);
                 *   base transforms of a bunch of bones is done)
                 *
                 * Unsolved Issues:
 -               * - Care is needed to ensure that multi-headed trees work out the same as in ik-tree building
 -               * - Animated chain-lengths are a problem...
 +               * - Care is needed to ensure that multi-headed trees work out the same
 +               *   as in ik-tree building
 +               * - Animated chain-lengths are a problem.
                 */
-               BLI_LISTBASE_FOREACH (bConstraint *, con, &pchan->constraints) {
+               LISTBASE_FOREACH (bConstraint *, con, &pchan->constraints) {
                        switch (con->type) {
                                case CONSTRAINT_TYPE_KINEMATIC:
                                        build_ik_pose(object, pchan, con);
@@@ -348,25 -274,17 +348,25 @@@ void DepsgraphNodeBuilder::build_proxy_
                                     DEG_OPCODE_POSE_INIT);
        op_node->set_as_entry();
  
-       BLI_LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
 -
+       LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
 -              op_node = add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name,
 -                                           NULL, DEG_OPCODE_BONE_LOCAL);
 +              op_node = add_operation_node(&object->id,
 +                                           DEG_NODE_TYPE_BONE,
 +                                           pchan->name,
 +                                           NULL,
 +                                           DEG_OPCODE_BONE_LOCAL);
                op_node->set_as_entry();
 -
 -              add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name,
 -                                 NULL, DEG_OPCODE_BONE_READY);
 -
 -              op_node = add_operation_node(&object->id, DEG_NODE_TYPE_BONE, pchan->name,
 -                                           NULL, DEG_OPCODE_BONE_DONE);
 +              /* Bone is ready for solvers. */
 +              add_operation_node(&object->id,
 +                                 DEG_NODE_TYPE_BONE,
 +                                 pchan->name,
 +                                 NULL,
 +                                 DEG_OPCODE_BONE_READY);
 +              /* Bone is fully evaluated. */
 +              op_node = add_operation_node(&object->id,
 +                                           DEG_NODE_TYPE_BONE,
 +                                           pchan->name,
 +                                           NULL,
 +                                           DEG_OPCODE_BONE_DONE);
                op_node->set_as_exit();
  
                /* Custom properties. */
index 4ca19f4e14fcd4b44036b987d8eef74793989f2f,0000000000000000000000000000000000000000..7a0fc79078025f63199b64f4963c37762d556bc0
mode 100644,000000..100644
--- /dev/null
@@@ -1,174 -1,0 +1,174 @@@
-               BLI_LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
 +/*
 + * ***** 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_nodes_view_layer.cc
 + *  \ingroup depsgraph
 + *
 + * Methods for constructing depsgraph's nodes
 + */
 +
 +#include "intern/builder/deg_builder_nodes.h"
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "BLI_utildefines.h"
 +#include "BLI_blenlib.h"
 +#include "BLI_string.h"
 +
 +extern "C" {
 +#include "DNA_node_types.h"
 +#include "DNA_layer_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/nodes/deg_node.h"
 +#include "intern/nodes/deg_node_component.h"
 +#include "intern/nodes/deg_node_operation.h"
 +#include "intern/depsgraph_types.h"
 +#include "intern/depsgraph_intern.h"
 +#include "util/deg_util_foreach.h"
 +
 +namespace DEG {
 +
 +void DepsgraphNodeBuilder::build_view_layer(
 +        Scene *scene,
 +        ViewLayer *view_layer,
 +        eDepsNode_LinkedState_Type linked_state)
 +{
 +      /* Scene ID block. */
 +      add_id_node(&scene->id);
 +      /* Time source. */
 +      add_time_source();
 +      /* Setup currently building context. */
 +      scene_ = scene;
 +      /* Expand Scene Cow datablock to get proper pointers to bases. */
 +      Scene *scene_cow;
 +      ViewLayer *view_layer_cow;
 +      if (DEG_depsgraph_use_copy_on_write()) {
 +              /* NOTE: We need to create ID nodes for all objects coming from bases,
 +               * otherwise remapping will not replace objects with their CoW versions
 +               * for CoW bases.
 +               */
-       BLI_LISTBASE_FOREACH (CacheFile *, cachefile, &bmain_->cachefiles) {
++              LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
 +                      Object *object = base->object;
 +                      add_id_node(&object->id, false);
 +              }
 +              /* Create ID node for nested ID of nodetree as well, otherwise remapping
 +               * will not work correct either.
 +               */
 +              if (scene->nodetree != NULL) {
 +                      add_id_node(&scene->nodetree->id, false);
 +              }
 +              /* Make sure we've got ID node, so we can get pointer to CoW datablock.
 +               */
 +              scene_cow = expand_cow_datablock(scene);
 +              view_layer_cow = (ViewLayer *)BLI_findstring(
 +                      &scene_cow->view_layers,
 +                      view_layer->name,
 +                      offsetof(ViewLayer, name));
 +      }
 +      else {
 +              scene_cow = scene;
 +              view_layer_cow = view_layer;
 +      }
 +      /* Scene objects. */
 +      int select_color = 1;
 +      /* NOTE: Base is used for function bindings as-is, so need to pass CoW base,
 +       * but object is expected to be an original one. Hence we go into some
 +       * tricks here iterating over the view layer.
 +       */
 +      for (Base *base_orig = (Base *)view_layer->object_bases.first,
 +                *base_cow = (Base *)view_layer_cow->object_bases.first;
 +           base_orig != NULL;
 +           base_orig = base_orig->next, base_cow = base_cow->next)
 +      {
 +              /* object itself */
 +              build_object(base_cow, base_orig->object, linked_state);
 +              base_orig->object->select_color = select_color++;
 +      }
 +      if (scene->camera != NULL) {
 +              build_object(NULL, scene->camera, DEG_ID_LINKED_INDIRECTLY);
 +      }
 +      /* 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);
 +      }
 +      /* Cache file. */
-       BLI_LISTBASE_FOREACH (Mask *, mask, &bmain_->mask) {
++      LISTBASE_FOREACH (CacheFile *, cachefile, &bmain_->cachefiles) {
 +              build_cachefile(cachefile);
 +      }
 +      /* Masks. */
-       BLI_LISTBASE_FOREACH (MovieClip *, clip, &bmain_->movieclip) {
++      LISTBASE_FOREACH (Mask *, mask, &bmain_->mask) {
 +              build_mask(mask);
 +      }
 +      /* Movie clips. */
++      LISTBASE_FOREACH (MovieClip *, clip, &bmain_->movieclip) {
 +              build_movieclip(clip);
 +      }
 +      /* Collections. */
 +      build_view_layer_collections(&scene->id, view_layer_cow);
 +      /* Parameters evaluation for scene relations mainly. */
 +      add_operation_node(&scene->id,
 +                         DEG_NODE_TYPE_PARAMETERS,
 +                         NULL,
 +                         DEG_OPCODE_PLACEHOLDER,
 +                         "Scene Eval");
 +      /* 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, DEG_ID_LINKED_VIA_SET);
 +      }
 +}
 +
 +}  // namespace DEG
index 12560fa8aa123bd3072c129ef83890942557fc1c,e01d5e53311a802f9f337a8bc69945d37d91dc7e..cff6c926278dc55d71b0de86cc39c3c788bdacef
@@@ -345,9 -347,9 +345,9 @@@ void DepsgraphRelationBuilder::add_forc
          bool add_absorption,
          const char *name)
  {
 -      ListBase *effectors = pdInitEffectors(scene, object, psys, eff, false);
 +      ListBase *effectors = pdInitEffectors(NULL, scene, object, psys, eff, false);
        if (effectors != NULL) {
-               BLI_LISTBASE_FOREACH (EffectorCache *, eff, effectors) {
 -              LISTBASE_FOREACH(EffectorCache *, eff, effectors) {
++              LISTBASE_FOREACH (EffectorCache *, eff, effectors) {
                        if (eff->ob != object) {
                                ComponentKey eff_key(&eff->ob->id, DEG_NODE_TYPE_TRANSFORM);
                                add_relation(eff_key, key, name);
@@@ -426,15 -429,12 +426,15 @@@ void DepsgraphRelationBuilder::build_gr
        OperationKey object_local_transform_key(object != NULL ? &object->id : NULL,
                                                DEG_NODE_TYPE_TRANSFORM,
                                                DEG_OPCODE_TRANSFORM_LOCAL);
 -      LISTBASE_FOREACH (GroupObject *, go, &group->gobject) {
 -              if (!group_done) {
 -                      build_object(go->ob);
 +      if (!group_done) {
-               BLI_LISTBASE_FOREACH (Base *, base, &group->view_layer->object_bases) {
++              LISTBASE_FOREACH (Base *, base, &group->view_layer->object_bases) {
 +                      build_object(NULL, base->object);
                }
 -              if (object != NULL) {
 -                      ComponentKey dupli_transform_key(&go->ob->id, DEG_NODE_TYPE_TRANSFORM);
 +              group_id->tag |= LIB_TAG_DOIT;
 +      }
 +      if (object != NULL) {
-               BLI_LISTBASE_FOREACH (Base *, base, &group->view_layer->object_bases) {
++              LISTBASE_FOREACH (Base *, base, &group->view_layer->object_bases) {
 +                      ComponentKey dupli_transform_key(&base->object->id, DEG_NODE_TYPE_TRANSFORM);
                        add_relation(dupli_transform_key, object_local_transform_key, "Dupligroup");
                }
        }
@@@ -1324,8 -1273,8 +1324,8 @@@ void DepsgraphRelationBuilder::build_ri
  
        /* objects - simulation participants */
        if (rbw->group) {
-               BLI_LISTBASE_FOREACH (Base *, base, &rbw->group->view_layer->object_bases) {
 -              LISTBASE_FOREACH (GroupObject *, go, &rbw->group->gobject) {
 -                      Object *object = go->ob;
++              LISTBASE_FOREACH (Base *, base, &rbw->group->view_layer->object_bases) {
 +                      Object *object = base->object;
                        if (object == NULL || object->type != OB_MESH) {
                                continue;
                        }
  
        /* constraints */
        if (rbw->constraints) {
-               BLI_LISTBASE_FOREACH (Base *, base, &rbw->constraints->view_layer->object_bases) {
 -              LISTBASE_FOREACH (GroupObject *, go, &rbw->constraints->gobject) {
 -                      Object *object = go->ob;
++              LISTBASE_FOREACH (Base *, base, &rbw->constraints->view_layer->object_bases) {
 +                      Object *object = base->object;
                        if (object == NULL || !object->rigidbody_constraint) {
                                continue;
                        }
@@@ -1414,15 -1363,10 +1414,15 @@@ void DepsgraphRelationBuilder::build_pa
                                   DEG_OPCODE_PARTICLE_SYSTEM_EVAL_INIT);
  
        /* Particle systems. */
-       BLI_LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) {
+       LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) {
                ParticleSettings *part = psys->part;
 -              /* Animation of particle settings, */
 -              build_animdata(&part->id);
 +
 +              /* Build particle settings relations.
 +               *
 +               * NOTE: The call itself ensures settings are only build once.
 +               */
 +              build_particle_settings(part);
 +
                /* This particle system. */
                OperationKey psys_key(&object->id,
                                      DEG_NODE_TYPE_EVAL_PARTICLES,
                                         part->effector_weights,
                                         part->type == PART_HAIR,
                                         "Particle Field");
 -              /* Boids*/
 +              /* Boids .*/
                if (part->boids) {
-                       BLI_LISTBASE_FOREACH (BoidState *, state, &part->boids->states) {
-                               BLI_LISTBASE_FOREACH (BoidRule *, rule, &state->rules) {
+                       LISTBASE_FOREACH (BoidState *, state, &part->boids->states) {
+                               LISTBASE_FOREACH (BoidRule *, rule, &state->rules) {
                                        Object *ruleob = NULL;
                                        if (rule->type == eBoidRuleType_Avoid) {
                                                ruleob = ((BoidRuleGoalAvoid *)rule)->ob;
@@@ -1652,23 -1557,13 +1652,23 @@@ void DepsgraphRelationBuilder::build_ob
        /* link components to each other */
        add_relation(obdata_geom_key, geom_key, "Object Geometry Base Data");
  
 +      OperationKey obdata_ubereval_key(&object->id,
 +                                       DEG_NODE_TYPE_GEOMETRY,
 +                                       DEG_OPCODE_GEOMETRY_UBEREVAL);
 +
 +      /* Special case: modifiers and DerivedMesh creation queries scene for various
 +       * things like data mask to be used. We add relation here to ensure object is
 +       * never evaluated prior to Scene's CoW is ready.
 +       */
 +      OperationKey scene_key(&scene_->id,
 +                             DEG_NODE_TYPE_PARAMETERS,
 +                             DEG_OPCODE_PLACEHOLDER,
 +                             "Scene Eval");
 +      add_relation(scene_key, obdata_ubereval_key, "CoW Relation");
 +
        /* Modifiers */
        if (object->modifiers.first != NULL) {
-               BLI_LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
 -              OperationKey obdata_ubereval_key(&object->id,
 -                                               DEG_NODE_TYPE_GEOMETRY,
 -                                               DEG_OPCODE_GEOMETRY_UBEREVAL);
 -
+               LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
                        const ModifierTypeInfo *mti = modifierType_getInfo((ModifierType)md->type);
                        if (mti->updateDepsgraph) {
                                DepsNodeHandle handle = create_node_handle(obdata_ubereval_key);
@@@ -1882,14 -1744,19 +1882,14 @@@ void DepsgraphRelationBuilder::build_la
  
  void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
  {
 -      if (!ntree)
 +      if (ntree == NULL) {
                return;
 -
 +      }
        ID *ntree_id = &ntree->id;
 -
        build_animdata(ntree_id);
 -
 -      OperationKey parameters_key(ntree_id,
 -                                  DEG_NODE_TYPE_PARAMETERS,
 -                                  DEG_OPCODE_PARAMETERS_EVAL);
 -
 +      ComponentKey shading_key(ntree_id, DEG_NODE_TYPE_SHADING);
        /* nodetree's nodes... */
-       BLI_LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) {
+       LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) {
                ID *id = bnode->id;
                if (id == NULL) {
                        continue;
index 9cf82b5fb47f8d31ce620e8f919dfc5004def530,0000000000000000000000000000000000000000..e2154558ed7dd9423d9fe5c1284faa807ecc7e0d
mode 100644,000000..100644
--- /dev/null
@@@ -1,124 -1,0 +1,124 @@@
-       BLI_LISTBASE_FOREACH (LayerCollection *, layer_collection, layer_collections) {
 +/*
 + * ***** 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_layer_collection.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"
 +
 +extern "C" {
 +#include "BLI_blenlib.h"
 +#include "BLI_utildefines.h"
 +
 +#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"
 +
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_build.h"
 +} /* extern "C" */
 +
 +#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_operation.h"
 +
 +#include "intern/depsgraph_intern.h"
 +#include "intern/depsgraph_types.h"
 +
 +#include "util/deg_util_foreach.h"
 +
 +namespace DEG {
 +
 +void DepsgraphRelationBuilder::build_layer_collection(
 +        ID *owner_id,
 +        LayerCollection *layer_collection,
 +        LayerCollectionState *state)
 +{
 +      OperationKey layer_key(owner_id,
 +                             DEG_NODE_TYPE_LAYER_COLLECTIONS,
 +                             DEG_OPCODE_VIEW_LAYER_EVAL,
 +                             layer_collection->scene_collection->name,
 +                             state->index);
 +      add_relation(state->prev_key, layer_key, "Layer collection order");
 +
 +      ++state->index;
 +      state->prev_key = layer_key;
 +
 +      /* Recurs into nested layer collections. */
 +      build_layer_collections(owner_id, &layer_collection->layer_collections, state);
 +}
 +
 +void DepsgraphRelationBuilder::build_layer_collections(
 +        ID *owner_id,
 +        ListBase *layer_collections,
 +        LayerCollectionState *state)
 +{
++      LISTBASE_FOREACH (LayerCollection *, layer_collection, layer_collections) {
 +              /* Recurs into the layer. */
 +              build_layer_collection(owner_id, layer_collection, state);
 +      }
 +}
 +
 +void DepsgraphRelationBuilder::build_view_layer_collections(
 +        ID *owner_id,
 +        ViewLayer *view_layer)
 +{
 +      LayerCollectionState state;
 +      state.index = 0;
 +
 +      OperationKey init_key(owner_id,
 +                            DEG_NODE_TYPE_LAYER_COLLECTIONS,
 +                            DEG_OPCODE_VIEW_LAYER_INIT);
 +      OperationKey done_key(owner_id,
 +                            DEG_NODE_TYPE_LAYER_COLLECTIONS,
 +                            DEG_OPCODE_VIEW_LAYER_DONE);
 +
 +      state.init_key = init_key;
 +      state.done_key = done_key;
 +      state.prev_key = init_key;
 +
 +      build_layer_collections(owner_id, &view_layer->layer_collections, &state);
 +
 +      add_relation(state.prev_key, done_key, "Layer collection order");
 +}
 +
 +}  // namespace DEG
index cfb98fe2f79be210a05dffaafbeed419413febdd,0000000000000000000000000000000000000000..074d20bb7503420055f333b91409f35c47783a76
mode 100644,000000..100644
--- /dev/null
@@@ -1,136 -1,0 +1,136 @@@
-       BLI_LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
 +/*
 + * ***** 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.
 +       */
-       BLI_LISTBASE_FOREACH (Mask *, mask, &bmain_->mask) {
++      LISTBASE_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. */
-       BLI_LISTBASE_FOREACH (MovieClip *, clip, &bmain_->movieclip) {
++      LISTBASE_FOREACH (Mask *, mask, &bmain_->mask) {
 +              build_mask(mask);
 +      }
 +      /* Movie clips. */
++      LISTBASE_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 b9264a25277c45e4618140d746267b1a104bfae1,0c093c26fc576d26a6cbecefdcaa8c620453e0e5..377f2d3b4c57d1c57bc4a91c89e01c97d15f84f0
@@@ -287,23 -282,34 +287,23 @@@ void DEG_graph_relations_update(Depsgra
                /* Graph is up to date, nothing to do. */
                return;
        }
 -
 -      /* Clear all previous nodes and operations. */
 -      graph->clear_all_nodes();
 -      graph->operations.clear();
 -      BLI_gset_clear(graph->entry_tags, NULL);
 -
 -      /* Build new nodes and relations. */
 -      DEG_graph_build_from_scene(reinterpret_cast< ::Depsgraph * >(graph),
 -                                 bmain,
 -                                 scene);
 -
 -      graph->need_update = false;
 -}
 -
 -/* Rebuild dependency graph only for a given scene. */
 -void DEG_scene_relations_rebuild(Main *bmain, Scene *scene)
 -{
 -      if (scene->depsgraph != NULL) {
 -              DEG_graph_tag_relations_update(scene->depsgraph);
 -      }
 -      DEG_scene_relations_update(bmain, scene);
 +      DEG_graph_build_from_view_layer(graph, bmain, scene, view_layer);
  }
  
 -void DEG_scene_graph_free(Scene *scene)
 +/* Tag all relations for update. */
 +void DEG_relations_tag_update(Main *bmain)
  {
 -      if (scene->depsgraph) {
 -              DEG_graph_free(scene->depsgraph);
 -              scene->depsgraph = NULL;
 +      DEG_DEBUG_PRINTF("%s: Tagging relations for update.\n", __func__);
-       BLI_LISTBASE_FOREACH (Scene *, scene, &bmain->scene) {
-               BLI_LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
++      LISTBASE_FOREACH (Scene *, scene, &bmain->scene) {
++              LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
 +                      Depsgraph *depsgraph =
 +                              (Depsgraph *)BKE_scene_get_depsgraph(scene,
 +                                                                   view_layer,
 +                                                                   false);
 +                      if (depsgraph != NULL) {
 +                              DEG_graph_tag_relations_update(depsgraph);
 +                      }
 +              }
        }
  }
  
index 0dc1391d3a6c019021fc2741afd26945584b0bc7,3ee7ef833209b49de263a4333e3f48511888f4d1..b0b3cbe0f8c65c5a2c01534a656b1a3ad1517075
@@@ -148,311 -126,159 +148,311 @@@ void depsgraph_select_tag_to_component_
        }
  }
  
 -#ifdef DEPSGRAPH_USE_LEGACY_TAGGING
 -void depsgraph_legacy_handle_update_tag(Main *bmain, ID *id, short flag)
 +void depsgraph_base_flags_tag_to_component_opcode(
 +        const ID *id,
 +        eDepsNode_Type *component_type,
 +        eDepsOperation_Code *operation_code)
  {
 -      if (flag) {
 -              Object *object;
 -              short idtype = GS(id->name);
 -              if (idtype == ID_PA) {
 -                      ParticleSystem *psys;
 -                      for (object = (Object *)bmain->object.first;
 -                           object != NULL;
 -                           object = (Object *)object->id.next)
 -                      {
 -                              for (psys = (ParticleSystem *)object->particlesystem.first;
 -                                   psys != NULL;
 -                                   psys = (ParticleSystem *)psys->next)
 -                              {
 -                                      if (&psys->part->id == id) {
 -                                              DEG_id_tag_update_ex(bmain, &object->id, flag & OB_RECALC_ALL);
 -                                              psys->recalc |= (flag & PSYS_RECALC);
 -                                      }
 -                              }
 -                      }
 -              }
 +      const ID_Type id_type = GS(id->name);
 +      if (id_type == ID_SCE) {
 +              *component_type = DEG_NODE_TYPE_LAYER_COLLECTIONS;
 +              *operation_code = DEG_OPCODE_VIEW_LAYER_INIT;
 +      }
 +      else if (id_type == ID_OB) {
 +              *component_type = DEG_NODE_TYPE_LAYER_COLLECTIONS;
 +              *operation_code = DEG_OPCODE_OBJECT_BASE_FLAGS;
        }
  }
 -#endif
  
 -}  /* namespace */
 +void depsgraph_tag_to_component_opcode(const ID *id,
 +                                       eDepsgraph_Tag tag,
 +                                       eDepsNode_Type *component_type,
 +                                       eDepsOperation_Code *operation_code)
 +{
 +      const ID_Type id_type = GS(id->name);
 +      *component_type = DEG_NODE_TYPE_UNDEFINED;
 +      *operation_code = DEG_OPCODE_OPERATION;
 +      /* Special case for now, in the future we should get rid of this. */
 +      if (tag == 0) {
 +              *component_type = DEG_NODE_TYPE_ID_REF;
 +              *operation_code = DEG_OPCODE_OPERATION;
 +              return;
 +      }
 +      switch (tag) {
 +              case DEG_TAG_TRANSFORM:
 +                      *component_type = DEG_NODE_TYPE_TRANSFORM;
 +                      break;
 +              case DEG_TAG_GEOMETRY:
 +                      depsgraph_geometry_tag_to_component(id, component_type);
 +                      break;
 +              case DEG_TAG_TIME:
 +                      *component_type = DEG_NODE_TYPE_ANIMATION;
 +                      break;
 +              case DEG_TAG_PSYS_REDO:
 +              case DEG_TAG_PSYS_RESET:
 +              case DEG_TAG_PSYS_TYPE:
 +              case DEG_TAG_PSYS_CHILD:
 +              case DEG_TAG_PSYS_PHYS:
 +                      if (id_type == ID_PA) {
 +                              /* NOTES:
 +                               * - For particle settings node we need to use different
 +                               *   component. Will be nice to get this unified with object,
 +                               *   but we can survive for now with single exception here.
 +                               *   Particles needs reconsideration anyway,
 +                               * - We do direct injection of particle settings recalc flag
 +                               *   here. This is what we need to do for until particles
 +                               *   are switched away from own recalc flag and are using
 +                               *   ID->recalc flags instead.
 +                               */
 +                              ParticleSettings *particle_settings = (ParticleSettings *)id;
 +                              particle_settings->recalc |= (tag & DEG_TAG_PSYS_ALL);
 +                              *component_type = DEG_NODE_TYPE_PARAMETERS;
 +                      }
 +                      else {
 +                              *component_type = DEG_NODE_TYPE_EVAL_PARTICLES;
 +                      }
 +                      break;
 +              case DEG_TAG_COPY_ON_WRITE:
 +                      *component_type = DEG_NODE_TYPE_COPY_ON_WRITE;
 +                      break;
 +              case DEG_TAG_SHADING_UPDATE:
 +                      if (id_type == ID_NT) {
 +                              *component_type = DEG_NODE_TYPE_SHADING_PARAMETERS;
 +                      }
 +                      else {
 +                              *component_type = DEG_NODE_TYPE_SHADING;
 +                      }
 +                      break;
 +              case DEG_TAG_SELECT_UPDATE:
 +                      depsgraph_select_tag_to_component_opcode(id,
 +                                                               component_type,
 +                                                               operation_code);
 +                      break;
 +              case DEG_TAG_BASE_FLAGS_UPDATE:
 +                      depsgraph_base_flags_tag_to_component_opcode(id,
 +                                                                   component_type,
 +                                                                   operation_code);
 +              case DEG_TAG_EDITORS_UPDATE:
 +                      /* There is no such node in depsgraph, this tag is to be handled
 +                       * separately.
 +                       */
 +                      break;
 +              case DEG_TAG_PSYS_ALL:
 +                      BLI_assert(!"Should not happen");
 +                      break;
 +      }
 +}
  
 -/* Tag all nodes in ID-block for update.
 - * This is a crude measure, but is most convenient for old code.
 - */
 -void DEG_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id)
 +void id_tag_update_ntree_special(Main *bmain, Depsgraph *graph, ID *id, int flag)
  {
 -      DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph);
 -      DEG::IDDepsNode *node = deg_graph->find_id_node(id);
 -      lib_id_recalc_tag(bmain, id);
 -      if (node != NULL) {
 -              node->tag_update(deg_graph);
 +      bNodeTree *ntree = ntreeFromID(id);
 +      if (ntree == NULL) {
 +              return;
        }
 +      deg_graph_id_tag_update(bmain, graph, &ntree->id, flag);
  }
  
 -/* Tag given ID for an update in all the dependency graphs. */
 -void DEG_id_tag_update(ID *id, short flag)
 +void depsgraph_update_editors_tag(Main *bmain, Depsgraph *graph, ID *id)
  {
 -      DEG_id_tag_update_ex(G.main, id, flag);
 +      /* NOTE: We handle this immediately, without delaying anything, to be
 +       * sure we don't cause threading issues with OpenGL.
 +       */
 +      /* TODO(sergey): Make sure this works for CoW-ed datablocks as well. */
 +      DEGEditorUpdateContext update_ctx = {NULL};
 +      update_ctx.bmain = bmain;
 +      update_ctx.depsgraph = (::Depsgraph *)graph;
 +      update_ctx.scene = graph->scene;
 +      update_ctx.view_layer = graph->view_layer;
 +      deg_editors_id_update(&update_ctx, id);
  }
  
 -void DEG_id_tag_update_ex(Main *bmain, ID *id, short flag)
 +void depsgraph_tag_component(Depsgraph *graph,
 +                             IDDepsNode *id_node,
 +                             eDepsNode_Type component_type,
 +                             eDepsOperation_Code operation_code)
  {
 -      if (id == NULL) {
 -              /* Ideally should not happen, but old depsgraph allowed this. */
 +      ComponentDepsNode *component_node =
 +              id_node->find_component(component_type);
 +      if (component_node == NULL) {
                return;
        }
 -      DEG_DEBUG_PRINTF("%s: id=%s flag=%d\n", __func__, id->name, flag);
 -      lib_id_recalc_tag_flag(bmain, id, flag);
 -      for (Scene *scene = (Scene *)bmain->scene.first;
 -           scene != NULL;
 -           scene = (Scene *)scene->id.next)
 -      {
 -              if (scene->depsgraph) {
 -                      Depsgraph *graph = scene->depsgraph;
 -                      if (flag == 0) {
 -                              /* TODO(sergey): Currently blender is still tagging IDs
 -                               * for recalc just using flag=0. This isn't totally correct
 -                               * but we'd better deal with such cases and don't fail.
 -                               */
 -                              DEG_graph_id_tag_update(bmain, graph, id);
 -                              continue;
 -                      }
 -                      if (flag & OB_RECALC_DATA && GS(id->name) == ID_OB) {
 +      if (operation_code == DEG_OPCODE_OPERATION) {
 +              component_node->tag_update(graph);
 +      }
 +      else {
 +              OperationDepsNode *operation_node =
 +                      component_node->find_operation(operation_code);
 +              if (operation_node != NULL) {
 +                      operation_node->tag_update(graph);
 +              }
 +      }
 +}
 +
 +/* This is a tag compatibility with legacy code.
 + *
 + * Mainly, old code was tagging object with OB_RECALC_DATA tag to inform
 + * that object's data datablock changed. Now API expects that ID is given
 + * explicitly, but not all areas are aware of this yet.
 + */
 +void deg_graph_id_tag_legacy_compat(Main *bmain,
 +                                    ID *id,
 +                                    eDepsgraph_Tag tag)
 +{
 +      if (tag == DEG_TAG_GEOMETRY || tag == 0) {
 +              switch (GS(id->name)) {
 +                      case ID_OB:
 +                      {
                                Object *object = (Object *)id;
 -                              if (object->data != NULL) {
 -                                      DEG_graph_id_tag_update(bmain,
 -                                                              graph,
 -                                                              (ID *)object->data);
 +                              ID *data_id = (ID *)object->data;
 +                              if (data_id != NULL) {
 +                                      DEG_id_tag_update_ex(bmain, data_id, 0);
                                }
 +                              break;
                        }
 -                      if (flag & (OB_RECALC_OB | OB_RECALC_DATA)) {
 -                              DEG_graph_id_tag_update(bmain, graph, id);
 +                      /* TODO(sergey): Shape keys are annoying, maybe we should find a
 +                       * way to chain geometry evaluation to them, so we don't need extra
 +                       * tagging here.
 +                       */
 +                      case ID_ME:
 +                      {
 +                              Mesh *mesh = (Mesh *)id;
 +                              ID *key_id = &mesh->key->id;
 +                              if (key_id != NULL) {
 +                                      DEG_id_tag_update_ex(bmain, key_id, 0);
 +                              }
 +                              break;
 +                      }
 +                      case ID_LT:
 +                      {
 +                              Lattice *lattice = (Lattice *)id;
 +                              ID *key_id = &lattice->key->id;
 +                              if (key_id != NULL) {
 +                                      DEG_id_tag_update_ex(bmain, key_id, 0);
 +                              }
 +                              break;
                        }
 -                      else if (flag & OB_RECALC_TIME) {
 -                              DEG_graph_id_tag_update(bmain, graph, id);
 +                      case ID_CU:
 +                      {
 +                              Curve *curve = (Curve *)id;
 +                              ID *key_id = &curve->key->id;
 +                              if (key_id != NULL) {
 +                                      DEG_id_tag_update_ex(bmain, key_id, 0);
 +                              }
 +                              break;
                        }
 +                      default:
 +                              break;
                }
        }
 -
 -#ifdef DEPSGRAPH_USE_LEGACY_TAGGING
 -      /* Special handling from the legacy depsgraph.
 -       * TODO(sergey): Need to get rid of those once all the areas
 -       * are re-formulated in terms of franular nodes.
 -       */
 -      depsgraph_legacy_handle_update_tag(bmain, id, flag);
 -#endif
  }
  
 -/* Mark a particular datablock type as having changing. */
 -void DEG_id_type_tag(Main *bmain, short idtype)
 +void deg_graph_id_tag_update_single_flag(Main *bmain,
 +                                         Depsgraph *graph,
 +                                         ID *id,
 +                                         IDDepsNode *id_node,
 +                                         eDepsgraph_Tag tag)
  {
 -      if (idtype == ID_NT) {
 -              /* Stupid workaround so parent datablocks of nested nodetree get looped
 -               * over when we loop over tagged datablock types.
 +      if (tag == DEG_TAG_EDITORS_UPDATE) {
 +              if (graph != NULL) {
 +                      depsgraph_update_editors_tag(bmain, graph, id);
 +              }
 +              return;
 +      }
 +      /* Get description of what is to be tagged. */
 +      eDepsNode_Type component_type;
 +      eDepsOperation_Code operation_code;
 +      depsgraph_tag_to_component_opcode(id,
 +                                        tag,
 +                                        &component_type,
 +                                        &operation_code);
 +      /* Check whether we've got something to tag. */
 +      if (component_type == DEG_NODE_TYPE_UNDEFINED) {
 +              /* Given ID does not support tag. */
 +              /* TODO(sergey): Shall we raise some panic here? */
 +              return;
 +      }
 +      /* Tag ID recalc flag. */
 +      DepsNodeFactory *factory = deg_type_get_factory(component_type);
 +      BLI_assert(factory != NULL);
 +      id->recalc |= factory->id_recalc_tag();
 +      /* Some sanity checks before moving forward. */
 +      if (id_node == NULL) {
 +              /* Happens when object is tagged for update and not yet in the
 +               * dependency graph (but will be after relations update).
                 */
 -              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);
 +              return;
        }
 +      /* Tag corresponding dependency graph operation for update. */
 +      if (component_type == DEG_NODE_TYPE_ID_REF) {
 +              id_node->tag_update(graph);
 +      }
 +      else {
 +              depsgraph_tag_component(graph, id_node, component_type, operation_code);
 +      }
 +      /* TODO(sergey): Get rid of this once all areas are using proper data ID
 +       * for tagging.
 +       */
 +      deg_graph_id_tag_legacy_compat(bmain, id, tag);
  
 -      bmain->id_tag_update[BKE_idcode_to_index(idtype)] = 1;
  }
  
 -/* Recursively push updates out to all nodes dependent on this,
 - * until all affected are tagged and/or scheduled up for eval
 - */
 -void DEG_ids_flush_tagged(Main *bmain)
 +void deg_graph_id_tag_update(Main *bmain, Depsgraph *graph, ID *id, int flag)
  {
 -      for (Scene *scene = (Scene *)bmain->scene.first;
 -           scene != NULL;
 -           scene = (Scene *)scene->id.next)
 -      {
 -              DEG_scene_flush_update(bmain, scene);
 +      IDDepsNode *id_node = (graph != NULL) ? graph->find_id_node(id)
 +                                            : NULL;
 +      DEG_id_type_tag(bmain, GS(id->name));
 +      if (flag == 0) {
 +              /* TODO(sergey): Which recalc flags to set here? */
 +              id->recalc |= ID_RECALC_ALL;
 +              if (id_node != NULL) {
 +                      id_node->tag_update(graph);
 +              }
 +              deg_graph_id_tag_legacy_compat(bmain, id, (eDepsgraph_Tag)0);
 +      }
 +      int current_flag = flag;
 +      while (current_flag != 0) {
 +              eDepsgraph_Tag tag =
 +                      (eDepsgraph_Tag)(1 << bitscan_forward_clear_i(&current_flag));
 +              deg_graph_id_tag_update_single_flag(bmain,
 +                                                  graph,
 +                                                  id,
 +                                                  id_node,
 +                                                  tag);
        }
 +      /* Special case for nested node tree datablocks. */
 +      id_tag_update_ntree_special(bmain, graph, id, flag);
  }
  
 -void DEG_scene_flush_update(Main *bmain, Scene *scene)
 +/* TODO(sergey): Consider storing scene and view layer at depsgraph allocation
 + * time.
 + */
 +void deg_ensure_scene_view_layer(Depsgraph *graph,
 +                                 Scene *scene,
 +                                 ViewLayer *view_layer)
  {
 -      if (scene->depsgraph == NULL) {
 +      if (!graph->need_update) {
                return;
        }
 -      DEG::deg_graph_flush_updates(
 -              bmain,
 -              reinterpret_cast<DEG::Depsgraph *>(scene->depsgraph));
 +      graph->scene = scene;
 +      graph->view_layer = view_layer;
  }
  
 -/* Update dependency graph when visible scenes/layers changes. */
 -void DEG_graph_on_visible_update(Main *bmain, Scene *scene)
 +void deg_id_tag_update(Main *bmain, ID *id, int flag)
  {
 -      DEG::Depsgraph *graph = reinterpret_cast<DEG::Depsgraph *>(scene->depsgraph);
 -      wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
 -      int old_layers = graph->layers;
 -      if (wm != NULL) {
 -              BKE_main_id_tag_listbase(&bmain->scene, LIB_TAG_DOIT, true);
 -              graph->layers = 0;
 -              for (wmWindow *win = (wmWindow *)wm->windows.first;
 -                   win != NULL;
 -                   win = (wmWindow *)win->next)
 -              {
 -                      Scene *scene = win->screen->scene;
 -                      if (scene->id.tag & LIB_TAG_DOIT) {
 -                              graph->layers |= BKE_screen_visible_layers(win->screen, scene);
 -                              scene->id.tag &= ~LIB_TAG_DOIT;
 +      deg_graph_id_tag_update(bmain, NULL, id, flag);
-       BLI_LISTBASE_FOREACH (Scene *, scene, &bmain->scene) {
-               BLI_LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
++      LISTBASE_FOREACH (Scene *, scene, &bmain->scene) {
++              LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
 +                      Depsgraph *depsgraph =
 +                              (Depsgraph *)BKE_scene_get_depsgraph(scene,
 +                                                                   view_layer,
 +                                                                   false);
 +                      if (depsgraph != NULL) {
 +                              /* Make sure depsgraph is pointing to a correct scene and
 +                               * view layer. This is mainly required in cases when depsgraph
 +                               * was not built yet.
 +                               */
 +                              deg_ensure_scene_view_layer(depsgraph, scene, view_layer);
 +                              deg_graph_id_tag_update(bmain, depsgraph, id, flag);
                        }
                }
        }
@@@ -555,15 -356,12 +555,15 @@@ void DEG_graph_on_visible_update(Main *
  
  void DEG_on_visible_update(Main *bmain, const bool UNUSED(do_time))
  {
-       BLI_LISTBASE_FOREACH (Scene *, scene, &bmain->scene) {
-               BLI_LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
 -      for (Scene *scene = (Scene *)bmain->scene.first;
 -           scene != NULL;
 -           scene = (Scene *)scene->id.next)
 -      {
 -              if (scene->depsgraph != NULL) {
 -                      DEG_graph_on_visible_update(bmain, scene);
++      LISTBASE_FOREACH (Scene *, scene, &bmain->scene) {
++              LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
 +                      Depsgraph *depsgraph =
 +                              (Depsgraph *)BKE_scene_get_depsgraph(scene,
 +                                                                   view_layer,
 +                                                                   false);
 +                      if (depsgraph != NULL) {
 +                              DEG_graph_on_visible_update(bmain, depsgraph);
 +                      }
                }
        }
  }
index a6c6a16a52803732972353de8dab34a4e8d81010,8ee66eddad651b94977d60b10a89fd60589f8544..c3aa84943d312ac0fa926cb46cd13db70e919d0d
@@@ -228,16 -249,19 +228,16 @@@ static void schedule_children(TaskPool 
   * \note Time sources should be all valid!
   */
  void deg_evaluate_on_refresh(EvaluationContext *eval_ctx,
 -                             Depsgraph *graph,
 -                             const unsigned int layers)
 +                             Depsgraph *graph)
  {
        /* Nothing to update, early out. */
-       if (BLI_gset_size(graph->entry_tags) == 0) {
+       if (BLI_gset_len(graph->entry_tags) == 0) {
                return;
        }
 -      DEG_DEBUG_PRINTF("%s: layers:%u, graph->layers:%u\n",
 -                       __func__,
 -                       layers,
 -                       graph->layers);
        /* Set time for the current graph evaluation context. */
        TimeSourceDepsNode *time_src = graph->find_time_source();
 +      eval_ctx->depsgraph = (::Depsgraph *)graph;
 +      eval_ctx->view_layer = DEG_get_evaluated_view_layer((::Depsgraph *)graph);
        eval_ctx->ctime = time_src->cfra;
        /* Set up evaluation context for depsgraph itself. */
        DepsgraphEvalState state;
index c0d5e08b80fd233036cc21798979c1043dacbc4c,4033e1325e6400aa6f3a780017cd62587ebe8a06..b4e1c2f4e1a40eb38207b194fb27b39b091d7e0c
@@@ -208,21 -237,8 +208,21 @@@ BLI_INLINE OperationDepsNode *flush_sch
        return result;
  }
  
 -BLI_INLINE void flush_editors_id_update(Main *bmain,
 -                                        Depsgraph *graph)
 +void flush_engine_data_update(ID *id)
 +{
 +      if (GS(id->name) != ID_OB) {
 +              return;
 +      }
 +      Object *object = (Object *)id;
-       BLI_LISTBASE_FOREACH(ObjectEngineData *, engine_data, &object->drawdata) {
++      LISTBASE_FOREACH(ObjectEngineData *, engine_data, &object->drawdata) {
 +              engine_data->recalc |= id->recalc;
 +      }
 +}
 +
 +/* NOTE: It will also accumulate flags from changed components. */
 +void flush_editors_id_update(Main *bmain,
 +                             Depsgraph *graph,
 +                             const DEGEditorUpdateContext *update_ctx)
  {
        foreach (IDDepsNode *id_node, graph->id_nodes) {
                if (id_node->done != ID_STATE_MODIFIED) {
index 1f56edd1f87f9f21e6edd8cfd80e879fefd957d9,8ebf7424c42d4d34815877d893ef31223d258571..6c33856555ebe844d98c677462c174d7cfc8b5d2
@@@ -342,9 -346,9 +342,9 @@@ OperationDepsNode *ComponentDepsNode::g
        return NULL;
  }
  
 -void ComponentDepsNode::finalize_build()
 +void ComponentDepsNode::finalize_build(Depsgraph * /*graph*/)
  {
-       operations.reserve(BLI_ghash_size(operations_map));
+       operations.reserve(BLI_ghash_len(operations_map));
        GHASH_FOREACH_BEGIN(OperationDepsNode *, op_node, operations_map)
        {
                operations.push_back(op_node);
index 622fbe968d287b14c2de8886f89badb4c788c1e3,0000000000000000000000000000000000000000..c049841912759b420a61b47e547554e7ed98d946
mode 100644,000000..100644
--- /dev/null
@@@ -1,4125 -1,0 +1,4125 @@@
-               BLI_mempool_clear_ex(DST.vmempool->calls, BLI_mempool_count(DST.vmempool->calls));
-               BLI_mempool_clear_ex(DST.vmempool->calls_generate, BLI_mempool_count(DST.vmempool->calls_generate));
-               BLI_mempool_clear_ex(DST.vmempool->shgroups, BLI_mempool_count(DST.vmempool->shgroups));
-               BLI_mempool_clear_ex(DST.vmempool->uniforms, BLI_mempool_count(DST.vmempool->uniforms));
-               BLI_mempool_clear_ex(DST.vmempool->passes, BLI_mempool_count(DST.vmempool->passes));
 +/*
 + * Copyright 2016, Blender Foundation.
 + *
 + * 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): Blender Institute
 + *
 + */
 +
 +/** \file blender/draw/intern/draw_manager.c
 + *  \ingroup draw
 + */
 +
 +#include <stdio.h>
 +
 +#include "BLI_listbase.h"
 +#include "BLI_mempool.h"
 +#include "BLI_rect.h"
 +#include "BLI_string.h"
 +#include "BLI_string_utils.h"
 +
 +#include "BIF_glutil.h"
 +
 +#include "BKE_curve.h"
 +#include "BKE_global.h"
 +#include "BKE_mesh.h"
 +#include "BKE_object.h"
 +#include "BKE_pbvh.h"
 +#include "BKE_paint.h"
 +#include "BKE_workspace.h"
 +
 +#include "BLT_translation.h"
 +#include "BLF_api.h"
 +
 +#include "DRW_engine.h"
 +#include "DRW_render.h"
 +
 +#include "DNA_camera_types.h"
 +#include "DNA_curve_types.h"
 +#include "DNA_view3d_types.h"
 +#include "DNA_screen_types.h"
 +#include "DNA_mesh_types.h"
 +#include "DNA_meshdata_types.h"
 +#include "DNA_meta_types.h"
 +
 +#include "ED_space_api.h"
 +#include "ED_screen.h"
 +
 +#include "intern/gpu_codegen.h"
 +#include "GPU_batch.h"
 +#include "GPU_draw.h"
 +#include "GPU_extensions.h"
 +#include "GPU_framebuffer.h"
 +#include "GPU_immediate.h"
 +#include "GPU_lamp.h"
 +#include "GPU_material.h"
 +#include "GPU_shader.h"
 +#include "GPU_texture.h"
 +#include "GPU_uniformbuffer.h"
 +#include "GPU_viewport.h"
 +#include "GPU_matrix.h"
 +
 +#include "IMB_colormanagement.h"
 +
 +#include "RE_engine.h"
 +#include "RE_pipeline.h"
 +
 +#include "UI_interface.h"
 +#include "UI_resources.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +
 +#include "draw_manager_text.h"
 +#include "draw_manager_profiling.h"
 +
 +/* only for callbacks */
 +#include "draw_cache_impl.h"
 +
 +#include "draw_instance_data.h"
 +
 +#include "draw_mode_engines.h"
 +#include "engines/clay/clay_engine.h"
 +#include "engines/eevee/eevee_engine.h"
 +#include "engines/basic/basic_engine.h"
 +#include "engines/external/external_engine.h"
 +
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_query.h"
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Local Features
 + * \{ */
 +
 +#define USE_PROFILE
 +
 +#ifdef USE_PROFILE
 +#  include "PIL_time.h"
 +
 +#  define PROFILE_TIMER_FALLOFF 0.1
 +
 +#  define PROFILE_START(time_start) \
 +      double time_start = PIL_check_seconds_timer();
 +
 +#  define PROFILE_END_ACCUM(time_accum, time_start) { \
 +      time_accum += (PIL_check_seconds_timer() - time_start) * 1e3; \
 +} ((void)0)
 +
 +/* exp average */
 +#  define PROFILE_END_UPDATE(time_update, time_start) { \
 +      double _time_delta = (PIL_check_seconds_timer() - time_start) * 1e3; \
 +      time_update = (time_update * (1.0 - PROFILE_TIMER_FALLOFF)) + \
 +                    (_time_delta * PROFILE_TIMER_FALLOFF); \
 +} ((void)0)
 +
 +#else  /* USE_PROFILE */
 +
 +#  define PROFILE_START(time_start) ((void)0)
 +#  define PROFILE_END_ACCUM(time_accum, time_start) ((void)0)
 +#  define PROFILE_END_UPDATE(time_update, time_start) ((void)0)
 +
 +#endif  /* USE_PROFILE */
 +
 +
 +/* Use draw manager to call GPU_select, see: DRW_draw_select_loop */
 +#define USE_GPU_SELECT
 +
 +#ifdef USE_GPU_SELECT
 +#  include "ED_view3d.h"
 +#  include "ED_armature.h"
 +#  include "GPU_select.h"
 +#endif
 +
 +/** \} */
 +
 +
 +#define MAX_ATTRIB_NAME 32
 +#define MAX_ATTRIB_COUNT 6 /* Can be adjusted for more */
 +#define MAX_PASS_NAME 32
 +#define MAX_CLIP_PLANES 6 /* GL_MAX_CLIP_PLANES is at least 6 */
 +
 +extern char datatoc_gpu_shader_2D_vert_glsl[];
 +extern char datatoc_gpu_shader_3D_vert_glsl[];
 +extern char datatoc_gpu_shader_fullscreen_vert_glsl[];
 +
 +/* Prototypes. */
 +static void drw_engines_enable_external(void);
 +
 +/* Structures */
 +typedef enum {
 +      DRW_UNIFORM_BOOL,
 +      DRW_UNIFORM_SHORT_TO_INT,
 +      DRW_UNIFORM_SHORT_TO_FLOAT,
 +      DRW_UNIFORM_INT,
 +      DRW_UNIFORM_FLOAT,
 +      DRW_UNIFORM_TEXTURE,
 +      DRW_UNIFORM_BUFFER,
 +      DRW_UNIFORM_MAT3,
 +      DRW_UNIFORM_MAT4,
 +      DRW_UNIFORM_BLOCK
 +} DRWUniformType;
 +
 +#define MAX_UNIFORM_DATA_SIZE 16
 +
 +struct DRWUniform {
 +      struct DRWUniform *next;
 +      const void *value;
 +      int location;
 +      char type; /* DRWUniformType */
 +      char length; /* cannot be more than 16 */
 +      char arraysize; /* cannot be more than 16 too */
 +};
 +
 +struct DRWInterface {
 +      DRWUniform *uniforms;   /* DRWUniform, single-linked list */
 +      /* Dynamic batch */
 +#ifdef USE_GPU_SELECT
 +      struct DRWInstanceData *inst_selectid;
 +      /* Override for single object instances. */
 +      int override_selectid;
 +#endif
 +      Gwn_VertBuf *instance_vbo;
 +      unsigned int instance_count;
 +#ifndef NDEBUG
 +      char attribs_count;
 +#endif
 +      /* matrices locations */
 +      int model;
 +      int modelinverse;
 +      int modelview;
 +      int modelviewinverse;
 +      int projection;
 +      int projectioninverse;
 +      int view;
 +      int viewinverse;
 +      int modelviewprojection;
 +      int viewprojection;
 +      int viewprojectioninverse;
 +      int normal;
 +      int worldnormal;
 +      int camtexfac;
 +      int orcotexfac;
 +      int eye;
 +      int clipplanes;
 +};
 +
 +struct DRWPass {
 +      /* Single linked list with last member to append */
 +      DRWShadingGroup *shgroups;
 +      DRWShadingGroup *shgroups_last;
 +
 +      DRWState state;
 +      char name[MAX_PASS_NAME];
 +};
 +
 +typedef struct DRWCallHeader {
 +      void *prev;
 +
 +#ifdef USE_GPU_SELECT
 +      int select_id;
 +#endif
 +      uchar type;
 +} DRWCallHeader;
 +
 +typedef struct DRWCall {
 +      DRWCallHeader head;
 +
 +      float obmat[4][4];
 +      Gwn_Batch *geometry;
 +
 +      Object *ob; /* Optional */
 +      ID *ob_data; /* Optional. */
 +} DRWCall;
 +
 +typedef struct DRWCallGenerate {
 +      DRWCallHeader head;
 +
 +      float obmat[4][4];
 +
 +      DRWCallGenerateFn *geometry_fn;
 +      void *user_data;
 +} DRWCallGenerate;
 +
 +struct DRWShadingGroup {
 +      struct DRWShadingGroup *next;
 +
 +      GPUShader *shader;               /* Shader to bind */
 +      DRWInterface interface;          /* Uniforms pointers */
 +
 +      /* DRWCall or DRWCallDynamic depending of type */
 +      void *calls;
 +      void *calls_first; /* To be able to traverse the list in the order of addition */
 +
 +      DRWState state_extra;            /* State changes for this batch only (or'd with the pass's state) */
 +      DRWState state_extra_disable;    /* State changes for this batch only (and'd with the pass's state) */
 +      unsigned int stencil_mask;       /* Stencil mask to use for stencil test / write operations */
 +      int type;
 +
 +      ID *instance_data;         /* Object->data to instance */
 +      Gwn_Batch *instance_geom;  /* Geometry to instance */
 +      Gwn_Batch *instancing_geom;/* Instances attributes */
 +      Gwn_Batch *batch_geom;     /* Result of call batching */
 +
 +#ifdef USE_GPU_SELECT
 +      /* backlink to pass we're in */
 +      DRWPass *pass_parent;
 +#endif
 +};
 +
 +/* Used by DRWShadingGroup.type */
 +enum {
 +      DRW_SHG_NORMAL,
 +      DRW_SHG_POINT_BATCH,
 +      DRW_SHG_LINE_BATCH,
 +      DRW_SHG_TRIANGLE_BATCH,
 +      DRW_SHG_INSTANCE,
 +      DRW_SHG_INSTANCE_EXTERNAL,
 +};
 +
 +/* Used by DRWCall.type */
 +enum {
 +      /* A single batch */
 +      DRW_CALL_SINGLE,
 +      /* Uses a callback to draw with any number of batches. */
 +      DRW_CALL_GENERATE,
 +      /* Arbitrary number of multiple args. */
 +      DRW_CALL_DYNAMIC,
 +};
 +
 +/** Render State: No persistent data between draw calls. */
 +static struct DRWGlobalState {
 +      /* Cache generation */
 +      ViewportMemoryPool *vmempool;
 +      DRWUniform *last_uniform;
 +      DRWCall *last_call;
 +      DRWCallGenerate *last_callgenerate;
 +      DRWShadingGroup *last_shgroup;
 +      DRWInstanceDataList *idatalist;
 +      DRWInstanceData *common_instance_data[MAX_INSTANCE_DATA_SIZE];
 +
 +      /* Rendering state */
 +      GPUShader *shader;
 +
 +      /* Managed by `DRW_state_set`, `DRW_state_reset` */
 +      DRWState state;
 +      unsigned int stencil_mask;
 +
 +      /* Per viewport */
 +      GPUViewport *viewport;
 +      struct GPUFrameBuffer *default_framebuffer;
 +      float size[2];
 +      float screenvecs[2][3];
 +      float pixsize;
 +
 +      GLenum backface, frontface;
 +
 +      /* Clip planes */
 +      int num_clip_planes;
 +      float clip_planes_eq[MAX_CLIP_PLANES][4];
 +
 +      struct {
 +              unsigned int is_select : 1;
 +              unsigned int is_depth : 1;
 +              unsigned int is_image_render : 1;
 +              unsigned int is_scene_render : 1;
 +              unsigned int draw_background : 1;
 +      } options;
 +
 +      /* Current rendering context */
 +      DRWContextState draw_ctx;
 +
 +      /* Convenience pointer to text_store owned by the viewport */
 +      struct DRWTextStore **text_store_p;
 +
 +      ListBase enabled_engines; /* RenderEngineType */
 +
 +      bool buffer_finish_called; /* Avoid bad usage of DRW_render_instance_buffer_finish */
 +
 +      /* Profiling */
 +      double cache_time;
 +} DST = {NULL};
 +
 +/** GPU Resource State: Memory storage between drawing. */
 +static struct DRWResourceState {
 +      GPUTexture **bound_texs;
 +
 +      bool *bound_tex_slots;
 +
 +      int bind_tex_inc;
 +      int bind_ubo_inc;
 +} RST = {NULL};
 +
 +static struct DRWMatrixOveride {
 +      float mat[6][4][4];
 +      bool override[6];
 +} viewport_matrix_override = {{{{0}}}};
 +
 +ListBase DRW_engines = {NULL, NULL};
 +
 +#ifdef USE_GPU_SELECT
 +static unsigned int g_DRW_select_id = (unsigned int)-1;
 +
 +void DRW_select_load_id(unsigned int id)
 +{
 +      BLI_assert(G.f & G_PICKSEL);
 +      g_DRW_select_id = id;
 +}
 +#endif
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Textures (DRW_texture)
 + * \{ */
 +
 +static void drw_texture_get_format(
 +        DRWTextureFormat format,
 +        GPUTextureFormat *r_data_type, int *r_channels)
 +{
 +      switch (format) {
 +              case DRW_TEX_RGBA_8: *r_data_type = GPU_RGBA8; break;
 +              case DRW_TEX_RGBA_16: *r_data_type = GPU_RGBA16F; break;
 +              case DRW_TEX_RGB_16: *r_data_type = GPU_RGB16F; break;
 +              case DRW_TEX_RGB_11_11_10: *r_data_type = GPU_R11F_G11F_B10F; break;
 +              case DRW_TEX_RG_8: *r_data_type = GPU_RG8; break;
 +              case DRW_TEX_RG_16: *r_data_type = GPU_RG16F; break;
 +              case DRW_TEX_RG_16I: *r_data_type = GPU_RG16I; break;
 +              case DRW_TEX_RG_32: *r_data_type = GPU_RG32F; break;
 +              case DRW_TEX_R_8: *r_data_type = GPU_R8; break;
 +              case DRW_TEX_R_16: *r_data_type = GPU_R16F; break;
 +              case DRW_TEX_R_32: *r_data_type = GPU_R32F; break;
 +#if 0
 +              case DRW_TEX_RGBA_32: *r_data_type = GPU_RGBA32F; break;
 +              case DRW_TEX_RGB_8: *r_data_type = GPU_RGB8; break;
 +              case DRW_TEX_RGB_32: *r_data_type = GPU_RGB32F; break;
 +#endif
 +              case DRW_TEX_DEPTH_16: *r_data_type = GPU_DEPTH_COMPONENT16; break;
 +              case DRW_TEX_DEPTH_24: *r_data_type = GPU_DEPTH_COMPONENT24; break;
 +              case DRW_TEX_DEPTH_24_STENCIL_8: *r_data_type = GPU_DEPTH24_STENCIL8; break;
 +              case DRW_TEX_DEPTH_32: *r_data_type = GPU_DEPTH_COMPONENT32F; break;
 +              default :
 +                      /* file type not supported you must uncomment it from above */
 +                      BLI_assert(false);
 +                      break;
 +      }
 +
 +      switch (format) {
 +              case DRW_TEX_RGBA_8:
 +              case DRW_TEX_RGBA_16:
 +              case DRW_TEX_RGBA_32:
 +                      *r_channels = 4;
 +                      break;
 +              case DRW_TEX_RGB_8:
 +              case DRW_TEX_RGB_16:
 +              case DRW_TEX_RGB_32:
 +              case DRW_TEX_RGB_11_11_10:
 +                      *r_channels = 3;
 +                      break;
 +              case DRW_TEX_RG_8:
 +              case DRW_TEX_RG_16:
 +              case DRW_TEX_RG_16I:
 +              case DRW_TEX_RG_32:
 +                      *r_channels = 2;
 +                      break;
 +              default:
 +                      *r_channels = 1;
 +                      break;
 +      }
 +}
 +
 +static void drw_texture_set_parameters(GPUTexture *tex, DRWTextureFlag flags)
 +{
 +      GPU_texture_bind(tex, 0);
 +      if (flags & DRW_TEX_MIPMAP) {
 +              GPU_texture_mipmap_mode(tex, true, flags & DRW_TEX_FILTER);
 +              DRW_texture_generate_mipmaps(tex);
 +      }
 +      else {
 +              GPU_texture_filter_mode(tex, flags & DRW_TEX_FILTER);
 +      }
 +      GPU_texture_wrap_mode(tex, flags & DRW_TEX_WRAP);
 +      GPU_texture_compare_mode(tex, flags & DRW_TEX_COMPARE);
 +      GPU_texture_unbind(tex);
 +}
 +
 +GPUTexture *DRW_texture_create_1D(int w, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels)
 +{
 +      GPUTexture *tex;
 +      GPUTextureFormat data_type;
 +      int channels;
 +
 +      drw_texture_get_format(format, &data_type, &channels);
 +      tex = GPU_texture_create_1D_custom(w, channels, data_type, fpixels, NULL);
 +      drw_texture_set_parameters(tex, flags);
 +
 +      return tex;
 +}
 +
 +GPUTexture *DRW_texture_create_2D(int w, int h, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels)
 +{
 +      GPUTexture *tex;
 +      GPUTextureFormat data_type;
 +      int channels;
 +
 +      drw_texture_get_format(format, &data_type, &channels);
 +      tex = GPU_texture_create_2D_custom(w, h, channels, data_type, fpixels, NULL);
 +      drw_texture_set_parameters(tex, flags);
 +
 +      return tex;
 +}
 +
 +GPUTexture *DRW_texture_create_2D_array(
 +        int w, int h, int d, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels)
 +{
 +      GPUTexture *tex;
 +      GPUTextureFormat data_type;
 +      int channels;
 +
 +      drw_texture_get_format(format, &data_type, &channels);
 +      tex = GPU_texture_create_2D_array_custom(w, h, d, channels, data_type, fpixels, NULL);
 +      drw_texture_set_parameters(tex, flags);
 +
 +      return tex;
 +}
 +
 +GPUTexture *DRW_texture_create_3D(
 +        int w, int h, int d, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels)
 +{
 +      GPUTexture *tex;
 +      GPUTextureFormat data_type;
 +      int channels;
 +
 +      drw_texture_get_format(format, &data_type, &channels);
 +      tex = GPU_texture_create_3D_custom(w, h, d, channels, data_type, fpixels, NULL);
 +      drw_texture_set_parameters(tex, flags);
 +
 +      return tex;
 +}
 +
 +GPUTexture *DRW_texture_create_cube(int w, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels)
 +{
 +      GPUTexture *tex;
 +      GPUTextureFormat data_type;
 +      int channels;
 +
 +      drw_texture_get_format(format, &data_type, &channels);
 +      tex = GPU_texture_create_cube_custom(w, channels, data_type, fpixels, NULL);
 +      drw_texture_set_parameters(tex, flags);
 +
 +      return tex;
 +}
 +
 +void DRW_texture_generate_mipmaps(GPUTexture *tex)
 +{
 +      GPU_texture_bind(tex, 0);
 +      GPU_texture_generate_mipmap(tex);
 +      GPU_texture_unbind(tex);
 +}
 +
 +void DRW_texture_update(GPUTexture *tex, const float *pixels)
 +{
 +      GPU_texture_update(tex, pixels);
 +}
 +
 +void DRW_texture_free(GPUTexture *tex)
 +{
 +      GPU_texture_free(tex);
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Uniform Buffer Object (DRW_uniformbuffer)
 + * \{ */
 +
 +GPUUniformBuffer *DRW_uniformbuffer_create(int size, const void *data)
 +{
 +      return GPU_uniformbuffer_create(size, data, NULL);
 +}
 +
 +void DRW_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data)
 +{
 +      GPU_uniformbuffer_update(ubo, data);
 +}
 +
 +void DRW_uniformbuffer_free(GPUUniformBuffer *ubo)
 +{
 +      GPU_uniformbuffer_free(ubo);
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Shaders (DRW_shader)
 + * \{ */
 +
 +GPUShader *DRW_shader_create(const char *vert, const char *geom, const char *frag, const char *defines)
 +{
 +      return GPU_shader_create(vert, frag, geom, NULL, defines);
 +}
 +
 +GPUShader *DRW_shader_create_with_lib(
 +        const char *vert, const char *geom, const char *frag, const char *lib, const char *defines)
 +{
 +      GPUShader *sh;
 +      char *vert_with_lib = NULL;
 +      char *frag_with_lib = NULL;
 +      char *geom_with_lib = NULL;
 +
 +      vert_with_lib = BLI_string_joinN(lib, vert);
 +      frag_with_lib = BLI_string_joinN(lib, frag);
 +
 +      if (geom) {
 +              geom_with_lib = BLI_string_joinN(lib, geom);
 +      }
 +
 +      sh = GPU_shader_create(vert_with_lib, frag_with_lib, geom_with_lib, NULL, defines);
 +
 +      MEM_freeN(vert_with_lib);
 +      MEM_freeN(frag_with_lib);
 +      if (geom) {
 +              MEM_freeN(geom_with_lib);
 +      }
 +
 +      return sh;
 +}
 +
 +GPUShader *DRW_shader_create_2D(const char *frag, const char *defines)
 +{
 +      return GPU_shader_create(datatoc_gpu_shader_2D_vert_glsl, frag, NULL, NULL, defines);
 +}
 +
 +GPUShader *DRW_shader_create_3D(const char *frag, const char *defines)
 +{
 +      return GPU_shader_create(datatoc_gpu_shader_3D_vert_glsl, frag, NULL, NULL, defines);
 +}
 +
 +GPUShader *DRW_shader_create_fullscreen(const char *frag, const char *defines)
 +{
 +      return GPU_shader_create(datatoc_gpu_shader_fullscreen_vert_glsl, frag, NULL, NULL, defines);
 +}
 +
 +GPUShader *DRW_shader_create_3D_depth_only(void)
 +{
 +      return GPU_shader_get_builtin_shader(GPU_SHADER_3D_DEPTH_ONLY);
 +}
 +
 +void DRW_shader_free(GPUShader *shader)
 +{
 +      GPU_shader_free(shader);
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Interface (DRW_interface)
 + * \{ */
 +
 +static void drw_interface_init(DRWInterface *interface, GPUShader *shader)
 +{
 +      interface->model = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_MODEL);
 +      interface->modelinverse = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_MODEL_INV);
 +      interface->modelview = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_MODELVIEW);
 +      interface->modelviewinverse = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_MODELVIEW_INV);
 +      interface->projection = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_PROJECTION);
 +      interface->projectioninverse = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_PROJECTION_INV);
 +      interface->view = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_VIEW);
 +      interface->viewinverse = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_VIEW_INV);
 +      interface->viewprojection = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_VIEWPROJECTION);
 +      interface->viewprojectioninverse = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_VIEWPROJECTION_INV);
 +      interface->modelviewprojection = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_MVP);
 +      interface->normal = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_NORMAL);
 +      interface->worldnormal = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_WORLDNORMAL);
 +      interface->camtexfac = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_CAMERATEXCO);
 +      interface->orcotexfac = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_ORCO);
 +      interface->clipplanes = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_CLIPPLANES);
 +      interface->eye = GPU_shader_get_builtin_uniform(shader, GWN_UNIFORM_EYE);
 +      interface->instance_count = 0;
 +#ifndef NDEBUG
 +      interface->attribs_count = 0;
 +#endif
 +      interface->uniforms = NULL;
 +#ifdef USE_GPU_SELECT
 +      interface->inst_selectid = NULL;
 +      interface->override_selectid = -1;
 +#endif
 +}
 +
 +static void drw_interface_instance_init(
 +        DRWShadingGroup *shgroup, GPUShader *shader, Gwn_VertFormat *format)
 +{
 +      DRWInterface *interface = &shgroup->interface;
 +      drw_interface_init(interface, shader);
 +
 +#ifndef NDEBUG
 +      interface->attribs_count = (format != NULL) ? format->attrib_ct : 0;
 +#endif
 +
 +      Gwn_PrimType type;
 +      Gwn_Batch **r_batch = NULL;
 +      switch (shgroup->type) {
 +              case DRW_SHG_INSTANCE:
 +                      r_batch = &shgroup->instancing_geom;
 +                      type = GWN_PRIM_POINTS;
 +                      break;
 +              case DRW_SHG_POINT_BATCH:
 +                      r_batch = &shgroup->batch_geom;
 +                      type = GWN_PRIM_POINTS;
 +                      break;
 +              case DRW_SHG_LINE_BATCH:
 +                      r_batch = &shgroup->batch_geom;
 +                      type = GWN_PRIM_LINES;
 +                      break;
 +              case DRW_SHG_TRIANGLE_BATCH:
 +                      r_batch = &shgroup->batch_geom;
 +                      type = GWN_PRIM_TRIS;
 +                      break;
 +              default:
 +                      BLI_assert(0);
 +      }
 +
 +      if (format != NULL) {
 +              DRW_instance_buffer_request(DST.idatalist, format, shgroup, r_batch, &interface->instance_vbo, type);
 +      }
 +      else {
 +              *r_batch = NULL;
 +      }
 +}
 +
 +static void drw_interface_uniform(DRWShadingGroup *shgroup, const char *name,
 +                                  DRWUniformType type, const void *value, int length, int arraysize)
 +{
 +      int location;
 +      if (type == DRW_UNIFORM_BLOCK) {
 +              location = GPU_shader_get_uniform_block(shgroup->shader, name);
 +      }
 +      else {
 +              location = GPU_shader_get_uniform(shgroup->shader, name);
 +      }
 +
 +      if (location == -1) {
 +              if (G.debug & G_DEBUG)
 +                      fprintf(stderr, "Uniform '%s' not found!\n", name);
 +              /* Nice to enable eventually, for now eevee uses uniforms that might not exist. */
 +              // BLI_assert(0);
 +              return;
 +      }
 +
 +      DRWUniform *uni = BLI_mempool_alloc(DST.vmempool->uniforms);
 +
 +      BLI_assert(arraysize > 0 && arraysize <= 16);
 +      BLI_assert(arraysize * length <= MAX_UNIFORM_DATA_SIZE);
 +
 +      uni->location = location;
 +      uni->type = type;
 +      uni->value = value;
 +      uni->length = length;
 +      uni->arraysize = arraysize;
 +
 +      /* Prepend */
 +      uni->next = shgroup->interface.uniforms;
 +      shgroup->interface.uniforms = uni;
 +}
 +
 +Gwn_VertFormat *DRW_shgroup_instance_format_array(const DRWInstanceAttribFormat attribs[], int arraysize)
 +{
 +      Gwn_VertFormat *format = MEM_callocN(sizeof(Gwn_VertFormat), "Gwn_VertFormat");
 +
 +      for (int i = 0; i < arraysize; ++i) {
 +              GWN_vertformat_attr_add(format, attribs[i].name,
 +                                      (attribs[i].type == DRW_ATTRIB_INT) ? GWN_COMP_I32 : GWN_COMP_F32,
 +                                      attribs[i].components,
 +                                      (attribs[i].type == DRW_ATTRIB_INT) ? GWN_FETCH_INT : GWN_FETCH_FLOAT);
 +      }
 +      return format;
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Shading Group (DRW_shgroup)
 + * \{ */
 +
 +static DRWShadingGroup *drw_shgroup_create_ex(struct GPUShader *shader, DRWPass *pass)
 +{
 +      DRWShadingGroup *shgroup = BLI_mempool_alloc(DST.vmempool->shgroups);
 +
 +      /* Append */
 +      if (pass->shgroups != NULL) {
 +              pass->shgroups_last->next = shgroup;
 +      }
 +      else {
 +              pass->shgroups = shgroup;
 +      }
 +      pass->shgroups_last = shgroup;
 +      shgroup->next = NULL;
 +      shgroup->type = DRW_SHG_NORMAL;
 +      shgroup->shader = shader;
 +      shgroup->state_extra = 0;
 +      shgroup->state_extra_disable = ~0x0;
 +      shgroup->stencil_mask = 0;
 +      shgroup->batch_geom = NULL;
 +      shgroup->instancing_geom = NULL;
 +      shgroup->instance_geom = NULL;
 +      shgroup->instance_data = NULL;
 +
 +      shgroup->calls = NULL;
 +      shgroup->calls_first = NULL;
 +
 +#ifdef USE_GPU_SELECT
 +      shgroup->pass_parent = pass;
 +#endif
 +
 +      return shgroup;
 +}
 +
 +static DRWShadingGroup *drw_shgroup_material_create_ex(
 +        struct GPUMaterial *material, DRWPass *pass, Gwn_VertFormat *format, bool use_instancing)
 +{
 +      double time = 0.0; /* TODO make time variable */
 +
 +      /* TODO : Ideally we should not convert. But since the whole codegen
 +       * is relying on GPUPass we keep it as is for now. */
 +      GPUPass *gpupass = GPU_material_get_pass(material);
 +
 +      if (!gpupass) {
 +              /* Shader compilation error */
 +              return NULL;
 +      }
 +
 +      struct GPUShader *shader = GPU_pass_shader(gpupass);
 +
 +      DRWShadingGroup *grp = drw_shgroup_create_ex(shader, pass);
 +
 +      if (use_instancing) {
 +              drw_interface_instance_init(grp, shader, format);
 +      }
 +      else {
 +              drw_interface_init(&grp->interface, shader);
 +      }
 +
 +      /* Converting dynamic GPUInput to DRWUniform */
 +      ListBase *inputs = &gpupass->inputs;
 +
 +      for (GPUInput *input = inputs->first; input; input = input->next) {
 +              /* Textures */
 +              if (input->ima) {
 +                      GPUTexture *tex = GPU_texture_from_blender(
 +                              input->ima, input->iuser, input->textarget, input->image_isdata, time, 1);
 +
 +                      if (input->bindtex) {
 +                              DRW_shgroup_uniform_texture(grp, input->shadername, tex);
 +                      }
 +              }
 +              /* Color Ramps */
 +              else if (input->tex) {
 +                      DRW_shgroup_uniform_texture(grp, input->shadername, input->tex);
 +              }
 +              /* Floats */
 +              else {
 +                      switch (input->type) {
 +                              case GPU_FLOAT:
 +                              case GPU_VEC2:
 +                              case GPU_VEC3:
 +                              case GPU_VEC4:
 +                                      /* Should already be in the material ubo. */
 +                                      break;
 +                              case GPU_MAT3:
 +                                      DRW_shgroup_uniform_mat3(grp, input->shadername, (float *)input->dynamicvec);
 +                                      break;
 +                              case GPU_MAT4:
 +                                      DRW_shgroup_uniform_mat4(grp, input->shadername, (float *)input->dynamicvec);
 +                                      break;
 +                              default:
 +                                      break;
 +                      }
 +              }
 +      }
 +
 +      GPUUniformBuffer *ubo = GPU_material_get_uniform_buffer(material);
 +      if (ubo != NULL) {
 +              DRW_shgroup_uniform_block(grp, GPU_UBO_BLOCK_NAME, ubo);
 +      }
 +
 +      return grp;
 +}
 +
 +DRWShadingGroup *DRW_shgroup_material_create(
 +        struct GPUMaterial *material, DRWPass *pass)
 +{
 +      DRWShadingGroup *shgroup = drw_shgroup_material_create_ex(material, pass, NULL, false);
 +      return shgroup;
 +}
 +
 +DRWShadingGroup *DRW_shgroup_material_instance_create(
 +        struct GPUMaterial *material, DRWPass *pass, Gwn_Batch *geom, Object *ob, Gwn_VertFormat *format)
 +{
 +      DRWShadingGroup *shgroup = drw_shgroup_material_create_ex(material, pass, format, true);
 +
 +      if (shgroup) {
 +              shgroup->type = DRW_SHG_INSTANCE;
 +              shgroup->instance_geom = geom;
 +              shgroup->instance_data = ob->data;
 +      }
 +
 +      return shgroup;
 +}
 +
 +DRWShadingGroup *DRW_shgroup_material_empty_tri_batch_create(
 +        struct GPUMaterial *material, DRWPass *pass, int size)
 +{
 +#ifdef USE_GPU_SELECT
 +      BLI_assert((G.f & G_PICKSEL) == 0);
 +#endif
 +      /* Calling drw_interface_init because we don't need instancing_geom. */
 +      DRWShadingGroup *shgroup = drw_shgroup_material_create_ex(material, pass, NULL, false);
 +
 +      if (shgroup) {
 +              shgroup->type = DRW_SHG_TRIANGLE_BATCH;
 +              shgroup->interface.instance_count = size * 3;
 +      }
 +
 +      return shgroup;
 +}
 +
 +DRWShadingGroup *DRW_shgroup_create(struct GPUShader *shader, DRWPass *pass)
 +{
 +      DRWShadingGroup *shgroup = drw_shgroup_create_ex(shader, pass);
 +      drw_interface_init(&shgroup->interface, shader);
 +      return shgroup;
 +}
 +
 +DRWShadingGroup *DRW_shgroup_instance_create(
 +        struct GPUShader *shader, DRWPass *pass, Gwn_Batch *geom, Gwn_VertFormat *format)
 +{
 +      DRWShadingGroup *shgroup = drw_shgroup_create_ex(shader, pass);
 +      shgroup->type = DRW_SHG_INSTANCE;
 +      shgroup->instance_geom = geom;
 +
 +      drw_interface_instance_init(shgroup, shader, format);
 +
 +      return shgroup;
 +}
 +
 +static Gwn_VertFormat *g_pos_format = NULL;
 +
 +DRWShadingGroup *DRW_shgroup_point_batch_create(struct GPUShader *shader, DRWPass *pass)
 +{
 +      DRW_shgroup_instance_format(g_pos_format, {{"pos", DRW_ATTRIB_FLOAT, 3}});
 +
 +      DRWShadingGroup *shgroup = drw_shgroup_create_ex(shader, pass);
 +      shgroup->type = DRW_SHG_POINT_BATCH;
 +
 +      drw_interface_instance_init(shgroup, shader, g_pos_format);
 +
 +      return shgroup;
 +}
 +
 +DRWShadingGroup *DRW_shgroup_line_batch_create(struct GPUShader *shader, DRWPass *pass)
 +{
 +      DRW_shgroup_instance_format(g_pos_format, {{"pos", DRW_ATTRIB_FLOAT, 3}});
 +
 +      DRWShadingGroup *shgroup = drw_shgroup_create_ex(shader, pass);
 +      shgroup->type = DRW_SHG_LINE_BATCH;
 +
 +      drw_interface_instance_init(shgroup, shader, g_pos_format);
 +
 +      return shgroup;
 +}
 +
 +/* Very special batch. Use this if you position
 + * your vertices with the vertex shader
 + * and dont need any VBO attrib */
 +DRWShadingGroup *DRW_shgroup_empty_tri_batch_create(struct GPUShader *shader, DRWPass *pass, int size)
 +{
 +#ifdef USE_GPU_SELECT
 +      BLI_assert((G.f & G_PICKSEL) == 0);
 +#endif
 +      DRWShadingGroup *shgroup = drw_shgroup_create_ex(shader, pass);
 +
 +      /* Calling drw_interface_init because we don't need instancing_geom. */
 +      drw_interface_init(&shgroup->interface, shader);
 +
 +      shgroup->type = DRW_SHG_TRIANGLE_BATCH;
 +      shgroup->interface.instance_count = size * 3;
 +
 +      return shgroup;
 +}
 +
 +void DRW_shgroup_free(struct DRWShadingGroup *UNUSED(shgroup))
 +{
 +      return;
 +}
 +
 +#define CALL_PREPEND(shgroup, call) { \
 +      if (shgroup->calls == NULL) { \
 +              shgroup->calls = call; \
 +              shgroup->calls_first = call; \
 +      } \
 +      else { \
 +              ((DRWCall *)(shgroup->calls))->head.prev = call; \
 +              shgroup->calls = call; \
 +      } \
 +      call->head.prev = NULL; \
 +} ((void)0)
 +
 +/* Specify an external batch instead of adding each attrib one by one. */
 +void DRW_shgroup_instance_batch(DRWShadingGroup *shgroup, struct Gwn_Batch *instances)
 +{
 +      BLI_assert(shgroup->type == DRW_SHG_INSTANCE);
 +      BLI_assert(shgroup->instancing_geom == NULL);
 +
 +      shgroup->type = DRW_SHG_INSTANCE_EXTERNAL;
 +      shgroup->instancing_geom = instances;
 +
 +#ifdef USE_GPU_SELECT
 +      DRWCall *call = BLI_mempool_alloc(DST.vmempool->calls);
 +      call->head.select_id = g_DRW_select_id;
 +
 +      CALL_PREPEND(shgroup, call);
 +#endif
 +}
 +
 +void DRW_shgroup_call_add(DRWShadingGroup *shgroup, Gwn_Batch *geom, float (*obmat)[4])
 +{
 +      BLI_assert(geom != NULL);
 +      BLI_assert(shgroup->type == DRW_SHG_NORMAL);
 +
 +      DRWCall *call = BLI_mempool_alloc(DST.vmempool->calls);
 +
 +      CALL_PREPEND(shgroup, call);
 +
 +      call->head.type = DRW_CALL_SINGLE;
 +#ifdef USE_GPU_SELECT
 +      call->head.select_id = g_DRW_select_id;
 +#endif
 +
 +      if (obmat != NULL) {
 +              copy_m4_m4(call->obmat, obmat);
 +      }
 +
 +      call->geometry = geom;
 +      call->ob_data = NULL;
 +}
 +
 +void DRW_shgroup_call_object_add(DRWShadingGroup *shgroup, Gwn_Batch *geom, Object *ob)
 +{
 +      BLI_assert(geom != NULL);
 +      BLI_assert(shgroup->type == DRW_SHG_NORMAL);
 +
 +      DRWCall *call = BLI_mempool_alloc(DST.vmempool->calls);
 +
 +      CALL_PREPEND(shgroup, call);
 +
 +      call->head.type = DRW_CALL_SINGLE;
 +#ifdef USE_GPU_SELECT
 +      call->head.select_id = g_DRW_select_id;
 +#endif
 +
 +      copy_m4_m4(call->obmat, ob->obmat);
 +      call->geometry = geom;
 +      call->ob_data = ob->data;
 +}
 +
 +void DRW_shgroup_call_generate_add(
 +        DRWShadingGroup *shgroup,
 +        DRWCallGenerateFn *geometry_fn, void *user_data,
 +        float (*obmat)[4])
 +{
 +      BLI_assert(geometry_fn != NULL);
 +      BLI_assert(shgroup->type == DRW_SHG_NORMAL);
 +
 +      DRWCallGenerate *call = BLI_mempool_alloc(DST.vmempool->calls_generate);
 +
 +      CALL_PREPEND(shgroup, call);
 +
 +      call->head.type = DRW_CALL_GENERATE;
 +#ifdef USE_GPU_SELECT
 +      call->head.select_id = g_DRW_select_id;
 +#endif
 +
 +      if (obmat != NULL) {
 +              copy_m4_m4(call->obmat, obmat);
 +      }
 +
 +      call->geometry_fn = geometry_fn;
 +      call->user_data = user_data;
 +}
 +
 +static void sculpt_draw_cb(
 +        DRWShadingGroup *shgroup,
 +        void (*draw_fn)(DRWShadingGroup *shgroup, Gwn_Batch *geom),
 +        void *user_data)
 +{
 +      Object *ob = user_data;
 +      PBVH *pbvh = ob->sculpt->pbvh;
 +
 +      if (pbvh) {
 +              BKE_pbvh_draw_cb(
 +                      pbvh, NULL, NULL, false,
 +                      (void (*)(void *, Gwn_Batch *))draw_fn, shgroup);
 +      }
 +}
 +
 +void DRW_shgroup_call_sculpt_add(DRWShadingGroup *shgroup, Object *ob, float (*obmat)[4])
 +{
 +      DRW_shgroup_call_generate_add(shgroup, sculpt_draw_cb, ob, obmat);
 +}
 +
 +void DRW_shgroup_call_dynamic_add_array(DRWShadingGroup *shgroup, const void *attr[], unsigned int attr_len)
 +{
 +      DRWInterface *interface = &shgroup->interface;
 +
 +#ifdef USE_GPU_SELECT
 +      if (G.f & G_PICKSEL) {
 +              if (interface->inst_selectid == NULL) {
 +                      interface->inst_selectid = DRW_instance_data_request(DST.idatalist, 1, 128);
 +              }
 +
 +              int *select_id = DRW_instance_data_next(interface->inst_selectid);
 +              *select_id = g_DRW_select_id;
 +      }
 +#endif
 +
 +      BLI_assert(attr_len == interface->attribs_count);
 +      UNUSED_VARS_NDEBUG(attr_len);
 +
 +      for (int i = 0; i < attr_len; ++i) {
 +              if (interface->instance_count == interface->instance_vbo->vertex_ct) {
 +                      GWN_vertbuf_data_resize(interface->instance_vbo, interface->instance_count + 32);
 +              }
 +              GWN_vertbuf_attr_set(interface->instance_vbo, i, interface->instance_count, attr[i]);
 +      }
 +
 +      interface->instance_count += 1;
 +}
 +
 +/* Used for instancing with no attributes */
 +void DRW_shgroup_set_instance_count(DRWShadingGroup *shgroup, unsigned int count)
 +{
 +      DRWInterface *interface = &shgroup->interface;
 +
 +      BLI_assert(interface->instance_count == 0);
 +      BLI_assert(interface->attribs_count == 0);
 +
 +#ifdef USE_GPU_SELECT
 +      if (G.f & G_PICKSEL) {
 +              interface->override_selectid = g_DRW_select_id;
 +      }
 +#endif
 +
 +      interface->instance_count = count;
 +}
 +
 +unsigned int DRW_shgroup_get_instance_count(const DRWShadingGroup *shgroup)
 +{
 +      BLI_assert(shgroup->type != DRW_SHG_NORMAL && shgroup->type != DRW_SHG_INSTANCE_EXTERNAL);
 +
 +      return shgroup->interface.instance_count;
 +}
 +
 +/**
 + * State is added to #Pass.state while drawing.
 + * Use to temporarily enable draw options.
 + */
 +void DRW_shgroup_state_enable(DRWShadingGroup *shgroup, DRWState state)
 +{
 +      shgroup->state_extra |= state;
 +}
 +
 +void DRW_shgroup_state_disable(DRWShadingGroup *shgroup, DRWState state)
 +{
 +      shgroup->state_extra_disable &= ~state;
 +}
 +
 +void DRW_shgroup_stencil_mask(DRWShadingGroup *shgroup, unsigned int mask)
 +{
 +      BLI_assert(mask <= 255);
 +      shgroup->stencil_mask = mask;
 +}
 +
 +void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, const GPUTexture *tex)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_TEXTURE, tex, 0, 1);
 +}
 +
 +void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, const GPUUniformBuffer *ubo)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_BLOCK, ubo, 0, 1);
 +}
 +
 +void DRW_shgroup_uniform_buffer(DRWShadingGroup *shgroup, const char *name, GPUTexture **tex)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_BUFFER, tex, 0, 1);
 +}
 +
 +void DRW_shgroup_uniform_bool(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_BOOL, value, 1, arraysize);
 +}
 +
 +void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 1, arraysize);
 +}
 +
 +void DRW_shgroup_uniform_vec2(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 2, arraysize);
 +}
 +
 +void DRW_shgroup_uniform_vec3(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 3, arraysize);
 +}
 +
 +void DRW_shgroup_uniform_vec4(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 4, arraysize);
 +}
 +
 +void DRW_shgroup_uniform_short_to_int(DRWShadingGroup *shgroup, const char *name, const short *value, int arraysize)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_SHORT_TO_INT, value, 1, arraysize);
 +}
 +
 +void DRW_shgroup_uniform_short_to_float(DRWShadingGroup *shgroup, const char *name, const short *value, int arraysize)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_SHORT_TO_FLOAT, value, 1, arraysize);
 +}
 +
 +void DRW_shgroup_uniform_int(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 1, arraysize);
 +}
 +
 +void DRW_shgroup_uniform_ivec2(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 2, arraysize);
 +}
 +
 +void DRW_shgroup_uniform_ivec3(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 3, arraysize);
 +}
 +
 +void DRW_shgroup_uniform_mat3(DRWShadingGroup *shgroup, const char *name, const float *value)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_MAT3, value, 9, 1);
 +}
 +
 +void DRW_shgroup_uniform_mat4(DRWShadingGroup *shgroup, const char *name, const float *value)
 +{
 +      drw_interface_uniform(shgroup, name, DRW_UNIFORM_MAT4, value, 16, 1);
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Passes (DRW_pass)
 + * \{ */
 +
 +DRWPass *DRW_pass_create(const char *name, DRWState state)
 +{
 +      DRWPass *pass = BLI_mempool_alloc(DST.vmempool->passes);
 +      pass->state = state;
 +      BLI_strncpy(pass->name, name, MAX_PASS_NAME);
 +
 +      pass->shgroups = NULL;
 +      pass->shgroups_last = NULL;
 +
 +      return pass;
 +}
 +
 +void DRW_pass_state_set(DRWPass *pass, DRWState state)
 +{
 +      pass->state = state;
 +}
 +
 +void DRW_pass_free(DRWPass *pass)
 +{
 +      for (DRWShadingGroup *shgroup = pass->shgroups; shgroup; shgroup = shgroup->next) {
 +              DRW_shgroup_free(shgroup);
 +      }
 +
 +      pass->shgroups = NULL;
 +      pass->shgroups_last = NULL;
 +}
 +
 +void DRW_pass_foreach_shgroup(DRWPass *pass, void (*callback)(void *userData, DRWShadingGroup *shgrp), void *userData)
 +{
 +      for (DRWShadingGroup *shgroup = pass->shgroups; shgroup; shgroup = shgroup->next) {
 +              callback(userData, shgroup);
 +      }
 +}
 +
 +typedef struct ZSortData {
 +      float *axis;
 +      float *origin;
 +} ZSortData;
 +
 +static int pass_shgroup_dist_sort(void *thunk, const void *a, const void *b)
 +{
 +      const ZSortData *zsortdata = (ZSortData *)thunk;
 +      const DRWShadingGroup *shgrp_a = (const DRWShadingGroup *)a;
 +      const DRWShadingGroup *shgrp_b = (const DRWShadingGroup *)b;
 +
 +      const DRWCall *call_a;
 +      const DRWCall *call_b;
 +
 +      call_a = shgrp_a->calls_first;
 +      call_b = shgrp_b->calls_first;
 +
 +      if (call_a == NULL) return -1;
 +      if (call_b == NULL) return -1;
 +
 +      float tmp[3];
 +      sub_v3_v3v3(tmp, zsortdata->origin, call_a->obmat[3]);
 +      const float a_sq = dot_v3v3(zsortdata->axis, tmp);
 +      sub_v3_v3v3(tmp, zsortdata->origin, call_b->obmat[3]);
 +      const float b_sq = dot_v3v3(zsortdata->axis, tmp);
 +
 +      if      (a_sq < b_sq) return  1;
 +      else if (a_sq > b_sq) return -1;
 +      else {
 +              /* If there is a depth prepass put it before */
 +              if ((shgrp_a->state_extra & DRW_STATE_WRITE_DEPTH) != 0) {
 +                      return -1;
 +              }
 +              else if ((shgrp_b->state_extra & DRW_STATE_WRITE_DEPTH) != 0) {
 +                      return  1;
 +              }
 +              else return  0;
 +      }
 +}
 +
 +/* ------------------ Shading group sorting --------------------- */
 +
 +#define SORT_IMPL_LINKTYPE DRWShadingGroup
 +
 +#define SORT_IMPL_USE_THUNK
 +#define SORT_IMPL_FUNC shgroup_sort_fn_r
 +#include "../../blenlib/intern/list_sort_impl.h"
 +#undef SORT_IMPL_FUNC
 +#undef SORT_IMPL_USE_THUNK
 +
 +#undef SORT_IMPL_LINKTYPE
 +
 +/**
 + * Sort Shading groups by decreasing Z of their first draw call.
 + * This is usefull for order dependant effect such as transparency.
 + **/
 +void DRW_pass_sort_shgroup_z(DRWPass *pass)
 +{
 +      RegionView3D *rv3d = DST.draw_ctx.rv3d;
 +
 +      float (*viewinv)[4];
 +      viewinv = (viewport_matrix_override.override[DRW_MAT_VIEWINV])
 +                ? viewport_matrix_override.mat[DRW_MAT_VIEWINV] : rv3d->viewinv;
 +
 +      ZSortData zsortdata = {viewinv[2], viewinv[3]};
 +
 +      if (pass->shgroups && pass->shgroups->next) {
 +              pass->shgroups = shgroup_sort_fn_r(pass->shgroups, pass_shgroup_dist_sort, &zsortdata);
 +
 +              /* Find the next last */
 +              DRWShadingGroup *last = pass->shgroups;
 +              while ((last = last->next)) {
 +                      /* Do nothing */
 +              }
 +              pass->shgroups_last = last;
 +      }
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Draw (DRW_draw)
 + * \{ */
 +
 +static void drw_state_set(DRWState state)
 +{
 +      if (DST.state == state) {
 +              return;
 +      }
 +
 +
 +#define CHANGED_TO(f) \
 +      ((DST.state & (f)) ? \
 +              ((state & (f)) ?  0 : -1) : \
 +              ((state & (f)) ?  1 :  0))
 +
 +#define CHANGED_ANY(f) \
 +      ((DST.state & (f)) != (state & (f)))
 +
 +#define CHANGED_ANY_STORE_VAR(f, enabled) \
 +      ((DST.state & (f)) != (enabled = (state & (f))))
 +
 +      /* Depth Write */
 +      {
 +              int test;
 +              if ((test = CHANGED_TO(DRW_STATE_WRITE_DEPTH))) {
 +                      if (test == 1) {
 +                              glDepthMask(GL_TRUE);
 +                      }
 +                      else {
 +                              glDepthMask(GL_FALSE);
 +                      }
 +              }
 +      }
 +
 +      /* Color Write */
 +      {
 +              int test;
 +              if ((test = CHANGED_TO(DRW_STATE_WRITE_COLOR))) {
 +                      if (test == 1) {
 +                              glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 +                      }
 +                      else {
 +                              glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
 +                      }
 +              }
 +      }
 +
 +      /* Cull */
 +      {
 +              DRWState test;
 +              if (CHANGED_ANY_STORE_VAR(
 +                      DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT,
 +                      test))
 +              {
 +                      if (test) {
 +                              glEnable(GL_CULL_FACE);
 +
 +                              if ((state & DRW_STATE_CULL_BACK) != 0) {
 +                                      glCullFace(GL_BACK);
 +                              }
 +                              else if ((state & DRW_STATE_CULL_FRONT) != 0) {
 +                                      glCullFace(GL_FRONT);
 +                              }
 +                              else {
 +                                      BLI_assert(0);
 +                              }
 +                      }
 +                      else {
 +                              glDisable(GL_CULL_FACE);
 +                      }
 +              }
 +      }
 +
 +      /* Depth Test */
 +      {
 +              DRWState test;
 +              if (CHANGED_ANY_STORE_VAR(
 +                      DRW_STATE_DEPTH_LESS | DRW_STATE_DEPTH_EQUAL | DRW_STATE_DEPTH_GREATER | DRW_STATE_DEPTH_ALWAYS,
 +                      test))
 +              {
 +                      if (test) {
 +                              glEnable(GL_DEPTH_TEST);
 +
 +                              if (state & DRW_STATE_DEPTH_LESS) {
 +                                      glDepthFunc(GL_LEQUAL);
 +                              }
 +                              else if (state & DRW_STATE_DEPTH_EQUAL) {
 +                                      glDepthFunc(GL_EQUAL);
 +                              }
 +                              else if (state & DRW_STATE_DEPTH_GREATER) {
 +                                      glDepthFunc(GL_GREATER);
 +                              }
 +                              else if (state & DRW_STATE_DEPTH_ALWAYS) {
 +                                      glDepthFunc(GL_ALWAYS);
 +                              }
 +                              else {
 +                                      BLI_assert(0);
 +                              }
 +                      }
 +                      else {
 +                              glDisable(GL_DEPTH_TEST);
 +                      }
 +              }
 +      }
 +
 +      /* Wire Width */
 +      {
 +              if (CHANGED_ANY(DRW_STATE_WIRE | DRW_STATE_WIRE_LARGE)) {
 +                      if ((state & DRW_STATE_WIRE) != 0) {
 +                              glLineWidth(1.0f);
 +                      }
 +                      else if ((state & DRW_STATE_WIRE_LARGE) != 0) {
 +                              glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f);
 +                      }
 +                      else {
 +                              /* do nothing */
 +                      }
 +              }
 +      }
 +
 +      /* Points Size */
 +      {
 +              int test;
 +              if ((test = CHANGED_TO(DRW_STATE_POINT))) {
 +                      if (test == 1) {
 +                              GPU_enable_program_point_size();
 +                              glPointSize(5.0f);
 +                      }
 +                      else {
 +                              GPU_disable_program_point_size();
 +                      }
 +              }
 +      }
 +
 +      /* Blending (all buffer) */
 +      {
 +              int test;
 +              if (CHANGED_ANY_STORE_VAR(
 +                      DRW_STATE_BLEND | DRW_STATE_ADDITIVE | DRW_STATE_MULTIPLY | DRW_STATE_TRANSMISSION |
 +                      DRW_STATE_ADDITIVE_FULL,
 +                      test))
 +              {
 +                      if (test) {
 +                              glEnable(GL_BLEND);
 +
 +                              if ((state & DRW_STATE_BLEND) != 0) {
 +                                      glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, /* RGB */
 +                                                          GL_ONE, GL_ONE_MINUS_SRC_ALPHA); /* Alpha */
 +                              }
 +                              else if ((state & DRW_STATE_MULTIPLY) != 0) {
 +                                      glBlendFunc(GL_DST_COLOR, GL_ZERO);
 +                              }
 +                              else if ((state & DRW_STATE_TRANSMISSION) != 0) {
 +                                      glBlendFunc(GL_ONE, GL_SRC_ALPHA);
 +                              }
 +                              else if ((state & DRW_STATE_ADDITIVE) != 0) {
 +                                      /* Do not let alpha accumulate but premult the source RGB by it. */
 +                                      glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, /* RGB */
 +                                                          GL_ZERO, GL_ONE); /* Alpha */
 +                              }
 +                              else if ((state & DRW_STATE_ADDITIVE_FULL) != 0) {
 +                                      /* Let alpha accumulate. */
 +                                      glBlendFunc(GL_ONE, GL_ONE);
 +                              }
 +                              else {
 +                                      BLI_assert(0);
 +                              }
 +                      }
 +                      else {
 +                              glDisable(GL_BLEND);
 +                      }
 +              }
 +      }
 +
 +      /* Clip Planes */
 +      {
 +              int test;
 +              if ((test = CHANGED_TO(DRW_STATE_CLIP_PLANES))) {
 +                      if (test == 1) {
 +                              for (int i = 0; i < DST.num_clip_planes; ++i) {
 +                                      glEnable(GL_CLIP_DISTANCE0 + i);
 +                              }
 +                      }
 +                      else {
 +                              for (int i = 0; i < MAX_CLIP_PLANES; ++i) {
 +                                      glDisable(GL_CLIP_DISTANCE0 + i);
 +                              }
 +                      }
 +              }
 +      }
 +
 +      /* Line Stipple */
 +      {
 +              int test;
 +              if (CHANGED_ANY_STORE_VAR(
 +                      DRW_STATE_STIPPLE_2 | DRW_STATE_STIPPLE_3 | DRW_STATE_STIPPLE_4,
 +                      test))
 +              {
 +                      if (test) {
 +                              if ((state & DRW_STATE_STIPPLE_2) != 0) {
 +                                      setlinestyle(2);
 +                              }
 +                              else if ((state & DRW_STATE_STIPPLE_3) != 0) {
 +                                      setlinestyle(3);
 +                              }
 +                              else if ((state & DRW_STATE_STIPPLE_4) != 0) {
 +                                      setlinestyle(4);
 +                              }
 +                              else {
 +                                      BLI_assert(0);
 +                              }
 +                      }
 +                      else {
 +                              setlinestyle(0);
 +                      }
 +              }
 +      }
 +
 +      /* Stencil */
 +      {
 +              DRWState test;
 +              if (CHANGED_ANY_STORE_VAR(
 +                      DRW_STATE_WRITE_STENCIL |
 +                      DRW_STATE_STENCIL_EQUAL,
 +                      test))
 +              {
 +                      if (test) {
 +                              glEnable(GL_STENCIL_TEST);
 +
 +                              /* Stencil Write */
 +                              if ((state & DRW_STATE_WRITE_STENCIL) != 0) {
 +                                      glStencilMask(0xFF);
 +                                      glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
 +                              }
 +                              /* Stencil Test */
 +                              else if ((state & DRW_STATE_STENCIL_EQUAL) != 0) {
 +                                      glStencilMask(0x00); /* disable write */
 +                                      DST.stencil_mask = 0;
 +                              }
 +                              else {
 +                                      BLI_assert(0);
 +                              }
 +                      }
 +                      else {
 +                              /* disable write & test */
 +                              DST.stencil_mask = 0;
 +                              glStencilMask(0x00);
 +                              glStencilFunc(GL_ALWAYS, 1, 0xFF);
 +                              glDisable(GL_STENCIL_TEST);
 +                      }
 +              }
 +      }
 +
 +#undef CHANGED_TO
 +#undef CHANGED_ANY
 +#undef CHANGED_ANY_STORE_VAR
 +
 +      DST.state = state;
 +}
 +
 +static void drw_stencil_set(unsigned int mask)
 +{
 +      if (DST.stencil_mask != mask) {
 +              /* Stencil Write */
 +              if ((DST.state & DRW_STATE_WRITE_STENCIL) != 0) {
 +                      glStencilFunc(GL_ALWAYS, mask, 0xFF);
 +                      DST.stencil_mask = mask;
 +              }
 +              /* Stencil Test */
 +              else if ((DST.state & DRW_STATE_STENCIL_EQUAL) != 0) {
 +                      glStencilFunc(GL_EQUAL, mask, 0xFF);
 +                      DST.stencil_mask = mask;
 +              }
 +      }
 +}
 +
 +typedef struct DRWBoundTexture {
 +      struct DRWBoundTexture *next, *prev;
 +      GPUTexture *tex;
 +} DRWBoundTexture;
 +
 +static void draw_geometry_prepare(
 +        DRWShadingGroup *shgroup, const float (*obmat)[4], const float *texcoloc, const float *texcosize)
 +{
 +      RegionView3D *rv3d = DST.draw_ctx.rv3d;
 +      DRWInterface *interface = &shgroup->interface;
 +
 +      float mvp[4][4], mv[4][4], mi[4][4], mvi[4][4], pi[4][4], n[3][3], wn[3][3];
 +      float orcofacs[2][3] = {{0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}};
 +      float eye[3] = { 0.0f, 0.0f, 1.0f }; /* looking into the screen */
 +      float viewcamtexcofac[4] = { 1.0f, 1.0f, 0.0f, 0.0f };
 +
 +      if (rv3d != NULL) {
 +              copy_v4_v4(viewcamtexcofac, rv3d->viewcamtexcofac);
 +      }
 +
 +      bool do_pi = (interface->projectioninverse != -1);
 +      bool do_mvp = (interface->modelviewprojection != -1);
 +      bool do_mi = (interface->modelinverse != -1);
 +      bool do_mv = (interface->modelview != -1);
 +      bool do_mvi = (interface->modelviewinverse != -1);
 +      bool do_n = (interface->normal != -1);
 +      bool do_wn = (interface->worldnormal != -1);
 +      bool do_eye = (interface->eye != -1);
 +      bool do_orco = (interface->orcotexfac != -1) && (texcoloc != NULL) && (texcosize != NULL);
 +
 +      /* Matrix override */
 +      float (*persmat)[4];
 +      float (*persinv)[4];
 +      float (*viewmat)[4];
 +      float (*viewinv)[4];
 +      float (*winmat)[4];
 +      float (*wininv)[4];
 +
 +      persmat = (viewport_matrix_override.override[DRW_MAT_PERS])
 +                ? viewport_matrix_override.mat[DRW_MAT_PERS] : rv3d->persmat;
 +      persinv = (viewport_matrix_override.override[DRW_MAT_PERSINV])
 +                ? viewport_matrix_override.mat[DRW_MAT_PERSINV] : rv3d->persinv;
 +      viewmat = (viewport_matrix_override.override[DRW_MAT_VIEW])
 +                ? viewport_matrix_override.mat[DRW_MAT_VIEW] : rv3d->viewmat;
 +      viewinv = (viewport_matrix_override.override[DRW_MAT_VIEWINV])
 +                ? viewport_matrix_override.mat[DRW_MAT_VIEWINV] : rv3d->viewinv;
 +      winmat = (viewport_matrix_override.override[DRW_MAT_WIN])
 +                ? viewport_matrix_override.mat[DRW_MAT_WIN] : rv3d->winmat;
 +      wininv = viewport_matrix_override.mat[DRW_MAT_WININV];
 +
 +      if (do_pi) {
 +              if (!viewport_matrix_override.override[DRW_MAT_WININV]) {
 +                      invert_m4_m4(pi, winmat);
 +                      wininv = pi;
 +              }
 +      }
 +      if (do_mi) {
 +              invert_m4_m4(mi, obmat);
 +      }
 +      if (do_mvp) {
 +              mul_m4_m4m4(mvp, persmat, obmat);
 +      }
 +      if (do_mv || do_mvi || do_n || do_eye) {
 +              mul_m4_m4m4(mv, viewmat, obmat);
 +      }
 +      if (do_mvi) {
 +              invert_m4_m4(mvi, mv);
 +      }
 +      if (do_n || do_eye) {
 +              copy_m3_m4(n, mv);
 +              invert_m3(n);
 +              transpose_m3(n);
 +      }
 +      if (do_wn) {
 +              copy_m3_m4(wn, obmat);
 +              invert_m3(wn);
 +              transpose_m3(wn);
 +      }
 +      if (do_eye) {
 +              /* Used by orthographic wires */
 +              float tmp[3][3];
 +              invert_m3_m3(tmp, n);
 +              /* set eye vector, transformed to object coords */
 +              mul_m3_v3(tmp, eye);
 +      }
 +      if (do_orco) {
 +              mul_v3_v3fl(orcofacs[1], texcosize, 2.0f);
 +              invert_v3(orcofacs[1]);
 +              sub_v3_v3v3(orcofacs[0], texcoloc, texcosize);
 +              negate_v3(orcofacs[0]);
 +              mul_v3_v3(orcofacs[0], orcofacs[1]); /* result in a nice MADD in the shader */
 +      }
 +
 +      /* Should be really simple */
 +      /* step 1 : bind object dependent matrices */
 +      /* TODO : Some of these are not object dependant.
 +       * They should be grouped inside a UBO updated once per redraw.
 +       * The rest can also go into a UBO to reduce API calls. */
 +      GPU_shader_uniform_vector(shgroup->shader, interface->model, 16, 1, (float *)obmat);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->modelinverse, 16, 1, (float *)mi);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->modelviewprojection, 16, 1, (float *)mvp);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->viewinverse, 16, 1, (float *)viewinv);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->viewprojection, 16, 1, (float *)persmat);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->viewprojectioninverse, 16, 1, (float *)persinv);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->projection, 16, 1, (float *)winmat);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->projectioninverse, 16, 1, (float *)wininv);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->view, 16, 1, (float *)viewmat);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->modelview, 16, 1, (float *)mv);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->modelviewinverse, 16, 1, (float *)mvi);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->normal, 9, 1, (float *)n);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->worldnormal, 9, 1, (float *)wn);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->camtexfac, 4, 1, (float *)viewcamtexcofac);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->orcotexfac, 3, 2, (float *)orcofacs);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->eye, 3, 1, (float *)eye);
 +      GPU_shader_uniform_vector(shgroup->shader, interface->clipplanes, 4, DST.num_clip_planes, (float *)DST.clip_planes_eq);
 +}
 +
 +static void draw_geometry_execute_ex(
 +        DRWShadingGroup *shgroup, Gwn_Batch *geom, unsigned int start, unsigned int count)
 +{
 +      /* Special case: empty drawcall, placement is done via shader, don't bind anything. */
 +      if (geom == NULL) {
 +              BLI_assert(shgroup->type == DRW_SHG_TRIANGLE_BATCH); /* Add other type if needed. */
 +              /* Shader is already bound. */
 +              Gwn_Batch *batch = DRW_cache_fullscreen_quad_get();
 +              GWN_batch_draw_procedural(batch, GWN_PRIM_TRIS, count);
 +              return;
 +      }
 +
 +      /* step 2 : bind vertex array & draw */
 +      GWN_batch_program_set(geom, GPU_shader_get_program(shgroup->shader), GPU_shader_get_interface(shgroup->shader));
 +      if (ELEM(shgroup->type, DRW_SHG_INSTANCE, DRW_SHG_INSTANCE_EXTERNAL)) {
 +              GWN_batch_draw_stupid_instanced(geom, shgroup->instancing_geom, start, count);
 +      }
 +      else {
 +              GWN_batch_draw_stupid(geom, start, count);
 +      }
 +      /* XXX this just tells gawain we are done with the shader.
 +       * This does not unbind the shader. */
 +      GWN_batch_program_unset(geom);
 +}
 +
 +static void draw_geometry_execute(DRWShadingGroup *shgroup, Gwn_Batch *geom)
 +{
 +      draw_geometry_execute_ex(shgroup, geom, 0, 0);
 +}
 +
 +static void draw_geometry(
 +        DRWShadingGroup *shgroup, Gwn_Batch *geom, const float (*obmat)[4], ID *ob_data,
 +        unsigned int start, unsigned int count)
 +{
 +      float *texcoloc = NULL;
 +      float *texcosize = NULL;
 +
 +      if (ob_data != NULL) {
 +              switch (GS(ob_data->name)) {
 +                      case ID_ME:
 +                              BKE_mesh_texspace_get_reference((Mesh *)ob_data, NULL, &texcoloc, NULL, &texcosize);
 +                              break;
 +                      case ID_CU:
 +                      {
 +                              Curve *cu = (Curve *)ob_data;
 +                              if (cu->bb == NULL || (cu->bb->flag & BOUNDBOX_DIRTY)) {
 +                                      BKE_curve_texspace_calc(cu);
 +                              }
 +                              texcoloc = cu->loc;
 +                              texcosize = cu->size;
 +                              break;
 +                      }
 +                      case ID_MB:
 +                      {
 +                              MetaBall *mb = (MetaBall *)ob_data;
 +                              texcoloc = mb->loc;
 +                              texcosize = mb->size;
 +                              break;
 +                      }
 +                      default:
 +                              break;
 +              }
 +      }
 +
 +      draw_geometry_prepare(shgroup, obmat, texcoloc, texcosize);
 +
 +      draw_geometry_execute_ex(shgroup, geom, start, count);
 +}
 +
 +static void bind_texture(GPUTexture *tex)
 +{
 +      int bind_num = GPU_texture_bound_number(tex);
 +      if (bind_num == -1) {
 +              for (int i = 0; i < GPU_max_textures(); ++i) {
 +                      RST.bind_tex_inc = (RST.bind_tex_inc + 1) % GPU_max_textures();
 +                      if (RST.bound_tex_slots[RST.bind_tex_inc] == false) {
 +                              if (RST.bound_texs[RST.bind_tex_inc] != NULL) {
 +                                      GPU_texture_unbind(RST.bound_texs[RST.bind_tex_inc]);
 +                              }
 +                              GPU_texture_bind(tex, RST.bind_tex_inc);
 +                              RST.bound_texs[RST.bind_tex_inc] = tex;
 +                              RST.bound_tex_slots[RST.bind_tex_inc] = true;
 +                              // printf("Binds Texture %d %p\n", RST.bind_tex_inc, tex);
 +                              return;
 +                      }
 +              }
 +
 +              printf("Not enough texture slots! Reduce number of textures used by your shader.\n");
 +      }
 +      RST.bound_tex_slots[bind_num] = true;
 +}
 +
 +static void bind_ubo(GPUUniformBuffer *ubo)
 +{
 +      if (RST.bind_ubo_inc < GPU_max_ubo_binds()) {
 +              GPU_uniformbuffer_bind(ubo, RST.bind_ubo_inc);
 +              RST.bind_ubo_inc++;
 +      }
 +      else {
 +              /* This is not depending on user input.
 +               * It is our responsability to make sure there enough slots. */
 +              BLI_assert(0 && "Not enough ubo slots! This should not happen!\n");
 +
 +              /* printf so user can report bad behaviour */
 +              printf("Not enough ubo slots! This should not happen!\n");
 +      }
 +}
 +
 +static void release_texture_slots(void)
 +{
 +      memset(RST.bound_tex_slots, 0x0, sizeof(bool) * GPU_max_textures());
 +}
 +
 +static void release_ubo_slots(void)
 +{
 +      RST.bind_ubo_inc = 0;
 +}
 +
 +static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
 +{
 +      BLI_assert(shgroup->shader);
 +
 +      DRWInterface *interface = &shgroup->interface;
 +      GPUTexture *tex;
 +      GPUUniformBuffer *ubo;
 +      int val;
 +      float fval;
 +
 +      if (DST.shader != shgroup->shader) {
 +              if (DST.shader) GPU_shader_unbind();
 +              GPU_shader_bind(shgroup->shader);
 +              DST.shader = shgroup->shader;
 +      }
 +
 +      release_texture_slots();
 +      release_ubo_slots();
 +
 +      drw_state_set((pass_state & shgroup->state_extra_disable) | shgroup->state_extra);
 +      drw_stencil_set(shgroup->stencil_mask);
 +
 +      /* Binding Uniform */
 +      /* Don't check anything, Interface should already contain the least uniform as possible */
 +      for (DRWUniform *uni = interface->uniforms; uni; uni = uni->next) {
 +              switch (uni->type) {
 +                      case DRW_UNIFORM_SHORT_TO_INT:
 +                              val = (int)*((short *)uni->value);
 +                              GPU_shader_uniform_vector_int(
 +                                      shgroup->shader, uni->location, uni->length, uni->arraysize, (int *)&val);
 +                              break;
 +                      case DRW_UNIFORM_SHORT_TO_FLOAT:
 +                              fval = (float)*((short *)uni->value);
 +                              GPU_shader_uniform_vector(
 +                                      shgroup->shader, uni->location, uni->length, uni->arraysize, (float *)&fval);
 +                              break;
 +                      case DRW_UNIFORM_BOOL:
 +                      case DRW_UNIFORM_INT:
 +                              GPU_shader_uniform_vector_int(
 +                                      shgroup->shader, uni->location, uni->length, uni->arraysize, (int *)uni->value);
 +                              break;
 +                      case DRW_UNIFORM_FLOAT:
 +                      case DRW_UNIFORM_MAT3:
 +                      case DRW_UNIFORM_MAT4:
 +                              GPU_shader_uniform_vector(
 +                                      shgroup->shader, uni->location, uni->length, uni->arraysize, (float *)uni->value);
 +                              break;
 +                      case DRW_UNIFORM_TEXTURE:
 +                              tex = (GPUTexture *)uni->value;
 +                              BLI_assert(tex);
 +                              bind_texture(tex);
 +                              GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
 +                              break;
 +                      case DRW_UNIFORM_BUFFER:
 +                              if (!DRW_state_is_fbo()) {
 +                                      break;
 +                              }
 +                              tex = *((GPUTexture **)uni->value);
 +                              BLI_assert(tex);
 +                              bind_texture(tex);
 +                              GPU_shader_uniform_texture(shgroup->shader, uni->location, tex);
 +                              break;
 +                      case DRW_UNIFORM_BLOCK:
 +                              ubo = (GPUUniformBuffer *)uni->value;
 +                              bind_ubo(ubo);
 +                              GPU_shader_uniform_buffer(shgroup->shader, uni->location, ubo);
 +                              break;
 +              }
 +      }
 +
 +#ifdef USE_GPU_SELECT
 +      /* use the first item because of selection we only ever add one */
 +#  define GPU_SELECT_LOAD_IF_PICKSEL(_call) \
 +      if ((G.f & G_PICKSEL) && (_call)) { \
 +              GPU_select_load_id((_call)->head.select_id); \
 +      } ((void)0)
 +
 +#  define GPU_SELECT_LOAD_IF_PICKSEL_LIST(_shgroup, _start, _count)  \
 +      _start = 0;                                                      \
 +      _count = _shgroup->interface.instance_count;                     \
 +      int *select_id = NULL;                                           \
 +      if (G.f & G_PICKSEL) {                                           \
 +              if (_shgroup->interface.override_selectid == -1) {                        \
 +                      select_id = DRW_instance_data_get(_shgroup->interface.inst_selectid); \
 +                      switch (_shgroup->type) {                                             \
 +                              case DRW_SHG_TRIANGLE_BATCH: _count = 3; break;                   \
 +                              case DRW_SHG_LINE_BATCH: _count = 2; break;                       \
 +                              default: _count = 1; break;                                       \
 +                      }                                                                     \
 +              }                                                                         \
 +              else {                                                                    \
 +                      GPU_select_load_id(_shgroup->interface.override_selectid);            \
 +              }                                                                         \
 +      }                                                                \
 +      while (_start < _shgroup->interface.instance_count) {            \
 +              if (select_id) {                                             \
 +                      GPU_select_load_id(select_id[_start]);                   \
 +              }
 +
 +# define GPU_SELECT_LOAD_IF_PICKSEL_LIST_END(_start, _count) \
 +              _start += _count;                                    \
 +      }
 +
 +#else
 +#  define GPU_SELECT_LOAD_IF_PICKSEL(call)
 +#  define GPU_SELECT_LOAD_IF_PICKSEL_LIST_END(start, count)
 +#  define GPU_SELECT_LOAD_IF_PICKSEL_LIST(_shgroup, _start, _count) \
 +      _start = 0;                                                     \
 +      _count = _shgroup->interface.instance_count;
 +
 +#endif
 +
 +      /* Rendering Calls */
 +      if (!ELEM(shgroup->type, DRW_SHG_NORMAL)) {
 +              /* Replacing multiple calls with only one */
 +              float obmat[4][4];
 +              unit_m4(obmat);
 +
 +              if (ELEM(shgroup->type, DRW_SHG_INSTANCE, DRW_SHG_INSTANCE_EXTERNAL)) {
 +                      if (shgroup->type == DRW_SHG_INSTANCE_EXTERNAL) {
 +                              if (shgroup->instancing_geom != NULL) {
 +                                      GPU_SELECT_LOAD_IF_PICKSEL((DRWCall *)shgroup->calls_first);
 +                                      draw_geometry(shgroup, shgroup->instance_geom, obmat, shgroup->instance_data, 0, 0);
 +                              }
 +                      }
 +                      else {
 +                              if (shgroup->interface.instance_count > 0) {
 +                                      unsigned int count, start;
 +                                      GPU_SELECT_LOAD_IF_PICKSEL_LIST(shgroup, start, count)
 +                                      {
 +                                              draw_geometry(shgroup, shgroup->instance_geom, obmat, shgroup->instance_data, start, count);
 +                                      }
 +                                      GPU_SELECT_LOAD_IF_PICKSEL_LIST_END(start, count)
 +                              }
 +                      }
 +              }
 +              else {
 +                      /* Some dynamic batch can have no geom (no call to aggregate) */
 +                      if (shgroup->interface.instance_count > 0) {
 +                              unsigned int count, start;
 +                              GPU_SELECT_LOAD_IF_PICKSEL_LIST(shgroup, start, count)
 +                              {
 +                                      draw_geometry(shgroup, shgroup->batch_geom, obmat, NULL, start, count);
 +                              }
 +                              GPU_SELECT_LOAD_IF_PICKSEL_LIST_END(start, count)
 +                      }
 +              }
 +      }
 +      else {
 +              for (DRWCall *call = shgroup->calls_first; call; call = call->head.prev) {
 +                      bool neg_scale = is_negative_m4(call->obmat);
 +
 +                      /* Negative scale objects */
 +                      if (neg_scale) {
 +                              glFrontFace(DST.backface);
 +                      }
 +
 +                      GPU_SELECT_LOAD_IF_PICKSEL(call);
 +
 +                      if (call->head.type == DRW_CALL_SINGLE) {
 +                              draw_geometry(shgroup, call->geometry, call->obmat, call->ob_data, 0, 0);
 +                      }
 +                      else {
 +                              BLI_assert(call->head.type == DRW_CALL_GENERATE);
 +                              DRWCallGenerate *callgen = ((DRWCallGenerate *)call);
 +                              draw_geometry_prepare(shgroup, callgen->obmat, NULL, NULL);
 +                              callgen->geometry_fn(shgroup, draw_geometry_execute, callgen->user_data);
 +                      }
 +
 +                      /* Reset state */
 +                      if (neg_scale) {
 +                              glFrontFace(DST.frontface);
 +                      }
 +              }
 +      }
 +
 +      /* TODO: remove, (currently causes alpha issue with sculpt, need to investigate) */
 +      DRW_state_reset();
 +}
 +
 +static void drw_draw_pass_ex(DRWPass *pass, DRWShadingGroup *start_group, DRWShadingGroup *end_group)
 +{
 +      /* Start fresh */
 +      DST.shader = NULL;
 +
 +      BLI_assert(DST.buffer_finish_called && "DRW_render_instance_buffer_finish had not been called before drawing");
 +
 +      drw_state_set(pass->state);
 +
 +      DRW_stats_query_start(pass->name);
 +
 +      for (DRWShadingGroup *shgroup = start_group; shgroup; shgroup = shgroup->next) {
 +              draw_shgroup(shgroup, pass->state);
 +              /* break if upper limit */
 +              if (shgroup == end_group) {
 +                      break;
 +              }
 +      }
 +
 +      /* Clear Bound textures */
 +      for (int i = 0; i < GPU_max_textures(); i++) {
 +              if (RST.bound_texs[i] != NULL) {
 +                      GPU_texture_unbind(RST.bound_texs[i]);
 +                      RST.bound_texs[i] = NULL;
 +              }
 +      }
 +
 +      if (DST.shader) {
 +              GPU_shader_unbind();
 +              DST.shader = NULL;
 +      }
 +
 +      DRW_stats_query_end();
 +}
 +
 +void DRW_draw_pass(DRWPass *pass)
 +{
 +      drw_draw_pass_ex(pass, pass->shgroups, pass->shgroups_last);
 +}
 +
 +/* Draw only a subset of shgroups. Used in special situations as grease pencil strokes */
 +void DRW_draw_pass_subset(DRWPass *pass, DRWShadingGroup *start_group, DRWShadingGroup *end_group)
 +{
 +      drw_draw_pass_ex(pass, start_group, end_group);
 +}
 +
 +void DRW_draw_callbacks_pre_scene(void)
 +{
 +      RegionView3D *rv3d = DST.draw_ctx.rv3d;
 +
 +      gpuLoadProjectionMatrix(rv3d->winmat);
 +      gpuLoadMatrix(rv3d->viewmat);
 +}
 +
 +void DRW_draw_callbacks_post_scene(void)
 +{
 +      RegionView3D *rv3d = DST.draw_ctx.rv3d;
 +
 +      gpuLoadProjectionMatrix(rv3d->winmat);
 +      gpuLoadMatrix(rv3d->viewmat);
 +}
 +
 +/* Reset state to not interfer with other UI drawcall */
 +void DRW_state_reset_ex(DRWState state)
 +{
 +      DST.state = ~state;
 +      drw_state_set(state);
 +}
 +
 +void DRW_state_reset(void)
 +{
 +      /* Reset blending function */
 +      glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
 +
 +      DRW_state_reset_ex(DRW_STATE_DEFAULT);
 +}
 +
 +/* NOTE : Make sure to reset after use! */
 +void DRW_state_invert_facing(void)
 +{
 +      SWAP(GLenum, DST.backface, DST.frontface);
 +      glFrontFace(DST.frontface);
 +}
 +
 +/**
 + * This only works if DRWPasses have been tagged with DRW_STATE_CLIP_PLANES,
 + * and if the shaders have support for it (see usage of gl_ClipDistance).
 + * Be sure to call DRW_state_clip_planes_reset() after you finish drawing.
 + **/
 +void DRW_state_clip_planes_add(float plane_eq[4])
 +{
 +      copy_v4_v4(DST.clip_planes_eq[DST.num_clip_planes++], plane_eq);
 +}
 +
 +void DRW_state_clip_planes_reset(void)
 +{
 +      DST.num_clip_planes = 0;
 +}
 +
 +/** \} */
 +
 +
 +struct DRWTextStore *DRW_text_cache_ensure(void)
 +{
 +      BLI_assert(DST.text_store_p);
 +      if (*DST.text_store_p == NULL) {
 +              *DST.text_store_p = DRW_text_cache_create();
 +      }
 +      return *DST.text_store_p;
 +}
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Settings
 + * \{ */
 +
 +bool DRW_object_is_renderable(Object *ob)
 +{
 +      BLI_assert(BKE_object_is_visible(ob, OB_VISIBILITY_CHECK_UNKNOWN_RENDER_MODE));
 +
 +      if (ob->type == OB_MESH) {
 +              if (ob == DST.draw_ctx.object_edit) {
 +                      IDProperty *props = BKE_layer_collection_engine_evaluated_get(ob, COLLECTION_MODE_EDIT, "");
 +                      bool do_show_occlude_wire = BKE_collection_engine_property_value_get_bool(props, "show_occlude_wire");
 +                      if (do_show_occlude_wire) {
 +                              return false;
 +                      }
 +                      bool do_show_weight = BKE_collection_engine_property_value_get_bool(props, "show_weight");
 +                      if (do_show_weight) {
 +                              return false;
 +                      }
 +              }
 +      }
 +
 +      return true;
 +}
 +
 +/**
 + * Return whether this object is visible depending if
 + * we are rendering or drawing in the viewport.
 + */
 +bool DRW_check_object_visible_within_active_context(Object *ob)
 +{
 +      const eObjectVisibilityCheck mode = DRW_state_is_scene_render() ?
 +                                           OB_VISIBILITY_CHECK_FOR_RENDER :
 +                                           OB_VISIBILITY_CHECK_FOR_VIEWPORT;
 +      return BKE_object_is_visible(ob, mode);
 +}
 +
 +bool DRW_object_is_flat_normal(const Object *ob)
 +{
 +      if (ob->type == OB_MESH) {
 +              const Mesh *me = ob->data;
 +              if (me->mpoly && me->mpoly[0].flag & ME_SMOOTH) {
 +                      return false;
 +              }
 +      }
 +      return true;
 +}
 +
 +/**
 + * Return true if the object has its own draw mode.
 + * Caller must check this is active */
 +int DRW_object_is_mode_shade(const Object *ob)
 +{
 +      BLI_assert(ob == DST.draw_ctx.obact);
 +      if ((DST.draw_ctx.object_mode & OB_MODE_EDIT) == 0) {
 +              if (DST.draw_ctx.object_mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) {
 +                      if ((DST.draw_ctx.v3d->flag2 & V3D_SHOW_MODE_SHADE_OVERRIDE) == 0) {
 +                              return true;
 +                      }
 +                      else {
 +                              return false;
 +                      }
 +              }
 +      }
 +      return -1;
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Framebuffers (DRW_framebuffer)
 + * \{ */
 +
 +static GPUTextureFormat convert_tex_format(
 +        int fbo_format,
 +        int *r_channels, bool *r_is_depth)
 +{
 +      *r_is_depth = ELEM(fbo_format, DRW_TEX_DEPTH_16, DRW_TEX_DEPTH_24, DRW_TEX_DEPTH_24_STENCIL_8);
 +
 +      switch (fbo_format) {
 +              case DRW_TEX_R_16:     *r_channels = 1; return GPU_R16F;
 +              case DRW_TEX_R_32:     *r_channels = 1; return GPU_R32F;
 +              case DRW_TEX_RG_8:     *r_channels = 2; return GPU_RG8;
 +              case DRW_TEX_RG_16:    *r_channels = 2; return GPU_RG16F;
 +              case DRW_TEX_RG_16I:   *r_channels = 2; return GPU_RG16I;
 +              case DRW_TEX_RG_32:    *r_channels = 2; return GPU_RG32F;
 +              case DRW_TEX_RGBA_8:   *r_channels = 4; return GPU_RGBA8;
 +              case DRW_TEX_RGBA_16:  *r_channels = 4; return GPU_RGBA16F;
 +              case DRW_TEX_RGBA_32:  *r_channels = 4; return GPU_RGBA32F;
 +              case DRW_TEX_DEPTH_16: *r_channels = 1; return GPU_DEPTH_COMPONENT16;
 +              case DRW_TEX_DEPTH_24: *r_channels = 1; return GPU_DEPTH_COMPONENT24;
 +              case DRW_TEX_DEPTH_24_STENCIL_8: *r_channels = 1; return GPU_DEPTH24_STENCIL8;
 +              case DRW_TEX_DEPTH_32: *r_channels = 1; return GPU_DEPTH_COMPONENT32F;
 +              case DRW_TEX_RGB_11_11_10: *r_channels = 3; return GPU_R11F_G11F_B10F;
 +              default:
 +                      BLI_assert(false && "Texture format unsupported as render target!");
 +                      *r_channels = 4; return GPU_RGBA8;
 +      }
 +}
 +
 +struct GPUFrameBuffer *DRW_framebuffer_create(void)
 +{
 +      return GPU_framebuffer_create();
 +}
 +
 +void DRW_framebuffer_init(
 +        struct GPUFrameBuffer **fb, void *engine_type, int width, int height,
 +        DRWFboTexture textures[MAX_FBO_TEX], int textures_len)
 +{
 +      BLI_assert(textures_len <= MAX_FBO_TEX);
 +      BLI_assert(width > 0 && height > 0);
 +
 +      bool create_fb = false;
 +      int color_attachment = -1;
 +
 +      if (!*fb) {
 +              *fb = GPU_framebuffer_create();
 +              create_fb = true;
 +      }
 +
 +      for (int i = 0; i < textures_len; ++i) {
 +              int channels;
 +              bool is_depth;
 +              bool create_tex = false;
 +
 +              DRWFboTexture fbotex = textures[i];
 +              bool is_temp = (fbotex.flag & DRW_TEX_TEMP) != 0;
 +
 +              GPUTextureFormat gpu_format = convert_tex_format(fbotex.format, &channels, &is_depth);
 +
 +              if (!*fbotex.tex || is_temp) {
 +                      /* Temp textures need to be queried each frame, others not. */
 +                      if (is_temp) {
 +                              *fbotex.tex = GPU_viewport_texture_pool_query(
 +                                      DST.viewport, engine_type, width, height, channels, gpu_format);
 +                      }
 +                      else {
 +                              *fbotex.tex = GPU_texture_create_2D_custom(
 +                                      width, height, channels, gpu_format, NULL, NULL);
 +                              create_tex = true;
 +                      }
 +              }
 +
 +              if (!is_depth) {
 +                      ++color_attachment;
 +              }
 +
 +              if (create_fb || create_tex) {
 +                      drw_texture_set_parameters(*fbotex.tex, fbotex.flag);
 +                      GPU_framebuffer_texture_attach(*fb, *fbotex.tex, color_attachment, 0);
 +              }
 +      }
 +
 +      if (create_fb && (textures_len > 0)) {
 +              if (!GPU_framebuffer_check_valid(*fb, NULL)) {
 +                      printf("Error invalid framebuffer\n");
 +              }
 +
 +              /* Detach temp textures */
 +              for (int i = 0; i < textures_len; ++i) {
 +                      DRWFboTexture fbotex = textures[i];
 +
 +                      if ((fbotex.flag & DRW_TEX_TEMP) != 0) {
 +                              GPU_framebuffer_texture_detach(*fbotex.tex);
 +                      }
 +              }
 +
 +              if (DST.default_framebuffer != NULL) {
 +                      GPU_framebuffer_bind(DST.default_framebuffer);
 +              }
 +      }
 +}
 +
 +void DRW_framebuffer_free(struct GPUFrameBuffer *fb)
 +{
 +      GPU_framebuffer_free(fb);
 +}
 +
 +void DRW_framebuffer_bind(struct GPUFrameBuffer *fb)
 +{
 +      GPU_framebuffer_bind(fb);
 +}
 +
 +void DRW_framebuffer_clear(bool color, bool depth, bool stencil, float clear_col[4], float clear_depth)
 +{
 +      if (color) {
 +              glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 +              glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]);
 +      }
 +      if (depth) {
 +              glDepthMask(GL_TRUE);
 +              glClearDepth(clear_depth);
 +      }
 +      if (stencil) {
 +              glStencilMask(0xFF);
 +      }
 +      glClear(((color) ? GL_COLOR_BUFFER_BIT : 0) |
 +              ((depth) ? GL_DEPTH_BUFFER_BIT : 0) |
 +              ((stencil) ? GL_STENCIL_BUFFER_BIT : 0));
 +}
 +
 +void DRW_framebuffer_read_data(int x, int y, int w, int h, int channels, int slot, float *data)
 +{
 +      GLenum type;
 +      switch (channels) {
 +              case 1: type = GL_RED; break;
 +              case 2: type = GL_RG; break;
 +              case 3: type = GL_RGB; break;
 +              case 4: type = GL_RGBA; break;
 +              default:
 +                      BLI_assert(false && "wrong number of read channels");
 +                      return;
 +      }
 +      glReadBuffer(GL_COLOR_ATTACHMENT0 + slot);
 +      glReadPixels(x, y, w, h, type, GL_FLOAT, data);
 +}
 +
 +void DRW_framebuffer_read_depth(int x, int y, int w, int h, float *data)
 +{
 +      GLenum type = GL_DEPTH_COMPONENT;
 +
 +      glReadBuffer(GL_COLOR_ATTACHMENT0); /* This is OK! */
 +      glReadPixels(x, y, w, h, type, GL_FLOAT, data);
 +}
 +
 +void DRW_framebuffer_texture_attach(struct GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip)
 +{
 +      GPU_framebuffer_texture_attach(fb, tex, slot, mip);
 +}
 +
 +void DRW_framebuffer_texture_layer_attach(struct GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int layer, int mip)
 +{
 +      GPU_framebuffer_texture_layer_attach(fb, tex, slot, layer, mip);
 +}
 +
 +void DRW_framebuffer_cubeface_attach(struct GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip)
 +{
 +      GPU_framebuffer_texture_cubeface_attach(fb, tex, slot, face, mip);
 +}
 +
 +void DRW_framebuffer_texture_detach(GPUTexture *tex)
 +{
 +      GPU_framebuffer_texture_detach(tex);
 +}
 +
 +void DRW_framebuffer_blit(struct GPUFrameBuffer *fb_read, struct GPUFrameBuffer *fb_write, bool depth, bool stencil)
 +{
 +      GPU_framebuffer_blit(fb_read, 0, fb_write, 0, depth, stencil);
 +}
 +
 +void DRW_framebuffer_recursive_downsample(
 +        struct GPUFrameBuffer *fb, struct GPUTexture *tex, int num_iter,
 +        void (*callback)(void *userData, int level), void *userData)
 +{
 +      GPU_framebuffer_recursive_downsample(fb, tex, num_iter, callback, userData);
 +}
 +
 +void DRW_framebuffer_viewport_size(struct GPUFrameBuffer *UNUSED(fb_read), int x, int y, int w, int h)
 +{
 +      glViewport(x, y, w, h);
 +}
 +
 +/* Use color management profile to draw texture to framebuffer */
 +void DRW_transform_to_display(GPUTexture *tex)
 +{
 +      drw_state_set(DRW_STATE_WRITE_COLOR);
 +
 +      Gwn_VertFormat *vert_format = immVertexFormat();
 +      unsigned int pos = GWN_vertformat_attr_add(vert_format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +      unsigned int texco = GWN_vertformat_attr_add(vert_format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +
 +      const float dither = 1.0f;
 +
 +      bool use_ocio = false;
 +
 +      /* View transform is already applied for offscreen, don't apply again, see: T52046 */
 +      if (!(DST.options.is_image_render && !DST.options.is_scene_render)) {
 +              Scene *scene = DST.draw_ctx.scene;
 +              use_ocio = IMB_colormanagement_setup_glsl_draw_from_space(
 +                      &scene->view_settings, &scene->display_settings, NULL, dither, false);
 +      }
 +
 +      if (!use_ocio) {
 +              /* View transform is already applied for offscreen, don't apply again, see: T52046 */
 +              if (DST.options.is_image_render && !DST.options.is_scene_render) {
 +                      immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
 +                      immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f);
 +              }
 +              else {
 +                      immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_LINEAR_TO_SRGB);
 +              }
 +              immUniform1i("image", 0);
 +      }
 +
 +      GPU_texture_bind(tex, 0); /* OCIO texture bind point is 0 */
 +
 +      float mat[4][4];
 +      unit_m4(mat);
 +      immUniformMatrix4fv("ModelViewProjectionMatrix", mat);
 +
 +      /* Full screen triangle */
 +      immBegin(GWN_PRIM_TRIS, 3);
 +      immAttrib2f(texco, 0.0f, 0.0f);
 +      immVertex2f(pos, -1.0f, -1.0f);
 +
 +      immAttrib2f(texco, 2.0f, 0.0f);
 +      immVertex2f(pos, 3.0f, -1.0f);
 +
 +      immAttrib2f(texco, 0.0f, 2.0f);
 +      immVertex2f(pos, -1.0f, 3.0f);
 +      immEnd();
 +
 +      GPU_texture_unbind(tex);
 +
 +      if (use_ocio) {
 +              IMB_colormanagement_finish_glsl_draw();
 +      }
 +      else {
 +              immUnbindProgram();
 +      }
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Viewport (DRW_viewport)
 + * \{ */
 +
 +static void *DRW_viewport_engine_data_ensure(void *engine_type)
 +{
 +      void *data = GPU_viewport_engine_data_get(DST.viewport, engine_type);
 +
 +      if (data == NULL) {
 +              data = GPU_viewport_engine_data_create(DST.viewport, engine_type);
 +      }
 +      return data;
 +}
 +
 +void DRW_engine_viewport_data_size_get(
 +        const void *engine_type_v,
 +        int *r_fbl_len, int *r_txl_len, int *r_psl_len, int *r_stl_len)
 +{
 +      const DrawEngineType *engine_type = engine_type_v;
 +
 +      if (r_fbl_len) {
 +              *r_fbl_len = engine_type->vedata_size->fbl_len;
 +      }
 +      if (r_txl_len) {
 +              *r_txl_len = engine_type->vedata_size->txl_len;
 +      }
 +      if (r_psl_len) {
 +              *r_psl_len = engine_type->vedata_size->psl_len;
 +      }
 +      if (r_stl_len) {
 +              *r_stl_len = engine_type->vedata_size->stl_len;
 +      }
 +}
 +
 +const float *DRW_viewport_size_get(void)
 +{
 +      return &DST.size[0];
 +}
 +
 +const float *DRW_viewport_screenvecs_get(void)
 +{
 +      return &DST.screenvecs[0][0];
 +}
 +
 +const float *DRW_viewport_pixelsize_get(void)
 +{
 +      return &DST.pixsize;
 +}
 +
 +static void drw_viewport_cache_resize(void)
 +{
 +      /* Release the memiter before clearing the mempools that references them */
 +      GPU_viewport_cache_release(DST.viewport);
 +
 +      if (DST.vmempool != NULL) {
++              BLI_mempool_clear_ex(DST.vmempool->calls, BLI_mempool_len(DST.vmempool->calls));
++              BLI_mempool_clear_ex(DST.vmempool->calls_generate, BLI_mempool_len(DST.vmempool->calls_generate));
++              BLI_mempool_clear_ex(DST.vmempool->shgroups, BLI_mempool_len(DST.vmempool->shgroups));
++              BLI_mempool_clear_ex(DST.vmempool->uniforms, BLI_mempool_len(DST.vmempool->uniforms));
++              BLI_mempool_clear_ex(DST.vmempool->passes, BLI_mempool_len(DST.vmempool->passes));
 +      }
 +
 +      DRW_instance_data_list_free_unused(DST.idatalist);
 +      DRW_instance_data_list_resize(DST.idatalist);
 +}
 +
 +
 +/* Not a viewport variable, we could split this out. */
 +static void drw_context_state_init(void)
 +{
 +      /* Edit object. */
 +      if (DST.draw_ctx.object_mode & OB_MODE_EDIT) {
 +              DST.draw_ctx.object_edit = DST.draw_ctx.obact;
 +      }
 +      else {
 +              DST.draw_ctx.object_edit = NULL;
 +      }
 +
 +      /* Pose object. */
 +      if (DST.draw_ctx.object_mode & OB_MODE_POSE) {
 +              DST.draw_ctx.object_pose = DST.draw_ctx.obact;
 +      }
 +      else if (DST.draw_ctx.object_mode & OB_MODE_WEIGHT_PAINT) {
 +              DST.draw_ctx.object_pose = BKE_object_pose_armature_get(DST.draw_ctx.obact);
 +      }
 +      else {
 +              DST.draw_ctx.object_pose = NULL;
 +      }
 +}
 +
 +/* It also stores viewport variable to an immutable place: DST
 + * This is because a cache uniform only store reference
 + * to its value. And we don't want to invalidate the cache
 + * if this value change per viewport */
 +static void drw_viewport_var_init(void)
 +{
 +      RegionView3D *rv3d = DST.draw_ctx.rv3d;
 +      /* Refresh DST.size */
 +      if (DST.viewport) {
 +              int size[2];
 +              GPU_viewport_size_get(DST.viewport, size);
 +              DST.size[0] = size[0];
 +              DST.size[1] = size[1];
 +
 +              DefaultFramebufferList *fbl = (DefaultFramebufferList *)GPU_viewport_framebuffer_list_get(DST.viewport);
 +              DST.default_framebuffer = fbl->default_fb;
 +
 +              DST.vmempool = GPU_viewport_mempool_get(DST.viewport);
 +
 +              if (DST.vmempool->calls == NULL) {
 +                      DST.vmempool->calls = BLI_mempool_create(sizeof(DRWCall), 0, 512, 0);
 +              }
 +              if (DST.vmempool->calls_generate == NULL) {
 +                      DST.vmempool->calls_generate = BLI_mempool_create(sizeof(DRWCallGenerate), 0, 512, 0);
 +              }
 +              if (DST.vmempool->shgroups == NULL) {
 +                      DST.vmempool->shgroups = BLI_mempool_create(sizeof(DRWShadingGroup), 0, 256, 0);
 +              }
 +              if (DST.vmempool->uniforms == NULL) {
 +                      DST.vmempool->uniforms = BLI_mempool_create(sizeof(DRWUniform), 0, 512, 0);
 +              }
 +              if (DST.vmempool->passes == NULL) {
 +                      DST.vmempool->passes = BLI_mempool_create(sizeof(DRWPass), 0, 64, 0);
 +              }
 +
 +              DST.idatalist = GPU_viewport_instance_data_list_get(DST.viewport);
 +              DRW_instance_data_list_reset(DST.idatalist);
 +      }
 +      else {
 +              DST.size[0] = 0;
 +              DST.size[1] = 0;
 +
 +              DST.default_framebuffer = NULL;
 +              DST.vmempool = NULL;
 +      }
 +
 +      if (rv3d != NULL) {
 +              /* Refresh DST.screenvecs */
 +              copy_v3_v3(DST.screenvecs[0], rv3d->viewinv[0]);
 +              copy_v3_v3(DST.screenvecs[1], rv3d->viewinv[1]);
 +              normalize_v3(DST.screenvecs[0]);
 +              normalize_v3(DST.screenvecs[1]);
 +
 +              /* Refresh DST.pixelsize */
 +              DST.pixsize = rv3d->pixsize;
 +      }
 +
 +      /* Reset facing */
 +      DST.frontface = GL_CCW;
 +      DST.backface = GL_CW;
 +      glFrontFace(DST.frontface);
 +
 +      if (DST.draw_ctx.object_edit) {
 +              ED_view3d_init_mats_rv3d(DST.draw_ctx.object_edit, rv3d);
 +      }
 +
 +      /* Alloc array of texture reference. */
 +      if (RST.bound_texs == NULL) {
 +              RST.bound_texs = MEM_callocN(sizeof(GPUTexture *) * GPU_max_textures(), "Bound GPUTexture refs");
 +      }
 +      if (RST.bound_tex_slots == NULL) {
 +              RST.bound_tex_slots = MEM_callocN(sizeof(bool) * GPU_max_textures(), "Bound Texture Slots");
 +      }
 +
 +      memset(viewport_matrix_override.override, 0x0, sizeof(viewport_matrix_override.override));
 +      memset(DST.common_instance_data, 0x0, sizeof(DST.common_instance_data));
 +}
 +
 +void DRW_viewport_matrix_get(float mat[4][4], DRWViewportMatrixType type)
 +{
 +      RegionView3D *rv3d = DST.draw_ctx.rv3d;
 +      BLI_assert(type >= DRW_MAT_PERS && type <= DRW_MAT_WININV);
 +
 +      if (viewport_matrix_override.override[type]) {
 +              copy_m4_m4(mat, viewport_matrix_override.mat[type]);
 +      }
 +      else {
 +              BLI_assert(rv3d != NULL); /* Can't use this in render mode. */
 +              switch (type) {
 +                      case DRW_MAT_PERS:
 +                              copy_m4_m4(mat, rv3d->persmat);
 +                              break;
 +                      case DRW_MAT_PERSINV:
 +                              copy_m4_m4(mat, rv3d->persinv);
 +                              break;
 +                      case DRW_MAT_VIEW:
 +                              copy_m4_m4(mat, rv3d->viewmat);
 +                              break;
 +                      case DRW_MAT_VIEWINV:
 +                              copy_m4_m4(mat, rv3d->viewinv);
 +                              break;
 +                      case DRW_MAT_WIN:
 +                              copy_m4_m4(mat, rv3d->winmat);
 +                              break;
 +                      case DRW_MAT_WININV:
 +                              invert_m4_m4(mat, rv3d->winmat);
 +                              break;
 +                      default:
 +                              BLI_assert(!"Matrix type invalid");
 +                              break;
 +              }
 +      }
 +}
 +
 +void DRW_viewport_matrix_override_set(float mat[4][4], DRWViewportMatrixType type)
 +{
 +      copy_m4_m4(viewport_matrix_override.mat[type], mat);
 +      viewport_matrix_override.override[type] = true;
 +}
 +
 +void DRW_viewport_matrix_override_unset(DRWViewportMatrixType type)
 +{
 +      viewport_matrix_override.override[type] = false;
 +}
 +
 +bool DRW_viewport_is_persp_get(void)
 +{
 +      RegionView3D *rv3d = DST.draw_ctx.rv3d;
 +      if (rv3d) {
 +              return rv3d->is_persp;
 +      }
 +      else {
 +              if (viewport_matrix_override.override[DRW_MAT_WIN]) {
 +                      return viewport_matrix_override.mat[DRW_MAT_WIN][3][3] == 0.0f;
 +              }
 +      }
 +      BLI_assert(0);
 +      return false;
 +}
 +
 +DefaultFramebufferList *DRW_viewport_framebuffer_list_get(void)
 +{
 +      return GPU_viewport_framebuffer_list_get(DST.viewport);
 +}
 +
 +DefaultTextureList *DRW_viewport_texture_list_get(void)
 +{
 +      return GPU_viewport_texture_list_get(DST.viewport);
 +}
 +
 +void DRW_viewport_request_redraw(void)
 +{
 +      GPU_viewport_tag_update(DST.viewport);
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +/** \name ViewLayers (DRW_scenelayer)
 + * \{ */
 +
 +void *DRW_view_layer_engine_data_get(DrawEngineType *engine_type)
 +{
 +      for (ViewLayerEngineData *sled = DST.draw_ctx.view_layer->drawdata.first; sled; sled = sled->next) {
 +              if (sled->engine_type == engine_type) {
 +                      return sled->storage;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +void **DRW_view_layer_engine_data_ensure(DrawEngineType *engine_type, void (*callback)(void *storage))
 +{
 +      ViewLayerEngineData *sled;
 +
 +      for (sled = DST.draw_ctx.view_layer->drawdata.first; sled; sled = sled->next) {
 +              if (sled->engine_type == engine_type) {
 +                      return &sled->storage;
 +              }
 +      }
 +
 +      sled = MEM_callocN(sizeof(ViewLayerEngineData), "ViewLayerEngineData");
 +      sled->engine_type = engine_type;
 +      sled->free = callback;
 +      BLI_addtail(&DST.draw_ctx.view_layer->drawdata, sled);
 +
 +      return &sled->storage;
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Objects (DRW_object)
 + * \{ */
 +
 +ObjectEngineData *DRW_object_engine_data_get(Object *ob, DrawEngineType *engine_type)
 +{
 +      for (ObjectEngineData *oed = ob->drawdata.first; oed; oed = oed->next) {
 +              if (oed->engine_type == engine_type) {
 +                      return oed;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +ObjectEngineData *DRW_object_engine_data_ensure(
 +        Object *ob,
 +        DrawEngineType *engine_type,
 +        size_t size,
 +        ObjectEngineDataInitCb init_cb,
 +        ObjectEngineDataFreeCb free_cb)
 +{
 +      BLI_assert(size >= sizeof(ObjectEngineData));
 +      /* Try to re-use existing data. */
 +      ObjectEngineData *oed = DRW_object_engine_data_get(ob, engine_type);
 +      if (oed != NULL) {
 +              return oed;
 +      }
 +      /* Allocate new data. */
 +      if ((ob->base_flag & BASE_FROMDUPLI) != 0) {
 +              /* NOTE: data is not persistent in this case. It is reset each redraw. */
 +              BLI_assert(free_cb == NULL); /* No callback allowed. */
 +              /* Round to sizeof(float) for DRW_instance_data_request(). */
 +              const size_t t = sizeof(float) - 1;
 +              size = (size + t) & ~t;
 +              size_t fsize = size / sizeof(float);
 +              if (DST.common_instance_data[fsize] == NULL) {
 +                      DST.common_instance_data[fsize] = DRW_instance_data_request(DST.idatalist, fsize, 16);
 +              }
 +              oed = (ObjectEngineData *)DRW_instance_data_next(DST.common_instance_data[fsize]);
 +              memset(oed, 0, size);
 +      }
 +      else {
 +              oed = MEM_callocN(size, "ObjectEngineData");
 +      }
 +      oed->engine_type = engine_type;
 +      oed->free = free_cb;
 +      /* Perform user-side initialization, if needed. */
 +      if (init_cb != NULL) {
 +              init_cb(oed);
 +      }
 +      /* Register in the list. */
 +      BLI_addtail(&ob->drawdata, oed);
 +      return oed;
 +}
 +
 +/* XXX There is definitly some overlap between this and DRW_object_engine_data_ensure.
 + * We should get rid of one of the two. */
 +LampEngineData *DRW_lamp_engine_data_ensure(Object *ob, RenderEngineType *engine_type)
 +{
 +      BLI_assert(ob->type == OB_LAMP);
 +
 +      Scene *scene = DST.draw_ctx.scene;
 +
 +      /* TODO Dupliobjects */
 +      /* TODO Should be per scenelayer */
 +      return GPU_lamp_engine_data_get(scene, ob, NULL, engine_type);
 +}
 +
 +void DRW_lamp_engine_data_free(LampEngineData *led)
 +{
 +      GPU_lamp_engine_data_free(led);
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Rendering (DRW_engines)
 + * \{ */
 +
 +static void drw_engines_init(void)
 +{
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(engine);
 +              PROFILE_START(stime);
 +
 +              if (engine->engine_init) {
 +                      engine->engine_init(data);
 +              }
 +
 +              PROFILE_END_UPDATE(data->init_time, stime);
 +      }
 +}
 +
 +static void drw_engines_cache_init(void)
 +{
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(engine);
 +
 +              if (data->text_draw_cache) {
 +                      DRW_text_cache_destroy(data->text_draw_cache);
 +                      data->text_draw_cache = NULL;
 +              }
 +              if (DST.text_store_p == NULL) {
 +                      DST.text_store_p = &data->text_draw_cache;
 +              }
 +
 +              if (engine->cache_init) {
 +                      engine->cache_init(data);
 +              }
 +      }
 +}
 +
 +static void drw_engines_cache_populate(Object *ob)
 +{
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(engine);
 +
 +              if (engine->id_update) {
 +                      engine->id_update(data, &ob->id);
 +              }
 +
 +              if (engine->cache_populate) {
 +                      engine->cache_populate(data, ob);
 +              }
 +      }
 +}
 +
 +static void drw_engines_cache_finish(void)
 +{
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(engine);
 +
 +              if (engine->cache_finish) {
 +                      engine->cache_finish(data);
 +              }
 +      }
 +}
 +
 +static void drw_engines_draw_background(void)
 +{
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(engine);
 +
 +              if (engine->draw_background) {
 +                      PROFILE_START(stime);
 +
 +                      DRW_stats_group_start(engine->idname);
 +                      engine->draw_background(data);
 +                      DRW_stats_group_end();
 +
 +                      PROFILE_END_UPDATE(data->background_time, stime);
 +                      return;
 +              }
 +      }
 +
 +      /* No draw_background found, doing default background */
 +      if (DRW_state_draw_background()) {
 +              DRW_draw_background();
 +      }
 +}
 +
 +static void drw_engines_draw_scene(void)
 +{
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(engine);
 +              PROFILE_START(stime);
 +
 +              if (engine->draw_scene) {
 +                      DRW_stats_group_start(engine->idname);
 +                      engine->draw_scene(data);
 +                      DRW_stats_group_end();
 +              }
 +
 +              PROFILE_END_UPDATE(data->render_time, stime);
 +      }
 +}
 +
 +static void drw_engines_draw_text(void)
 +{
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(engine);
 +              PROFILE_START(stime);
 +
 +              if (data->text_draw_cache) {
 +                      DRW_text_cache_draw(data->text_draw_cache, DST.draw_ctx.v3d, DST.draw_ctx.ar, false);
 +              }
 +
 +              PROFILE_END_UPDATE(data->render_time, stime);
 +      }
 +}
 +
 +#define MAX_INFO_LINES 10
 +
 +/**
 + * Returns the offset required for the drawing of engines info.
 + */
 +int DRW_draw_region_engine_info_offset(void)
 +{
 +      int lines = 0;
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(engine);
 +
 +              /* Count the number of lines. */
 +              if (data->info[0] != '\0') {
 +                      lines++;
 +                      char *c = data->info;
 +                      while (*c++ != '\0') {
 +                              if (*c == '\n') {
 +                                      lines++;
 +                              }
 +                      }
 +              }
 +      }
 +      return MIN2(MAX_INFO_LINES, lines) * UI_UNIT_Y;
 +}
 +
 +/**
 + * Actual drawing;
 + */
 +void DRW_draw_region_engine_info(void)
 +{
 +      const char *info_array_final[MAX_INFO_LINES + 1];
 +      /* This should be maxium number of engines running at the same time. */
 +      char info_array[MAX_INFO_LINES][GPU_INFO_SIZE];
 +      int i = 0;
 +
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      ARegion *ar = draw_ctx->ar;
 +      float fill_color[4] = {0.0f, 0.0f, 0.0f, 0.25f};
 +
 +      UI_GetThemeColor3fv(TH_HIGH_GRAD, fill_color);
 +      mul_v3_fl(fill_color, fill_color[3]);
 +
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(engine);
 +
 +              if (data->info[0] != '\0') {
 +                      char *chr_current = data->info;
 +                      char *chr_start = chr_current;
 +                      int line_len = 0;
 +
 +                      while (*chr_current++ != '\0') {
 +                              line_len++;
 +                              if (*chr_current == '\n') {
 +                                      BLI_strncpy(info_array[i++], chr_start, line_len + 1);
 +                                      /* Re-start counting. */
 +                                      chr_start = chr_current + 1;
 +                                      line_len = -1;
 +                              }
 +                      }
 +
 +                      BLI_strncpy(info_array[i++], chr_start, line_len + 1);
 +
 +                      if (i >= MAX_INFO_LINES) {
 +                              break;
 +                      }
 +              }
 +      }
 +
 +      for (int j = 0; j < i; j++) {
 +              info_array_final[j] = info_array[j];
 +      }
 +      info_array_final[i] = NULL;
 +
 +      if (info_array[0] != NULL) {
 +              ED_region_info_draw_multiline(ar, info_array_final, fill_color, true);
 +      }
 +}
 +
 +#undef MAX_INFO_LINES
 +
 +static void use_drw_engine(DrawEngineType *engine)
 +{
 +      LinkData *ld = MEM_callocN(sizeof(LinkData), "enabled engine link data");
 +      ld->data = engine;
 +      BLI_addtail(&DST.enabled_engines, ld);
 +}
 +
 +/* TODO revisit this when proper layering is implemented */
 +/* Gather all draw engines needed and store them in DST.enabled_engines
 + * That also define the rendering order of engines */
 +static void drw_engines_enable_from_engine(RenderEngineType *engine_type)
 +{
 +      /* TODO layers */
 +      if (engine_type->draw_engine != NULL) {
 +              use_drw_engine(engine_type->draw_engine);
 +      }
 +
 +      if ((engine_type->flag & RE_INTERNAL) == 0) {
 +              drw_engines_enable_external();
 +      }
 +}
 +
 +static void drw_engines_enable_from_object_mode(void)
 +{
 +      use_drw_engine(&draw_engine_object_type);
 +}
 +
 +static void drw_engines_enable_from_mode(int mode)
 +{
 +      switch (mode) {
 +              case CTX_MODE_EDIT_MESH:
 +                      use_drw_engine(&draw_engine_edit_mesh_type);
 +                      break;
 +              case CTX_MODE_EDIT_CURVE:
 +                      use_drw_engine(&draw_engine_edit_curve_type);
 +                      break;
 +              case CTX_MODE_EDIT_SURFACE:
 +                      use_drw_engine(&draw_engine_edit_surface_type);
 +                      break;
 +              case CTX_MODE_EDIT_TEXT:
 +                      use_drw_engine(&draw_engine_edit_text_type);
 +                      break;
 +              case CTX_MODE_EDIT_ARMATURE:
 +                      use_drw_engine(&draw_engine_edit_armature_type);
 +                      break;
 +              case CTX_MODE_EDIT_METABALL:
 +                      use_drw_engine(&draw_engine_edit_metaball_type);
 +                      break;
 +              case CTX_MODE_EDIT_LATTICE:
 +                      use_drw_engine(&draw_engine_edit_lattice_type);
 +                      break;
 +              case CTX_MODE_POSE:
 +                      use_drw_engine(&draw_engine_pose_type);
 +                      break;
 +              case CTX_MODE_SCULPT:
 +                      use_drw_engine(&draw_engine_sculpt_type);
 +                      break;
 +              case CTX_MODE_PAINT_WEIGHT:
 +                      use_drw_engine(&draw_engine_pose_type);
 +                      use_drw_engine(&draw_engine_paint_weight_type);
 +                      break;
 +              case CTX_MODE_PAINT_VERTEX:
 +                      use_drw_engine(&draw_engine_paint_vertex_type);
 +                      break;
 +              case CTX_MODE_PAINT_TEXTURE:
 +                      use_drw_engine(&draw_engine_paint_texture_type);
 +                      break;
 +              case CTX_MODE_PARTICLE:
 +                      use_drw_engine(&draw_engine_particle_type);
 +                      break;
 +              case CTX_MODE_OBJECT:
 +                      break;
 +              default:
 +                      BLI_assert(!"Draw mode invalid");
 +                      break;
 +      }
 +}
 +
 +/**
 + * Use for select and depth-drawing.
 + */
 +static void drw_engines_enable_basic(void)
 +{
 +      use_drw_engine(DRW_engine_viewport_basic_type.draw_engine);
 +}
 +
 +/**
 + * Use for external render engines.
 + */
 +static void drw_engines_enable_external(void)
 +{
 +      use_drw_engine(DRW_engine_viewport_external_type.draw_engine);
 +}
 +
 +static void drw_engines_enable(ViewLayer *view_layer, RenderEngineType *engine_type)
 +{
 +      Object *obact = OBACT(view_layer);
 +      const int mode = CTX_data_mode_enum_ex(DST.draw_ctx.object_edit, obact, DST.draw_ctx.object_mode);
 +
 +      drw_engines_enable_from_engine(engine_type);
 +
 +      if (DRW_state_draw_support()) {
 +              drw_engines_enable_from_object_mode();
 +              drw_engines_enable_from_mode(mode);
 +      }
 +}
 +
 +static void drw_engines_disable(void)
 +{
 +      BLI_freelistN(&DST.enabled_engines);
 +}
 +
 +static unsigned int DRW_engines_get_hash(void)
 +{
 +      unsigned int hash = 0;
 +      /* The cache depends on enabled engines */
 +      /* FIXME : if collision occurs ... segfault */
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *engine = link->data;
 +              hash += BLI_ghashutil_strhash_p(engine->idname);
 +      }
 +
 +      return hash;
 +}
 +
 +static void draw_stat(rcti *rect, int u, int v, const char *txt, const int size)
 +{
 +      BLF_draw_default_ascii(rect->xmin + (1 + u * 5) * U.widget_unit,
 +                             rect->ymax - (3 + v) * U.widget_unit, 0.0f,
 +                             txt, size);
 +}
 +
 +/* CPU stats */
 +static void drw_debug_cpu_stats(void)
 +{
 +      int u, v;
 +      double init_tot_time = 0.0, background_tot_time = 0.0, render_tot_time = 0.0, tot_time = 0.0;
 +      /* local coordinate visible rect inside region, to accomodate overlapping ui */
 +      rcti rect;
 +      struct ARegion *ar = DST.draw_ctx.ar;
 +      ED_region_visible_rect(ar, &rect);
 +
 +      UI_FontThemeColor(BLF_default(), TH_TEXT_HI);
 +
 +      /* row by row */
 +      v = 0; u = 0;
 +      /* Label row */
 +      char col_label[32];
 +      sprintf(col_label, "Engine");
 +      draw_stat(&rect, u++, v, col_label, sizeof(col_label));
 +      sprintf(col_label, "Init");
 +      draw_stat(&rect, u++, v, col_label, sizeof(col_label));
 +      sprintf(col_label, "Background");
 +      draw_stat(&rect, u++, v, col_label, sizeof(col_label));
 +      sprintf(col_label, "Render");
 +      draw_stat(&rect, u++, v, col_label, sizeof(col_label));
 +      sprintf(col_label, "Total (w/o cache)");
 +      draw_stat(&rect, u++, v, col_label, sizeof(col_label));
 +      v++;
 +
 +      /* Engines rows */
 +      char time_to_txt[16];
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              u = 0;
 +              DrawEngineType *engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(engine);
 +
 +              draw_stat(&rect, u++, v, engine->idname, sizeof(engine->idname));
 +
 +              init_tot_time += data->init_time;
 +              sprintf(time_to_txt, "%.2fms", data->init_time);
 +              draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
 +
 +              background_tot_time += data->background_time;
 +              sprintf(time_to_txt, "%.2fms", data->background_time);
 +              draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
 +
 +              render_tot_time += data->render_time;
 +              sprintf(time_to_txt, "%.2fms", data->render_time);
 +              draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
 +
 +              tot_time += data->init_time + data->background_time + data->render_time;
 +              sprintf(time_to_txt, "%.2fms", data->init_time + data->background_time + data->render_time);
 +              draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
 +              v++;
 +      }
 +
 +      /* Totals row */
 +      u = 0;
 +      sprintf(col_label, "Sub Total");
 +      draw_stat(&rect, u++, v, col_label, sizeof(col_label));
 +      sprintf(time_to_txt, "%.2fms", init_tot_time);
 +      draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
 +      sprintf(time_to_txt, "%.2fms", background_tot_time);
 +      draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
 +      sprintf(time_to_txt, "%.2fms", render_tot_time);
 +      draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
 +      sprintf(time_to_txt, "%.2fms", tot_time);
 +      draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
 +      v += 2;
 +
 +      u = 0;
 +      sprintf(col_label, "Cache Time");
 +      draw_stat(&rect, u++, v, col_label, sizeof(col_label));
 +      sprintf(time_to_txt, "%.2fms", DST.cache_time);
 +      draw_stat(&rect, u++, v, time_to_txt, sizeof(time_to_txt));
 +}
 +
 +/* Display GPU time for each passes */
 +static void drw_debug_gpu_stats(void)
 +{
 +      /* local coordinate visible rect inside region, to accomodate overlapping ui */
 +      rcti rect;
 +      struct ARegion *ar = DST.draw_ctx.ar;
 +      ED_region_visible_rect(ar, &rect);
 +
 +      UI_FontThemeColor(BLF_default(), TH_TEXT_HI);
 +
 +      int v = BLI_listbase_count(&DST.enabled_engines) + 5;
 +
 +      char stat_string[32];
 +
 +      /* Memory Stats */
 +      unsigned int tex_mem = GPU_texture_memory_usage_get();
 +      unsigned int vbo_mem = GWN_vertbuf_get_memory_usage();
 +
 +      sprintf(stat_string, "GPU Memory");
 +      draw_stat(&rect, 0, v, stat_string, sizeof(stat_string));
 +      sprintf(stat_string, "%.2fMB", (double)(tex_mem + vbo_mem) / 1000000.0);
 +      draw_stat(&rect, 1, v++, stat_string, sizeof(stat_string));
 +      sprintf(stat_string, "   |--> Textures");
 +      draw_stat(&rect, 0, v, stat_string, sizeof(stat_string));
 +      sprintf(stat_string, "%.2fMB", (double)tex_mem / 1000000.0);
 +      draw_stat(&rect, 1, v++, stat_string, sizeof(stat_string));
 +      sprintf(stat_string, "   |--> Meshes");
 +      draw_stat(&rect, 0, v, stat_string, sizeof(stat_string));
 +      sprintf(stat_string, "%.2fMB", (double)vbo_mem / 1000000.0);
 +      draw_stat(&rect, 1, v++, stat_string, sizeof(stat_string));
 +
 +      /* Pre offset for stats_draw */
 +      rect.ymax -= (3 + ++v) * U.widget_unit;
 +
 +      /* Rendering Stats */
 +      DRW_stats_draw(&rect);
 +}
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name View Update
 + * \{ */
 +
 +void DRW_notify_view_update(const DRWUpdateContext *update_ctx)
 +{
 +      RenderEngineType *engine_type = update_ctx->engine_type;
 +      ARegion *ar = update_ctx->ar;
 +      View3D *v3d = update_ctx->v3d;
 +      RegionView3D *rv3d = ar->regiondata;
 +      Depsgraph *depsgraph = update_ctx->depsgraph;
 +      Scene *scene = update_ctx->scene;
 +      ViewLayer *view_layer = update_ctx->view_layer;
 +
 +      if (rv3d->viewport == NULL) {
 +              return;
 +      }
 +
 +
 +      /* Reset before using it. */
 +      memset(&DST, 0x0, sizeof(DST));
 +
 +      DST.viewport = rv3d->viewport;
 +      DST.draw_ctx = (DRWContextState){
 +              ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, OB_MODE_OBJECT,
 +              NULL,
 +      };
 +
 +      drw_engines_enable(view_layer, engine_type);
 +
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *draw_engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(draw_engine);
 +
 +              if (draw_engine->view_update) {
 +                      draw_engine->view_update(data);
 +              }
 +      }
 +
 +      DST.viewport = NULL;
 +
 +      drw_engines_disable();
 +}
 +
 +/** \} */
 +
 +/** \name ID Update
 + * \{ */
 +
 +/* TODO(sergey): This code is run for each changed ID (including the ones which
 + * are changed indirectly via update flush. Need to find a way to make this to
 + * run really fast, hopefully without any memory allocations on a heap
 + * Idea here could be to run every known engine's id_update() and make them
 + * do nothing if there is no engine-specific data yet.
 + */
 +void DRW_notify_id_update(const DRWUpdateContext *update_ctx, ID *id)
 +{
 +      RenderEngineType *engine_type = update_ctx->engine_type;
 +      ARegion *ar = update_ctx->ar;
 +      View3D *v3d = update_ctx->v3d;
 +      RegionView3D *rv3d = ar->regiondata;
 +      Depsgraph *depsgraph = update_ctx->depsgraph;
 +      Scene *scene = update_ctx->scene;
 +      ViewLayer *view_layer = update_ctx->view_layer;
 +      if (rv3d->viewport == NULL) {
 +              return;
 +      }
 +      /* Reset before using it. */
 +      memset(&DST, 0x0, sizeof(DST));
 +      DST.viewport = rv3d->viewport;
 +      DST.draw_ctx = (DRWContextState){
 +              ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, OB_MODE_OBJECT, NULL,
 +      };
 +      drw_engines_enable(view_layer, engine_type);
 +      for (LinkData *link = DST.enabled_engines.first; link; link = link->next) {
 +              DrawEngineType *draw_engine = link->data;
 +              ViewportEngineData *data = DRW_viewport_engine_data_ensure(draw_engine);
 +              if (draw_engine->id_update) {
 +                      draw_engine->id_update(data, id);
 +              }
 +      }
 +      DST.viewport = NULL;
 +      drw_engines_disable();
 +}
 +
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Main Draw Loops (DRW_draw)
 + * \{ */
 +
 +/* Everything starts here.
 + * This function takes care of calling all cache and rendering functions
 + * for each relevant engine / mode engine. */
 +void DRW_draw_view(const bContext *C)
 +{
 +      EvaluationContext eval_ctx;
 +      CTX_data_eval_ctx(C, &eval_ctx);
 +      RenderEngineType *engine_type = CTX_data_engine_type(C);
 +      ARegion *ar = CTX_wm_region(C);
 +      View3D *v3d = CTX_wm_view3d(C);
 +
 +      /* Reset before using it. */
 +      memset(&DST, 0x0, sizeof(DST));
 +      DRW_draw_render_loop_ex(eval_ctx.depsgraph, engine_type, ar, v3d, eval_ctx.object_mode, C);
 +}
 +
 +/**
 + * Used for both regular and off-screen drawing.
 + * Need to reset DST before calling this function
 + */
 +void DRW_draw_render_loop_ex(
 +        struct Depsgraph *depsgraph,
 +        RenderEngineType *engine_type,
 +        ARegion *ar, View3D *v3d, const eObjectMode object_mode,
 +        const bContext *evil_C)
 +{
 +      Scene *scene = DEG_get_evaluated_scene(depsgraph);
 +      ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
 +      RegionView3D *rv3d = ar->regiondata;
 +
 +      DST.draw_ctx.evil_C = evil_C;
 +
 +      DST.viewport = rv3d->viewport;
 +      v3d->zbuf = true;
 +
 +      /* Setup viewport */
 +      GPU_viewport_engines_data_validate(DST.viewport, DRW_engines_get_hash());
 +
 +      DST.draw_ctx = (DRWContextState){
 +          ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, object_mode,
 +
 +          /* reuse if caller sets */
 +          DST.draw_ctx.evil_C,
 +      };
 +      drw_context_state_init();
 +      drw_viewport_var_init();
 +
 +      /* Get list of enabled engines */
 +      drw_engines_enable(view_layer, engine_type);
 +
 +      /* Update ubos */
 +      DRW_globals_update();
 +
 +      /* Init engines */
 +      drw_engines_init();
 +
 +      /* Cache filling */
 +      {
 +              PROFILE_START(stime);
 +              drw_engines_cache_init();
 +
 +              DEG_OBJECT_ITER_FOR_RENDER_ENGINE(depsgraph, ob, DRW_iterator_mode_get())
 +              {
 +                      drw_engines_cache_populate(ob);
 +              }
 +              DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END
 +
 +              drw_engines_cache_finish();
 +
 +              DRW_render_instance_buffer_finish();
 +              PROFILE_END_ACCUM(DST.cache_time, stime);
 +      }
 +
 +      DRW_stats_begin();
 +
 +      /* Start Drawing */
 +      DRW_state_reset();
 +
 +      drw_engines_draw_background();
 +
 +      /* WIP, single image drawn over the camera view (replace) */
 +      bool do_bg_image = false;
 +      if (rv3d->persp == RV3D_CAMOB) {
 +              Object *cam_ob = v3d->camera;
 +              if (cam_ob && cam_ob->type == OB_CAMERA) {
 +                      Camera *cam = cam_ob->data;
 +                      if (!BLI_listbase_is_empty(&cam->bg_images)) {
 +                              do_bg_image = true;
 +                      }
 +              }
 +      }
 +
 +      if (do_bg_image) {
 +              ED_view3d_draw_bgpic_test(scene, depsgraph, ar, v3d, false, true);
 +      }
 +
 +
 +      DRW_draw_callbacks_pre_scene();
 +      if (DST.draw_ctx.evil_C) {
 +              ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.ar, REGION_DRAW_PRE_VIEW);
 +      }
 +
 +      drw_engines_draw_scene();
 +
 +      DRW_draw_callbacks_post_scene();
 +      if (DST.draw_ctx.evil_C) {
 +              ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.ar, REGION_DRAW_POST_VIEW);
 +      }
 +
 +      DRW_state_reset();
 +
 +      drw_engines_draw_text();
 +
 +      if (DST.draw_ctx.evil_C) {
 +              /* needed so manipulator isn't obscured */
 +              glDisable(GL_DEPTH_TEST);
 +              DRW_draw_manipulator_3d();
 +
 +              DRW_draw_region_info();
 +
 +              /* Draw 2D after region info so we can draw on top of the camera passepartout overlay.
 +               * 'DRW_draw_region_info' sets the projection in pixel-space. */
 +              DRW_draw_manipulator_2d();
 +              glEnable(GL_DEPTH_TEST);
 +      }
 +
 +      DRW_stats_reset();
 +
 +      if (do_bg_image) {
 +              ED_view3d_draw_bgpic_test(scene, depsgraph, ar, v3d, true, true);
 +      }
 +
 +      if (G.debug_value > 20) {
 +              drw_debug_cpu_stats();
 +              drw_debug_gpu_stats();
 +      }
 +
 +      DRW_state_reset();
 +      drw_engines_disable();
 +
 +      drw_viewport_cache_resize();
 +
 +#ifdef DEBUG
 +      /* Avoid accidental reuse. */
 +      memset(&DST, 0xFF, sizeof(DST));
 +#endif
 +}
 +
 +void DRW_draw_render_loop(
 +        struct Depsgraph *depsgraph,
 +        ARegion *ar, View3D *v3d, const eObjectMode object_mode)
 +{
 +      /* Reset before using it. */
 +      memset(&DST, 0x0, sizeof(DST));
 +
 +      Scene *scene = DEG_get_evaluated_scene(depsgraph);
 +      RenderEngineType *engine_type = RE_engines_find(scene->view_render.engine_id);
 +
 +      DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, object_mode, NULL);
 +}
 +
 +/* @viewport CAN be NULL, in this case we create one. */
 +void DRW_draw_render_loop_offscreen(
 +        struct Depsgraph *depsgraph, RenderEngineType *engine_type,
 +        ARegion *ar, View3D *v3d, const eObjectMode object_mode,
 +        const bool draw_background, GPUOffScreen *ofs,
 +        GPUViewport *viewport)
 +{
 +      RegionView3D *rv3d = ar->regiondata;
 +
 +      /* backup */
 +      void *backup_viewport = rv3d->viewport;
 +      {
 +              /* backup (_never_ use rv3d->viewport) */
 +              if (viewport == NULL) {
 +                      rv3d->viewport = GPU_viewport_create_from_offscreen(ofs);
 +              }
 +              else {
 +                      rv3d->viewport = viewport;
 +              }
 +      }
 +
 +      /* Reset before using it. */
 +      memset(&DST, 0x0, sizeof(DST));
 +      DST.options.is_image_render = true;
 +      DST.options.draw_background = draw_background;
 +      DRW_draw_render_loop_ex(depsgraph, engine_type, ar, v3d, object_mode, NULL);
 +
 +      /* restore */
 +      {
 +              if (viewport == NULL) {
 +                      /* don't free data owned by 'ofs' */
 +                      GPU_viewport_clear_from_offscreen(rv3d->viewport);
 +                      GPU_viewport_free(rv3d->viewport);
 +                      MEM_freeN(rv3d->viewport);
 +              }
 +
 +              rv3d->viewport = backup_viewport;
 +      }
 +
 +      /* we need to re-bind (annoying!) */
 +      GPU_offscreen_bind(ofs, false);
 +}
 +
 +void DRW_render_to_image(RenderEngine *re, struct Depsgraph *depsgraph)
 +{
 +      Scene *scene = DEG_get_evaluated_scene(depsgraph);
 +      ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
 +      RenderEngineType *engine_type = re->type;
 +      DrawEngineType *draw_engine_type = engine_type->draw_engine;
 +      RenderData *r = &scene->r;
 +      Render *render = re->re;
 +      const EvaluationContext *eval_ctx = RE_GetEvalCtx(render);
 +
 +      /* Reset before using it. */
 +      memset(&DST, 0x0, sizeof(DST));
 +      DST.options.is_image_render = true;
 +      DST.options.is_scene_render = true;
 +      DST.options.draw_background = scene->r.alphamode == R_ADDSKY;
 +
 +      DST.draw_ctx = (DRWContextState){
 +          NULL, NULL, NULL, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, eval_ctx->object_mode, NULL,
 +      };
 +      drw_context_state_init();
 +
 +      DST.viewport = GPU_viewport_create();
 +      const int size[2] = {(r->size * r->xsch) / 100, (r->size * r->ysch) / 100};
 +      GPU_viewport_size_set(DST.viewport, size);
 +
 +      drw_viewport_var_init();
 +
 +      ViewportEngineData *data = DRW_viewport_engine_data_ensure(draw_engine_type);
 +
 +      /* set default viewport */
 +      gpuPushAttrib(GPU_ENABLE_BIT | GPU_VIEWPORT_BIT);
 +      glDisable(GL_SCISSOR_TEST);
 +      glViewport(0, 0, size[0], size[1]);
 +
 +      if ((r->scemode & R_MULTIVIEW) != 0) {
 +              for (SceneRenderView *srv = r->views.first; srv; srv = srv->next) {
 +                      if (BKE_scene_multiview_is_render_view_active(r, srv) == false)
 +                              continue;
 +
 +                      RE_SetActiveRenderView(render, srv->name);
 +
 +                      engine_type->draw_engine->render_to_image(data, re, depsgraph);
 +              }
 +      }
 +      else {
 +              engine_type->draw_engine->render_to_image(data, re, depsgraph);
 +      }
 +
 +      DST.buffer_finish_called = false;
 +
 +      /* TODO grease pencil */
 +
 +      GPU_viewport_free(DST.viewport);
 +      MEM_freeN(DST.viewport);
 +
 +      DRW_state_reset();
 +      /* FIXME GL_DEPTH_TEST is enabled by default but it seems
 +       * to trigger some bad behaviour / artifacts if it's turned
 +       * on at this point. */
 +      glDisable(GL_DEPTH_TEST);
 +
 +      /* Restore Drawing area. */
 +      gpuPopAttrib();
 +      glEnable(GL_SCISSOR_TEST);
 +      GPU_framebuffer_restore();
 +
 +#ifdef DEBUG
 +      /* Avoid accidental reuse. */
 +      memset(&DST, 0xFF, sizeof(DST));
 +#endif
 +}
 +
 +void DRW_render_object_iter(
 +      void *vedata, RenderEngine *engine, struct Depsgraph *depsgraph,
 +      void (*callback)(void *vedata, Object *ob, RenderEngine *engine, struct Depsgraph *depsgraph))
 +{
 +      DEG_OBJECT_ITER_FOR_RENDER_ENGINE(depsgraph, ob, DRW_iterator_mode_get())
 +      {
 +              callback(vedata, ob, engine, depsgraph);
 +      }
 +      DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END
 +}
 +
 +/* Must run after all instance datas have been added. */
 +void DRW_render_instance_buffer_finish(void)
 +{
 +      BLI_assert(!DST.buffer_finish_called && "DRW_render_instance_buffer_finish called twice!");
 +      DST.buffer_finish_called = true;
 +      DRW_instance_buffer_finish(DST.idatalist);
 +}
 +
 +/**
 + * object mode select-loop, see: ED_view3d_draw_select_loop (legacy drawing).
 + */
 +void DRW_draw_select_loop(
 +        struct Depsgraph *depsgraph,
 +        ARegion *ar, View3D *v3d, const eObjectMode object_mode,
 +        bool UNUSED(use_obedit_skip), bool UNUSED(use_nearest), const rcti *rect)
 +{
 +      Scene *scene = DEG_get_evaluated_scene(depsgraph);
 +      RenderEngineType *engine_type = RE_engines_find(scene->view_render.engine_id);
 +      ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
 +      Object *obact = OBACT(view_layer);
 +#ifndef USE_GPU_SELECT
 +      UNUSED_VARS(vc, scene, view_layer, v3d, ar, rect);
 +#else
 +      RegionView3D *rv3d = ar->regiondata;
 +
 +      /* Reset before using it. */
 +      memset(&DST, 0x0, sizeof(DST));
 +
 +      /* backup (_never_ use rv3d->viewport) */
 +      void *backup_viewport = rv3d->viewport;
 +      rv3d->viewport = NULL;
 +
 +      bool use_obedit = false;
 +      int obedit_mode = 0;
 +      if (object_mode & OB_MODE_EDIT) {
 +              if (obact->type == OB_MBALL) {
 +                      use_obedit = true;
 +                      obedit_mode = CTX_MODE_EDIT_METABALL;
 +              }
 +              else if (obact->type == OB_ARMATURE) {
 +                      use_obedit = true;
 +                      obedit_mode = CTX_MODE_EDIT_ARMATURE;
 +              }
 +      }
 +
 +      struct GPUViewport *viewport = GPU_viewport_create();
 +      GPU_viewport_size_set(viewport, (const int[2]){BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)});
 +
 +      bool cache_is_dirty;
 +      DST.viewport = viewport;
 +      v3d->zbuf = true;
 +
 +      DST.options.is_select = true;
 +
 +      /* Get list of enabled engines */
 +      if (use_obedit) {
 +              drw_engines_enable_from_mode(obedit_mode);
 +      }
 +      else {
 +              drw_engines_enable_basic();
 +              drw_engines_enable_from_object_mode();
 +      }
 +
 +      /* Setup viewport */
 +      cache_is_dirty = true;
 +
 +      /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */
 +      DST.draw_ctx = (DRWContextState){
 +              ar, rv3d, v3d, scene, view_layer, obact, engine_type, depsgraph, object_mode,
 +              (bContext *)NULL,
 +      };
 +      drw_context_state_init();
 +      drw_viewport_var_init();
 +
 +      /* Update ubos */
 +      DRW_globals_update();
 +
 +      /* Init engines */
 +      drw_engines_init();
 +
 +      /* TODO : tag to refresh by the dependency graph */
 +      /* ideally only refresh when objects are added/removed */
 +      /* or render properties / materials change */
 +      if (cache_is_dirty) {
 +              drw_engines_cache_init();
 +
 +              if (use_obedit) {
 +                      drw_engines_cache_populate(obact);
 +              }
 +              else {
 +                      DEG_OBJECT_ITER(depsgraph, ob, DRW_iterator_mode_get(),
 +                                      DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
 +                                      DEG_ITER_OBJECT_FLAG_VISIBLE |
 +                                      DEG_ITER_OBJECT_FLAG_DUPLI)
 +                      {
 +                              if ((ob->base_flag & BASE_SELECTABLED) != 0) {
 +                                      DRW_select_load_id(ob->select_color);
 +                                      drw_engines_cache_populate(ob);
 +                              }
 +                      }
 +                      DEG_OBJECT_ITER_END
 +              }
 +
 +              drw_engines_cache_finish();
 +
 +              DRW_render_instance_buffer_finish();
 +      }
 +
 +      /* Start Drawing */
 +      DRW_state_reset();
 +      DRW_draw_callbacks_pre_scene();
 +      drw_engines_draw_scene();
 +      DRW_draw_callbacks_post_scene();
 +
 +      DRW_state_reset();
 +      drw_engines_disable();
 +
 +#ifdef DEBUG
 +      /* Avoid accidental reuse. */
 +      memset(&DST, 0xFF, sizeof(DST));
 +#endif
 +
 +      /* Cleanup for selection state */
 +      GPU_viewport_free(viewport);
 +      MEM_freeN(viewport);
 +
 +      /* restore */
 +      rv3d->viewport = backup_viewport;
 +#endif  /* USE_GPU_SELECT */
 +}
 +
 +/**
 + * object mode select-loop, see: ED_view3d_draw_depth_loop (legacy drawing).
 + */
 +void DRW_draw_depth_loop(
 +        Depsgraph *depsgraph,
 +        ARegion *ar, View3D *v3d, const eObjectMode object_mode)
 +{
 +      Scene *scene = DEG_get_evaluated_scene(depsgraph);
 +      RenderEngineType *engine_type = RE_engines_find(scene->view_render.engine_id);
 +      ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
 +      RegionView3D *rv3d = ar->regiondata;
 +
 +      /* backup (_never_ use rv3d->viewport) */
 +      void *backup_viewport = rv3d->viewport;
 +      rv3d->viewport = NULL;
 +
 +      /* Reset before using it. */
 +      memset(&DST, 0x0, sizeof(DST));
 +
 +      struct GPUViewport *viewport = GPU_viewport_create();
 +      GPU_viewport_size_set(viewport, (const int[2]){ar->winx, ar->winy});
 +
 +      bool cache_is_dirty;
 +      DST.viewport = viewport;
 +      v3d->zbuf = true;
 +
 +      DST.options.is_depth = true;
 +
 +      /* Get list of enabled engines */
 +      {
 +              drw_engines_enable_basic();
 +              drw_engines_enable_from_object_mode();
 +      }
 +
 +      /* Setup viewport */
 +      cache_is_dirty = true;
 +
 +      /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */
 +      DST.draw_ctx = (DRWContextState){
 +              ar, rv3d, v3d, scene, view_layer, OBACT(view_layer), engine_type, depsgraph, object_mode,
 +              (bContext *)NULL,
 +      };
 +      drw_context_state_init();
 +      drw_viewport_var_init();
 +
 +      /* Update ubos */
 +      DRW_globals_update();
 +
 +      /* Init engines */
 +      drw_engines_init();
 +
 +      /* TODO : tag to refresh by the dependency graph */
 +      /* ideally only refresh when objects are added/removed */
 +      /* or render properties / materials change */
 +      if (cache_is_dirty) {
 +              drw_engines_cache_init();
 +
 +              DEG_OBJECT_ITER_FOR_RENDER_ENGINE(depsgraph, ob, DRW_iterator_mode_get())
 +              {
 +                      drw_engines_cache_populate(ob);
 +              }
 +              DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END
 +
 +              drw_engines_cache_finish();
 +
 +              DRW_render_instance_buffer_finish();
 +      }
 +
 +      /* Start Drawing */
 +      DRW_state_reset();
 +      DRW_draw_callbacks_pre_scene();
 +      drw_engines_draw_scene();
 +      DRW_draw_callbacks_post_scene();
 +
 +      DRW_state_reset();
 +      drw_engines_disable();
 +
 +#ifdef DEBUG
 +      /* Avoid accidental reuse. */
 +      memset(&DST, 0xFF, sizeof(DST));
 +#endif
 +
 +      /* Cleanup for selection state */
 +      GPU_viewport_free(viewport);
 +      MEM_freeN(viewport);
 +
 +      /* restore */
 +      rv3d->viewport = backup_viewport;
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Draw Manager State (DRW_state)
 + * \{ */
 +
 +void DRW_state_dfdy_factors_get(float dfdyfac[2])
 +{
 +      GPU_get_dfdy_factors(dfdyfac);
 +}
 +
 +/**
 + * When false, drawing doesn't output to a pixel buffer
 + * eg: Occlusion queries, or when we have setup a context to draw in already.
 + */
 +bool DRW_state_is_fbo(void)
 +{
 +      return ((DST.default_framebuffer != NULL) || DST.options.is_image_render);
 +}
 +
 +/**
 + * For when engines need to know if this is drawing for selection or not.
 + */
 +bool DRW_state_is_select(void)
 +{
 +      return DST.options.is_select;
 +}
 +
 +bool DRW_state_is_depth(void)
 +{
 +      return DST.options.is_depth;
 +}
 +
 +/**
 + * Whether we are rendering for an image
 + */
 +bool DRW_state_is_image_render(void)
 +{
 +      return DST.options.is_image_render;
 +}
 +
 +/**
 + * Whether we are rendering only the render engine,
 + * or if we should also render the mode engines.
 + */
 +bool DRW_state_is_scene_render(void)
 +{
 +      BLI_assert(DST.options.is_scene_render ?
 +                 DST.options.is_image_render : true);
 +      return DST.options.is_scene_render;
 +}
 +
 +/**
 + * Gives you the iterator mode to use for depsgraph.
 + */
 +eDepsObjectIteratorMode DRW_iterator_mode_get(void)
 +{
 +      return DRW_state_is_scene_render() ? DEG_ITER_OBJECT_MODE_RENDER :
 +                                           DEG_ITER_OBJECT_MODE_VIEWPORT;
 +}
 +
 +/**
 + * Should text draw in this mode?
 + */
 +bool DRW_state_show_text(void)
 +{
 +      return (DST.options.is_select) == 0 &&
 +             (DST.options.is_depth) == 0 &&
 +             (DST.options.is_scene_render) == 0;
 +}
 +
 +/**
 + * Should draw support elements
 + * Objects center, selection outline, probe data, ...
 + */
 +bool DRW_state_draw_support(void)
 +{
 +      View3D *v3d = DST.draw_ctx.v3d;
 +      return (DRW_state_is_scene_render() == false) &&
 +              (v3d != NULL) &&
 +              ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0);
 +}
 +
 +/**
 + * Whether we should render the background
 + */
 +bool DRW_state_draw_background(void)
 +{
 +      if (DRW_state_is_image_render() == false) {
 +              return true;
 +      }
 +      return DST.options.draw_background;
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Context State (DRW_context_state)
 + * \{ */
 +
 +const DRWContextState *DRW_context_state_get(void)
 +{
 +      return &DST.draw_ctx;
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Init/Exit (DRW_engines)
 + * \{ */
 +
 +bool DRW_engine_render_support(DrawEngineType *draw_engine_type)
 +{
 +      return draw_engine_type->render_to_image;
 +}
 +
 +void DRW_engine_register(DrawEngineType *draw_engine_type)
 +{
 +      BLI_addtail(&DRW_engines, draw_engine_type);
 +}
 +
 +void DRW_engines_register(void)
 +{
 +#ifdef WITH_CLAY_ENGINE
 +      RE_engines_register(NULL, &DRW_engine_viewport_clay_type);
 +#endif
 +      RE_engines_register(NULL, &DRW_engine_viewport_eevee_type);
 +
 +      DRW_engine_register(&draw_engine_object_type);
 +      DRW_engine_register(&draw_engine_edit_armature_type);
 +      DRW_engine_register(&draw_engine_edit_curve_type);
 +      DRW_engine_register(&draw_engine_edit_lattice_type);
 +      DRW_engine_register(&draw_engine_edit_mesh_type);
 +      DRW_engine_register(&draw_engine_edit_metaball_type);
 +      DRW_engine_register(&draw_engine_edit_surface_type);
 +      DRW_engine_register(&draw_engine_edit_text_type);
 +      DRW_engine_register(&draw_engine_paint_texture_type);
 +      DRW_engine_register(&draw_engine_paint_vertex_type);
 +      DRW_engine_register(&draw_engine_paint_weight_type);
 +      DRW_engine_register(&draw_engine_particle_type);
 +      DRW_engine_register(&draw_engine_pose_type);
 +      DRW_engine_register(&draw_engine_sculpt_type);
 +
 +      /* setup callbacks */
 +      {
 +              /* BKE: mball.c */
 +              extern void *BKE_mball_batch_cache_dirty_cb;
 +              extern void *BKE_mball_batch_cache_free_cb;
 +              /* BKE: curve.c */
 +              extern void *BKE_curve_batch_cache_dirty_cb;
 +              extern void *BKE_curve_batch_cache_free_cb;
 +              /* BKE: mesh.c */
 +              extern void *BKE_mesh_batch_cache_dirty_cb;
 +              extern void *BKE_mesh_batch_cache_free_cb;
 +              /* BKE: lattice.c */
 +              extern void *BKE_lattice_batch_cache_dirty_cb;
 +              extern void *BKE_lattice_batch_cache_free_cb;
 +              /* BKE: particle.c */
 +              extern void *BKE_particle_batch_cache_dirty_cb;
 +              extern void *BKE_particle_batch_cache_free_cb;
 +
 +              BKE_mball_batch_cache_dirty_cb = DRW_mball_batch_cache_dirty;
 +              BKE_mball_batch_cache_free_cb = DRW_mball_batch_cache_free;
 +
 +              BKE_curve_batch_cache_dirty_cb = DRW_curve_batch_cache_dirty;
 +              BKE_curve_batch_cache_free_cb = DRW_curve_batch_cache_free;
 +
 +              BKE_mesh_batch_cache_dirty_cb = DRW_mesh_batch_cache_dirty;
 +              BKE_mesh_batch_cache_free_cb = DRW_mesh_batch_cache_free;
 +
 +              BKE_lattice_batch_cache_dirty_cb = DRW_lattice_batch_cache_dirty;
 +              BKE_lattice_batch_cache_free_cb = DRW_lattice_batch_cache_free;
 +
 +              BKE_particle_batch_cache_dirty_cb = DRW_particle_batch_cache_dirty;
 +              BKE_particle_batch_cache_free_cb = DRW_particle_batch_cache_free;
 +      }
 +}
 +
 +extern struct GPUUniformBuffer *globals_ubo; /* draw_common.c */
 +extern struct GPUTexture *globals_ramp; /* draw_common.c */
 +void DRW_engines_free(void)
 +{
 +      DRW_shape_cache_free();
 +      DRW_stats_free();
 +      DRW_globals_free();
 +
 +      DrawEngineType *next;
 +      for (DrawEngineType *type = DRW_engines.first; type; type = next) {
 +              next = type->next;
 +              BLI_remlink(&R_engines, type);
 +
 +              if (type->engine_free) {
 +                      type->engine_free();
 +              }
 +      }
 +
 +      if (globals_ubo)
 +              GPU_uniformbuffer_free(globals_ubo);
 +
 +      if (globals_ramp)
 +              GPU_texture_free(globals_ramp);
 +
 +      MEM_SAFE_FREE(g_pos_format);
 +
 +      MEM_SAFE_FREE(RST.bound_texs);
 +      MEM_SAFE_FREE(RST.bound_tex_slots);
 +
 +#ifdef WITH_CLAY_ENGINE
 +      BLI_remlink(&R_engines, &DRW_engine_viewport_clay_type);
 +#endif
 +}
 +
 +/** \} */
index 671a868947e59283702aa2d95b955e402b8773c6,76a0d874a0b3fdb91479fdb61f2b68fbb2622a14..d9bbec7082db8d434ae0a75bfbdd1277654bba47
@@@ -1155,10 -1134,8 +1155,10 @@@ static void knifetool_draw(const bConte
                BLI_mempool_iter iter;
                KnifeEdge *kfe;
  
 +              immUniformColor3ubv(kcd->colors.line);
                glLineWidth(1.0);
 -              glBegin(GL_LINES);
 +
-               immBeginAtMost(GWN_PRIM_LINES, BLI_mempool_count(kcd->kedges) * 2);
++              immBeginAtMost(GWN_PRIM_LINES, BLI_mempool_len(kcd->kedges) * 2);
  
                BLI_mempool_iternew(kcd->kedges, &iter);
                for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
                BLI_mempool_iter iter;
                KnifeVert *kfv;
  
 +              immUniformColor3ubv(kcd->colors.point);
                glPointSize(5.0);
  
-               immBeginAtMost(GWN_PRIM_POINTS, BLI_mempool_count(kcd->kverts));
 -              glBegin(GL_POINTS);
++              immBeginAtMost(GWN_PRIM_POINTS, BLI_mempool_len(kcd->kverts));
 +
                BLI_mempool_iternew(kcd->kverts, &iter);
                for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) {
                        if (!kfv->is_cut)
index 92f9dd9ee26152ed03f4ddcf6047ae29ae30a29f,4e02ff77a3175fb7faec95abd43887e14768d2b4..7b2fcc156493b063bfdcffc79476ad064d39c245
@@@ -191,23 -179,18 +191,23 @@@ void ED_render_engine_area_exit(Main *b
  void ED_render_engine_changed(Main *bmain)
  {
        /* on changing the render engine type, clear all running render engines */
 -      bScreen *sc;
 -      ScrArea *sa;
 -      Scene *scene;
 -
 -      for (sc = bmain->screen.first; sc; sc = sc->id.next)
 -              for (sa = sc->areabase.first; sa; sa = sa->next)
 +      for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
 +              for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
                        ED_render_engine_area_exit(bmain, sa);
 -
 +              }
 +      }
        RE_FreePersistentData();
 -
 -      for (scene = bmain->scene.first; scene; scene = scene->id.next) {
 -              ED_render_id_flush_update(bmain, &scene->id);
 +      /* Inform all render engines and draw managers. */
 +      DEGEditorUpdateContext update_ctx = {NULL};
 +      update_ctx.bmain = bmain;
 +      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +              update_ctx.scene = scene;
-               BLI_LISTBASE_FOREACH(ViewLayer *, view_layer, &scene->view_layers) {
++              LISTBASE_FOREACH(ViewLayer *, view_layer, &scene->view_layers) {
 +                      /* TDODO(sergey): Iterate over depsgraphs instead? */
 +                      update_ctx.depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true);
 +                      update_ctx.view_layer = view_layer;
 +                      ED_render_id_flush_update(&update_ctx, &scene->id);
 +              }
                if (scene->nodetree) {
                        ntreeCompositUpdateRLayers(scene->nodetree);
                }
index 3d5f12a94fdc89d35af1c47df7c8695a56a88325,0000000000000000000000000000000000000000..efc28eea91771dc78b8d84284985236de189d252
mode 100644,000000..100644
--- /dev/null
@@@ -1,1054 -1,0 +1,1054 @@@
-               BLI_LISTBASE_FOREACH (LinkData *, link, &data.scene_collections_array) {
 +/*
 + * ***** 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): Blender Foundation, Dalai Felinto
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/editors/space_outliner/outliner_collections.c
 + *  \ingroup spoutliner
 + */
 +
 +#include "BLI_utildefines.h"
 +#include "BLI_listbase.h"
 +
 +#include "BKE_context.h"
 +#include "BKE_collection.h"
 +#include "BKE_layer.h"
 +#include "BKE_main.h"
 +#include "BKE_report.h"
 +
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_build.h"
 +
 +#include "DNA_group_types.h"
 +#include "DNA_object_types.h"
 +
 +#include "ED_screen.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +
 +#include "RNA_access.h"
 +#include "RNA_define.h"
 +#include "RNA_enum_types.h"
 +
 +#include "UI_resources.h"
 +
 +#include "outliner_intern.h" /* own include */
 +
 +/* Prototypes. */
 +static int collection_delete_exec(struct bContext *C, struct wmOperator *op);
 +
 +/* -------------------------------------------------------------------- */
 +
 +static LayerCollection *outliner_collection_active(bContext *C)
 +{
 +      return CTX_data_layer_collection(C);
 +}
 +
 +SceneCollection *outliner_scene_collection_from_tree_element(TreeElement *te)
 +{
 +      TreeStoreElem *tselem = TREESTORE(te);
 +
 +      if (tselem->type == TSE_SCENE_COLLECTION) {
 +              return te->directdata;
 +      }
 +      else if (tselem->type == TSE_LAYER_COLLECTION) {
 +              LayerCollection *lc = te->directdata;
 +              return lc->scene_collection;
 +      }
 +
 +      return NULL;
 +}
 +
 +/* -------------------------------------------------------------------- */
 +/* Poll functions. */
 +
 +static int collections_editor_poll(bContext *C)
 +{
 +      SpaceOops *so = CTX_wm_space_outliner(C);
 +      return (so != NULL) && (so->outlinevis == SO_COLLECTIONS);
 +}
 +
 +static int view_layer_editor_poll(bContext *C)
 +{
 +      SpaceOops *so = CTX_wm_space_outliner(C);
 +      return (so != NULL) && (so->outlinevis == SO_VIEW_LAYER);
 +}
 +
 +static int outliner_either_collection_editor_poll(bContext *C)
 +{
 +      SpaceOops *so = CTX_wm_space_outliner(C);
 +      return (so != NULL) && (ELEM(so->outlinevis, SO_VIEW_LAYER, SO_COLLECTIONS));
 +}
 +
 +static int outliner_objects_collection_poll(bContext *C)
 +{
 +      SpaceOops *so = CTX_wm_space_outliner(C);
 +      if (so == NULL) {
 +              return 0;
 +      }
 +
 +      /* Groups don't support filtering. */
 +      if ((so->outlinevis != SO_GROUPS) &&
 +          ((so->filter & (SO_FILTER_ENABLE | SO_FILTER_NO_COLLECTION)) ==
 +          (SO_FILTER_ENABLE | SO_FILTER_NO_COLLECTION)))
 +      {
 +              return 0;
 +      }
 +
 +      return ELEM(so->outlinevis, SO_VIEW_LAYER, SO_COLLECTIONS, SO_GROUPS);
 +}
 +
 +/* -------------------------------------------------------------------- */
 +/* collection manager operators */
 +
 +/**
 + * Recursively get the collection for a given index
 + */
 +static SceneCollection *scene_collection_from_index(ListBase *lb, const int number, int *i)
 +{
 +      for (SceneCollection *sc = lb->first; sc; sc = sc->next) {
 +              if (*i == number) {
 +                      return sc;
 +              }
 +
 +              (*i)++;
 +
 +              SceneCollection *sc_nested = scene_collection_from_index(&sc->scene_collections, number, i);
 +              if (sc_nested) {
 +                      return sc_nested;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +typedef struct TreeElementFindData {
 +      SceneCollection *collection;
 +      TreeElement *r_result_te;
 +} TreeElementFindData;
 +
 +static TreeTraversalAction tree_element_find_by_scene_collection_cb(TreeElement *te, void *customdata)
 +{
 +      TreeElementFindData *data = customdata;
 +      const SceneCollection *current_element_sc = outliner_scene_collection_from_tree_element(te);
 +
 +      if (current_element_sc == data->collection) {
 +              data->r_result_te = te;
 +              return TRAVERSE_BREAK;
 +      }
 +
 +      return TRAVERSE_CONTINUE;
 +}
 +
 +static TreeElement *outliner_tree_element_from_layer_collection_index(
 +        SpaceOops *soops, ViewLayer *view_layer,
 +        const int index)
 +{
 +      LayerCollection *lc = BKE_layer_collection_from_index(view_layer, index);
 +
 +      if (lc == NULL) {
 +              return NULL;
 +      }
 +
 +      /* Find the tree element containing the LayerCollection's scene_collection. */
 +      TreeElementFindData data = {
 +              .collection = lc->scene_collection,
 +              .r_result_te = NULL,
 +      };
 +      outliner_tree_traverse(soops, &soops->tree, 0, 0, tree_element_find_by_scene_collection_cb, &data);
 +
 +      return data.r_result_te;
 +}
 +
 +static int collection_link_exec(bContext *C, wmOperator *op)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      SceneCollection *sc_master = BKE_collection_master(&scene->id);
 +      SceneCollection *sc;
 +
 +      int scene_collection_index = RNA_enum_get(op->ptr, "scene_collection");
 +      if (scene_collection_index == 0) {
 +              sc = sc_master;
 +      }
 +      else {
 +              int index = 1;
 +              sc = scene_collection_from_index(&sc_master->scene_collections, scene_collection_index, &index);
 +              BLI_assert(sc);
 +      }
 +
 +      BKE_collection_link(view_layer, sc);
 +
 +      DEG_relations_tag_update(CTX_data_main(C));
 +
 +      /* TODO(sergey): Use proper flag for tagging here. */
 +      DEG_id_tag_update(&scene->id, 0);
 +
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
 +      return OPERATOR_FINISHED;
 +}
 +
 +static int collection_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      SceneCollection *master_collection = BKE_collection_master(&scene->id);
 +      if (master_collection->scene_collections.first == NULL) {
 +              RNA_enum_set(op->ptr, "scene_collection", 0);
 +              return collection_link_exec(C, op);
 +      }
 +      else {
 +              return WM_enum_search_invoke(C, op, event);
 +      }
 +}
 +
 +static void collection_scene_collection_itemf_recursive(
 +        EnumPropertyItem *tmp, EnumPropertyItem **item, int *totitem, int *value, SceneCollection *sc)
 +{
 +      tmp->value = *value;
 +      tmp->icon = ICON_COLLAPSEMENU;
 +      tmp->identifier = sc->name;
 +      tmp->name = sc->name;
 +      RNA_enum_item_add(item, totitem, tmp);
 +
 +      (*value)++;
 +
 +      for (SceneCollection *ncs = sc->scene_collections.first; ncs; ncs = ncs->next) {
 +              collection_scene_collection_itemf_recursive(tmp, item, totitem, value, ncs);
 +      }
 +}
 +
 +static const EnumPropertyItem *collection_scene_collection_itemf(
 +        bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
 +{
 +      EnumPropertyItem tmp = {0, "", 0, "", ""};
 +      EnumPropertyItem *item = NULL;
 +      int value = 0, totitem = 0;
 +
 +      Scene *scene = CTX_data_scene(C);
 +      SceneCollection *sc = BKE_collection_master(&scene->id);
 +
 +      collection_scene_collection_itemf_recursive(&tmp, &item, &totitem, &value, sc);
 +      RNA_enum_item_end(&item, &totitem);
 +      *r_free = true;
 +
 +      return item;
 +}
 +
 +void OUTLINER_OT_collection_link(wmOperatorType *ot)
 +{
 +      PropertyRNA *prop;
 +
 +      /* identifiers */
 +      ot->name = "Link Collection";
 +      ot->idname = "OUTLINER_OT_collection_link";
 +      ot->description = "Link a new collection to the active layer";
 +
 +      /* api callbacks */
 +      ot->exec = collection_link_exec;
 +      ot->invoke = collection_link_invoke;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +
 +      prop = RNA_def_enum(ot->srna, "scene_collection", DummyRNA_NULL_items, 0, "Scene Collection", "");
 +      RNA_def_enum_funcs(prop, collection_scene_collection_itemf);
 +      RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
 +      ot->prop = prop;
 +}
 +
 +/**
 + * Returns true if selected element is a collection directly
 + * linked to the active ViewLayer (not a nested collection)
 + */
 +static int collection_unlink_poll(bContext *C)
 +{
 +      if (view_layer_editor_poll(C) == 0) {
 +              return 0;
 +      }
 +
 +      LayerCollection *lc = outliner_collection_active(C);
 +
 +      if (lc == NULL) {
 +              return 0;
 +      }
 +
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      return BLI_findindex(&view_layer->layer_collections, lc) != -1 ? 1 : 0;
 +}
 +
 +static int collection_unlink_exec(bContext *C, wmOperator *op)
 +{
 +      LayerCollection *lc = outliner_collection_active(C);
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +
 +      if (lc == NULL) {
 +              BKE_report(op->reports, RPT_ERROR, "Active element is not a collection");
 +              return OPERATOR_CANCELLED;
 +      }
 +
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      BKE_collection_unlink(view_layer, lc);
 +
 +      if (soops) {
 +              outliner_cleanup_tree(soops);
 +      }
 +
 +      DEG_relations_tag_update(CTX_data_main(C));
 +
 +      /* TODO(sergey): Use proper flag for tagging here. */
 +      DEG_id_tag_update(&CTX_data_scene(C)->id, 0);
 +
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
 +      return OPERATOR_FINISHED;
 +}
 +
 +void OUTLINER_OT_collection_unlink(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Unlink Collection";
 +      ot->idname = "OUTLINER_OT_collection_unlink";
 +      ot->description = "Unlink collection from the active layer";
 +
 +      /* api callbacks */
 +      ot->exec = collection_unlink_exec;
 +      ot->poll = collection_unlink_poll;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
 +
 +/**********************************************************************************/
 +/* Add new collection. */
 +
 +static int collection_new_exec(bContext *C, wmOperator *UNUSED(op))
 +{
 +      Main *bmain = CTX_data_main(C);
 +      Scene *scene = CTX_data_scene(C);
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      SceneCollection *scene_collection_parent = BKE_collection_master(&scene->id);
 +      SceneCollection *scene_collection = BKE_collection_add(&scene->id, scene_collection_parent, COLLECTION_TYPE_NONE, NULL);
 +      BKE_collection_link(view_layer, scene_collection);
 +
 +      DEG_relations_tag_update(bmain);
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
 +      return OPERATOR_FINISHED;
 +}
 +
 +void OUTLINER_OT_collection_new(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "New Collection";
 +      ot->idname = "OUTLINER_OT_collection_new";
 +      ot->description = "Add a new collection to the scene";
 +
 +      /* api callbacks */
 +      ot->exec = collection_new_exec;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
 +
 +/**********************************************************************************/
 +/* Add new nested collection. */
 +
 +struct CollectionNewData
 +{
 +      bool error;
 +      SceneCollection *scene_collection;
 +};
 +
 +static TreeTraversalAction collection_find_selected_to_add(TreeElement *te, void *customdata)
 +{
 +      struct CollectionNewData *data = customdata;
 +      SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te);
 +
 +      if (!scene_collection) {
 +              return TRAVERSE_SKIP_CHILDS;
 +      }
 +
 +      if (data->scene_collection != NULL) {
 +              data->error = true;
 +              return TRAVERSE_BREAK;
 +      }
 +
 +      data->scene_collection = scene_collection;
 +      return TRAVERSE_CONTINUE;
 +}
 +
 +static int collection_nested_new_exec(bContext *C, wmOperator *op)
 +{
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +      Main *bmain = CTX_data_main(C);
 +      Scene *scene = CTX_data_scene(C);
 +
 +      struct CollectionNewData data = {
 +              .error = false,
 +              .scene_collection = NULL,
 +      };
 +
 +      outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_to_add, &data);
 +
 +      if (data.error) {
 +              BKE_report(op->reports, RPT_ERROR, "More than one collection is selected");
 +              return OPERATOR_CANCELLED;
 +      }
 +
 +      BKE_collection_add(
 +                  &scene->id,
 +                  data.scene_collection,
 +                  COLLECTION_TYPE_NONE,
 +                  NULL);
 +
 +      outliner_cleanup_tree(soops);
 +      DEG_relations_tag_update(bmain);
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
 +      return OPERATOR_FINISHED;
 +}
 +
 +void OUTLINER_OT_collection_nested_new(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "New Nested Collection";
 +      ot->idname = "OUTLINER_OT_collection_nested_new";
 +      ot->description = "Add a new collection inside selected collection";
 +
 +      /* api callbacks */
 +      ot->exec = collection_nested_new_exec;
 +      ot->poll = collections_editor_poll;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
 +
 +/**********************************************************************************/
 +/* Delete selected collection. */
 +
 +void OUTLINER_OT_collection_delete_selected(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Delete Selected Collections";
 +      ot->idname = "OUTLINER_OT_collection_delete_selected";
 +      ot->description = "Delete all the selected collections";
 +
 +      /* api callbacks */
 +      ot->exec = collection_delete_exec;
 +      ot->poll = collections_editor_poll;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
 +
 +/**********************************************************************************/
 +/* Add new selected objects. */
 +
 +struct SceneCollectionSelectedData {
 +      ListBase scene_collections_array;
 +};
 +
 +static TreeTraversalAction collection_find_selected_scene_collections(TreeElement *te, void *customdata)
 +{
 +      struct SceneCollectionSelectedData *data = customdata;
 +      SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te);
 +
 +      if (!scene_collection) {
 +              return TRAVERSE_SKIP_CHILDS;
 +      }
 +
 +      BLI_addtail(&data->scene_collections_array, BLI_genericNodeN(scene_collection));
 +      return TRAVERSE_CONTINUE;
 +}
 +
 +static int collection_objects_add_exec(bContext *C, wmOperator *op)
 +{
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +      Main *bmain = CTX_data_main(C);
 +      Scene *scene = CTX_data_scene(C);
 +
 +      struct SceneCollectionSelectedData data = {
 +              .scene_collections_array = {NULL, NULL},
 +      };
 +
 +      outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_scene_collections, &data);
 +
 +      if (BLI_listbase_is_empty(&data.scene_collections_array)) {
 +              BKE_report(op->reports, RPT_ERROR, "No collection is selected");
 +              return OPERATOR_CANCELLED;
 +      }
 +
 +      CTX_DATA_BEGIN (C, struct Object *, ob, selected_objects)
 +      {
-               BLI_LISTBASE_FOREACH (LinkData *, link, &data.scene_collections_array) {
++              LISTBASE_FOREACH (LinkData *, link, &data.scene_collections_array) {
 +                      SceneCollection *scene_collection = link->data;
 +                      BKE_collection_object_add(
 +                                  &scene->id,
 +                                  scene_collection,
 +                                  ob);
 +              }
 +      }
 +      CTX_DATA_END;
 +      BLI_freelistN(&data.scene_collections_array);
 +
 +      outliner_cleanup_tree(soops);
 +      DEG_relations_tag_update(bmain);
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
 +      return OPERATOR_FINISHED;
 +}
 +
 +void OUTLINER_OT_collection_objects_add(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Add Objects";
 +      ot->idname = "OUTLINER_OT_collection_objects_add";
 +      ot->description = "Add selected objects to collection";
 +
 +      /* api callbacks */
 +      ot->exec = collection_objects_add_exec;
 +      ot->poll = collections_editor_poll;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
 +
 +/**********************************************************************************/
 +/* Remove selected objects. */
 +
 +
 +static int collection_objects_remove_exec(bContext *C, wmOperator *op)
 +{
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +      Main *bmain = CTX_data_main(C);
 +      Scene *scene = CTX_data_scene(C);
 +
 +      struct SceneCollectionSelectedData data = {
 +              .scene_collections_array = {NULL, NULL},
 +      };
 +
 +      outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_selected_scene_collections, &data);
 +
 +      if (BLI_listbase_is_empty(&data.scene_collections_array)) {
 +              BKE_report(op->reports, RPT_ERROR, "No collection is selected");
 +              return OPERATOR_CANCELLED;
 +      }
 +
 +      CTX_DATA_BEGIN (C, struct Object *, ob, selected_objects)
 +      {
-       BLI_LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) {
++              LISTBASE_FOREACH (LinkData *, link, &data.scene_collections_array) {
 +                      SceneCollection *scene_collection = link->data;
 +                      BKE_collection_object_remove(
 +                                  bmain,
 +                                  &scene->id,
 +                                  scene_collection,
 +                                  ob,
 +                                  true);
 +              }
 +      }
 +      CTX_DATA_END;
 +      BLI_freelistN(&data.scene_collections_array);
 +
 +      outliner_cleanup_tree(soops);
 +      DEG_relations_tag_update(bmain);
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
 +      return OPERATOR_FINISHED;
 +}
 +
 +void OUTLINER_OT_collection_objects_remove(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Remove Objects";
 +      ot->idname = "OUTLINER_OT_collection_objects_remove";
 +      ot->description = "Remove selected objects from collection";
 +
 +      /* api callbacks */
 +      ot->exec = collection_objects_remove_exec;
 +      ot->poll = collections_editor_poll;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
 +
 +static TreeElement *outliner_collection_parent_element_get(TreeElement *te)
 +{
 +      TreeElement *te_parent = te;
 +      while ((te_parent = te_parent->parent)) {
 +              if (outliner_scene_collection_from_tree_element(te->parent)) {
 +                      return te_parent;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +static int object_collection_remove_exec(bContext *C, wmOperator *UNUSED(op))
 +{
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +      Main *bmain = CTX_data_main(C);
 +
 +      struct ObjectsSelectedData data = {
 +              .objects_selected_array = {NULL, NULL},
 +      };
 +
 +      outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &data);
 +
-       BLI_LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) {
++      LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) {
 +              TreeElement *te = (TreeElement *)link->data;
 +              Object *ob = (Object *)TREESTORE(te)->id;
 +              SceneCollection *scene_collection = NULL;
 +
 +              TreeElement *te_parent = outliner_collection_parent_element_get(te);
 +              if (te_parent != NULL) {
 +                      scene_collection = outliner_scene_collection_from_tree_element(te_parent);
 +                      ID *owner_id = TREESTORE(te_parent)->id;
 +                      BKE_collection_object_remove(bmain, owner_id, scene_collection, ob, true);
 +                      DEG_id_tag_update(owner_id, DEG_TAG_BASE_FLAGS_UPDATE);
 +              }
 +      }
 +
 +      BLI_freelistN(&data.objects_selected_array);
 +
 +      outliner_cleanup_tree(soops);
 +      DEG_relations_tag_update(bmain);
 +
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
 +      WM_main_add_notifier(NC_SCENE | ND_OB_ACTIVE, NULL);
 +      WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, NULL);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void OUTLINER_OT_object_remove_from_collection(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Remove Object from Collection";
 +      ot->idname = "OUTLINER_OT_object_remove_from_collection";
 +      ot->description = "Remove selected objects from their respective collection";
 +
 +      /* api callbacks */
 +      ot->exec = object_collection_remove_exec;
 +      ot->poll = outliner_objects_collection_poll;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
 +
 +static int object_add_to_new_collection_exec(bContext *C, wmOperator *op)
 +{
 +      int operator_result = OPERATOR_CANCELLED;
 +
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +      Main *bmain = CTX_data_main(C);
 +
 +      SceneCollection *scene_collection_parent, *scene_collection_new;
 +      TreeElement *te_active, *te_parent;
 +
 +      struct ObjectsSelectedData data = {NULL}, active = {NULL};
 +
 +      outliner_tree_traverse(soops, &soops->tree, 0, TSE_HIGHLIGHTED, outliner_find_selected_objects, &active);
 +      if (BLI_listbase_is_empty(&active.objects_selected_array)) {
 +              BKE_report(op->reports, RPT_ERROR, "No object is selected");
 +              goto cleanup;
 +      }
 +
 +      outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &data);
 +      if (BLI_listbase_is_empty(&data.objects_selected_array)) {
 +              BKE_report(op->reports, RPT_ERROR, "No objects are selected");
 +              goto cleanup;
 +      }
 +
 +      /* Heuristic to get the "active" / "last object" */
 +      te_active = ((LinkData *)active.objects_selected_array.first)->data;
 +      te_parent = outliner_collection_parent_element_get(te_active);
 +
 +      if (te_parent == NULL) {
 +              BKE_reportf(op->reports, RPT_ERROR, "Couldn't find collection of \"%s\" object", te_active->name);
 +              goto cleanup;
 +      }
 +
 +      ID *owner_id = TREESTORE(te_parent)->id;
 +      scene_collection_parent = outliner_scene_collection_from_tree_element(te_parent);
 +      scene_collection_new = BKE_collection_add(owner_id, scene_collection_parent, scene_collection_parent->type, NULL);
 +
++      LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) {
 +              TreeElement *te = (TreeElement *)link->data;
 +              Object *ob = (Object *)TREESTORE(te)->id;
 +              BKE_collection_object_add(owner_id, scene_collection_new, ob);
 +      }
 +
 +      outliner_cleanup_tree(soops);
 +      DEG_relations_tag_update(bmain);
 +
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
 +
 +      operator_result = OPERATOR_FINISHED;
 +cleanup:
 +      BLI_freelistN(&active.objects_selected_array);
 +      BLI_freelistN(&data.objects_selected_array);
 +      return operator_result;
 +}
 +
 +void OUTLINER_OT_object_add_to_new_collection(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Add Objects to New Collection";
 +      ot->idname = "OUTLINER_OT_object_add_to_new_collection";
 +      ot->description = "Add objects to a new collection";
 +
 +      /* api callbacks */
 +      ot->exec = object_add_to_new_collection_exec;
 +      ot->poll = outliner_objects_collection_poll;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
 +
 +struct CollectionDeleteData {
 +      Scene *scene;
 +      SpaceOops *soops;
 +      GSet *collections_to_delete;
 +};
 +
 +static TreeTraversalAction collection_find_data_to_delete(TreeElement *te, void *customdata)
 +{
 +      struct CollectionDeleteData *data = customdata;
 +      SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te);
 +
 +      if (!scene_collection) {
 +              return TRAVERSE_SKIP_CHILDS;
 +      }
 +
 +      if (scene_collection == BKE_collection_master(&data->scene->id)) {
 +              /* skip - showing warning/error message might be missleading
 +               * when deleting multiple collections, so just do nothing */
 +      }
 +      else {
 +              BLI_gset_add(data->collections_to_delete, scene_collection);
 +              return TRAVERSE_SKIP_CHILDS; /* Childs will be gone anyway, no need to recurse deeper. */
 +      }
 +
 +      return TRAVERSE_CONTINUE;
 +}
 +
 +static TreeTraversalAction collection_delete_elements_from_collection(TreeElement *te, void *customdata)
 +{
 +      struct CollectionDeleteData *data = customdata;
 +      SceneCollection *scene_collection = outliner_scene_collection_from_tree_element(te);
 +
 +      if (!scene_collection) {
 +              return TRAVERSE_SKIP_CHILDS;
 +      }
 +
 +      const bool will_be_deleted = BLI_gset_haskey(data->collections_to_delete, scene_collection);
 +      if (will_be_deleted) {
 +              outliner_free_tree_element(te, te->parent ? &te->parent->subtree : &data->soops->tree);
 +              /* Childs are freed now, so don't recurse into them. */
 +              return TRAVERSE_SKIP_CHILDS;
 +      }
 +
 +      return TRAVERSE_CONTINUE;
 +}
 +
 +static int collection_delete_exec(bContext *C, wmOperator *UNUSED(op))
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +      struct CollectionDeleteData data = {.scene = scene, .soops = soops};
 +
 +      data.collections_to_delete = BLI_gset_ptr_new(__func__);
 +
 +      /* We first walk over and find the SceneCollections we actually want to delete (ignoring duplicates). */
 +      outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_delete, &data);
 +
 +      /* Now, delete all tree elements representing a collection that will be deleted. We'll look for a
 +       * new element to select in a few lines, so we can't wait until the tree is recreated on redraw. */
 +      outliner_tree_traverse(soops, &soops->tree, 0, 0, collection_delete_elements_from_collection, &data);
 +
 +      /* Effectively delete the collections. */
 +      GSetIterator collections_to_delete_iter;
 +      GSET_ITER(collections_to_delete_iter, data.collections_to_delete) {
 +              SceneCollection *sc = BLI_gsetIterator_getKey(&collections_to_delete_iter);
 +              BKE_collection_remove(&data.scene->id, sc);
 +      }
 +
 +      BLI_gset_free(data.collections_to_delete, NULL);
 +
 +      TreeElement *select_te = outliner_tree_element_from_layer_collection_index(soops, CTX_data_view_layer(C), 0);
 +      if (select_te) {
 +              outliner_item_select(soops, select_te, false, false);
 +      }
 +
 +      DEG_relations_tag_update(CTX_data_main(C));
 +
 +      /* TODO(sergey): Use proper flag for tagging here. */
 +      DEG_id_tag_update(&scene->id, 0);
 +
 +      soops->storeflag |= SO_TREESTORE_REDRAW;
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void OUTLINER_OT_collections_delete(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Delete";
 +      ot->idname = "OUTLINER_OT_collections_delete";
 +      ot->description = "Delete selected overrides or collections";
 +
 +      /* api callbacks */
 +      ot->exec = collection_delete_exec;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
 +
 +static int collection_select_exec(bContext *C, wmOperator *op)
 +{
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      const int collection_index = RNA_int_get(op->ptr, "collection_index");
 +      view_layer->active_collection = collection_index;
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
 +      return OPERATOR_FINISHED;
 +}
 +
 +void OUTLINER_OT_collection_select(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Select";
 +      ot->idname = "OUTLINER_OT_collection_select";
 +      ot->description = "Change active collection or override";
 +
 +      /* api callbacks */
 +      ot->exec = collection_select_exec;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +
 +      RNA_def_int(ot->srna, "collection_index", 0, 0, INT_MAX, "Index",
 +                  "Index of collection to select", 0, INT_MAX);
 +}
 +
 +#define ACTION_DISABLE 0
 +#define ACTION_ENABLE 1
 +#define ACTION_TOGGLE 2
 +
 +static int collection_toggle_exec(bContext *C, wmOperator *op)
 +{
 +      Main *bmain = CTX_data_main(C);
 +      Scene *scene = CTX_data_scene(C);
 +      int action = RNA_enum_get(op->ptr, "action");
 +      LayerCollection *layer_collection = CTX_data_layer_collection(C);
 +
 +      if (layer_collection->flag & COLLECTION_DISABLED) {
 +              if (ELEM(action, ACTION_TOGGLE, ACTION_ENABLE)) {
 +                      layer_collection->flag &= ~COLLECTION_DISABLED;
 +              }
 +              else { /* ACTION_DISABLE */
 +                      BKE_reportf(op->reports, RPT_ERROR, "Layer collection %s already disabled",
 +                                  layer_collection->scene_collection->name);
 +                      return OPERATOR_CANCELLED;
 +              }
 +      }
 +      else {
 +              if (ELEM(action, ACTION_TOGGLE, ACTION_DISABLE)) {
 +                      layer_collection->flag |= COLLECTION_DISABLED;
 +              }
 +              else { /* ACTION_ENABLE */
 +                      BKE_reportf(op->reports, RPT_ERROR, "Layer collection %s already enabled",
 +                                  layer_collection->scene_collection->name);
 +                      return OPERATOR_CANCELLED;
 +              }
 +      }
 +
 +      DEG_relations_tag_update(bmain);
 +      /* TODO(sergey): Use proper flag for tagging here. */
 +      DEG_id_tag_update(&scene->id, 0);
 +
 +      WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
 +      WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void OUTLINER_OT_collection_toggle(wmOperatorType *ot)
 +{
 +      PropertyRNA *prop;
 +
 +      static EnumPropertyItem actions_items[] = {
 +              {ACTION_DISABLE, "DISABLE", 0, "Disable", "Disable selected markers"},
 +              {ACTION_ENABLE, "ENABLE", 0, "Enable", "Enable selected markers"},
 +              {ACTION_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
 +              {0, NULL, 0, NULL, NULL}
 +      };
 +
 +      /* identifiers */
 +      ot->name = "Toggle Collection";
 +      ot->idname = "OUTLINER_OT_collection_toggle";
 +      ot->description = "Deselect collection objects";
 +
 +      /* api callbacks */
 +      ot->exec = collection_toggle_exec;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +
 +      /* properties */
 +      prop = RNA_def_int(ot->srna, "collection_index", -1, -1, INT_MAX, "Collection Index", "Index of collection to toggle", 0, INT_MAX);
 +      RNA_def_property_flag(prop, PROP_SKIP_SAVE);
 +      prop = RNA_def_enum(ot->srna, "action", actions_items, ACTION_TOGGLE, "Action", "Selection action to execute");
 +      RNA_def_property_flag(prop, PROP_SKIP_SAVE);
 +}
 +
 +#undef ACTION_TOGGLE
 +#undef ACTION_ENABLE
 +#undef ACTION_DISABLE
 +
 +struct CollectionObjectsSelectData {
 +      bool error;
 +      LayerCollection *layer_collection;
 +};
 +
 +static TreeTraversalAction outliner_find_first_selected_layer_collection(TreeElement *te, void *customdata)
 +{
 +      struct CollectionObjectsSelectData *data = customdata;
 +      TreeStoreElem *tselem = TREESTORE(te);
 +
 +      switch (tselem->type) {
 +              case TSE_LAYER_COLLECTION:
 +                      data->layer_collection = te->directdata;
 +                      return TRAVERSE_BREAK;
 +              case TSE_LAYER_COLLECTION_BASE:
 +                      return TRAVERSE_CONTINUE;
 +              default:
 +                      return TRAVERSE_SKIP_CHILDS;
 +      }
 +}
 +
 +static LayerCollection *outliner_active_layer_collection(bContext *C)
 +{
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +
 +      struct CollectionObjectsSelectData data = {
 +              .layer_collection = NULL,
 +      };
 +
 +      outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_layer_collection, &data);
 +      return data.layer_collection;
 +}
 +
 +static int collection_objects_select_exec(bContext *C, wmOperator *UNUSED(op))
 +{
 +      LayerCollection *layer_collection = outliner_active_layer_collection(C);
 +
 +      if (layer_collection == NULL) {
 +              return OPERATOR_CANCELLED;
 +      }
 +
 +      BKE_layer_collection_objects_select(layer_collection);
 +      WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, CTX_data_scene(C));
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void OUTLINER_OT_collection_objects_select(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Select Objects";
 +      ot->idname = "OUTLINER_OT_collection_objects_select";
 +      ot->description = "Select all the collection objects";
 +
 +      /* api callbacks */
 +      ot->exec = collection_objects_select_exec;
 +      ot->poll = view_layer_editor_poll;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
 +
 +struct CollectionDuplicateData {
 +      TreeElement *te;
 +};
 +
 +static TreeTraversalAction outliner_find_first_selected_collection(TreeElement *te, void *customdata)
 +{
 +      struct CollectionDuplicateData *data = customdata;
 +      TreeStoreElem *tselem = TREESTORE(te);
 +
 +      switch (tselem->type) {
 +              case TSE_LAYER_COLLECTION:
 +              case TSE_SCENE_COLLECTION:
 +                      data->te = te;
 +                      return TRAVERSE_BREAK;
 +              case TSE_LAYER_COLLECTION_BASE:
 +              default:
 +                      return TRAVERSE_CONTINUE;
 +      }
 +}
 +
 +static TreeElement *outliner_active_collection(bContext *C)
 +{
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +
 +      struct CollectionDuplicateData data = {
 +              .te = NULL,
 +      };
 +
 +      outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_first_selected_collection, &data);
 +      return data.te;
 +}
 +
 +static int collection_duplicate_exec(bContext *C, wmOperator *op)
 +{
 +      SpaceOops *soops = CTX_wm_space_outliner(C);
 +      TreeElement *te = outliner_active_collection(C);
 +
 +      BLI_assert(te != NULL);
 +      if (BKE_collection_master(TREESTORE(te)->id) == outliner_scene_collection_from_tree_element(te)) {
 +              BKE_report(op->reports, RPT_ERROR, "You can't duplicate the master collection");
 +              return OPERATOR_CANCELLED;
 +      }
 +
 +      switch (soops->outlinevis) {
 +              case SO_COLLECTIONS:
 +                      BKE_collection_duplicate(TREESTORE(te)->id, (SceneCollection *)te->directdata);
 +                      break;
 +              case SO_VIEW_LAYER:
 +              case SO_GROUPS:
 +                      BKE_layer_collection_duplicate(TREESTORE(te)->id, (LayerCollection *)te->directdata);
 +                      break;
 +      }
 +
 +      DEG_relations_tag_update(CTX_data_main(C));
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, CTX_data_scene(C));
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +void OUTLINER_OT_collection_duplicate(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Duplicate Collection";
 +      ot->idname = "OUTLINER_OT_collection_duplicate";
 +      ot->description = "Duplicate collection";
 +
 +      /* api callbacks */
 +      ot->exec = collection_duplicate_exec;
 +      ot->poll = outliner_either_collection_editor_poll;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
index e2bd4c5672c0f0850c6b300985850e255bbcb2ab,051605b10796c00955f894ad3ab6c4bd02483f84..00eb4efde02a528c4582f0bb1314900b69c3bbd8
@@@ -423,121 -483,20 +423,121 @@@ static void outliner_add_scene_contents
  #endif
  }
  
-       BLI_LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) {
 +TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *customdata)
 +{
 +      struct ObjectsSelectedData *data = customdata;
 +      TreeStoreElem *tselem = TREESTORE(te);
 +
 +      if (ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION)) {
 +              return TRAVERSE_CONTINUE;
 +      }
 +
 +      if (tselem->type || (tselem->id == NULL) || (GS(tselem->id->name) != ID_OB)) {
 +              return TRAVERSE_SKIP_CHILDS;
 +      }
 +
 +      BLI_addtail(&data->objects_selected_array, BLI_genericNodeN(te));
 +
 +      return TRAVERSE_CONTINUE;
 +}
 +
 +/**
 + * Move objects from a collection to another.
 + * We ignore the original object being inserted, we used it for polling only.
 + * Instead we move all the selected objects around.
 + */
 +static void outliner_object_reorder(
 +        Main *bmain, SpaceOops *soops,
 +        TreeElement *insert_element,
 +        TreeElement *insert_handle, TreeElementInsertType action,
 +        const wmEvent *event)
 +{
 +      SceneCollection *sc = outliner_scene_collection_from_tree_element(insert_handle);
 +      SceneCollection *sc_ob_parent = NULL;
 +      ID *id = insert_handle->store_elem->id;
 +
 +      BLI_assert(action == TE_INSERT_INTO);
 +      UNUSED_VARS_NDEBUG(action);
 +
 +      struct ObjectsSelectedData data = {
 +              .objects_selected_array  = {NULL, NULL},
 +      };
 +
 +      const bool is_append = event->ctrl;
 +
 +      /* Make sure we include the originally inserted element as well. */
 +      TREESTORE(insert_element)->flag |= TSE_SELECTED;
 +
 +      outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &data);
++      LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) {
 +              TreeElement *ten_selected = (TreeElement *)link->data;
 +              Object *ob = (Object *)TREESTORE(ten_selected)->id;
 +
 +              if (is_append) {
 +                      BKE_collection_object_add(id, sc, ob);
 +                      continue;
 +              }
 +
 +              /* Find parent scene-collection of object. */
 +              if (ten_selected->parent) {
 +                      for (TreeElement *te_ob_parent = ten_selected->parent; te_ob_parent; te_ob_parent = te_ob_parent->parent) {
 +                              if (ELEM(TREESTORE(te_ob_parent)->type, TSE_SCENE_COLLECTION, TSE_LAYER_COLLECTION)) {
 +                                      sc_ob_parent = outliner_scene_collection_from_tree_element(te_ob_parent);
 +                                      break;
 +                              }
 +                      }
 +              }
 +              else {
 +                      sc_ob_parent = BKE_collection_master(id);
 +              }
 +
 +              BKE_collection_object_move(id, sc, sc_ob_parent, ob);
 +      }
 +
 +      BLI_freelistN(&data.objects_selected_array);
 +
 +      DEG_relations_tag_update(bmain);
 +
 +      /* TODO(sergey): Use proper flag for tagging here. */
 +      DEG_id_tag_update(id, 0);
 +
 +      WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
 +}
 +
 +static bool outliner_object_reorder_poll(
 +        const TreeElement *insert_element,
 +        TreeElement **io_insert_handle, TreeElementInsertType *io_action)
 +{
 +      TreeStoreElem *tselem_handle = TREESTORE(*io_insert_handle);
 +      if (ELEM(tselem_handle->type, TSE_SCENE_COLLECTION, TSE_LAYER_COLLECTION) &&
 +          (insert_element->parent != *io_insert_handle))
 +      {
 +              *io_action = TE_INSERT_INTO;
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
  // can be inlined if necessary
 -static void outliner_add_object_contents(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, Object *ob)
 +static void outliner_add_object_contents(
 +        SpaceOops *soops, const EvaluationContext *eval_ctx,
 +        TreeElement *te, TreeStoreElem *tselem, Object *ob)
  {
 +      te->reinsert = outliner_object_reorder;
 +      te->reinsert_poll = outliner_object_reorder_poll;
 +
        if (outliner_animdata_test(ob->adt))
 -              outliner_add_element(soops, &te->subtree, ob, te, TSE_ANIM_DATA, 0);
 -      
 -      outliner_add_element(soops, &te->subtree, ob->poselib, te, 0, 0); // XXX FIXME.. add a special type for this
 +              outliner_add_element(soops, eval_ctx, &te->subtree, ob, te, TSE_ANIM_DATA, 0);
 +
 +      outliner_add_element(soops, eval_ctx, &te->subtree, ob->poselib, te, 0, 0); // XXX FIXME.. add a special type for this
        
        if (ob->proxy && !ID_IS_LINKED(ob))
 -              outliner_add_element(soops, &te->subtree, ob->proxy, te, TSE_PROXY, 0);
 +              outliner_add_element(soops, eval_ctx, &te->subtree, ob->proxy, te, TSE_PROXY, 0);
                
 -      outliner_add_element(soops, &te->subtree, ob->gpd, te, 0, 0);
 +      outliner_add_element(soops, eval_ctx, &te->subtree, ob->gpd, te, 0, 0);
        
 -      outliner_add_element(soops, &te->subtree, ob->data, te, 0, 0);
 +      outliner_add_element(soops, eval_ctx, &te->subtree, ob->data, te, 0, 0);
        
        if (ob->pose) {
                bArmature *arm = ob->data;
@@@ -2227,13 -1630,12 +2227,13 @@@ static void outliner_filter_tree(SpaceO
  
  /* Main entry point for building the tree data-structure that the outliner represents */
  // TODO: split each mode into its own function?
 -void outliner_build_tree(Main *mainvar, Scene *scene, SpaceOops *soops)
 +void outliner_build_tree(
 +        Main *mainvar, const EvaluationContext *eval_ctx, Scene *scene,
 +        ViewLayer *view_layer, SpaceOops *soops, ARegion *ar)
  {
 -      Base *base;
        TreeElement *te = NULL, *ten;
        TreeStoreElem *tselem;
-       int show_opened = !soops->treestore || !BLI_mempool_count(soops->treestore); /* on first view, we open scenes */
+       int show_opened = !soops->treestore || !BLI_mempool_len(soops->treestore); /* on first view, we open scenes */
  
        /* Are we looking for something - we want to tag parents to filter child matches
         * - NOT in datablocks view - searching all datablocks takes way too long to be useful
index bc84d0b6748b1451ce4e61ca91d48f66fc3cbd16,0000000000000000000000000000000000000000..153052d7e1cb2550f7e1719fb56d2fc04aa11588
mode 100644,000000..100644
--- /dev/null
@@@ -1,1186 -1,0 +1,1186 @@@
-       wm_manipulatormap_select_array_ensure_len_alloc(mmap, BLI_ghash_size(hash));
 +/*
 + * ***** 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) 2014 Blender Foundation.
 + * All rights reserved.
 + *
 + * Contributor(s): Blender Foundation
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/windowmanager/manipulators/intern/wm_manipulator_map.c
 + *  \ingroup wm
 + */
 +
 +#include <string.h>
 +
 +#include "BLI_listbase.h"
 +#include "BLI_math.h"
 +#include "BLI_rect.h"
 +#include "BLI_string.h"
 +#include "BLI_ghash.h"
 +
 +#include "BKE_context.h"
 +#include "BKE_global.h"
 +
 +#include "ED_screen.h"
 +#include "ED_view3d.h"
 +
 +#include "GPU_glew.h"
 +#include "GPU_matrix.h"
 +#include "GPU_select.h"
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +#include "wm_event_system.h"
 +
 +/* for tool-tips */
 +#include "UI_interface.h"
 +
 +#include "DEG_depsgraph.h"
 +
 +/* own includes */
 +#include "wm_manipulator_wmapi.h"
 +#include "wm_manipulator_intern.h"
 +
 +/**
 + * Store all manipulator-maps here. Anyone who wants to register a manipulator for a certain
 + * area type can query the manipulator-map to do so.
 + */
 +static ListBase manipulatormaptypes = {NULL, NULL};
 +
 +/**
 + * Update when manipulator-map types change.
 + */
 +/* so operator removal can trigger update */
 +typedef enum eWM_ManipulatorGroupTypeGlobalFlag {
 +      WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT = (1 << 0),
 +      WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE = (1 << 1),
 +} eWM_ManipulatorGroupTypeGlobalFlag;
 +
 +static eWM_ManipulatorGroupTypeGlobalFlag wm_mmap_type_update_flag = 0;
 +
 +/**
 + * Manipulator-map update tagging.
 + */
 +enum {
 +      /** #manipulatormap_prepare_drawing has run */
 +      MANIPULATORMAP_IS_PREPARE_DRAW = (1 << 0),
 +      MANIPULATORMAP_IS_REFRESH_CALLBACK = (1 << 1),
 +};
 +
 +
 +/* -------------------------------------------------------------------- */
 +/** \name wmManipulatorMap Selection Array API
 + *
 + * Just handle ``wm_manipulatormap_select_array_*``, not flags or callbacks.
 + *
 + * \{ */
 +
 +static void wm_manipulatormap_select_array_ensure_len_alloc(wmManipulatorMap *mmap, int len)
 +{
 +      wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
 +      if (len <= msel->len_alloc) {
 +              return;
 +      }
 +      msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * len);
 +      msel->len_alloc = len;
 +}
 +
 +void wm_manipulatormap_select_array_clear(wmManipulatorMap *mmap)
 +{
 +      wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
 +      MEM_SAFE_FREE(msel->items);
 +      msel->len = 0;
 +      msel->len_alloc = 0;
 +}
 +
 +void wm_manipulatormap_select_array_shrink(wmManipulatorMap *mmap, int len_subtract)
 +{
 +      wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
 +      msel->len -= len_subtract;
 +      if (msel->len <= 0) {
 +              wm_manipulatormap_select_array_clear(mmap);
 +      }
 +      else {
 +              if (msel->len < msel->len_alloc / 2) {
 +                      msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len);
 +                      msel->len_alloc = msel->len;</