Page MenuHome

Fix T94479: GPU Subdivision surface modifier does not apply to Cycles renders
ClosedPublic

Authored by Kévin Dietrich (kevindietrich) on Feb 8 2022, 12:45 PM.

Details

Summary

Since now we delegate the evaluation of the last subsurf modifier in the stack
to the draw code, Cycles does not get a subdivided mesh anymore. This is because
the subdivision wrapper for generating a CPU side subdivision is never created
as it is only ever created via BKE_object_get_evaluated_mesh which Cycles does
not call (rather, it accesses the Mesh either via object.data(), or via
object.to_mesh()).

This ensures that a subdivision wrapper is created when accessing the object data
or converting an Object to a Mesh via the RNA/Python API.

Diff Detail

Repository
rB Blender

Event Timeline

Kévin Dietrich (kevindietrich) requested review of this revision.Feb 8 2022, 12:45 PM
Kévin Dietrich (kevindietrich) created this revision.

If I understand correctly, for the to_mesh case the subdivision will be potentially re-done on every Cycles export, since it's store in a temporary object_as_temp_mesh that will be freed again during export. That could be problematic for performance.

I think for the object.data case it will be cached? Is it possible to do it somewhere deeper in to_mesh, so that the subdivision is cached?

Doing this on object.data access is not ideal, since a Python script might access that but not need the actual mesh topology. However I also don't see another practical solution that would avoid breaking Python API compatibility, and Python scripts accessing object.data for depsgraph evaluated objects are very likely to be reading the mesh topology.

For the object.data case, there should be caching, as long as object.data is not modified (the wrapper is stored in the Mesh runtime, as long as this is valid, the wrapper will be cached).

For the to_mesh case there are a few code paths, which makes caching tricky:

Either we copy the original mesh, then we could just generate the wrapper on this original mesh, and this could cache it. However, the MDATA ("Mesh DATA") wrapper is also generated and we can apparently only have one wrapper at a time so this would cause some conflict and the subdivision wrapper will also have to be regenerated every time it is called. Thinking about it, the MDATA wrapper is about generating polygons and vertices from an EditMesh, I think we could here check if we already have a subdivision wrapper which would imply we already have the Mesh data, so we do not destroy the precomputed subdivision data.

Otherwise, a new mesh from evaluating the modifier stack is created, which will force the subdivision wrapper to also be regenerated, so no caching is really possible.

Thanks for the explanation.

However, the MDATA ("Mesh DATA") wrapper is also generated and we can apparently only have one wrapper at a time so this would cause some conflict and the subdivision wrapper will also have to be regenerated every time it is called. Thinking about it, the MDATA wrapper is about generating polygons and vertices from an EditMesh, I think we could here check if we already have a subdivision wrapper which would imply we already have the Mesh data, so we do not destroy the precomputed subdivision data.

Aren't the editmesh and subdivision surface wrappers mutually exclusive? I thought the editmesh wrapper was only possible when there are no modifiers. And there can be only one me->runtime.wrapper_type set at the same time. Or at least I would think you run into this issue already in places outside Cycles?

Besides that, it seems reasonable then to add BKE_mesh_wrapper_ensure_subdivision on the original mesh in mesh_new_from_mesh. And then also in mesh_new_from_mesh_object_with_layers after modifier evaluation.

Otherwise, a new mesh from evaluating the modifier stack is created, which will force the subdivision wrapper to also be regenerated, so no caching is really possible.

Modifiers are re-evaluated if preserve_all_data_layers or preserve_origindex is used. Cycles does not use these options, and when using them there should be no expectation of caching, so that's fine.

Add BKE_mesh_wrapper_ensure_subdivision to mesh_new_from_mesh and
mesh_new_from_mesh_object_with_layers.

For mesh_new_from_mesh a check is added on the current wrapper to decide if
we create a mdata wrapper or a subdivision one as
BKE_mesh_wrapper_ensure_mdata will unconditionnally set the wrapper type
to MDATA even if no mdata is needed. This should prevent doing
unnecessary work.

This revision is now accepted and ready to land.Feb 14 2022, 4:21 PM