A faster way to extract mesh data from addons (Proposal)

The “official” fast way of extracting mesh data from Python is using foreach_get().
However, foreach_get only follows the fast path (a memcpy in rna_raw_access) when the Python API and the underlying C data layout match, which in the case of the Mesh API is not too often.

Most Mesh data in Blender is stored in SoA fashion (MCol, MLoopUV, FreestyleEdge, CustomDataLayer…) but the Python API usually exposes it as AoS (MeshVertices, MeshEdges, MeshFaces…), so in most cases, foreach_get has to retrieve the data through Python, at a great performance cost.

The alternative is to retrieve pointers with as_pointer(), and use C/C++ and the internal Blender structs to copy the data.
I do this in Malt, but similar approaches are followed by other render engines like LuxCore and appleseed.
(Cycles follows this path too, but it also has easier access to the internal Blender API)

This is much faster, but not every mesh buffer type is easily accessible with this method.
For full access, a copy of the Mesh DNA headers is needed, but this is also harder to maintain since the Blender DNA data layout can change very often.

If the CustomData_get_layer functions were exposed to Python, only some (rarely changed) structs from DNA_meshdata_types.h would be needed by addons and the maintenance cost would be much lower.

I think even something like Mesh.get_data_as_pointer(domain, data_type) where domain is an enum value from {VERTEX, EDGE, POLY, LOOP}, data_type is an enum value from CustomDataType and the return value is a void pointer would be enough.

Does this seem feasible?
I’m open to contributing the patch myself if I get the ok.

3 Likes

@ideasman42 Since this is for the most part related to modules you own, could you take a look at this?

As far as I know, what you’re proposing already exists with the attribute API (mesh.attributes) for custom data layers. If it doesn’t it could probably be relatively easily added there. I don’t think another custom data API should be added besides the attribute.

For mesh data that stores topology, like MPoly or MLoop, ideally the existing mesh.polygons would be able to give you access to the underlying type, which in both of those cases would just be a pair of integers.

However, one problem with that approach is that some data layers also contain special flags, or hard-coded storage of builtin attributes like material_index. For that, I would propose T95965: Mesh Struct of Arrays Refactor as the solution. Everything except topology information should be accessible as a regular attribute layer.

1 Like

I don’t really know enough about the python side, so take my experience with a grain of salt but:

Isn’t the pointer you get from .as_pointer() mostly the same pointer you’d get from the CustomData_get_layer()?

You probably looked into this better than me, so there’s probably a difference. Just making sure you looked at this connection before embarking in large works :wink:

1 Like

AFAIK, mesh.attributes don’t provide access to most of the data stored in CustomDataLayers, they are empty by default.
But I agree that, if possible, adding them to the mesh.attributes API could be a better solution.
Seems like mesh.polygon_normals were added in 3.1, which I guess it might be the data stored in the ldata CD_NORMAL layer? (I’ll look into it)

Currently, it’s possible to get direct pointers to the MVert, MEdge, MPoly, MLoop, MLoopTri, MLoopUV and MLoopCol arrays from Python with .as_pointer().
The issue is those that aren’t, like normals, tangents, or freestyle edges.

That would be great, but since some data is already stored in a SoA layout, I would start by exposing that, since the data that is stored as AoS is already accessible with '.as_pointer()`.

Not everything can be retrieved directly with .as_pointer(). (see my previous comment)

Yes, I added that, here is the commit: rBb7fe27314b25
The data is not stored in CustomData at all actually.

Tangents should be accessible with a similar function to vertex_normals and polygon_normals I think. Though I’m not sure if the result can be owned by the mesh. Normals I’ve already mentioned. Freestyle edge tags should be converted into a generic boolean attribute, so I don’t think we’ll add special access to those.

2 Likes

Awesome! Thank you for that.

If tangents and freestyle edges were added too, I think that would provide reasonably easy access to all the relevant mesh data in a performant way.
I have no idea about how the tangents ownership works, though.

1 Like