diff options
Diffstat (limited to 'source/blender/makesrna/intern/rna_access.c')
-rw-r--r-- | source/blender/makesrna/intern/rna_access.c | 1018 |
1 files changed, 796 insertions, 222 deletions
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index daa01f58e12..0532aac1bc3 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -34,6 +34,8 @@ #include "DNA_ID.h" #include "DNA_scene_types.h" +#include "DNA_constraint_types.h" +#include "DNA_modifier_types.h" #include "DNA_windowmanager_types.h" #include "BLI_blenlib.h" @@ -42,6 +44,10 @@ #include "BLI_ghash.h" #include "BLI_math.h" +#ifdef DEBUG_OVERRIDE_TIMEIT +# include "PIL_time_utildefines.h" +#endif + #include "BLF_api.h" #include "BLT_translation.h" @@ -51,18 +57,21 @@ #include "BKE_idprop.h" #include "BKE_fcurve.h" #include "BKE_library.h" +#include "BKE_library_override.h" #include "BKE_main.h" #include "BKE_report.h" +#include "DEG_depsgraph.h" + #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" #include "WM_api.h" +#include "WM_message.h" /* flush updates */ #include "DNA_object_types.h" -#include "BKE_depsgraph.h" #include "WM_types.h" #include "rna_internal.h" @@ -431,7 +440,7 @@ static PropertyRNA *arraytypemap[IDP_NUMTYPES] = { (PropertyRNA *)&rna_PropertyGroupItem_double_array }; -IDProperty *rna_idproperty_check(PropertyRNA **prop, PointerRNA *ptr) +static void *rna_idproperty_check_ex(PropertyRNA **prop, PointerRNA *ptr, const bool return_rnaprop) { /* This is quite a hack, but avoids some complexity in the API. we * pass IDProperty structs as PropertyRNA pointers to the outside. @@ -453,8 +462,9 @@ IDProperty *rna_idproperty_check(PropertyRNA **prop, PointerRNA *ptr) return idprop; } - else - return NULL; + else { + return return_rnaprop ? *prop : NULL; + } } { @@ -469,6 +479,19 @@ IDProperty *rna_idproperty_check(PropertyRNA **prop, PointerRNA *ptr) } } +/* This function only returns an IDProperty, + * or NULL (in case IDProp could not be found, or prop is a real RNA property). */ +IDProperty *rna_idproperty_check(PropertyRNA **prop, PointerRNA *ptr) +{ + return rna_idproperty_check_ex(prop, ptr, false); +} + +/* This function always return the valid, real data pointer, be it a regular RNA property one, or an IDProperty one. */ +PropertyRNA *rna_ensure_property_realdata(PropertyRNA **prop, PointerRNA *ptr) +{ + return rna_idproperty_check_ex(prop, ptr, true); +} + static PropertyRNA *rna_ensure_property(PropertyRNA *prop) { /* the quick version if we don't need the idproperty */ @@ -982,6 +1005,11 @@ int RNA_property_flag(PropertyRNA *prop) return rna_ensure_property(prop)->flag; } +int RNA_property_override_flag(PropertyRNA *prop) +{ + return rna_ensure_property(prop)->flag_override; +} + /** * Get the tags set for \a prop as int bitfield. * \note Doesn't perform any validity check on the set bits. #RNA_def_property_tags does this @@ -1819,7 +1847,8 @@ bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop) return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0 && - (!id || !ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION))); + (!id || ((!ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)) && + (!id->override_static || RNA_property_overridable_get(ptr, prop))))); } /** @@ -1845,11 +1874,19 @@ bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char * } /* property from linked data-block */ - if (id && ID_IS_LINKED(id) && (prop->flag & PROP_LIB_EXCEPTION) == 0) { - if (!(*r_info)[0]) { - *r_info = N_("Can't edit this property from a linked data-block"); + if (id) { + if (ID_IS_LINKED(id) && (prop->flag & PROP_LIB_EXCEPTION) == 0) { + if (!(*r_info)[0]) { + *r_info = N_("Can't edit this property from a linked data-block."); + } + return false; + } + if (id->override_static != NULL && !RNA_property_overridable_get(ptr, prop)) { + if (!(*r_info)[0]) { + *r_info = N_("Can't edit this property from an override data-block."); + } + return false; } - return false; } return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0); @@ -1923,6 +1960,70 @@ bool RNA_property_animated(PointerRNA *ptr, PropertyRNA *prop) return false; } +/** \note Does not take into account editable status, this has to be checked separately + * (using RNA_property_edtiable_flag() usually). */ +bool RNA_property_overridable_get(PointerRNA *ptr, PropertyRNA *prop) +{ + if (prop->magic == RNA_MAGIC) { + /* Special handling for insertions of constraints or modifiers... */ + /* TODO Note We may want to add a more generic system to RNA (like a special property in struct of items) + * if we get more overrideable collections, for now we can live with those special-cases handling I think. */ + if (RNA_struct_is_a(ptr->type, &RNA_Constraint)) { + bConstraint *con = ptr->data; + if (con->flag & CONSTRAINT_STATICOVERRIDE_LOCAL) { + return true; + } + } + else if (RNA_struct_is_a(ptr->type, &RNA_Modifier)) { + ModifierData *mod = ptr->data; + if (mod->flag & eModifierFlag_StaticOverride_Local) { + return true; + } + } + /* If this is a RNA-defined property (real or 'virtual' IDProp), we want to use RNA prop flag. */ + return !(prop->flag_override & PROPOVERRIDE_NO_COMPARISON) && (prop->flag_override & PROPOVERRIDE_OVERRIDABLE_STATIC); + } + else { + /* If this is a real 'pure' IDProp (aka custom property), we want to use the IDProp flag. */ + return !(prop->flag_override & PROPOVERRIDE_NO_COMPARISON) && (((IDProperty *)prop)->flag & IDP_FLAG_OVERRIDABLE_STATIC); + } +} + +/* Should only be used for custom properties */ +bool RNA_property_overridable_static_set(PointerRNA *UNUSED(ptr), PropertyRNA *prop, const bool is_overridable) +{ + /* Only works for pure custom properties IDProps. */ + if (prop->magic != RNA_MAGIC) { + IDProperty *idprop = (IDProperty *)prop; + + idprop->flag = is_overridable ? (idprop->flag | IDP_FLAG_OVERRIDABLE_STATIC) : + (idprop->flag & ~IDP_FLAG_OVERRIDABLE_STATIC); + return true; + } + + return false; +} + + +bool RNA_property_overridden(PointerRNA *ptr, PropertyRNA *prop) +{ + char *rna_path = RNA_path_from_ID_to_property(ptr, prop); + ID *id = ptr->id.data; + + if (rna_path == NULL || id == NULL || id->override_static == NULL) { + return false; + } + + return (BKE_override_static_property_find(id->override_static, rna_path) != NULL); +} + +bool RNA_property_comparable(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +{ + prop = rna_ensure_property(prop); + + return !(prop->flag_override & PROPOVERRIDE_NO_COMPARISON); +} + /* this function is to check if its possible to create a valid path from the ID * its slow so don't call in a loop */ bool RNA_property_path_from_ID_check(PointerRNA *ptr, PropertyRNA *prop) @@ -1957,7 +2058,7 @@ static void rna_property_update(bContext *C, Main *bmain, Scene *scene, PointerR * parts of the code that need it still, so we have this exception */ if (prop->flag & PROP_CONTEXT_UPDATE) { if (C) { - if (prop->flag & PROP_CONTEXT_PROPERTY_UPDATE) { + if ((prop->flag & PROP_CONTEXT_PROPERTY_UPDATE) == PROP_CONTEXT_PROPERTY_UPDATE) { ((ContextPropUpdateFunc)prop->update)(C, ptr, prop); } else { @@ -1968,14 +2069,35 @@ static void rna_property_update(bContext *C, Main *bmain, Scene *scene, PointerR else prop->update(bmain, scene, ptr); } - if (prop->noteflag) + +#if 1 + /* TODO(campbell): Should eventually be replaced entirely by message bus (below) + * for now keep since COW, bugs are hard to track when we have other missing updates. */ + if (prop->noteflag) { WM_main_add_notifier(prop->noteflag, ptr->id.data); + } +#endif + + /* if C is NULL, we're updating from animation. + * avoid slow-down from f-curves by not publishing (for now). */ + if (C != NULL) { + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + /* we could add NULL check, for now don't */ + WM_msg_publish_rna(mbus, ptr, prop); + } + if (ptr->id.data != NULL) { + const short id_type = GS(((ID *)ptr->id.data)->name); + if (ID_TYPE_IS_COW(id_type)) { + DEG_id_tag_update(ptr->id.data, DEG_TAG_COPY_ON_WRITE); + } + } + /* End message bus. */ } if (!is_rna || (prop->flag & PROP_IDPROPERTY)) { /* WARNING! This is so property drivers update the display! * not especially nice */ - DAG_id_tag_update(ptr->id.data, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); + DEG_id_tag_update(ptr->id.data, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); WM_main_add_notifier(NC_WINDOW, NULL); /* Not nice as well, but the only way to make sure material preview * is updated with custom nodes. @@ -6994,120 +7116,47 @@ bool RNA_property_reset(PointerRNA *ptr, PropertyRNA *prop, int index) } } +static bool rna_property_override_operation_apply( + PointerRNA *ptr_local, PointerRNA *ptr_override, PointerRNA *ptr_storage, + PropertyRNA *prop_local, PropertyRNA *prop_override, PropertyRNA *prop_storage, + IDOverrideStaticPropertyOperation *opop); + bool RNA_property_copy(PointerRNA *ptr, PointerRNA *fromptr, PropertyRNA *prop, int index) { - int len, fromlen; - PropertyRNA *fromprop = prop; + if (!RNA_property_editable(ptr, prop)) { + return false; + } - if (prop->magic != RNA_MAGIC) { - /* In case of IDProperty, we have to find the *real* idprop of ptr, - * since prop in this case is just a fake wrapper around actual IDProp data, and not a 'real' PropertyRNA. */ - prop = (PropertyRNA *)rna_idproperty_find(ptr, ((IDProperty *)fromprop)->name); + PropertyRNA *prop_dst = prop; + PropertyRNA *prop_src = prop; - /* its possible the custom-prop doesn't exist on this data-block */ - if (prop == NULL) { - return false; - } + /* Ensure we get real property data, be it an actual RNA property, or an IDProperty in disguise. */ + prop_dst = rna_ensure_property_realdata(&prop_dst, ptr); + prop_src = rna_ensure_property_realdata(&prop_src, fromptr); - /* Even though currently we now prop will always be the 'fromprop', this might not be the case in the future. */ - if (prop == fromprop) { - fromprop = (PropertyRNA *)rna_idproperty_find(fromptr, ((IDProperty *)prop)->name); - } + /* IDprops: destination may not exist, if source does and is set, try to create it. */ + /* Note: this is sort of quick hack/bandage to fix the issue, we need to rethink how IDProps are handled + * in 'diff' RNA code completely, imho... */ + if (prop_src != NULL && prop_dst == NULL && RNA_property_is_set(fromptr, prop)) { + BLI_assert(prop_src->magic != RNA_MAGIC); + IDProperty *idp_dst = RNA_struct_idprops(ptr, true); + IDProperty *prop_idp_dst = IDP_CopyProperty((IDProperty *)prop_src); + IDP_AddToGroup(idp_dst, prop_idp_dst); + rna_idproperty_touch(prop_idp_dst); + /* Nothing else to do here... */ + return true; } - /* get the length of the array to work with */ - len = RNA_property_array_length(ptr, prop); - fromlen = RNA_property_array_length(fromptr, fromprop); - - if (len != fromlen) + if (ELEM(NULL, prop_dst, prop_src)) { return false; - - /* get and set the default values as appropriate for the various types */ - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - if (len) { - if (index == -1) { - int *tmparray = MEM_callocN(sizeof(int) * len, "copy - boolean"); - - RNA_property_boolean_get_array(fromptr, fromprop, tmparray); - RNA_property_boolean_set_array(ptr, prop, tmparray); - - MEM_freeN(tmparray); - } - else { - int value = RNA_property_boolean_get_index(fromptr, fromprop, index); - RNA_property_boolean_set_index(ptr, prop, index, value); - } - } - else { - int value = RNA_property_boolean_get(fromptr, fromprop); - RNA_property_boolean_set(ptr, prop, value); - } - return true; - case PROP_INT: - if (len) { - if (index == -1) { - int *tmparray = MEM_callocN(sizeof(int) * len, "copy - int"); - - RNA_property_int_get_array(fromptr, fromprop, tmparray); - RNA_property_int_set_array(ptr, prop, tmparray); - - MEM_freeN(tmparray); - } - else { - int value = RNA_property_int_get_index(fromptr, fromprop, index); - RNA_property_int_set_index(ptr, prop, index, value); - } - } - else { - int value = RNA_property_int_get(fromptr, fromprop); - RNA_property_int_set(ptr, prop, value); - } - return true; - case PROP_FLOAT: - if (len) { - if (index == -1) { - float *tmparray = MEM_callocN(sizeof(float) * len, "copy - float"); - - RNA_property_float_get_array(fromptr, fromprop, tmparray); - RNA_property_float_set_array(ptr, prop, tmparray); - - MEM_freeN(tmparray); - } - else { - float value = RNA_property_float_get_index(fromptr, fromprop, index); - RNA_property_float_set_index(ptr, prop, index, value); - } - } - else { - float value = RNA_property_float_get(fromptr, fromprop); - RNA_property_float_set(ptr, prop, value); - } - return true; - case PROP_ENUM: - { - int value = RNA_property_enum_get(fromptr, fromprop); - RNA_property_enum_set(ptr, prop, value); - return true; - } - case PROP_POINTER: - { - PointerRNA value = RNA_property_pointer_get(fromptr, fromprop); - RNA_property_pointer_set(ptr, prop, value); - return true; - } - case PROP_STRING: - { - char *value = RNA_property_string_get_alloc(fromptr, fromprop, NULL, 0, NULL); - RNA_property_string_set(ptr, prop, value); - MEM_freeN(value); - return true; - } - default: - return false; } - return false; + IDOverrideStaticPropertyOperation opop = { + .operation = IDOVERRIDESTATIC_OP_REPLACE, + .subitem_reference_index = index, + .subitem_local_index = index + }; + return rna_property_override_operation_apply(ptr, fromptr, NULL, prop_dst, prop_src, NULL, &opop); } /* use RNA_warning macro which includes __func__ suffix */ @@ -7132,177 +7181,683 @@ void _RNA_warning(const char *format, ...) #endif } -bool RNA_property_equals(PointerRNA *a, PointerRNA *b, PropertyRNA *prop, eRNAEqualsMode mode) +static int rna_property_override_diff( + PointerRNA *ptr_a, PointerRNA *ptr_b, PropertyRNA *prop, PropertyRNA *prop_a, PropertyRNA *prop_b, const char *rna_path, + eRNACompareMode mode, IDOverrideStatic *override, const int flags, eRNAOverrideMatchResult *r_report_flags); + +bool RNA_property_equals(PointerRNA *ptr_a, PointerRNA *ptr_b, PropertyRNA *prop, eRNACompareMode mode) +{ + BLI_assert(ELEM(mode, RNA_EQ_STRICT, RNA_EQ_UNSET_MATCH_ANY, RNA_EQ_UNSET_MATCH_NONE)); + + return (rna_property_override_diff(ptr_a, ptr_b, prop, NULL, NULL, NULL, mode, NULL, 0, NULL) == 0); +} + +bool RNA_struct_equals(PointerRNA *ptr_a, PointerRNA *ptr_b, eRNACompareMode mode) +{ + CollectionPropertyIterator iter; + PropertyRNA *iterprop; + bool equals = true; + + if (ptr_a == NULL && ptr_b == NULL) + return true; + else if (ptr_a == NULL || ptr_b == NULL) + return false; + else if (ptr_a->type != ptr_b->type) + return false; + + iterprop = RNA_struct_iterator_property(ptr_a->type); + + RNA_property_collection_begin(ptr_a, iterprop, &iter); + for (; iter.valid; RNA_property_collection_next(&iter)) { + PropertyRNA *prop = iter.ptr.data; + + if (!RNA_property_equals(ptr_a, ptr_b, prop, mode)) { + equals = false; + break; + } + } + RNA_property_collection_end(&iter); + + return equals; +} + +/* Low-level functions, also used by non-override RNA API like copy or equality check. */ + +/** Generic RNA property diff function. + * + * \note about \a prop and \a prop_a/prop_b parameters: the former is exptected to be an 'un-resolved' one, + * while the two laters are expected to be fully resolved ones (i.e. to be the IDProps when they should be, etc.). + * When \a prop is given, \a prop_a and \a prop_b should always be NULL, and vice-versa. + * This is necessary, because we cannot perform 'set/unset' checks on resolved properties + * (unset IDProps would merely be NULL then). + * + * \note When there is no equality, but we cannot determine an order (greater than/lesser than), we return 1. + */ +static int rna_property_override_diff( + PointerRNA *ptr_a, PointerRNA *ptr_b, PropertyRNA *prop, PropertyRNA *prop_a, PropertyRNA *prop_b, + const char *rna_path, eRNACompareMode mode, + IDOverrideStatic *override, const int flags, eRNAOverrideMatchResult *r_report_flags) { - int len, fromlen; + if (prop != NULL) { + BLI_assert(prop_a == NULL && prop_b == NULL); + prop_a = prop; + prop_b = prop; + } + + if (ELEM(NULL, prop_a, prop_b)) { + return (prop_a == prop_b) ? 0 : 1; + } + + if (!RNA_property_comparable(ptr_a, prop_a) || !RNA_property_comparable(ptr_b, prop_b)) { + return 0; + } if (mode == RNA_EQ_UNSET_MATCH_ANY) { /* uninitialized properties are assumed to match anything */ - if (!RNA_property_is_set(a, prop) || !RNA_property_is_set(b, prop)) - return true; + if (!RNA_property_is_set(ptr_a, prop_a) || !RNA_property_is_set(ptr_b, prop_b)) { + return 0; + } } else if (mode == RNA_EQ_UNSET_MATCH_NONE) { /* unset properties never match set properties */ - if (RNA_property_is_set(a, prop) != RNA_property_is_set(b, prop)) - return false; + if (RNA_property_is_set(ptr_a, prop_a) != RNA_property_is_set(ptr_b, prop_b)) { + return 1; + } + } + + if (prop != NULL) { + /* Ensure we get real property data, be it an actual RNA property, or an IDProperty in disguise. */ + prop_a = rna_ensure_property_realdata(&prop_a, ptr_a); + prop_b = rna_ensure_property_realdata(&prop_b, ptr_b); + + if (ELEM(NULL, prop_a, prop_b)) { + return (prop_a == prop_b) ? 0 : 1; + } + } + + /* Check if we are working with arrays. */ + const bool is_array_a = RNA_property_array_check(prop_a); + const bool is_array_b = RNA_property_array_check(prop_b); + + if (is_array_a != is_array_b) { + /* Should probably never happen actually... */ + BLI_assert(0); + return is_array_a ? 1 : -1; + } + + /* Get the length of the array to work with. */ + const int len_a = RNA_property_array_length(ptr_a, prop_a); + const int len_b = RNA_property_array_length(ptr_b, prop_b); + + if (len_a != len_b) { + /* Do not handle override in that case, we do not support insertion/deletion from arrays for now. */ + return len_a > len_b ? 1 : -1; + } + + if (is_array_a && len_a == 0) { + /* Empty arrays, will happen in some case with dynamic ones. */ + return 0; + } + + RNAPropOverrideDiff override_diff = NULL; + /* Special case for IDProps, we use default callback then. */ + if (prop_a->magic != RNA_MAGIC) { + override_diff = rna_property_override_diff_default; + if (prop_b->magic == RNA_MAGIC && prop_b->override_diff != override_diff) { + override_diff = NULL; + } + } + else if (prop_b->magic != RNA_MAGIC) { + override_diff = rna_property_override_diff_default; + if (prop_a->override_diff != override_diff) { + override_diff = NULL; + } + } + else if (prop_a->override_diff == prop_b->override_diff) { + override_diff = prop_a->override_diff; + } + + if (override_diff == NULL) { +#ifndef NDEBUG + printf("'%s' gives unmatching or NULL RNA diff callbacks, should not happen (%d vs. %d).\n", + rna_path ? rna_path : (prop_a->magic != RNA_MAGIC ? ((IDProperty *)prop_a)->name : prop_a->identifier), + prop_a->magic == RNA_MAGIC, prop_b->magic == RNA_MAGIC); +#endif + BLI_assert(0); + return 1; + } + + bool override_changed = false; + int diff_flags = flags; + if (!RNA_property_overridable_get(ptr_a, prop_a)) { + diff_flags &= ~RNA_OVERRIDE_COMPARE_CREATE; + } + const int diff = override_diff( + ptr_a, ptr_b, prop_a, prop_b, len_a, len_b, + mode, override, rna_path, diff_flags, &override_changed); + if (override_changed && r_report_flags) { + *r_report_flags |= RNA_OVERRIDE_MATCH_RESULT_CREATED; + } + + return diff; +} + +/* Modify local data-block to make it ready for override application (only needed for diff operations, where we use + * the local data-block's data as second operand). */ +static bool rna_property_override_operation_store( + PointerRNA *ptr_local, PointerRNA *ptr_reference, PointerRNA *ptr_storage, + PropertyRNA *prop_local, PropertyRNA *prop_reference, PropertyRNA *prop_storage, + IDOverrideStaticProperty *op) +{ + int len_local, len_reference, len_storage = 0; + bool changed = false; + + if (ptr_storage == NULL) { + return changed; + } + + /* get the length of the array to work with */ + len_local = RNA_property_array_length(ptr_local, prop_local); + len_reference = RNA_property_array_length(ptr_reference, prop_reference); + if (prop_storage) { + len_storage = RNA_property_array_length(ptr_storage, prop_storage); + } + + if (len_local != len_reference || len_local != len_storage) { + /* Do not handle override in that case, we do not support insertion/deletion from arrays for now. */ + return changed; + } + + BLI_assert(prop_local->override_store == prop_reference->override_store && + (!ptr_storage || prop_local->override_store == prop_storage->override_store) && + prop_local->override_store != NULL); + + for (IDOverrideStaticPropertyOperation *opop = op->operations.first; opop; opop = opop->next) { + /* Only needed for diff operations. */ + if (!ELEM(opop->operation, IDOVERRIDESTATIC_OP_ADD, IDOVERRIDESTATIC_OP_SUBTRACT, IDOVERRIDESTATIC_OP_MULTIPLY)) { + continue; + } + + if (prop_local->override_store( + ptr_local, ptr_reference, ptr_storage, prop_local, prop_reference, prop_storage, + len_local, len_reference, len_storage, opop)) + { + changed = true; + } + } + + return changed; +} + +static bool rna_property_override_operation_apply( + PointerRNA *ptr_local, PointerRNA *ptr_override, PointerRNA *ptr_storage, + PropertyRNA *prop_local, PropertyRNA *prop_override, PropertyRNA *prop_storage, + IDOverrideStaticPropertyOperation *opop) +{ + int len_local, len_reference, len_storage = 0; + + const short override_op = opop->operation; + + if (override_op == IDOVERRIDESTATIC_OP_NOOP) { + return true; + } + + if (ELEM(override_op, IDOVERRIDESTATIC_OP_ADD, IDOVERRIDESTATIC_OP_SUBTRACT, IDOVERRIDESTATIC_OP_MULTIPLY) && !ptr_storage) { + /* We cannot apply 'diff' override operations without some refference storage. + * This should typically only happen at read time of .blend file... */ + return false; + } + + if (ELEM(override_op, IDOVERRIDESTATIC_OP_ADD, IDOVERRIDESTATIC_OP_SUBTRACT, IDOVERRIDESTATIC_OP_MULTIPLY) && !prop_storage) { + /* We cannot apply 'diff' override operations without some refference storage. + * This should typically only happen at read time of .blend file... */ + return false; + } + + RNAPropOverrideApply override_apply = NULL; + /* Special case for IDProps, we use default callback then. */ + if (prop_local->magic != RNA_MAGIC) { + override_apply = rna_property_override_apply_default; + if (prop_override->magic == RNA_MAGIC && prop_override->override_apply != override_apply) { + override_apply = NULL; + } + } + else if (prop_override->magic != RNA_MAGIC) { + override_apply = rna_property_override_apply_default; + if (prop_local->override_apply != override_apply) { + override_apply = NULL; + } + } + else if (prop_local->override_apply == prop_override->override_apply) { + override_apply = prop_local->override_apply; + } + + if (ptr_storage && prop_storage->magic == RNA_MAGIC && prop_storage->override_apply != override_apply) { + override_apply = NULL; + } + + if (override_apply == NULL) { +#ifndef NDEBUG + printf("'%s' gives unmatching or NULL RNA copy callbacks, should not happen (%d vs. %d).\n", + prop_local->magic != RNA_MAGIC ? ((IDProperty *)prop_local)->name : prop_local->identifier, + prop_local->magic == RNA_MAGIC, prop_override->magic == RNA_MAGIC); +#endif + BLI_assert(0); + return false; } /* get the length of the array to work with */ - len = RNA_property_array_length(a, prop); - fromlen = RNA_property_array_length(b, prop); + len_local = RNA_property_array_length(ptr_local, prop_local); + len_reference = RNA_property_array_length(ptr_override, prop_override); + if (ptr_storage) { + len_storage = RNA_property_array_length(ptr_storage, prop_storage); + } - if (len != fromlen) + if (len_local != len_reference || (ptr_storage && len_local != len_storage)) { + /* Do not handle override in that case, we do not support insertion/deletion from arrays for now. */ return false; + } /* get and set the default values as appropriate for the various types */ - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - { - if (len) { - int fixed_a[16], fixed_b[16]; - int *array_a, *array_b; - bool equals; + return override_apply( + ptr_local, ptr_override, ptr_storage, + prop_local, prop_override, prop_storage, + len_local, len_reference, len_storage, + opop); +} + +/** + * Check whether reference and local overriden data match (are the same), + * with respect to given restrictive sets of properties. + * If requested, will generate needed new property overrides, and/or restore values from reference. + * + * \param r_report_flags If given, will be set with flags matching actions taken by the function on \a ptr_local. + * + * \return True if _resulting_ \a ptr_local does match \a ptr_reference. + */ +bool RNA_struct_override_matches( + PointerRNA *ptr_local, PointerRNA *ptr_reference, const char *root_path, + IDOverrideStatic *override, const eRNAOverrideMatch flags, + eRNAOverrideMatchResult *r_report_flags) +{ + CollectionPropertyIterator iter; + PropertyRNA *iterprop; + bool matching = true; + + BLI_assert(ptr_local->type == ptr_reference->type); + BLI_assert(ptr_local->id.data && ptr_reference->id.data); + + const bool ignore_non_overridable = (flags & RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE) != 0; + const bool ignore_overridden = (flags & RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN) != 0; + const bool do_create = (flags & RNA_OVERRIDE_COMPARE_CREATE) != 0; + const bool do_restore = (flags & RNA_OVERRIDE_COMPARE_RESTORE) != 0; + +//#define DEBUG_OVERRIDE_TIMEIT +#ifdef DEBUG_OVERRIDE_TIMEIT + static float _sum_time_global = 0.0f; + static float _num_time_global = 0.0f; + double _timeit_time_global; + static float _sum_time_diffing = 0.0f; + static float _delta_time_diffing = 0.0f; + static int _num_delta_time_diffing = 0.0f; + static float _num_time_diffing = 0.0f; + double _timeit_time_diffing; + + if (!root_path) { + _delta_time_diffing = 0.0f; + _num_delta_time_diffing = 0; + _timeit_time_global = PIL_check_seconds_timer(); + } +#endif + + iterprop = RNA_struct_iterator_property(ptr_local->type); + + for (RNA_property_collection_begin(ptr_local, iterprop, &iter); iter.valid; RNA_property_collection_next(&iter)) { + PropertyRNA *prop_local = iter.ptr.data; + PropertyRNA *prop_reference = iter.ptr.data; - array_a = (len > 16) ? MEM_mallocN(sizeof(int) * len, "RNA equals") : fixed_a; - array_b = (len > 16) ? MEM_mallocN(sizeof(int) * len, "RNA equals") : fixed_b; + /* Ensure we get real property data, be it an actual RNA property, or an IDProperty in disguise. */ + prop_local = rna_ensure_property_realdata(&prop_local, ptr_local); + prop_reference = rna_ensure_property_realdata(&prop_reference, ptr_reference); - RNA_property_boolean_get_array(a, prop, array_a); - RNA_property_boolean_get_array(b, prop, array_b); + if (ELEM(NULL, prop_local, prop_reference)) { + continue; + } - equals = memcmp(array_a, array_b, sizeof(int) * len) == 0; + if (ignore_non_overridable && !RNA_property_overridable_get(ptr_local, prop_local)) { + continue; + } - if (array_a != fixed_a) MEM_freeN(array_a); - if (array_b != fixed_b) MEM_freeN(array_b); +#if 0 /* This actually makes things slower, since it has to check for animation paths etc! */ + if (RNA_property_animated(ptr_local, prop_local)) { + /* We cannot do anything here really, animation is some kind of dynamic overrides that has + * precedence over static one... */ + continue; + } +#endif - return equals; +#define RNA_PATH_BUFFSIZE 8192 +#define RNA_PATH_PRINTF(_str, ...) \ + if (BLI_snprintf(rna_path, RNA_PATH_BUFFSIZE, \ + (_str), __VA_ARGS__) >= RNA_PATH_BUFFSIZE) \ + { rna_path = BLI_sprintfN((_str), __VA_ARGS__); }(void)0 +#define RNA_PATH_FREE \ + if (rna_path != rna_path_buffer) MEM_freeN(rna_path) + + char rna_path_buffer[RNA_PATH_BUFFSIZE]; + char *rna_path = rna_path_buffer; + + /* XXX TODO this will have to be refined to handle collections insertions, and array items */ + if (root_path) { + /* Inlined building, much much more efficient. */ + if (prop_local->magic == RNA_MAGIC) { + RNA_PATH_PRINTF("%s.%s", root_path, RNA_property_identifier(prop_local)); } else { - int value = RNA_property_boolean_get(a, prop); - return value == RNA_property_boolean_get(b, prop); + RNA_PATH_PRINTF("%s[\"%s\"]", root_path, RNA_property_identifier(prop_local)); } } + else { + /* This is rather slow, but is not much called, so not really worth optimizing. */ + rna_path = RNA_path_from_ID_to_property(ptr_local, prop_local); + } + if (rna_path == NULL) { + continue; + } - case PROP_INT: - { - if (len) { - int fixed_a[16], fixed_b[16]; - int *array_a, *array_b; - bool equals; +// printf("Override Checking %s\n", rna_path); - array_a = (len > 16) ? MEM_mallocN(sizeof(int) * len, "RNA equals") : fixed_a; - array_b = (len > 16) ? MEM_mallocN(sizeof(int) * len, "RNA equals") : fixed_b; + if (ignore_overridden && BKE_override_static_property_find(override, rna_path) != NULL) { + RNA_PATH_FREE; + continue; + } - RNA_property_int_get_array(a, prop, array_a); - RNA_property_int_get_array(b, prop, array_b); +#ifdef DEBUG_OVERRIDE_TIMEIT + if (!root_path) { + _timeit_time_diffing = PIL_check_seconds_timer(); + } +#endif - equals = memcmp(array_a, array_b, sizeof(int) * len) == 0; + eRNAOverrideMatchResult report_flags = 0; + const int diff = rna_property_override_diff( + ptr_local, ptr_reference, NULL, prop_local, prop_reference, rna_path, + RNA_EQ_STRICT, override, flags, &report_flags); - if (array_a != fixed_a) MEM_freeN(array_a); - if (array_b != fixed_b) MEM_freeN(array_b); +#ifdef DEBUG_OVERRIDE_TIMEIT + if (!root_path) { + const float _delta_time = (float)(PIL_check_seconds_timer() - _timeit_time_diffing); + _delta_time_diffing += _delta_time; + _num_delta_time_diffing++; + } +#endif - return equals; + matching = matching && diff == 0; + if (r_report_flags) { + *r_report_flags |= report_flags; + } + + if (diff != 0) { + /* XXX TODO: refine this for per-item overriding of arrays... */ + IDOverrideStaticProperty *op = BKE_override_static_property_find(override, rna_path); + IDOverrideStaticPropertyOperation *opop = op ? op->operations.first : NULL; + + if (do_restore && (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) == 0) { + /* We are allowed to restore to reference's values. */ + if (ELEM(NULL, op, opop) || opop->operation == IDOVERRIDESTATIC_OP_NOOP) { + /* We should restore that property to its reference value */ + if (RNA_property_editable(ptr_local, prop_local)) { + IDOverrideStaticPropertyOperation opop_tmp = { + .operation = IDOVERRIDESTATIC_OP_REPLACE, + .subitem_reference_index = -1, + .subitem_local_index = -1 + }; + rna_property_override_operation_apply(ptr_local, ptr_reference, NULL, + prop_local, prop_reference, NULL, &opop_tmp); + if (r_report_flags) { + *r_report_flags |= RNA_OVERRIDE_MATCH_RESULT_RESTORED; + } + } + else { + /* Too noisy for now, this triggers on runtime props like transform matrices etc. */ + /* BLI_assert(!"We have differences between reference and overriding data on non-editable property."); */ + matching = false; + } + } } - else { - int value = RNA_property_int_get(a, prop); - return value == RNA_property_int_get(b, prop); + else if ((report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) == 0 && ELEM(NULL, op, opop)) { + /* This property is not overridden, and differs from reference, so we have no match. */ + matching = false; + if (!(do_create || do_restore)) { + /* Since we have no 'changing' action allowed, we can break here. */ + MEM_SAFE_FREE(rna_path); + break; + } } } - case PROP_FLOAT: - { - if (len) { - float fixed_a[16], fixed_b[16]; - float *array_a, *array_b; - bool equals; + RNA_PATH_FREE; - array_a = (len > 16) ? MEM_mallocN(sizeof(float) * len, "RNA equals") : fixed_a; - array_b = (len > 16) ? MEM_mallocN(sizeof(float) * len, "RNA equals") : fixed_b; +#undef RNA_PATH_BUFFSIZE +#undef RNA_PATH_PRINTF +#undef RNA_PATH_FREE + } + RNA_property_collection_end(&iter); - RNA_property_float_get_array(a, prop, array_a); - RNA_property_float_get_array(b, prop, array_b); +#ifdef DEBUG_OVERRIDE_TIMEIT + if (!root_path) { + const float _delta_time = (float)(PIL_check_seconds_timer() - _timeit_time_global); + _sum_time_global += _delta_time; + _num_time_global++; + _sum_time_diffing += _delta_time_diffing; + _num_time_diffing++; + printf("ID: %s\n", ((ID *)ptr_local->id.data)->name); + printf("time end (%s): %.6f\n", __func__, _delta_time); + printf("time averaged (%s): %.6f (total: %.6f, in %d runs)\n", __func__, + (_sum_time_global / _num_time_global), _sum_time_global, (int)_num_time_global); + printf("diffing time end (%s): %.6f (in %d runs)\n", __func__, _delta_time_diffing, _num_delta_time_diffing); + printf("diffing time averaged (%s): %.6f (total: %.6f, in %d runs)\n", __func__, + (_sum_time_diffing / _num_time_diffing), _sum_time_diffing, (int)_num_time_diffing); + } +#endif - equals = memcmp(array_a, array_b, sizeof(float) * len) == 0; + return matching; +} - if (array_a != fixed_a) MEM_freeN(array_a); - if (array_b != fixed_b) MEM_freeN(array_b); - return equals; +/** Store needed second operands into \a storage data-block for differential override operations. */ +bool RNA_struct_override_store( + PointerRNA *ptr_local, PointerRNA *ptr_reference, PointerRNA *ptr_storage, IDOverrideStatic *override) +{ + bool changed = false; + +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_START_AVERAGED(RNA_struct_override_store); +#endif + for (IDOverrideStaticProperty *op = override->properties.first; op; op = op->next) { + /* Simplified for now! */ + PointerRNA data_reference, data_local; + PropertyRNA *prop_reference, *prop_local; + + if (RNA_path_resolve_property(ptr_local, op->rna_path, &data_local, &prop_local) && + RNA_path_resolve_property(ptr_reference, op->rna_path, &data_reference, &prop_reference)) + { + PointerRNA data_storage; + PropertyRNA *prop_storage = NULL; + + /* It is totally OK if this does not success, only a subset of override operations actually need storage. */ + if (ptr_storage && (ptr_storage->id.data != NULL)) { + RNA_path_resolve_property(ptr_storage, op->rna_path, &data_storage, &prop_storage); } - else { - float value = RNA_property_float_get(a, prop); - return value == RNA_property_float_get(b, prop); + + if (rna_property_override_operation_store(&data_local, &data_reference, &data_storage, + prop_reference, prop_local, prop_storage, op)) + { + changed = true; } } + } +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_END_AVERAGED(RNA_struct_override_store); +#endif - case PROP_ENUM: - { - int value = RNA_property_enum_get(a, prop); - return value == RNA_property_enum_get(b, prop); - } + return changed; +} - case PROP_STRING: +static void rna_property_override_apply_ex( + PointerRNA *ptr_local, PointerRNA *ptr_override, PointerRNA *ptr_storage, + PropertyRNA *prop_local, PropertyRNA *prop_override, PropertyRNA *prop_storage, + IDOverrideStaticProperty *op, const bool do_insert) +{ + for (IDOverrideStaticPropertyOperation *opop = op->operations.first; opop; opop = opop->next) { + if (!do_insert != !ELEM(opop->operation, IDOVERRIDESTATIC_OP_INSERT_AFTER, IDOVERRIDESTATIC_OP_INSERT_BEFORE)) { + if (!do_insert) { + printf("Skipping insert override operations in first pass (%s)!\n", op->rna_path); + } + continue; + } + if (!rna_property_override_operation_apply(ptr_local, ptr_override, ptr_storage, + prop_local, prop_override, prop_storage, opop)) { - char fixed_a[128], fixed_b[128]; - int len_a, len_b; - char *value_a = RNA_property_string_get_alloc(a, prop, fixed_a, sizeof(fixed_a), &len_a); - char *value_b = RNA_property_string_get_alloc(b, prop, fixed_b, sizeof(fixed_b), &len_b); - bool equals = STREQ(value_a, value_b); + /* TODO No assert here, would be much much better to just report as warning, + * failing override applications will probably be fairly common! */ + BLI_assert(0); + } + } +} - if (value_a != fixed_a) MEM_freeN(value_a); - if (value_b != fixed_b) MEM_freeN(value_b); +/** Apply given \a override operations on \a ptr_local, using \a ptr_override + * (and \a ptr_storage form differential ops) as source. */ +void RNA_struct_override_apply( + PointerRNA *ptr_local, PointerRNA *ptr_override, PointerRNA *ptr_storage, IDOverrideStatic *override) +{ +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_START_AVERAGED(RNA_struct_override_apply); +#endif + /* Note: Applying insert operations in a separate pass is mandatory. + * We could optimize this later, but for now, as inneficient as it is, don't think this is a critical point. + */ + bool do_insert = false; + for (int i = 0; i < 2; i++, do_insert = true) { + for (IDOverrideStaticProperty *op = override->properties.first; op; op = op->next) { + /* Simplified for now! */ + PointerRNA data_override, data_local; + PropertyRNA *prop_override, *prop_local; + + if (RNA_path_resolve_property(ptr_local, op->rna_path, &data_local, &prop_local) && + RNA_path_resolve_property(ptr_override, op->rna_path, &data_override, &prop_override)) + { + PointerRNA data_storage; + PropertyRNA *prop_storage = NULL; - return equals; - } + /* It is totally OK if this does not success, only a subset of override operations actually need storage. */ + if (ptr_storage && (ptr_storage->id.data != NULL)) { + RNA_path_resolve_property(ptr_storage, op->rna_path, &data_storage, &prop_storage); + } - case PROP_POINTER: - { - if (!STREQ(RNA_property_identifier(prop), "rna_type")) { - PointerRNA propptr_a = RNA_property_pointer_get(a, prop); - PointerRNA propptr_b = RNA_property_pointer_get(b, prop); - return RNA_struct_equals(&propptr_a, &propptr_b, mode); + rna_property_override_apply_ex( + &data_local, &data_override, prop_storage ? &data_storage : NULL, + prop_local, prop_override, prop_storage, op, do_insert); } - break; +#ifndef NDEBUG + else { + printf("Failed to apply static override operation to '%s.%s' (could not resolve some properties)\n", + ((ID *)ptr_override->id.data)->name, op->rna_path); + } +#endif } + } +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_END_AVERAGED(RNA_struct_override_apply); +#endif +} - default: - break; +IDOverrideStaticProperty *RNA_property_override_property_find(PointerRNA *ptr, PropertyRNA *prop) +{ + ID *id = ptr->id.data; + + if (!id || !id->override_static) { + return NULL; } - return true; + char *rna_path = RNA_path_from_ID_to_property(ptr, prop); + if (rna_path) { + IDOverrideStaticProperty *op = BKE_override_static_property_find(id->override_static, rna_path); + MEM_freeN(rna_path); + return op; + } + return NULL; } -bool RNA_struct_equals(PointerRNA *a, PointerRNA *b, eRNAEqualsMode mode) +IDOverrideStaticProperty *RNA_property_override_property_get(PointerRNA *ptr, PropertyRNA *prop, bool *r_created) { - CollectionPropertyIterator iter; -// CollectionPropertyRNA *citerprop; /* UNUSED */ - PropertyRNA *iterprop; - bool equals = true; + ID *id = ptr->id.data; - if (a == NULL && b == NULL) - return true; - else if (a == NULL || b == NULL) - return false; - else if (a->type != b->type) - return false; + if (!id || !id->override_static) { + return NULL; + } + + char *rna_path = RNA_path_from_ID_to_property(ptr, prop); + if (rna_path) { + IDOverrideStaticProperty *op = BKE_override_static_property_get(id->override_static, rna_path, r_created); + MEM_freeN(rna_path); + return op; + } + return NULL; +} - iterprop = RNA_struct_iterator_property(a->type); -// citerprop = (CollectionPropertyRNA *)rna_ensure_property(iterprop); /* UNUSED */ +IDOverrideStaticPropertyOperation *RNA_property_override_property_operation_find( + PointerRNA *ptr, PropertyRNA *prop, const int index, const bool strict, bool *r_strict) +{ + IDOverrideStaticProperty *op = RNA_property_override_property_find(ptr, prop); - RNA_property_collection_begin(a, iterprop, &iter); - for (; iter.valid; RNA_property_collection_next(&iter)) { - PropertyRNA *prop = iter.ptr.data; + if (!op) { + return NULL; + } - if (!RNA_property_equals(a, b, prop, mode)) { - equals = false; - break; + return BKE_override_static_property_operation_find(op, NULL, NULL, index, index, strict, r_strict); +} + +IDOverrideStaticPropertyOperation *RNA_property_override_property_operation_get( + PointerRNA *ptr, PropertyRNA *prop, const short operation, const int index, + const bool strict, bool *r_strict, bool *r_created) +{ + IDOverrideStaticProperty *op = RNA_property_override_property_get(ptr, prop, NULL); + + if (!op) { + return NULL; + } + + return BKE_override_static_property_operation_get(op, operation, NULL, NULL, index, index, strict, r_strict, r_created); +} + +eRNAOverrideStatus RNA_property_static_override_status(PointerRNA *ptr, PropertyRNA *prop, const int index) +{ + int override_status = 0; + + if (!ptr || !prop || !ptr->id.data || !((ID *)ptr->id.data)->override_static) { + return override_status; + } + + if (RNA_property_overridable_get(ptr, prop) && RNA_property_editable_flag(ptr, prop)) { + override_status |= RNA_OVERRIDE_STATUS_OVERRIDABLE; + } + + IDOverrideStaticPropertyOperation *opop = RNA_property_override_property_operation_find(ptr, prop, index, false, NULL); + if (opop != NULL) { + override_status |= RNA_OVERRIDE_STATUS_OVERRIDDEN; + if (opop->flag & IDOVERRIDESTATIC_FLAG_MANDATORY) { + override_status |= RNA_OVERRIDE_STATUS_MANDATORY; + } + if (opop->flag & IDOVERRIDESTATIC_FLAG_LOCKED) { + override_status |= RNA_OVERRIDE_STATUS_LOCKED; } } - RNA_property_collection_end(&iter); - return equals; + return override_status; } + bool RNA_path_resolved_create( PointerRNA *ptr, struct PropertyRNA *prop, const int prop_index, @@ -7321,3 +7876,22 @@ bool RNA_path_resolved_create( return false; } } + +static char rna_struct_state_owner[64]; +void RNA_struct_state_owner_set(const char *name) +{ + if (name) { + BLI_strncpy(rna_struct_state_owner, name, sizeof(rna_struct_state_owner)); + } + else { + rna_struct_state_owner[0] = '\0'; + } +} + +const char *RNA_struct_state_owner_get(void) +{ + if (rna_struct_state_owner[0]) { + return rna_struct_state_owner; + } + return NULL; +} |