Core: RNA: Use VectorSet for StructRNA properties lookup
Replace the `Ghash` `prophash` properties map in RNA containers by a
`CustomIDVectorSet` `prop_lookup_set`.
This commit also allows for runtime-defined RNA Struct types to use
`CustomIDVectorSet` for properties lookup, instead of only relying on linear
search in the ListBase property list.
NOTE: This also simplifies a bit the code compared to using Ghash, since the
string key is contained whiting the property, and not stored separately as in the
GHash. It also means that the string key (the property identifier) is 'automatically'
updated in the VectorSet when updated in the property itself. This avoids the
need to re-insert the property when its identifier string is duplicated into a new
memory address. However, modifying the property identifier would cause
undefined behavior if the property is not removed from the vector set first.
-----
While its an impractical example, the following custom PropertyGroup example shows the potential slowdowns just
of looking through the ListBase
``` py
import bpy
test_properties_script = "import bpy\nclass TestProperties(bpy.types.PropertyGroup):"
property_count=10000
for x in range(property_count):
test_properties_script+="\n prop_{0} : bpy.props.IntProperty(name=\"Prop {0}\")".format(x)
exec(test_properties_script)
class LayoutDemoPanel(bpy.types.Panel):
"""Creates a Test in the text editor side panel to test ui perfomance"""
bl_label = "Test"
bl_category = "Test"
bl_idname = "TEXT_PT_test"
bl_space_type = 'TEXT_EDITOR'
bl_region_type = 'UI'
def draw(self, context):
layout = self.layout
layout.use_property_split=True
layout.use_property_decorate=False
for x in range(property_count):
row = layout.row()
row.prop(bpy.data.scenes['Scene'].test_pointer, "prop_{}".format(x))
classes = [
TestProperties,
LayoutDemoPanel,
]
if hasattr(bpy.types.Scene,'test_pointer'):
del bpy.types.Scene.test_pointer
class_register, class_unregister = bpy.utils.register_classes_factory(classes)
class_register()
bpy.types.Scene.test_pointer = bpy.props.PointerProperty(type=TestProperties)
```
<video src="attachments/95e36a02-b781-44b1-9a18-9e453f8a1432" title="2025-02-03 14-12-47.mp4" controls></video>
Pull Request: https://projects.blender.org/blender/blender/pulls/134000
This commit is contained in:
committed by
Bastien Montagne
parent
97055868a3
commit
82e2a57dcd
@@ -87,12 +87,14 @@ void RNA_init()
|
||||
for (srna = static_cast<StructRNA *>(BLENDER_RNA.structs.first); srna;
|
||||
srna = static_cast<StructRNA *>(srna->cont.next))
|
||||
{
|
||||
if (!srna->cont.prophash) {
|
||||
srna->cont.prophash = BLI_ghash_str_new("RNA_init gh");
|
||||
if (!srna->cont.prop_lookup_set) {
|
||||
srna->cont.prop_lookup_set =
|
||||
MEM_new<blender::CustomIDVectorSet<PropertyRNA *, PropertyRNAIdentifierGetter>>(
|
||||
__func__);
|
||||
|
||||
LISTBASE_FOREACH (PropertyRNA *, prop, &srna->cont.properties) {
|
||||
if (!(prop->flag_internal & PROP_INTERN_BUILTIN)) {
|
||||
BLI_ghash_insert(srna->cont.prophash, (void *)prop->identifier, prop);
|
||||
srna->cont.prop_lookup_set->add(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,10 +126,7 @@ void RNA_exit()
|
||||
for (srna = static_cast<StructRNA *>(BLENDER_RNA.structs.first); srna;
|
||||
srna = static_cast<StructRNA *>(srna->cont.next))
|
||||
{
|
||||
if (srna->cont.prophash) {
|
||||
BLI_ghash_free(srna->cont.prophash, nullptr, nullptr);
|
||||
srna->cont.prophash = nullptr;
|
||||
}
|
||||
MEM_SAFE_DELETE(srna->cont.prop_lookup_set);
|
||||
}
|
||||
|
||||
RNA_free(&BLENDER_RNA);
|
||||
|
||||
@@ -814,7 +814,7 @@ void RNA_struct_free(BlenderRNA *brna, StructRNA *srna)
|
||||
srna_identifier);
|
||||
}
|
||||
}
|
||||
|
||||
MEM_SAFE_DELETE(srna->cont.prop_lookup_set);
|
||||
for (prop = static_cast<PropertyRNA *>(srna->cont.properties.first); prop; prop = nextprop) {
|
||||
nextprop = prop->next;
|
||||
|
||||
@@ -951,7 +951,7 @@ StructRNA *RNA_def_struct_ptr(BlenderRNA *brna, const char *identifier, StructRN
|
||||
/* Copy from struct to derive stuff, a bit clumsy since we can't
|
||||
* use #MEM_dupallocN, data structs may not be allocated but builtin. */
|
||||
memcpy(srna, srnafrom, sizeof(StructRNA));
|
||||
srna->cont.prophash = nullptr;
|
||||
srna->cont.prop_lookup_set = nullptr;
|
||||
BLI_listbase_clear(&srna->cont.properties);
|
||||
BLI_listbase_clear(&srna->functions);
|
||||
srna->py_type = nullptr;
|
||||
@@ -1003,6 +1003,8 @@ StructRNA *RNA_def_struct_ptr(BlenderRNA *brna, const char *identifier, StructRN
|
||||
}
|
||||
else {
|
||||
RNA_def_struct_flag(srna, STRUCT_RUNTIME);
|
||||
srna->cont.prop_lookup_set =
|
||||
MEM_new<blender::CustomIDVectorSet<PropertyRNA *, PropertyRNAIdentifierGetter>>(__func__);
|
||||
}
|
||||
|
||||
if (srnafrom) {
|
||||
@@ -1013,6 +1015,10 @@ StructRNA *RNA_def_struct_ptr(BlenderRNA *brna, const char *identifier, StructRN
|
||||
/* define some builtin properties */
|
||||
prop = RNA_def_property(&srna->cont, "rna_properties", PROP_COLLECTION, PROP_NONE);
|
||||
prop->flag_internal |= PROP_INTERN_BUILTIN;
|
||||
/* Properties with internal flag #PROP_INTERN_BUILTIN are not included for lookup. */
|
||||
if (srna->cont.prop_lookup_set) {
|
||||
srna->cont.prop_lookup_set->remove_as(prop->identifier);
|
||||
}
|
||||
RNA_def_property_ui_text(prop, "Properties", "RNA property collection");
|
||||
|
||||
if (DefRNA.preprocess) {
|
||||
@@ -1490,8 +1496,8 @@ PropertyRNA *RNA_def_property(StructOrFunctionRNA *cont_,
|
||||
RNA_def_property_flag(prop, PROP_IDPROPERTY);
|
||||
prop->flag_internal |= PROP_INTERN_RUNTIME;
|
||||
#ifdef RNA_RUNTIME
|
||||
if (cont->prophash) {
|
||||
BLI_ghash_insert(cont->prophash, (void *)prop->identifier, prop);
|
||||
if (cont->prop_lookup_set) {
|
||||
cont->prop_lookup_set->add(prop);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -4863,21 +4869,12 @@ void RNA_def_func_free_pointers(FunctionRNA *func)
|
||||
}
|
||||
}
|
||||
|
||||
void RNA_def_property_duplicate_pointers(StructOrFunctionRNA *cont_, PropertyRNA *prop)
|
||||
void RNA_def_property_duplicate_pointers(StructOrFunctionRNA * /*cont_*/, PropertyRNA *prop)
|
||||
{
|
||||
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
|
||||
int a;
|
||||
|
||||
/* annoying since we just added this to a hash, could make this add the correct key to the hash
|
||||
* in the first place */
|
||||
if (prop->identifier) {
|
||||
if (cont->prophash) {
|
||||
prop->identifier = BLI_strdup(prop->identifier);
|
||||
BLI_ghash_reinsert(cont->prophash, (void *)prop->identifier, prop, nullptr, nullptr);
|
||||
}
|
||||
else {
|
||||
prop->identifier = BLI_strdup(prop->identifier);
|
||||
}
|
||||
prop->identifier = BLI_strdup(prop->identifier);
|
||||
}
|
||||
|
||||
if (prop->name) {
|
||||
@@ -5053,8 +5050,8 @@ static void rna_def_property_free(StructOrFunctionRNA *cont_, PropertyRNA *prop)
|
||||
ContainerRNA *cont = static_cast<ContainerRNA *>(cont_);
|
||||
|
||||
if (prop->flag_internal & PROP_INTERN_RUNTIME) {
|
||||
if (cont->prophash) {
|
||||
BLI_ghash_remove(cont->prophash, prop->identifier, nullptr, nullptr);
|
||||
if (cont->prop_lookup_set) {
|
||||
cont->prop_lookup_set->remove_as(prop->identifier);
|
||||
}
|
||||
|
||||
RNA_def_property_free_pointers(prop);
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
@@ -287,11 +289,15 @@ struct RNAPropertyOverrideApplyContext {
|
||||
};
|
||||
using RNAPropOverrideApply = bool (*)(Main *bmain, RNAPropertyOverrideApplyContext &rnaapply_ctx);
|
||||
|
||||
struct PropertyRNAIdentifierGetter {
|
||||
blender::StringRef operator()(const PropertyRNA *prop) const;
|
||||
};
|
||||
|
||||
/* Container - generic abstracted container of RNA properties */
|
||||
struct ContainerRNA {
|
||||
void *next, *prev;
|
||||
|
||||
struct GHash *prophash;
|
||||
blender::CustomIDVectorSet<PropertyRNA *, PropertyRNAIdentifierGetter> *prop_lookup_set;
|
||||
ListBase properties;
|
||||
};
|
||||
|
||||
@@ -385,6 +391,11 @@ struct PropertyRNA {
|
||||
void *py_data;
|
||||
};
|
||||
|
||||
inline blender::StringRef PropertyRNAIdentifierGetter::operator()(const PropertyRNA *prop) const
|
||||
{
|
||||
return prop->identifier;
|
||||
}
|
||||
|
||||
/* internal flags WARNING! 16bits only! */
|
||||
enum PropertyFlagIntern {
|
||||
PROP_INTERN_BUILTIN = (1 << 0),
|
||||
|
||||
@@ -563,9 +563,9 @@ bool rna_builtin_properties_lookup_string(PointerRNA *ptr, const char *key, Poin
|
||||
srna = ptr->type;
|
||||
|
||||
do {
|
||||
if (srna->cont.prophash) {
|
||||
prop = static_cast<PropertyRNA *>(BLI_ghash_lookup(srna->cont.prophash, (void *)key));
|
||||
|
||||
if (srna->cont.prop_lookup_set) {
|
||||
PropertyRNA *const *lookup_prop = srna->cont.prop_lookup_set->lookup_key_ptr_as(key);
|
||||
prop = lookup_prop ? *lookup_prop : nullptr;
|
||||
if (prop) {
|
||||
*r_ptr = {nullptr, &RNA_Property, prop};
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user