[Bf-taskforce25] RNA and API wrap-up
Elia Sarti
vekoon at gmail.com
Mon Jun 29 14:06:47 CEST 2009
Hi,
I've been almost absent in the last couple of months (which I apologize
for) and as I'm trying to make a bit of a summary of current state of
API related stuff I thought it would be useful to write it down in order
for everyone to have a more clear idea and make it possible to discuss
further so that then we can put up something definite and have some
specs for other things to be settled that depend on this (like
exporters/importers etc).
Data access and performance
Data access as of now for pyscripts always happens through RNA. The
advantage of having everything abstracted as always presents the same
problem, which is performance. While it should work fine to access
almost any data it would get very slow for things like vertices and in
general anything which is a huge collection of pieces of data.
Talking a bit on IRC Campbell proposed having a special function
implemented PYAPI side in the form of obj.foreach_set("name", [array])
and same for getting obj.foreach_get("name", [array]). For instance (as
I remember it) mesh.foreach_get("verts", vertex_array) would get all the
vertices for the mesh "mesh" and put them into "vertex_array". We could
also extend this to use RNA paths like mesh.foreach_get("verts.co",
coords). I'm not sure what the complications of this could be, I think
trying it out is the best option here. As Campbell said this is
essential to have importers/exporters working which again is essential
in order to have Blender working.
Operators design
Operators are more or less defined/complete at least C side. API side
though we still have things undefined or problematic. For instance an
issue is unique naming. Although the current naming convention fits
perfectly and it's cohesive on our core application side it presents
some limitations for extensibility as we cannot really control how many
pyoperators will be written and handle collisions between these. A way
could be simply to always prepend pyops with PY_ (or tell py devs to use
this convention). We could tell devs to prepend their pyoperators with
their own names/ids of choice. Or we could use python own module/package
system to combine the final operator name. As of now I don't seem to see
any check on operator names? We could allow a
module.OBJECT_OT_do_something() type of name syntax which could be
translated automatically from py? Not sure if this conflicts with something.
Another issue for operators in general is context constraints. For
instance as of now as Ton mentioned in IRC add_object operator works in
node editor. Is this fine? Can it be expected by the user? From my point
of view being able to run operators from contexts you wouldn't expect is
totally acceptable architecture-wise as running constraints are defined
in terms of "needs" and if requirements are respected I see no reason
why the operator should not run. They should just be hidden from the
user in order not to create confusion. I think we could do this with
some type of "flag" both on operators and space types defining the
constraints of context. Something like CTX_HANDLE_OBJECT,
CTX_HANDLE_NODE etc. On the operator this is the same as represented by
the prefix of its idname so we could actually use this on spaces
although string comparison would introduce some issues but would be more
solid e.g. for py/plugin spaces which brings me back to pyops naming as
using this method would leave module/package approach as the only usable
naming convention.
You might wonder why not to put the space check directly on the
operator's poll? Well the reason for this is that as I said poll()
should just check requirements and not interface issues which is instead
something spaces take care of. This is not just a design choice but also
has some practical examples. For instance, the same example I made on
IRC, suppose at some point Outliner space gets capacity of
listing/handling nodes. To support node tools in outliner you would have
to go and change every single node operator to add additional check on
space type while with the former approach you just update outliner space
to accept NODE_* operators. This of course is even more clear for
python/plugin spaces.
Type registration
This is less important and more of a personal question than general issue.
Types can get registered for adding customization like it happens for
Panels or Menus. If I'm not mistaken this is how current architecture works:
script.py -> class SomePanel(Panel): [...] bpy.types.register(SomePanel) ->
RNA: Panel.register() -> register the type in some custom way ->
(ARegionType.paneltypes)
ARegionType.draw() ->
ED_region_panels("context") ->
foreach (panel in paneltypes) if (panel.context ==
"context") draw(panel)
How it should work in my opinion:
script.py -> class SomePanel(Panel): [...] bpy.types.register(SomePanel) ->
[RNA: insert SomePanel in optimized lookup list]
ARegionType.init() ->
retrieve all Panel-inherited RNA types and register ->
(ARegionType.autopaneltypes)
ARegionType.draw() ->
ED_region_panels("context") ->
foreach (panel in paneltypes) draw(panel)
foreach (panel in autopaneltypes) if (panel.context ==
"context") draw(panel)
Advantages of this kind of architecture are that it keeps RNA more
consistent and self-contained, no messing around (i.e. no real logics)
with interface. By consequence it also keeps interface stuff more
self-contained by letting it handle its own supported custom types.
The apparent disadvantage is low performance, having to do lookups at
every init. I understand this could be why the first method was adopted
but to me this doesn't seem a valid reason. To solve this problem
instead we could create a coexistent fast-to-access list of registered
types. This could be ordered like some type of tree to optimize access
based on type hierarchy lookup. I know caches are bad but still I think
they're better than code fragmentation. Once we have this type of cache
we could further optimize for instance like (in pseudo-code):
if type_tree.is_hierarchy_changed_for(Panel):
re-init ARegionType.autopaneltypes
This could simply be a flag on the cache-stored type updated by the
cache itself. This way we would re-do the lookup only when some new
specific type gets registered/unregistered.
I think I mentioned the main issues that must be either solved, decided
upon or simply settled, if you think I forgot something feel free to add it.
I'm also thinking about writing some more detailed docs on RNA and its
internals in order not to turn it into a black box that only a few
understand. This of course with the assistance of Brecht.
More information about the Bf-taskforce25
mailing list