Merge ID static override, part I: core changes in DNA, BKE and .blend read/write.

See https://developer.blender.org/D2417 for details.

Note that since static overrides rely heavily on RNA, this commit is
essentially invisible from user PoV, more in next commits.
This commit is contained in:
Bastien Montagne 2017-11-29 15:05:03 +01:00
parent a36a47fe15
commit 39b8a33068
11 changed files with 1099 additions and 130 deletions

View File

@ -0,0 +1,86 @@
/*
* ***** 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) 2016 by Blender Foundation.
* All rights reserved.
*
* Contributor(s): Bastien Montagne.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef __BKE_LIBRARY_OVERRIDE_H__
#define __BKE_LIBRARY_OVERRIDE_H__
/** \file BKE_library_override.h
* \ingroup bke
* \since December 2016
* \author mont29
*/
struct ID;
struct IDOverrideStatic;
struct IDOverrideStaticProperty;
struct IDOverrideStaticPropertyOperation;
struct Main;
struct IDOverrideStatic *BKE_override_static_init(struct ID *local_id, struct ID *reference_id);
void BKE_override_static_copy(struct ID *dst_id, const struct ID *src_id);
void BKE_override_static_clear(struct IDOverrideStatic *override);
void BKE_override_static_free(struct IDOverrideStatic **override);
struct ID *BKE_override_static_create_from(struct Main *bmain, struct ID *reference_id);
struct IDOverrideStaticProperty *BKE_override_static_property_find(struct IDOverrideStatic *override, const char *rna_path);
struct IDOverrideStaticProperty *BKE_override_static_property_get(struct IDOverrideStatic *override, const char *rna_path, bool *r_created);
void BKE_override_static_property_delete(struct IDOverrideStatic *override, struct IDOverrideStaticProperty *override_property);
struct IDOverrideStaticPropertyOperation *BKE_override_static_property_operation_find(
struct IDOverrideStaticProperty *override_property,
const char *subitem_refname, const char *subitem_locname,
const int subitem_refindex, const int subitem_locindex, const bool strict, bool *r_strict);
struct IDOverrideStaticPropertyOperation *BKE_override_static_property_operation_get(
struct IDOverrideStaticProperty *override_property, const short operation,
const char *subitem_refname, const char *subitem_locname,
const int subitem_refindex, const int subitem_locindex,
const bool strict, bool *r_strict, bool *r_created);
void BKE_override_static_property_operation_delete(
struct IDOverrideStaticProperty *override_property, struct IDOverrideStaticPropertyOperation *override_property_operation);
bool BKE_override_static_status_check_local(struct ID *local);
bool BKE_override_static_status_check_reference(struct ID *local);
bool BKE_override_static_operations_create(struct ID *local);
void BKE_main_override_static_operations_create(struct Main *bmain);
void BKE_override_static_update(struct Main *bmain, struct ID *local);
void BKE_main_override_static_update(struct Main *bmain);
/* Storage (.blend file writing) part. */
/* For now, we just use a temp main list. */
typedef struct Main OverrideStaticStorage;
OverrideStaticStorage *BKE_override_static_operations_store_initialize(void);
struct ID *BKE_override_static_operations_store_start(OverrideStaticStorage *override_storage, struct ID *local);
void BKE_override_static_operations_store_end(OverrideStaticStorage *override_storage, struct ID *local);
void BKE_override_static_operations_store_finalize(OverrideStaticStorage *override_storage);
#endif /* __BKE_LIBRARY_OVERRIDE_H__ */

View File

@ -56,6 +56,9 @@ enum {
* How to handle that kind of cases totally depends on what caller code is doing... */
IDWALK_CB_LOOPBACK = (1 << 4),
/** That ID is used as static override's reference by its owner. */
IDWALK_CB_STATIC_OVERRIDE_REFERENCE = (1 << 5),
/**
* Adjusts #ID.us reference-count.
* \note keep in sync with 'newlibadr_us' use in readfile.c

View File

@ -51,6 +51,8 @@ enum {
* This is needed e.g. in reload scenario, since we have to ensure remapping of Armature data of local proxy
* is also performed. Usual nightmare... */
ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE = 1 << 4,
/* Do not remap static override pointers. */
ID_REMAP_SKIP_STATIC_OVERRIDE = 1 << 5,
};
/* Note: Requiring new_id to be non-null, this *may* not be the case ultimately, but makes things simpler for now. */

View File

@ -124,6 +124,7 @@ set(SRC
intern/lattice.c
intern/library.c
intern/library_idmap.c
intern/library_override.c
intern/library_query.c
intern/library_remap.c
intern/linestyle.c
@ -256,6 +257,7 @@ set(SRC
BKE_lattice.h
BKE_library.h
BKE_library_idmap.h
BKE_library_override.h
BKE_library_query.h
BKE_library_remap.h
BKE_linestyle.h

View File

@ -104,6 +104,7 @@
#include "BKE_lamp.h"
#include "BKE_lattice.h"
#include "BKE_library.h"
#include "BKE_library_override.h"
#include "BKE_library_query.h"
#include "BKE_library_remap.h"
#include "BKE_linestyle.h"
@ -1411,6 +1412,15 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int fla
new_id->properties = IDP_CopyProperty_ex(id->properties, flag);
}
/* XXX Again... We need a way to control what we copy in a much more refined way.
* We cannot always copy this, some internal copying will die on it! */
/* For now, upper level code will have to do that itself when required. */
#if 0
if (id->override != NULL) {
BKE_override_copy(new_id, id);
}
#endif
/* the duplicate should get a copy of the animdata */
id_copy_animdata(bmain, new_id, (flag & LIB_ID_COPY_ACTIONS) != 0 && (flag & LIB_ID_CREATE_NO_MAIN) == 0);

View File

@ -0,0 +1,680 @@
/*
* ***** 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) 2016 by Blender Foundation.
* All rights reserved.
*
* Contributor(s): Bastien Montagne.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenkernel/intern/library_override.c
* \ingroup bke
*/
#include <stdlib.h>
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
#include "DNA_object_types.h"
#include "DEG_depsgraph.h"
#include "BKE_library.h"
#include "BKE_library_override.h"
#include "BKE_library_remap.h"
#include "BKE_main.h"
#include "BLI_utildefines.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "RNA_access.h"
#include "RNA_types.h"
#include "PIL_time.h"
#include "PIL_time_utildefines.h"
#define OVERRIDE_AUTO_CHECK_DELAY 0.2 /* 200ms between auto-override checks. */
static void bke_override_property_copy(IDOverrideStaticProperty *op_dst, IDOverrideStaticProperty *op_src);
static void bke_override_property_operation_copy(IDOverrideStaticPropertyOperation *opop_dst, IDOverrideStaticPropertyOperation *opop_src);
static void bke_override_property_clear(IDOverrideStaticProperty *op);
static void bke_override_property_operation_clear(IDOverrideStaticPropertyOperation *opop);
/** Initialize empty overriding of \a reference_id by \a local_id. */
IDOverrideStatic *BKE_override_static_init(ID *local_id, ID *reference_id)
{
/* If reference_id is NULL, we are creating an override template for purely local data.
* Else, reference *must* be linked data. */
BLI_assert(reference_id == NULL || reference_id->lib != NULL);
BLI_assert(local_id->override_static == NULL);
if (reference_id != NULL && reference_id->override_static != NULL && reference_id->override_static->reference == NULL) {
/* reference ID has an override template, use it! */
BKE_override_static_copy(local_id, reference_id);
return local_id->override_static;
}
/* Else, generate new empty override. */
local_id->override_static = MEM_callocN(sizeof(*local_id->override_static), __func__);
local_id->override_static->reference = reference_id;
id_us_plus(reference_id);
local_id->tag &= ~LIB_TAG_OVERRIDESTATIC_OK;
/* TODO do we want to add tag or flag to referee to mark it as such? */
return local_id->override_static;
}
/** Deep copy of a whole override from \a src_id to \a dst_id. */
void BKE_override_static_copy(ID *dst_id, const ID *src_id)
{
BLI_assert(src_id->override_static != NULL);
if (dst_id->override_static != NULL) {
if (src_id->override_static == NULL) {
BKE_override_static_free(&dst_id->override_static);
return;
}
else {
BKE_override_static_clear(dst_id->override_static);
}
}
else if (src_id->override_static == NULL) {
return;
}
else {
BKE_override_static_init(dst_id, NULL);
}
/* Source is already overriding data, we copy it but reuse its reference for dest ID.
* otherwise, source is only an override template, it then becomes reference of dest ID. */
dst_id->override_static->reference = src_id->override_static->reference ? src_id->override_static->reference : (ID *)src_id;
id_us_plus(dst_id->override_static->reference);
BLI_duplicatelist(&dst_id->override_static->properties, &src_id->override_static->properties);
for (IDOverrideStaticProperty *op_dst = dst_id->override_static->properties.first, *op_src = src_id->override_static->properties.first;
op_dst;
op_dst = op_dst->next, op_src = op_src->next)
{
bke_override_property_copy(op_dst, op_src);
}
dst_id->tag &= ~LIB_TAG_OVERRIDESTATIC_OK;
}
/** Clear any overriding data from given \a override. */
void BKE_override_static_clear(IDOverrideStatic *override)
{
BLI_assert(override != NULL);
for (IDOverrideStaticProperty *op = override->properties.first; op; op = op->next) {
bke_override_property_clear(op);
}
BLI_freelistN(&override->properties);
id_us_min(override->reference);
/* override->storage should never be refcounted... */
}
/** Free given \a override. */
void BKE_override_static_free(struct IDOverrideStatic **override)
{
BLI_assert(*override != NULL);
BKE_override_static_clear(*override);
MEM_freeN(*override);
*override = NULL;
}
/** Create an overriden local copy of linked reference. */
ID *BKE_override_static_create_from(Main *bmain, ID *reference_id)
{
BLI_assert(reference_id != NULL);
BLI_assert(reference_id->lib != NULL);
ID *local_id;
if (!id_copy(bmain, reference_id, (ID **)&local_id, false)) {
return NULL;
}
id_us_min(local_id);
BKE_override_static_init(local_id, reference_id);
local_id->flag |= LIB_OVERRIDE_STATIC_AUTO;
/* Remapping, we obviously only want to affect local data (and not our own reference pointer to overriden ID). */
BKE_libblock_remap(bmain, reference_id, local_id, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_STATIC_OVERRIDE);
return local_id;
}
/**
* Find override property from given RNA path, if it exists.
*/
IDOverrideStaticProperty *BKE_override_static_property_find(IDOverrideStatic *override, const char *rna_path)
{
/* XXX TODO we'll most likely want a runtime ghash to store that mapping at some point. */
return BLI_findstring_ptr(&override->properties, rna_path, offsetof(IDOverrideStaticProperty, rna_path));
}
/**
* Find override property from given RNA path, or create it if it does not exist.
*/
IDOverrideStaticProperty *BKE_override_static_property_get(IDOverrideStatic *override, const char *rna_path, bool *r_created)
{
/* XXX TODO we'll most likely want a runtime ghash to store taht mapping at some point. */
IDOverrideStaticProperty *op = BKE_override_static_property_find(override, rna_path);
if (op == NULL) {
op = MEM_callocN(sizeof(IDOverrideStaticProperty), __func__);
op->rna_path = BLI_strdup(rna_path);
BLI_addtail(&override->properties, op);
if (r_created) {
*r_created = true;
}
}
else if (r_created) {
*r_created = false;
}
return op;
}
void bke_override_property_copy(IDOverrideStaticProperty *op_dst, IDOverrideStaticProperty *op_src)
{
op_dst->rna_path = BLI_strdup(op_src->rna_path);
BLI_duplicatelist(&op_dst->operations, &op_src->operations);
for (IDOverrideStaticPropertyOperation *opop_dst = op_dst->operations.first, *opop_src = op_src->operations.first;
opop_dst;
opop_dst = opop_dst->next, opop_src = opop_src->next)
{
bke_override_property_operation_copy(opop_dst, opop_src);
}
}
void bke_override_property_clear(IDOverrideStaticProperty *op)
{
BLI_assert(op->rna_path != NULL);
MEM_freeN(op->rna_path);
for (IDOverrideStaticPropertyOperation *opop = op->operations.first; opop; opop = opop->next) {
bke_override_property_operation_clear(opop);
}
BLI_freelistN(&op->operations);
}
/**
* Remove and free given \a override_property from given ID \a override.
*/
void BKE_override_static_property_delete(IDOverrideStatic *override, IDOverrideStaticProperty *override_property)
{
bke_override_property_clear(override_property);
BLI_freelinkN(&override->properties, override_property);
}
/**
* Find override property operation from given sub-item(s), if it exists.
*/
IDOverrideStaticPropertyOperation *BKE_override_static_property_operation_find(
IDOverrideStaticProperty *override_property,
const char *subitem_refname, const char *subitem_locname,
const int subitem_refindex, const int subitem_locindex, const bool strict, bool *r_strict)
{
IDOverrideStaticPropertyOperation *opop;
const int subitem_defindex = -1;
if (r_strict) {
*r_strict = true;
}
if (subitem_locname &&
(opop = BLI_findstring_ptr(&override_property->operations, subitem_locname,
offsetof(IDOverrideStaticPropertyOperation, subitem_local_name))))
{
return opop;
}
if (subitem_refname &&
(opop = BLI_findstring_ptr(&override_property->operations, subitem_refname,
offsetof(IDOverrideStaticPropertyOperation, subitem_reference_name))))
{
return opop;
}
if ((opop = BLI_listbase_bytes_find(&override_property->operations, &subitem_locindex, sizeof(subitem_locindex),
offsetof(IDOverrideStaticPropertyOperation, subitem_local_index))))
{
return opop;
}
if ((opop = BLI_listbase_bytes_find(&override_property->operations, &subitem_refindex, sizeof(subitem_refindex),
offsetof(IDOverrideStaticPropertyOperation, subitem_reference_index))))
{
return opop;
}
/* index == -1 means all indices, that is valid fallback in case we requested specific index. */
if (!strict && (subitem_locindex != subitem_defindex) &&
(opop = BLI_listbase_bytes_find(&override_property->operations, &subitem_defindex, sizeof(subitem_defindex),
offsetof(IDOverrideStaticPropertyOperation, subitem_local_index))))
{
if (r_strict) {
*r_strict = false;
}
return opop;
}
return NULL;
}
/**
* Find override property operation from given sub-item(s), or create it if it does not exist.
*/
IDOverrideStaticPropertyOperation *BKE_override_static_property_operation_get(
IDOverrideStaticProperty *override_property, const short operation,
const char *subitem_refname, const char *subitem_locname,
const int subitem_refindex, const int subitem_locindex,
const bool strict, bool *r_strict, bool *r_created)
{
IDOverrideStaticPropertyOperation *opop = BKE_override_static_property_operation_find(override_property,
subitem_refname, subitem_locname,
subitem_refindex, subitem_locindex,
strict, r_strict);
if (opop == NULL) {
opop = MEM_callocN(sizeof(IDOverrideStaticPropertyOperation), __func__);
opop->operation = operation;
if (subitem_locname) {
opop->subitem_local_name = BLI_strdup(subitem_locname);
}
if (subitem_refname) {
opop->subitem_reference_name = BLI_strdup(subitem_refname);
}
opop->subitem_local_index = subitem_locindex;
opop->subitem_reference_index = subitem_refindex;
BLI_addtail(&override_property->operations, opop);
if (r_created) {
*r_created = true;
}
}
else if (r_created) {
*r_created = false;
}
return opop;
}
void bke_override_property_operation_copy(IDOverrideStaticPropertyOperation *opop_dst, IDOverrideStaticPropertyOperation *opop_src)
{
if (opop_src->subitem_reference_name) {
opop_dst->subitem_reference_name = BLI_strdup(opop_src->subitem_reference_name);
}
if (opop_src->subitem_local_name) {
opop_dst->subitem_local_name = BLI_strdup(opop_src->subitem_local_name);
}
}
void bke_override_property_operation_clear(IDOverrideStaticPropertyOperation *opop)
{
if (opop->subitem_reference_name) {
MEM_freeN(opop->subitem_reference_name);
}
if (opop->subitem_local_name) {
MEM_freeN(opop->subitem_local_name);
}
}
/**
* Remove and free given \a override_property_operation from given ID \a override_property.
*/
void BKE_override_static_property_operation_delete(
IDOverrideStaticProperty *override_property, IDOverrideStaticPropertyOperation *override_property_operation)
{
bke_override_property_operation_clear(override_property_operation);
BLI_freelinkN(&override_property->operations, override_property_operation);
}
/**
* Check that status of local data-block is still valid against current reference one.
*
* It means that all overridable, but not overridden, properties' local values must be equal to reference ones.
* Clears LIB_TAG_OVERRIDE_OK if they do not.
*
* This is typically used to detect whether some property has been changed in local and a new IDOverrideProperty
* (of IDOverridePropertyOperation) has to be added.
*
* \return true if status is OK, false otherwise. */
bool BKE_override_static_status_check_local(ID *local)
{
BLI_assert(local->override_static != NULL);
ID *reference = local->override_static->reference;
if (reference == NULL) {
/* This is an override template, local status is always OK! */
return true;
}
BLI_assert(GS(local->name) == GS(reference->name));
/* Note that reference is assumed always valid, caller has to ensure that itself. */
PointerRNA rnaptr_local, rnaptr_reference;
RNA_id_pointer_create(local, &rnaptr_local);
RNA_id_pointer_create(reference, &rnaptr_reference);
// if (!RNA_struct_override_matches(&rnaptr_local, &rnaptr_reference, local->override_static, true, true)) {
// local->tag &= ~LIB_TAG_OVERRIDESTATIC_OK;
// return false;
// }
return true;
}
/**
* Check that status of reference data-block is still valid against current local one.
*
* It means that all non-overridden properties' local values must be equal to reference ones.
* Clears LIB_TAG_OVERRIDE_OK if they do not.
*
* This is typically used to detect whether some reference has changed and local needs to be updated against it.
*
* \return true if status is OK, false otherwise. */
bool BKE_override_static_status_check_reference(ID *local)
{
BLI_assert(local->override_static != NULL);
ID *reference = local->override_static->reference;
if (reference == NULL) {
/* This is an override template, reference is virtual, so its status is always OK! */
return true;
}
BLI_assert(GS(local->name) == GS(reference->name));
if (reference->override_static && (reference->tag & LIB_TAG_OVERRIDESTATIC_OK) == 0) {
if (!BKE_override_static_status_check_reference(reference)) {
/* If reference is also override of another data-block, and its status is not OK,
* then this override is not OK either.
* Note that this should only happen when reloading libraries... */
local->tag &= ~LIB_TAG_OVERRIDESTATIC_OK;
return false;
}
}
PointerRNA rnaptr_local, rnaptr_reference;
RNA_id_pointer_create(local, &rnaptr_local);
RNA_id_pointer_create(reference, &rnaptr_reference);
// if (!RNA_struct_override_matches(&rnaptr_local, &rnaptr_reference, local->override_static, false, true)) {
// local->tag &= ~LIB_TAG_OVERRIDESTATIC_OK;
// return false;
// }
return true;
}
/**
* Compares local and reference data-blocks and create new override operations as needed,
* or reset to reference values if overriding is not allowed.
*
* \note Defining override operations is only mandatory before saving a .blend file on disk (not for undo!).
* Knowing that info at runtime is only useful for UI/UX feedback.
*
* \note This is by far the biggest operation (the more time-consuming) of the three so far, since it has to go over
* all properties in depth (all overridable ones at least). Generating diff values and applying overrides
* are much cheaper.
*
* \return true is new overriding op was created, or some local data was reset. */
bool BKE_override_static_operations_create(ID *local)
{
BLI_assert(local->override_static != NULL);
const bool is_template = (local->override_static->reference == NULL);
bool ret = false;
if (!is_template && local->flag & LIB_OVERRIDE_STATIC_AUTO) {
PointerRNA rnaptr_local, rnaptr_reference;
RNA_id_pointer_create(local, &rnaptr_local);
RNA_id_pointer_create(local->override_static->reference, &rnaptr_reference);
// ret = RNA_struct_auto_override(&rnaptr_local, &rnaptr_reference, local->override_static, NULL);
#ifndef NDEBUG
if (ret) {
printf("We did generate static override rules for %s\n", local->name);
}
else {
printf("No new static override rules for %s\n", local->name);
}
#endif
}
return ret;
}
/** Check all overrides from given \a bmain and create/update overriding operations as needed. */
void BKE_main_override_static_operations_create(Main *bmain)
{
ListBase *lbarray[MAX_LIBARRAY];
int base_count, i;
base_count = set_listbasepointers(bmain, lbarray);
for (i = 0; i < base_count; i++) {
ListBase *lb = lbarray[i];
ID *id;
for (id = lb->first; id; id = id->next) {
/* TODO Maybe we could also add an 'override update' tag e.g. when tagging for DEG update? */
if (id->lib == NULL && id->override_static != NULL && id->override_static->reference != NULL && (id->flag & LIB_OVERRIDE_STATIC_AUTO)) {
BKE_override_static_operations_create(id);
}
}
}
}
/** Update given override from its reference (re-applying overriden properties). */
void BKE_override_static_update(Main *bmain, ID *local)
{
if (local->override_static == NULL || local->override_static->reference == NULL) {
return;
}
/* Recursively do 'ancestors' overrides first, if any. */
if (local->override_static->reference->override_static && (local->override_static->reference->tag & LIB_TAG_OVERRIDESTATIC_OK) == 0) {
BKE_override_static_update(bmain, local->override_static->reference);
}
/* We want to avoid having to remap here, however creating up-to-date override is much simpler if based
* on reference than on current override.
* So we work on temp copy of reference, and 'swap' its content with local. */
/* XXX We need a way to get off-Main copies of IDs (similar to localized mats/texts/ etc.)!
* However, this is whole bunch of code work in itself, so for now plain stupid ID copy will do,
* as innefficient as it is. :/
* Actually, maybe not! Since we are swapping with original ID's local content, we want to keep
* usercount in correct state when freeing tmp_id (and that usercounts of IDs used by 'new' local data
* also remain correct). */
/* This would imply change in handling of usercout all over RNA (and possibly all over Blender code).
* Not impossible to do, but would rather see first if extra useless usual user handling is actually
* a (performances) issue here. */
ID *tmp_id;
id_copy(bmain, local->override_static->reference, &tmp_id, false);
if (tmp_id == NULL) {
return;
}
PointerRNA rnaptr_src, rnaptr_dst, rnaptr_storage_stack, *rnaptr_storage = NULL;
RNA_id_pointer_create(local, &rnaptr_src);
RNA_id_pointer_create(tmp_id, &rnaptr_dst);
if (local->override_static->storage) {
rnaptr_storage = &rnaptr_storage_stack;
RNA_id_pointer_create(local->override_static->storage, rnaptr_storage);
}
// RNA_struct_override_apply(&rnaptr_dst, &rnaptr_src, rnaptr_storage, local->override_static);
/* This also transfers all pointers (memory) owned by local to tmp_id, and vice-versa. So when we'll free tmp_id,
* we'll actually free old, outdated data from local. */
BKE_id_swap(bmain, local, tmp_id);
/* Again, horribly innefficient in our case, we need something off-Main (aka moar generic nolib copy/free stuff)! */
/* XXX And crashing in complex cases (e.g. because depsgraph uses same data...). */
BKE_libblock_free_ex(bmain, tmp_id, true, false);
if (local->override_static->storage) {
/* We know this datablock is not used anywhere besides local->override->storage. */
/* XXX For until we get fully shadow copies, we still need to ensure storage releases
* its usage of any ID pointers it may have. */
BKE_libblock_free_ex(bmain, local->override_static->storage, true, false);
local->override_static->storage = NULL;
}
local->tag |= LIB_TAG_OVERRIDESTATIC_OK;
/* Full rebuild of Depsgraph! */
DEG_on_visible_update(bmain, true); /* XXX Is this actual valid replacement for old DAG_relations_tag_update(bmain) ? */
}
/** Update all overrides from given \a bmain. */
void BKE_main_override_static_update(Main *bmain)
{
ListBase *lbarray[MAX_LIBARRAY];
int base_count, i;
base_count = set_listbasepointers(bmain, lbarray);
for (i = 0; i < base_count; i++) {
ListBase *lb = lbarray[i];
ID *id;
for (id = lb->first; id; id = id->next) {
if (id->override_static != NULL && id->lib == NULL) {
BKE_override_static_update(bmain, id);
}
}
}
}
/***********************************************************************************************************************
* Storage (how to wtore overriding data into .blend files).
*
* Basically:
* I) Only 'differential' storage needs special handling here. All others (replacing values or
* inserting/removing items from a collection) can be handled with simply storing current content of local data-block.
* II) We store the differential value into a second 'ghost' data-block, which is an empty ID of same type as local one,
* where we only define values that need differential data.
*
* This avoids us having to modify 'real' data-block at write time (and retoring it afterwards), which is inneficient,
* and potentially dangerous (in case of concurrent access...), while not using much extra memory in typical cases.
* It also ensures stored data-block always contains exact same data as "desired" ones (kind of "baked" data-blocks).
*/
/** Initialize an override storage. */
OverrideStaticStorage *BKE_override_static_operations_store_initialize(void)
{
return BKE_main_new();
}
/**
* Generate suitable 'write' data (this only affects differential override operations).
*
* Note that \a local ID is no more modified by this call, all extra data are stored in its temp \a storage_id copy. */
ID *BKE_override_static_operations_store_start(OverrideStaticStorage *override_storage, ID *local)
{
BLI_assert(local->override_static != NULL);
BLI_assert(override_storage != NULL);
const bool is_template = (local->override_static->reference == NULL);
if (is_template) {
/* This is actually purely local data with an override template, nothing to do here! */
return NULL;
}
/* Forcefully ensure we know about all needed override operations. */
BKE_override_static_operations_create(local);
ID *storage_id;
#ifdef DEBUG_OVERRIDE_TIMEIT
TIMEIT_START_AVERAGED(BKE_override_operations_store_start);
#endif
/* XXX TODO We may also want a specialized handling of things here too, to avoid copying heavy never-overridable
* data (like Mesh geometry etc.)? And also maybe avoid lib refcounting completely (shallow copy...). */
/* This would imply change in handling of usercout all over RNA (and possibly all over Blender code).
* Not impossible to do, but would rather see first is extra useless usual user handling is actually
* a (performances) issue here, before doing it. */
id_copy((Main *)override_storage, local, &storage_id, false);
if (storage_id != NULL) {
PointerRNA rnaptr_reference, rnaptr_final, rnaptr_storage;
RNA_id_pointer_create(local->override_static->reference, &rnaptr_reference);
RNA_id_pointer_create(local, &rnaptr_final);
RNA_id_pointer_create(storage_id, &rnaptr_storage);
// if (!RNA_struct_override_store(&rnaptr_final, &rnaptr_reference, &rnaptr_storage, local->override_static)) {
// BKE_libblock_free_ex(override_storage, storage_id, true, false);
// storage_id = NULL;
// }
}
local->override_static->storage = storage_id;
#ifdef DEBUG_OVERRIDE_TIMEIT
TIMEIT_END_AVERAGED(BKE_override_operations_store_start);
#endif
return storage_id;
}
/** Restore given ID modified by \a BKE_override_operations_store_start, to its original state. */
void BKE_override_static_operations_store_end(OverrideStaticStorage *UNUSED(override_storage), ID *local)
{
BLI_assert(local->override_static != NULL);
/* Nothing else to do here really, we need to keep all temp override storage data-blocks in memory until
* whole file is written anyway (otherwise we'd get mem pointers overlap...). */
local->override_static->storage = NULL;
}
void BKE_override_static_operations_store_finalize(OverrideStaticStorage *override_storage)
{
/* We cannot just call BKE_main_free(override_storage), not until we have option to make 'ghost' copies of IDs
* without increasing usercount of used data-blocks... */
ListBase *lbarray[MAX_LIBARRAY];
int base_count, i;
base_count = set_listbasepointers(override_storage, lbarray);
for (i = 0; i < base_count; i++) {
ListBase *lb = lbarray[i];
ID *id;
while ((id = lb->first)) {
BKE_libblock_free_ex(override_storage, id, true, false);
}
}
BKE_main_free(override_storage);
}

View File

@ -373,6 +373,10 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
#define CALLBACK_INVOKE(check_id_super, cb_flag) \
FOREACH_CALLBACK_INVOKE(&data, check_id_super, cb_flag)
if (id->override_static != NULL) {
CALLBACK_INVOKE_ID(id->override_static->reference, IDWALK_CB_USER | IDWALK_CB_STATIC_OVERRIDE_REFERENCE);
}
for (; id != NULL; id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL) {
data.self_id = id;
data.cb_flag = ID_IS_LINKED(id) ? IDWALK_CB_INDIRECT_USAGE : 0;

View File

@ -87,6 +87,7 @@
#include "BKE_lattice.h"
#include "BKE_layer.h"
#include "BKE_library.h"
#include "BKE_library_override.h"
#include "BKE_library_query.h"
#include "BKE_library_remap.h"
#include "BKE_linestyle.h"
@ -181,6 +182,7 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id
}
if (*id_p && (*id_p == old_id)) {
const bool is_reference = (cb_flag & IDWALK_CB_STATIC_OVERRIDE_REFERENCE) != 0;
const bool is_indirect = (cb_flag & IDWALK_CB_INDIRECT_USAGE) != 0;
const bool skip_indirect = (id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0;
/* Note: proxy usage implies LIB_TAG_EXTERN, so on this aspect it is direct,
@ -191,6 +193,7 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id
const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id));
const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) &&
(id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0);
const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_STATIC_OVERRIDE) != 0;
const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0;
#ifdef DEBUG_PRINT
@ -205,7 +208,8 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id
/* Special hack in case it's Object->data and we are in edit mode (skipped_direct too). */
if ((is_never_null && skip_never_null) ||
(is_obj_editmode && (((Object *)id)->data == *id_p)) ||
(skip_indirect && is_indirect))
(skip_indirect && is_indirect) ||
(is_reference && skip_reference))
{
if (is_indirect) {
id_remap_data->skipped_indirect++;
@ -218,7 +222,7 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id
}
}
}
else if (is_never_null || is_obj_editmode) {
else if (is_never_null || is_obj_editmode || is_reference) {
id_remap_data->skipped_direct++;
}
else {
@ -742,6 +746,10 @@ void BKE_libblock_free_data(ID *id, const bool do_id_user)
MEM_freeN(id->properties);
}
if (id->override_static) {
BKE_override_static_free(&id->override_static);
}
/* XXX TODO remove animdata handling from each type's freeing func, and do it here, like for copy! */
}

View File

@ -136,6 +136,7 @@
#include "BKE_layer.h"
#include "BKE_library.h" // for which_libbase
#include "BKE_library_idmap.h"
#include "BKE_library_override.h"
#include "BKE_library_query.h"
#include "BKE_idcode.h"
#include "BKE_idprop.h"
@ -2214,6 +2215,42 @@ static PreviewImage *direct_link_preview_image(FileData *fd, PreviewImage *old_p
/* ************ READ ID *************** */
static void lib_link_id(FileData *fd, Main *main)
{
ListBase *lbarray[MAX_LIBARRAY];
int base_count, i;
base_count = set_listbasepointers(main, lbarray);
for (i = 0; i < base_count; i++) {
ListBase *lb = lbarray[i];
ID *id;
for (id = lb->first; id; id = id->next) {
if (id->override_static) {
id->override_static->reference = newlibadr_us(fd, id->lib, id->override_static->reference);
id->override_static->storage = newlibadr_us(fd, id->lib, id->override_static->storage);
}
}
}
}
static void direct_link_id_override_property_operation_cb(FileData *fd, void *data)
{
IDOverrideStaticPropertyOperation *opop = data;
opop->subitem_reference_name = newdataadr(fd, opop->subitem_reference_name);
opop->subitem_local_name = newdataadr(fd, opop->subitem_local_name);
}
static void direct_link_id_override_property_cb(FileData *fd, void *data)
{
IDOverrideStaticProperty *op = data;
op->rna_path = newdataadr(fd, op->rna_path);
link_list_ex(fd, &op->operations, direct_link_id_override_property_operation_cb);
}
static void direct_link_id(FileData *fd, ID *id)
{
/*link direct data of ID properties*/
@ -2223,6 +2260,12 @@ static void direct_link_id(FileData *fd, ID *id)
IDP_DirectLinkGroup_OrFree(&id->properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}
id->py_instance = NULL;
/* Link direct data of overrides. */
if (id->override_static) {
id->override_static = newdataadr(fd, id->override_static);
link_list_ex(fd, &id->override_static->properties, direct_link_id_override_property_cb);
}
}
/* ************ READ CurveMapping *************** */
@ -8683,6 +8726,8 @@ static void lib_link_all(FileData *fd, Main *main)
{
oldnewmap_sort(fd);
lib_link_id(fd, main);
/* No load UI for undo memfiles */
if (fd->memfile == NULL) {
lib_link_windowmanager(fd, main);
@ -8908,6 +8953,12 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
BKE_main_id_tag_all(bfd->main, LIB_TAG_NEW, false);
/* Now that all our data-blocks are loaded, we can re-generate overrides from their references. */
if (fd->memfile == NULL) {
/* Do not apply in undo case! */
BKE_main_override_static_update(bfd->main);
}
lib_verify_nodetree(bfd->main, true);
fix_relpaths_library(fd->relabase, bfd->main); /* make all relative paths, relative to the open blend file */

View File

@ -170,6 +170,7 @@
#include "BKE_global.h" // for G
#include "BKE_idcode.h"
#include "BKE_library.h" // for set_listbasepointers
#include "BKE_library_override.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_report.h"
@ -683,6 +684,25 @@ static void write_iddata(void *wd, const ID *id)
if (id->properties && !ELEM(GS(id->name), ID_WM)) {
IDP_WriteProperty(id->properties, wd);
}
if (id->override_static) {
writestruct(wd, DATA, IDOverrideStatic, 1, id->override_static);
writelist(wd, DATA, IDOverrideStaticProperty, &id->override_static->properties);
for (IDOverrideStaticProperty *op = id->override_static->properties.first; op; op = op->next) {
writedata(wd, DATA, strlen(op->rna_path) + 1, op->rna_path);
writelist(wd, DATA, IDOverrideStaticPropertyOperation, &op->operations);
for (IDOverrideStaticPropertyOperation *opop = op->operations.first; opop; opop = opop->next) {
if (opop->subitem_reference_name) {
writedata(wd, DATA, strlen(opop->subitem_reference_name) + 1, opop->subitem_reference_name);
}
if (opop->subitem_local_name) {
writedata(wd, DATA, strlen(opop->subitem_local_name) + 1, opop->subitem_local_name);
}
}
}
}
}
static void write_previews(WriteData *wd, const PreviewImage *prv_orig)
@ -3789,6 +3809,8 @@ static void write_libraries(WriteData *wd, Main *main)
/* XXX needs rethink, just like save UI in undo files now - would be nice to append things only for the]
* quit.blend and temp saves */
if (found_one) {
/* Not overridable. */
writestruct(wd, ID_LI, Library, 1, main->curlib);
write_iddata(wd, &main->curlib->id);
@ -3923,137 +3945,159 @@ static bool write_file_handle(
* avoid thumbnail detecting changes because of this. */
mywrite_flush(wd);
ListBase *lbarray[MAX_LIBARRAY];
int a = set_listbasepointers(mainvar, lbarray);
while (a--) {
ID *id = lbarray[a]->first;
OverrideStaticStorage *override_storage = !wd->current ? BKE_override_static_operations_store_initialize() : NULL;
if (id && GS(id->name) == ID_LI) {
continue; /* Libraries are handled separately below. */
}
/* This outer loop allows to save first datablocks from real mainvar, then the temp ones from override process,
* if needed, without duplicating whole code. */
Main *bmain = mainvar;
do {
ListBase *lbarray[MAX_LIBARRAY];
int a = set_listbasepointers(bmain, lbarray);
while (a--) {
ID *id = lbarray[a]->first;
for (; id; id = id->next) {
/* We should never attempt to write non-regular IDs (i.e. all kind of temp/runtime ones). */
BLI_assert((id->tag & (LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT | LIB_TAG_NOT_ALLOCATED)) == 0);
switch ((ID_Type)GS(id->name)) {
case ID_WM:
write_windowmanager(wd, (wmWindowManager *)id);
break;
case ID_WS:
write_workspace(wd, (WorkSpace *)id);
break;
case ID_SCR:
write_screen(wd, (bScreen *)id);
break;
case ID_MC:
write_movieclip(wd, (MovieClip *)id);
break;
case ID_MSK:
write_mask(wd, (Mask *)id);
break;
case ID_SCE:
write_scene(wd, (Scene *)id);
break;
case ID_CU:
write_curve(wd, (Curve *)id);
break;
case ID_MB:
write_mball(wd, (MetaBall *)id);
break;
case ID_IM:
write_image(wd, (Image *)id);
break;
case ID_CA:
write_camera(wd, (Camera *)id);
break;
case ID_LA:
write_lamp(wd, (Lamp *)id);
break;
case ID_LT:
write_lattice(wd, (Lattice *)id);
break;
case ID_VF:
write_vfont(wd, (VFont *)id);
break;
case ID_KE:
write_key(wd, (Key *)id);
break;
case ID_WO:
write_world(wd, (World *)id);
break;
case ID_TXT:
write_text(wd, (Text *)id);
break;
case ID_SPK:
write_speaker(wd, (Speaker *)id);
break;
case ID_LP:
write_probe(wd, (LightProbe *)id);
break;
case ID_SO:
write_sound(wd, (bSound *)id);
break;
case ID_GR:
write_group(wd, (Group *)id);
break;
case ID_AR:
write_armature(wd, (bArmature *)id);
break;
case ID_AC:
write_action(wd, (bAction *)id);
break;
case ID_OB:
write_object(wd, (Object *)id);
break;
case ID_MA:
write_material(wd, (Material *)id);
break;
case ID_TE:
write_texture(wd, (Tex *)id);
break;
case ID_ME:
write_mesh(wd, (Mesh *)id);
break;
case ID_PA:
write_particlesettings(wd, (ParticleSettings *)id);
break;
case ID_NT:
write_nodetree(wd, (bNodeTree *)id);
break;
case ID_BR:
write_brush(wd, (Brush *)id);
break;
case ID_PAL:
write_palette(wd, (Palette *)id);
break;
case ID_PC:
write_paintcurve(wd, (PaintCurve *)id);
break;
case ID_GD:
write_gpencil(wd, (bGPdata *)id);
break;
case ID_LS:
write_linestyle(wd, (FreestyleLineStyle *)id);
break;
case ID_CF:
write_cachefile(wd, (CacheFile *)id);
break;
case ID_LI:
/* Do nothing, handled below - and should never be reached. */
BLI_assert(0);
break;
case ID_IP:
/* Do nothing, deprecated. */
break;
default:
/* Should never be reached. */
BLI_assert(0);
break;
if (id && GS(id->name) == ID_LI) {
continue; /* Libraries are handled separately below. */
}
}
mywrite_flush(wd);
for (; id; id = id->next) {
/* We should never attempt to write non-regular IDs (i.e. all kind of temp/runtime ones). */
BLI_assert((id->tag & (LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT | LIB_TAG_NOT_ALLOCATED)) == 0);
const bool do_override = !ELEM(override_storage, NULL, bmain) && id->override_static;
if (do_override) {
BKE_override_static_operations_store_start(override_storage, id);
}
switch ((ID_Type)GS(id->name)) {
case ID_WM:
write_windowmanager(wd, (wmWindowManager *)id);
break;
case ID_WS:
write_workspace(wd, (WorkSpace *)id);
break;
case ID_SCR:
write_screen(wd, (bScreen *)id);
break;
case ID_MC:
write_movieclip(wd, (MovieClip *)id);
break;
case ID_MSK:
write_mask(wd, (Mask *)id);
break;
case ID_SCE:
write_scene(wd, (Scene *)id);
break;
case ID_CU:
write_curve(wd,(Curve *)id);
break;
case ID_MB:
write_mball(wd, (MetaBall *)id);
break;
case ID_IM:
write_image(wd, (Image *)id);
break;
case ID_CA:
write_camera(wd, (Camera *)id);
break;
case ID_LA:
write_lamp(wd, (Lamp *)id);
break;
case ID_LT:
write_lattice(wd, (Lattice *)id);
break;
case ID_VF:
write_vfont(wd, (VFont *)id);
break;
case ID_KE:
write_key(wd, (Key *)id);
break;
case ID_WO:
write_world(wd, (World *)id);
break;
case ID_TXT:
write_text(wd, (Text *)id);
break;
case ID_SPK:
write_speaker(wd, (Speaker *)id);
break;
case ID_LP:
write_probe(wd, (LightProbe *)id);
break;
case ID_SO:
write_sound(wd, (bSound *)id);
break;
case ID_GR:
write_group(wd, (Group *)id);
break;
case ID_AR:
write_armature(wd, (bArmature *)id);
break;
case ID_AC:
write_action(wd, (bAction *)id);
break;
case ID_OB:
write_object(wd, (Object *)id);
break;
case ID_MA:
write_material(wd, (Material *)id);
break;
case ID_TE:
write_texture(wd, (Tex *)id);
break;
case ID_ME:
write_mesh(wd, (Mesh *)id);
break;
case ID_PA:
write_particlesettings(wd, (ParticleSettings *)id);
break;
case ID_NT:
write_nodetree(wd, (bNodeTree *)id);
break;
case ID_BR:
write_brush(wd, (Brush *)id);
break;
case ID_PAL:
write_palette(wd, (Palette *)id);
break;
case ID_PC:
write_paintcurve(wd, (PaintCurve *)id);
break;
case ID_GD:
write_gpencil(wd, (bGPdata *)id);
break;
case ID_LS:
write_linestyle(wd, (FreestyleLineStyle *)id);
break;
case ID_CF:
write_cachefile(wd, (CacheFile *)id);
break;
case ID_LI:
/* Do nothing, handled below - and should never be reached. */
BLI_assert(0);
break;
case ID_IP:
/* Do nothing, deprecated. */
break;
default:
/* Should never be reached. */
BLI_assert(0);
break;
}
if (do_override) {
BKE_override_static_operations_store_end(override_storage, id);
}
}
mywrite_flush(wd);
}
} while ((bmain != override_storage) && (bmain = override_storage));
if (override_storage) {
BKE_override_static_operations_store_finalize(override_storage);
override_storage = NULL;
}
/* Special handling, operating over split Mains... */

View File

@ -115,6 +115,76 @@ enum {
/* add any future new id property types here.*/
/* Static ID override structs. */
typedef struct IDOverrideStaticPropertyOperation {
struct IDOverrideStaticPropertyOperation *next, *prev;
/* Type of override. */
short operation;
short flag;
short pad_s1[2];
/* Sub-item references, if needed (for arrays or collections only).
* We need both reference and local values to allow e.g. insertion into collections (constraints, modifiers...).
* In collection case, if names are defined, they are used in priority.
* Names are pointers (instead of char[64]) to save some space, NULL when unset.
* Indices are -1 when unset. */
char *subitem_reference_name;
char *subitem_local_name;
int subitem_reference_index;
int subitem_local_index;
} IDOverrideStaticPropertyOperation;
/* IDOverridePropertyOperation->operation. */
enum {
/* Basic operations. */
IDOVERRIDESTATIC_OP_NOOP = 0, /* Special value, forbids any overriding. */
IDOVERRIDESTATIC_OP_REPLACE = 1, /* Fully replace local value by reference one. */
/* Numeric-only operations. */
IDOVERRIDESTATIC_OP_ADD = 101, /* Add local value to reference one. */
/* Subtract local value from reference one (needed due to unsigned values etc.). */
IDOVERRIDESTATIC_OP_SUBTRACT = 102,
/* Multiply reference value by local one (more useful than diff for scales and the like). */
IDOVERRIDESTATIC_OP_MULTIPLY = 103,
/* Collection-only operations. */
IDOVERRIDESTATIC_OP_INSERT_AFTER = 201, /* Insert after given reference's subitem. */
IDOVERRIDESTATIC_OP_INSERT_BEFORE = 202, /* Insert before given reference's subitem. */
/* We can add more if needed (move, delete, ...). */
};
/* IDOverridePropertyOperation->flag. */
enum {
IDOVERRIDESTATIC_FLAG_MANDATORY = 1 << 0, /* User cannot remove that override operation. */
IDOVERRIDESTATIC_FLAG_LOCKED = 1 << 1, /* User cannot change that override operation. */
};
/* A single overriden property, contain all operations on this one. */
typedef struct IDOverrideStaticProperty {
struct IDOverrideStaticProperty *next, *prev;
/* Path from ID to overridden property. *Does not* include indices/names for final arrays/collections items. */
char *rna_path;
ListBase operations; /* List of overriding operations (IDOverridePropertyOperation) applied to this property. */
} IDOverrideStaticProperty;
/* Main container for all overriding data info of a data-block. */
typedef struct IDOverrideStatic {
struct ID *reference; /* Reference linked ID which this one overrides. */
ListBase properties; /* List of IDOverrideProperty structs. */
/* Read/write data. */
/* Temp ID storing extra override data (used for differential operations only currently).
* Always NULL outside of read/write context. */
struct ID *storage;
} IDOverrideStatic;
/* watch it: Sequence has identical beginning. */
/**
* ID is the first thing included in all serializable types. It
@ -142,7 +212,12 @@ typedef struct ID {
int us;
int icon_id;
IDProperty *properties;
IDOverrideStatic *override_static; /* Reference linked ID which this one overrides. */
void *py_instance;
void *pad1;
} ID;
/**
@ -309,7 +384,8 @@ typedef enum ID_Type {
/* id->flag (persitent). */
enum {
LIB_FAKEUSER = 1 << 9,
LIB_OVERRIDE_STATIC_AUTO = 1 << 0, /* Allow automatic generation of overriding rules. */
LIB_FAKEUSER = 1 << 9,
};
/**
@ -344,6 +420,9 @@ enum {
/* RESET_NEVER tag datablock as a place-holder (because the real one could not be linked from its library e.g.). */
LIB_TAG_MISSING = 1 << 6,
/* RESET_NEVER tag datablock as being up-to-date regarding its reference. */
LIB_TAG_OVERRIDESTATIC_OK = 1 << 9,
/* tag datablock has having an extra user. */
LIB_TAG_EXTRAUSER = 1 << 2,
/* tag datablock has having actually increased usercount for the extra virtual user. */