diff options
Diffstat (limited to 'source/blender/makesrna/intern/rna_access.c')
-rw-r--r-- | source/blender/makesrna/intern/rna_access.c | 837 |
1 files changed, 612 insertions, 225 deletions
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 18e42488829..b8dbc69f352 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -42,6 +42,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 +55,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" @@ -422,7 +429,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. @@ -445,7 +452,7 @@ IDProperty *rna_idproperty_check(PropertyRNA **prop, PointerRNA *ptr) return idprop; } else - return NULL; + return return_rnaprop ? *prop : NULL; } { @@ -460,6 +467,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 */ @@ -1821,7 +1841,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 || (prop->flag & PROP_OVERRIDABLE_STATIC))))); } /** @@ -1847,11 +1868,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 = "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 = "Can't edit this property from a linked data-block."; + } + return false; + } + if (id->override_static != NULL && (prop->flag & PROP_OVERRIDABLE_STATIC) == 0) { + if (!(*r_info)[0]) { + *r_info = "Can't edit this property from an override data-block."; + } + return false; } - return false; } return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0); @@ -1959,7 +1988,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 { @@ -1970,14 +1999,24 @@ static void rna_property_update(bContext *C, Main *bmain, Scene *scene, PointerR else prop->update(bmain, scene, ptr); } +#if 0 if (prop->noteflag) WM_main_add_notifier(prop->noteflag, ptr->id.data); +#else + /* 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); + } +#endif } 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. @@ -6996,120 +7035,34 @@ bool RNA_property_reset(PointerRNA *ptr, PropertyRNA *prop, int index) } } +static bool rna_property_override_operation_apply( + PointerRNA *ptr_local, PointerRNA *ptr_reference, PointerRNA *ptr_storage, + PropertyRNA *prop_local, PropertyRNA *prop_reference, PropertyRNA *prop_storage, + IDOverrideStaticPropertyOperation *opop); + bool RNA_property_copy(PointerRNA *ptr, PointerRNA *fromptr, PropertyRNA *prop, int index) { - int len, fromlen; - PropertyRNA *fromprop = prop; - - 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); - - /* its possible the custom-prop doesn't exist on this data-block */ - if (prop == NULL) { - return false; - } - - /* 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); - } + if (!RNA_property_editable(ptr, prop)) { + return false; } - /* get the length of the array to work with */ - len = RNA_property_array_length(ptr, prop); - fromlen = RNA_property_array_length(fromptr, fromprop); + PropertyRNA *prop_dst = prop; + PropertyRNA *prop_src = prop; - if (len != fromlen) - 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); - /* 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; + if (ELEM(NULL, prop_dst, prop_src)) { + 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 */ @@ -7134,176 +7087,610 @@ 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). + */ +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 (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_flag(prop_a) & PROP_OVERRIDABLE_STATIC) == 0) { + 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 = 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_reference, prop_reference); + if (prop_storage) { + len_storage = RNA_property_array_length(ptr_storage, prop_storage); + } - if (len != fromlen) - return false; + 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; + } - /* get and set the default values as appropriate for the various types */ - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: + 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)) { - if (len) { - int fixed_a[16], fixed_b[16]; - int *array_a, *array_b; - bool equals; + changed = true; + } + } - 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; + return changed; +} - RNA_property_boolean_get_array(a, prop, array_a); - RNA_property_boolean_get_array(b, prop, array_b); +static bool rna_property_override_operation_apply( + PointerRNA *ptr_local, PointerRNA *ptr_reference, PointerRNA *ptr_storage, + PropertyRNA *prop_local, PropertyRNA *prop_reference, PropertyRNA *prop_storage, + IDOverrideStaticPropertyOperation *opop) +{ + int len_local, len_reference, len_storage = 0; - equals = memcmp(array_a, array_b, sizeof(int) * len) == 0; + const short override_op = opop->operation; - if (array_a != fixed_a) MEM_freeN(array_a); - if (array_b != fixed_b) MEM_freeN(array_b); + if (override_op == IDOVERRIDESTATIC_OP_NOOP) { + return true; + } - return equals; - } - else { - int value = RNA_property_boolean_get(a, prop); - return value == RNA_property_boolean_get(b, prop); - } + 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_reference->magic == RNA_MAGIC && prop_reference->override_apply != override_apply) { + override_apply = NULL; + } + } + else if (prop_reference->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_reference->override_apply) { + override_apply = prop_local->override_apply; + } - case PROP_INT: - { - if (len) { - int fixed_a[16], fixed_b[16]; - int *array_a, *array_b; - bool equals; + 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_reference->magic == RNA_MAGIC); +#endif + BLI_assert(0); + return false; + } + + /* 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 (ptr_storage) { + len_storage = RNA_property_array_length(ptr_storage, prop_storage); + } + + 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 */ + return override_apply( + ptr_local, ptr_reference, ptr_storage, + prop_local, prop_reference, 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; + +#ifdef DEBUG_OVERRIDE_TIMEIT + static float _sum_time = 0.0f; + static float _num_time = 0.0f; + double _timeit_time; + if (!root_path) { + _timeit_time = PIL_check_seconds_timer(); + } +#endif - 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; + iterprop = RNA_struct_iterator_property(ptr_local->type); - RNA_property_int_get_array(a, prop, array_a); - RNA_property_int_get_array(b, prop, array_b); + 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; - equals = memcmp(array_a, array_b, sizeof(int) * len) == 0; + /* 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); + + if (ELEM(NULL, prop_local, prop_reference)) { + continue; + } + + if (ignore_non_overridable && !(prop_local->flag & PROP_OVERRIDABLE_STATIC)) { + continue; + } - if (array_a != fixed_a) MEM_freeN(array_a); - if (array_b != fixed_b) MEM_freeN(array_b); + 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; + } - return equals; + /* XXX TODO this will have to be refined to handle collections insertions, and array items */ + char *rna_path; + if (root_path) { + /* Inlined building, much much more efficient. */ + if (prop_local->magic == RNA_MAGIC) { + rna_path = BLI_sprintfN("%s.%s", root_path, RNA_property_identifier(prop_local)); } else { - int value = RNA_property_int_get(a, prop); - return value == RNA_property_int_get(b, prop); + rna_path = BLI_sprintfN("%s[\"%s\"]", root_path, RNA_property_identifier(prop_local)); } } + else { + rna_path = RNA_path_from_ID_to_property(ptr_local, prop_local); + } + if (rna_path == NULL) { + continue; + } - case PROP_FLOAT: - { - if (len) { - float fixed_a[16], fixed_b[16]; - float *array_a, *array_b; - bool equals; + if (ignore_overridden && BKE_override_static_property_find(override, rna_path) != NULL) { + MEM_SAFE_FREE(rna_path); + continue; + } - 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; + 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); + + 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 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; + } + } + } - RNA_property_float_get_array(a, prop, array_a); - RNA_property_float_get_array(b, prop, array_b); + MEM_SAFE_FREE(rna_path); + } + RNA_property_collection_end(&iter); + +#ifdef DEBUG_OVERRIDE_TIMEIT + if (!root_path) { + const float _delta_time = (float)(PIL_check_seconds_timer() - _timeit_time); + _sum_time += _delta_time; + _num_time++; + 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 / _num_time), _sum_time, (int)_num_time); + } +#endif - equals = memcmp(array_a, array_b, sizeof(float) * len) == 0; + return matching; +} + + +/** 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 (array_a != fixed_a) MEM_freeN(array_a); - if (array_b != fixed_b) MEM_freeN(array_b); + 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; - 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); } - 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: + return changed; +} + +static void rna_property_override_apply_ex( + PointerRNA *ptr_local, PointerRNA *ptr_reference, PointerRNA *ptr_storage, + PropertyRNA *prop_local, PropertyRNA *prop_reference, PropertyRNA *prop_storage, IDOverrideStaticProperty *op) +{ + for (IDOverrideStaticPropertyOperation *opop = op->operations.first; opop; opop = opop->next) { + if (!rna_property_override_operation_apply(ptr_local, ptr_reference, ptr_storage, + prop_local, prop_reference, prop_storage, opop)) { - int value = RNA_property_enum_get(a, prop); - return value == RNA_property_enum_get(b, prop); + BLI_assert(0); } + } +} - case PROP_STRING: +/** Apply given \a override operations on \a dst, using \a src as source. */ +void RNA_struct_override_apply( + PointerRNA *ptr_local, PointerRNA *ptr_reference, PointerRNA *ptr_storage, IDOverrideStatic *override) +{ +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_START_AVERAGED(RNA_struct_override_apply); +#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)) { - 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); + PointerRNA data_storage; + PropertyRNA *prop_storage = NULL; - if (value_a != fixed_a) MEM_freeN(value_a); - if (value_b != fixed_b) MEM_freeN(value_b); + /* 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); + } - return equals; + rna_property_override_apply_ex( + &data_local, &data_reference, prop_storage ? &data_storage : NULL, + prop_local, prop_reference, prop_storage, op); } - - 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); - } - break; +#ifndef NDEBUG + else { + printf("Failed to apply static override operation to '%s.%s' (could not resolve some properties)\n", + ((ID *)ptr_reference->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; + } - iterprop = RNA_struct_iterator_property(a->type); -// citerprop = (CollectionPropertyRNA *)rna_ensure_property(iterprop); /* UNUSED */ + 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; +} - RNA_property_collection_begin(a, iterprop, &iter); - for (; iter.valid; RNA_property_collection_next(&iter)) { - PropertyRNA *prop = iter.ptr.data; +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); - if (!RNA_property_equals(a, b, prop, mode)) { - equals = false; - break; - } + if (!op) { + return NULL; } - RNA_property_collection_end(&iter); - return equals; + 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); +} + +void RNA_property_override_status( + PointerRNA *ptr, PropertyRNA *prop, const int index, + bool *r_overridable, bool *r_overridden, bool *r_mandatory, bool *r_locked) +{ +#define SET_RET(_name, _val) if (_name != NULL) *_name = (_val) + + SET_RET(r_overridable, false); + SET_RET(r_overridden, false); + SET_RET(r_mandatory, false); + SET_RET(r_locked, false); + + if (!ptr || !prop || !ptr->id.data || !((ID *)ptr->id.data)->override_static) { + return; + } + + SET_RET(r_overridable, (prop->flag & PROP_OVERRIDABLE_STATIC) && (prop->flag & PROP_EDITABLE)); + + if (r_overridden || r_mandatory || r_locked) { + IDOverrideStaticPropertyOperation *opop = RNA_property_override_property_operation_find(ptr, prop, index, false, NULL); + SET_RET(r_overridden, opop != NULL); + SET_RET(r_mandatory, (opop->flag & IDOVERRIDESTATIC_FLAG_MANDATORY) != 0); + SET_RET(r_locked, (opop->flag & IDOVERRIDESTATIC_FLAG_LOCKED) != 0); + } +} + + bool RNA_path_resolved_create( PointerRNA *ptr, struct PropertyRNA *prop, |