Cycles: Adaptive subdivision no longer doubles up with Blender levels
Use the mesh wrapper mechanism from GPU subdivision to get the base mesh. This can significantly reduce memory usage and render setup time if the level was not manually set to zero. Pull Request: https://projects.blender.org/blender/blender/pulls/135895
This commit is contained in:
@@ -32,7 +32,8 @@ static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_parti
|
||||
}
|
||||
|
||||
if (b_ob_info.object_data.is_a(&RNA_Volume) ||
|
||||
(b_ob_info.object_data == object_get_data(b_ob_info.real_object) &&
|
||||
(b_ob_info.object_data ==
|
||||
object_get_data(b_ob_info.real_object, b_ob_info.use_adaptive_subdivision) &&
|
||||
object_fluid_gas_domain_find(b_ob_info.real_object)))
|
||||
{
|
||||
return Geometry::VOLUME;
|
||||
|
||||
@@ -1106,14 +1106,8 @@ void BlenderSync::sync_mesh(BObjectInfo &b_ob_info, Mesh *mesh)
|
||||
new_mesh.set_used_shaders(used_shaders);
|
||||
|
||||
if (view_layer.use_surfaces) {
|
||||
/* Adaptive subdivision setup. Not for baking since that requires
|
||||
* exact mapping to the Blender mesh. */
|
||||
if (b_ob_info.real_object != b_bake_target) {
|
||||
object_subdivision_to_mesh(b_ob_info.real_object, new_mesh, preview, experimental);
|
||||
}
|
||||
|
||||
/* For some reason, meshes do not need this... */
|
||||
BL::Mesh b_mesh = object_to_mesh(b_ob_info, new_mesh.get_subdivision_type());
|
||||
object_subdivision_to_mesh(b_ob_info.real_object, new_mesh, preview, use_adaptive_subdivision);
|
||||
BL::Mesh b_mesh = object_to_mesh(b_ob_info);
|
||||
|
||||
if (b_mesh) {
|
||||
/* Motion blur attribute is relative to seconds, we need it relative to frames. */
|
||||
@@ -1189,7 +1183,7 @@ void BlenderSync::sync_mesh_motion(BObjectInfo &b_ob_info, Mesh *mesh, const int
|
||||
BL::Mesh b_mesh_rna(PointerRNA_NULL);
|
||||
if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) {
|
||||
/* get derived mesh */
|
||||
b_mesh_rna = object_to_mesh(b_ob_info, mesh->get_subdivision_type());
|
||||
b_mesh_rna = object_to_mesh(b_ob_info);
|
||||
}
|
||||
|
||||
const std::string ob_name = b_ob_info.real_object.name();
|
||||
|
||||
@@ -91,14 +91,14 @@ bool BlenderSync::object_can_have_geometry(BL::Object &b_ob)
|
||||
|
||||
bool BlenderSync::object_is_light(BL::Object &b_ob)
|
||||
{
|
||||
BL::ID b_ob_data = object_get_data(b_ob);
|
||||
BL::ID b_ob_data = object_get_data(b_ob, true);
|
||||
|
||||
return (b_ob_data && b_ob_data.is_a(&RNA_Light));
|
||||
}
|
||||
|
||||
bool BlenderSync::object_is_camera(BL::Object &b_ob)
|
||||
{
|
||||
BL::ID b_ob_data = object_get_data(b_ob);
|
||||
BL::ID b_ob_data = object_get_data(b_ob, true);
|
||||
|
||||
return (b_ob_data && b_ob_data.is_a(&RNA_Camera));
|
||||
}
|
||||
@@ -157,7 +157,11 @@ Object *BlenderSync::sync_object(BL::ViewLayer &b_view_layer,
|
||||
BL::Object b_ob = b_instance.object();
|
||||
BL::Object b_parent = is_instance ? b_instance.parent() : b_instance.object();
|
||||
BL::Object b_real_object = is_instance ? b_instance.instance_object() : b_ob;
|
||||
BObjectInfo b_ob_info{b_ob, b_real_object, object_get_data(b_ob)};
|
||||
const bool use_adaptive_subdiv = object_subdivision_type(
|
||||
b_real_object, preview, use_adaptive_subdivision) !=
|
||||
Mesh::SUBDIVISION_NONE;
|
||||
BObjectInfo b_ob_info{
|
||||
b_ob, b_real_object, object_get_data(b_ob, use_adaptive_subdiv), use_adaptive_subdiv};
|
||||
const bool motion = motion_time != 0.0f;
|
||||
/*const*/ Transform tfm = get_transform(b_ob.matrix_world());
|
||||
int *persistent_id = nullptr;
|
||||
@@ -582,7 +586,7 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
|
||||
BL::MeshSequenceCacheModifier b_mesh_cache(PointerRNA_NULL);
|
||||
|
||||
/* Experimental as Blender does not have good support for procedurals at the moment. */
|
||||
if (experimental) {
|
||||
if (use_experimental_procedural) {
|
||||
b_mesh_cache = object_mesh_cache_find(b_ob, &has_subdivision_modifier);
|
||||
use_procedural = b_mesh_cache && b_mesh_cache.cache_file().use_render_procedural();
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine,
|
||||
world_recalc(false),
|
||||
scene(scene),
|
||||
preview(preview),
|
||||
experimental(false),
|
||||
use_developer_ui(use_developer_ui),
|
||||
dicing_rate(1.0f),
|
||||
max_subdivisions(12),
|
||||
@@ -100,7 +99,7 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
|
||||
/* Sync recalc flags from blender to cycles. Actual update is done separate,
|
||||
* so we can do it later on if doing it immediate is not suitable. */
|
||||
|
||||
if (experimental) {
|
||||
if (use_adaptive_subdivision) {
|
||||
/* Mark all meshes as needing to be exported again if dicing changed. */
|
||||
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
|
||||
bool dicing_prop_changed = false;
|
||||
@@ -177,10 +176,14 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
|
||||
object_map.set_recalc(b_ob);
|
||||
}
|
||||
|
||||
if (updated_geometry ||
|
||||
(object_subdivision_type(b_ob, preview, experimental) != Mesh::SUBDIVISION_NONE))
|
||||
{
|
||||
BL::ID const key = BKE_object_is_modified(b_ob) ? b_ob : object_get_data(b_ob);
|
||||
const bool use_adaptive_subdiv = object_subdivision_type(
|
||||
b_ob, preview, use_adaptive_subdivision) !=
|
||||
Mesh::SUBDIVISION_NONE;
|
||||
|
||||
if (updated_geometry || use_adaptive_subdiv) {
|
||||
BL::ID const key = BKE_object_is_modified(b_ob) ?
|
||||
b_ob :
|
||||
object_get_data(b_ob, use_adaptive_subdiv);
|
||||
geometry_map.set_recalc(key);
|
||||
|
||||
/* Sync all contained geometry instances as well when the object changed.. */
|
||||
@@ -314,7 +317,9 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer,
|
||||
{
|
||||
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
|
||||
|
||||
experimental = (get_enum(cscene, "feature_set") != 0);
|
||||
/* No adaptive subdivision for baking, mesh needs to match Blender exactly. */
|
||||
use_adaptive_subdivision = (get_enum(cscene, "feature_set") != 0) && !b_bake_target;
|
||||
use_experimental_procedural = (get_enum(cscene, "feature_set") != 0);
|
||||
|
||||
Integrator *integrator = scene->integrator;
|
||||
|
||||
|
||||
@@ -240,7 +240,8 @@ class BlenderSync {
|
||||
|
||||
Scene *scene;
|
||||
bool preview;
|
||||
bool experimental;
|
||||
bool use_experimental_procedural = false;
|
||||
bool use_adaptive_subdivision = false;
|
||||
bool use_developer_ui;
|
||||
|
||||
float dicing_rate;
|
||||
|
||||
@@ -28,11 +28,11 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
static inline BL::ID object_get_data(const BL::Object &b_ob)
|
||||
static inline BL::ID object_get_data(const BL::Object &b_ob, const bool use_adaptive_subdivision)
|
||||
{
|
||||
::Object *object = reinterpret_cast<::Object *>(b_ob.ptr.data);
|
||||
|
||||
if (object->type == OB_MESH) {
|
||||
if (!use_adaptive_subdivision && object->type == OB_MESH) {
|
||||
::Mesh *mesh = static_cast<::Mesh *>(object->data);
|
||||
mesh = BKE_mesh_wrapper_ensure_subdivision(mesh);
|
||||
return BL::ID(RNA_id_pointer_create(&mesh->id));
|
||||
@@ -55,20 +55,23 @@ struct BObjectInfo {
|
||||
* iterator is done. It might have a different type compared to object_get_data(real_object). */
|
||||
BL::ID object_data;
|
||||
|
||||
/* Object will use adaptive subdivision. */
|
||||
bool use_adaptive_subdivision;
|
||||
|
||||
/* True when the current geometry is the data of the referenced object. False when it is a
|
||||
* geometry instance that does not have a 1-to-1 relationship with an object. */
|
||||
bool is_real_object_data() const
|
||||
{
|
||||
return object_get_data(const_cast<BL::Object &>(real_object)) == object_data;
|
||||
return object_get_data(const_cast<BL::Object &>(real_object), use_adaptive_subdivision) ==
|
||||
object_data;
|
||||
}
|
||||
};
|
||||
|
||||
static inline BL::Mesh object_copy_mesh_data(const BObjectInfo &b_ob_info,
|
||||
const Mesh::SubdivisionType subdivision_type)
|
||||
static inline BL::Mesh object_copy_mesh_data(const BObjectInfo &b_ob_info)
|
||||
{
|
||||
::Object *object = static_cast<::Object *>(b_ob_info.real_object.ptr.data);
|
||||
::Mesh *mesh = BKE_mesh_new_from_object(
|
||||
nullptr, object, false, false, subdivision_type == Mesh::SUBDIVISION_NONE);
|
||||
nullptr, object, false, false, !b_ob_info.use_adaptive_subdivision);
|
||||
return BL::Mesh(RNA_id_pointer_create(&mesh->id));
|
||||
}
|
||||
|
||||
@@ -78,32 +81,15 @@ BlenderAttributeType blender_attribute_name_split_type(ustring name, string *r_r
|
||||
void python_thread_state_save(void **python_thread_state);
|
||||
void python_thread_state_restore(void **python_thread_state);
|
||||
|
||||
static bool mesh_use_corner_normals(BL::Mesh &mesh, Mesh::SubdivisionType subdivision_type)
|
||||
static bool mesh_use_corner_normals(const BObjectInfo &b_ob_info, BL::Mesh &mesh)
|
||||
{
|
||||
return mesh && (subdivision_type == Mesh::SUBDIVISION_NONE) &&
|
||||
return mesh && !b_ob_info.use_adaptive_subdivision &&
|
||||
(static_cast<const ::Mesh *>(mesh.ptr.data)->normals_domain(true) ==
|
||||
blender::bke::MeshNormalDomain::Corner);
|
||||
}
|
||||
|
||||
static inline BL::Mesh object_to_mesh(
|
||||
BObjectInfo &b_ob_info, Mesh::SubdivisionType subdivision_type = Mesh::SUBDIVISION_NONE)
|
||||
static inline BL::Mesh object_to_mesh(BObjectInfo &b_ob_info)
|
||||
{
|
||||
/* TODO: make this work with copy-on-evaluation, modifiers are already evaluated. */
|
||||
#if 0
|
||||
bool subsurf_mod_show_render = false;
|
||||
bool subsurf_mod_show_viewport = false;
|
||||
|
||||
if (subdivision_type != Mesh::SUBDIVISION_NONE) {
|
||||
BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length() - 1];
|
||||
|
||||
subsurf_mod_show_render = subsurf_mod.show_render();
|
||||
subsurf_mod_show_viewport = subsurf_mod.show_viewport();
|
||||
|
||||
subsurf_mod.show_render(false);
|
||||
subsurf_mod.show_viewport(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
BL::Mesh mesh = (b_ob_info.object_data.is_a(&RNA_Mesh)) ? BL::Mesh(b_ob_info.object_data) :
|
||||
BL::Mesh(PointerRNA_NULL);
|
||||
|
||||
@@ -113,40 +99,31 @@ static inline BL::Mesh object_to_mesh(
|
||||
if (mesh) {
|
||||
if (mesh.is_editmode()) {
|
||||
/* Flush edit-mesh to mesh, including all data layers. */
|
||||
mesh = object_copy_mesh_data(b_ob_info, subdivision_type);
|
||||
use_corner_normals = mesh_use_corner_normals(mesh, subdivision_type);
|
||||
mesh = object_copy_mesh_data(b_ob_info);
|
||||
use_corner_normals = mesh_use_corner_normals(b_ob_info, mesh);
|
||||
}
|
||||
else if (mesh_use_corner_normals(mesh, subdivision_type)) {
|
||||
else if (mesh_use_corner_normals(b_ob_info, mesh)) {
|
||||
/* Make a copy to split faces. */
|
||||
mesh = object_copy_mesh_data(b_ob_info, subdivision_type);
|
||||
mesh = object_copy_mesh_data(b_ob_info);
|
||||
use_corner_normals = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mesh = object_copy_mesh_data(b_ob_info, subdivision_type);
|
||||
use_corner_normals = mesh_use_corner_normals(mesh, subdivision_type);
|
||||
mesh = object_copy_mesh_data(b_ob_info);
|
||||
use_corner_normals = mesh_use_corner_normals(b_ob_info, mesh);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* TODO: what to do about non-mesh geometry instances? */
|
||||
use_corner_normals = mesh_use_corner_normals(mesh, subdivision_type);
|
||||
use_corner_normals = mesh_use_corner_normals(b_ob_info, mesh);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (subdivision_type != Mesh::SUBDIVISION_NONE) {
|
||||
BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length() - 1];
|
||||
|
||||
subsurf_mod.show_render(subsurf_mod_show_render);
|
||||
subsurf_mod.show_viewport(subsurf_mod_show_viewport);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mesh) {
|
||||
if (use_corner_normals) {
|
||||
mesh.split_faces();
|
||||
}
|
||||
|
||||
if (subdivision_type == Mesh::SUBDIVISION_NONE) {
|
||||
if (b_ob_info.use_adaptive_subdivision) {
|
||||
mesh.calc_loop_triangles();
|
||||
}
|
||||
}
|
||||
@@ -161,7 +138,7 @@ static inline void free_object_to_mesh(BObjectInfo &b_ob_info, BL::Mesh &mesh)
|
||||
}
|
||||
/* Free mesh if we didn't just use the existing one. */
|
||||
BL::Object object = b_ob_info.real_object;
|
||||
if (object_get_data(object).ptr.data != mesh.ptr.data) {
|
||||
if (object_get_data(object, b_ob_info.use_adaptive_subdivision).ptr.data != mesh.ptr.data) {
|
||||
BKE_id_free(nullptr, static_cast<ID *>(mesh.ptr.data));
|
||||
}
|
||||
}
|
||||
@@ -693,13 +670,11 @@ static inline BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b
|
||||
return BL::MeshSequenceCacheModifier(PointerRNA_NULL);
|
||||
}
|
||||
|
||||
static BL::SubsurfModifier object_subdivision_modifier(BL::Object &b_ob,
|
||||
const bool preview,
|
||||
const bool experimental)
|
||||
static BL::SubsurfModifier object_subdivision_modifier(BL::Object &b_ob, const bool preview)
|
||||
{
|
||||
PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles");
|
||||
|
||||
if (cobj.data && !b_ob.modifiers.empty() && experimental) {
|
||||
if (cobj.data && !b_ob.modifiers.empty()) {
|
||||
BL::Modifier mod = b_ob.modifiers[b_ob.modifiers.length() - 1];
|
||||
const bool enabled = preview ? mod.show_viewport() : mod.show_render();
|
||||
|
||||
@@ -716,9 +691,13 @@ static BL::SubsurfModifier object_subdivision_modifier(BL::Object &b_ob,
|
||||
|
||||
static inline Mesh::SubdivisionType object_subdivision_type(BL::Object &b_ob,
|
||||
const bool preview,
|
||||
const bool experimental)
|
||||
const bool use_adaptive_subdivision)
|
||||
{
|
||||
BL::SubsurfModifier subsurf = object_subdivision_modifier(b_ob, preview, experimental);
|
||||
if (!use_adaptive_subdivision) {
|
||||
return Mesh::SUBDIVISION_NONE;
|
||||
}
|
||||
|
||||
BL::SubsurfModifier subsurf = object_subdivision_modifier(b_ob, preview);
|
||||
|
||||
if (subsurf) {
|
||||
if (subsurf.subdivision_type() == BL::SubsurfModifier::subdivision_type_CATMULL_CLARK) {
|
||||
@@ -733,9 +712,14 @@ static inline Mesh::SubdivisionType object_subdivision_type(BL::Object &b_ob,
|
||||
static inline void object_subdivision_to_mesh(BL::Object &b_ob,
|
||||
Mesh &mesh,
|
||||
const bool preview,
|
||||
const bool experimental)
|
||||
const bool use_adaptive_subdivision)
|
||||
{
|
||||
BL::SubsurfModifier subsurf = object_subdivision_modifier(b_ob, preview, experimental);
|
||||
if (!use_adaptive_subdivision) {
|
||||
mesh.set_subdivision_type(Mesh::SUBDIVISION_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
BL::SubsurfModifier subsurf = object_subdivision_modifier(b_ob, preview);
|
||||
|
||||
if (!subsurf) {
|
||||
mesh.set_subdivision_type(Mesh::SUBDIVISION_NONE);
|
||||
|
||||
@@ -68,8 +68,9 @@ bool BKE_subsurf_modifier_has_split_normals(const SubsurfModifierData *smd, cons
|
||||
bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(const SubsurfModifierData *smd,
|
||||
const Mesh *mesh);
|
||||
/**
|
||||
* \param skip_check_is_last: When true, we assume that the modifier passed is the last enabled
|
||||
* modifier in the stack.
|
||||
* Return true if GPU subdivision can be used for this modifier. It does not check if
|
||||
* the modifier is in the right place in the modifier stack, only if the settings and
|
||||
* GPU are compatible.
|
||||
*/
|
||||
bool BKE_subsurf_modifier_can_do_gpu_subdiv(const SubsurfModifierData *smd, const Mesh *mesh);
|
||||
|
||||
|
||||
@@ -210,13 +210,6 @@ static ModifierData *modifier_get_last_enabled_for_mode(const Scene *scene,
|
||||
return md;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if GPU subdivision evaluation is disabled by force due to incompatible mesh or
|
||||
* modifier settings. This will only return true if GPU subdivision is enabled in the preferences
|
||||
* and supported by the GPU. It is mainly useful for showing UI messages.
|
||||
*/
|
||||
bool BKE_subsurf_modifier_can_use_gpu_evaluation(const SubsurfModifierData *smd, const Mesh *mesh);
|
||||
|
||||
/* Modifier itself. */
|
||||
|
||||
static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
|
||||
|
||||
Submodule tests/data updated: a61cf75cfc...4bb5c44abd
Reference in New Issue
Block a user