BPY: Implement get_transform and set_transform for runtime-defined RNA properties.

Improve handling of runtime defined python RNA properties. Mainly:
* Add `get_transform` and `set_transform` new callbacks.
  These allow to edit the value, while still using the default
  (IDProperty-based) storage system.
* Read-only properties should now be defined using a new `options` flag,
  `READ_ONLY`.
* `get`/`set` should only be used when storing data outside of the
  default system now.
  * Having a `get` without a `set` defined forces property to be
    read-only (same behavior as before).
  * Having a `set` without a `get` is now an error.
* Just like with existing `get/set` callbacks, `get_/set_transform`
  callbacks must always generate values matching the constraints defined
  by their `bpy.props` property definition (same type, within required
  range, same dimensions/sizes for the `Vector` properties, etc.).
* To simplify handling of non-statically sized strings, the relevant
  RNA API has been modified, to use `std::string` instead of
  (allocated) char arrays.

Relevant unittests and benchmarking have been added or updated as part
of this project.

Note: From initial benchmarking, 'transform' versions of get/set are
several times faster than 'real' get/set.

Implements #141042.

Pull Request: https://projects.blender.org/blender/blender/pulls/141303
This commit is contained in:
Bastien Montagne
2025-09-02 11:30:09 +02:00
committed by Bastien Montagne
parent 75c66158aa
commit 469f54f484
19 changed files with 2762 additions and 740 deletions

View File

@@ -69,18 +69,28 @@ def _run_runtime_group_register_access(args):
do_register = args.get("do_register", False)
do_access = args.get("do_access", False)
do_get_set = args.get("do_get_set", False)
do_transform = args.get("do_transform", False)
property_type = args.get("property_type", 'IntProperty')
property_definition_cb = getattr(bpy.props, property_type)
assert (not (do_get_set and do_transform))
# Define basic 'transform' callbacks to test setting value,
# default to just setting untransformed value for 'unknown'/undefined property types.
property_transform_cb = {
property_transform_set_cb = {
'BoolProperty': lambda v: not v,
'IntProperty': lambda v: v + 1,
'FloatVectorProperty': lambda v: [v[2] + 1.0, v[0], v[1]],
'StringProperty': lambda v: ("B" if (v and v[0] == "A") else "A") + v[1:],
}.get(property_type, lambda v: v)
property_transform_get_cb = {
'BoolProperty': lambda v: not v,
'IntProperty': lambda v: v - 1,
'FloatVectorProperty': lambda v: [v[0], v[1], v[2]],
'StringProperty': lambda v: ("B" if (v and v[0] == "A") else "A") + v[1:],
}.get(property_type, lambda v: v)
if do_get_set:
class DummyGroup(bpy.types.PropertyGroup):
dummy_prop: property_definition_cb(
@@ -95,6 +105,12 @@ def _run_runtime_group_register_access(args):
"dummy_prop",
val),
)
elif do_transform:
class DummyGroup(bpy.types.PropertyGroup):
dummy_prop: property_definition_cb(
get_transform=lambda self, curr_v, is_set: property_transform_get_cb(curr_v),
set_transform=lambda self, curr_v, new_v, is_set: property_transform_set_cb(curr_v),
)
else:
class DummyGroup(bpy.types.PropertyGroup):
dummy_prop: property_definition_cb()
@@ -115,9 +131,14 @@ def _run_runtime_group_register_access(args):
bpy.utils.register_class(DummyGroup)
bpy.types.Scene.dummy_group = bpy.props.PointerProperty(type=DummyGroup)
for i in range(iterations):
v = sce.dummy_group.dummy_prop
sce.dummy_group.dummy_prop = property_transform_cb(v)
if do_transform:
for i in range(iterations):
v = sce.dummy_group.dummy_prop
sce.dummy_group.dummy_prop = v
else:
for i in range(iterations):
v = sce.dummy_group.dummy_prop
sce.dummy_group.dummy_prop = property_transform_set_cb(v)
del bpy.types.Scene.dummy_group
bpy.utils.unregister_class(DummyGroup)
@@ -165,4 +186,10 @@ def generate(env):
{"do_access": True, "do_get_set": True, "property_type": 'FloatVectorProperty'}),
BPYRNATest("Py-Defined StringProperty Custom Get/Set Access", _run_runtime_group_register_access, 10 * 1000,
{"do_access": True, "do_get_set": True, "property_type": 'StringProperty'}),
BPYRNATest("Py-Defined BoolProperty Custom Transform Access", _run_runtime_group_register_access, 1000 * 1000,
{"do_access": True, "do_transform": True, "property_type": 'BoolProperty'}),
BPYRNATest("Py-Defined FloatVectorProperty Custom Transform Access", _run_runtime_group_register_access, 1000 * 1000,
{"do_access": True, "do_transform": True, "property_type": 'FloatVectorProperty'}),
BPYRNATest("Py-Defined StringProperty Custom Transform Access", _run_runtime_group_register_access, 1000 * 1000,
{"do_access": True, "do_transform": True, "property_type": 'StringProperty'}),
]