Subdivision Surface: add dependency graph tracking when CPU mesh is needed #104461
|
@ -19,6 +19,7 @@ extern "C" {
|
|||
* programmatic way of detecting this. */
|
||||
#define MAX_GPU_SUBDIV_SSBOS 12
|
||||
|
||||
struct Depsgraph;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
|
@ -68,12 +69,15 @@ bool BKE_subsurf_modifier_use_custom_loop_normals(const struct SubsurfModifierDa
|
|||
* and supported by the GPU. It is mainly useful for showing UI messages.
|
||||
*/
|
||||
bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(
|
||||
const struct SubsurfModifierData *smd, const struct Mesh *mesh);
|
||||
const struct Depsgraph *depsgraph,
|
||||
const struct Object *ob,
|
||||
const struct Mesh *mesh,
|
||||
const struct SubsurfModifierData *smd);
|
||||
/**
|
||||
* \param skip_check_is_last: When true, we assume that the modifier passed is the last enabled
|
||||
* modifier in the stack.
|
||||
*/
|
||||
bool BKE_subsurf_modifier_can_do_gpu_subdiv(const struct Scene *scene,
|
||||
bool BKE_subsurf_modifier_can_do_gpu_subdiv(const struct Depsgraph *depsgraph,
|
||||
const struct Object *ob,
|
||||
const struct Mesh *mesh,
|
||||
const struct SubsurfModifierData *smd,
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include "BKE_modifier.h"
|
||||
#include "BKE_subdiv.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
#include "GPU_context.h"
|
||||
|
||||
|
@ -112,8 +114,25 @@ static bool is_subdivision_evaluation_possible_on_gpu()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(const SubsurfModifierData *smd,
|
||||
const Mesh *mesh)
|
||||
static bool subsurf_modifier_has_cpu_dependents(const Depsgraph *depsgraph, const Object *ob)
|
||||
{
|
||||
/* The sculpt and paint mode UI requires the mesh - see sculpt_update_object. */
|
||||
if ((ob->mode & OB_MODE_ALL_SCULPT) && DEG_is_active(depsgraph)) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Some dependencies request it through depsgraph. */
|
||||
if (DEG_get_eval_flags_for_id(depsgraph, &ob->id) & DAG_EVAL_NEED_CPU_EVALUATED_MESH) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(const Depsgraph *depsgraph,
|
||||
const Object *ob,
|
||||
const Mesh *mesh,
|
||||
const SubsurfModifierData *smd)
|
||||
{
|
||||
if ((U.gpu_flag & USER_GPU_FLAG_SUBDIVISION_EVALUATION) == 0) {
|
||||
/* GPU subdivision is explicitly disabled, so we don't force it. */
|
||||
|
@ -125,10 +144,11 @@ bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(const SubsurfMod
|
|||
return false;
|
||||
}
|
||||
|
||||
return subsurf_modifier_use_autosmooth_or_split_normals(smd, mesh);
|
||||
return subsurf_modifier_use_autosmooth_or_split_normals(smd, mesh) ||
|
||||
subsurf_modifier_has_cpu_dependents(depsgraph, ob);
|
||||
}
|
||||
|
||||
bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene,
|
||||
bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Depsgraph *depsgraph,
|
||||
const Object *ob,
|
||||
const Mesh *mesh,
|
||||
const SubsurfModifierData *smd,
|
||||
|
@ -144,6 +164,12 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene,
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Deactivate if some other dependencies need a final CPU mesh. */
|
||||
if (subsurf_modifier_has_cpu_dependents(depsgraph, ob)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
||||
ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode);
|
||||
if (md != (const ModifierData *)smd) {
|
||||
return false;
|
||||
|
|
|
@ -55,6 +55,8 @@ enum {
|
|||
/* A shrinkwrap modifier or constraint targeting this mesh needs information
|
||||
* about non-manifold boundary edges for the Target Normal Project mode. */
|
||||
DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY = (1 << 1),
|
||||
/* A modifier or constraints needs fully subdivided mesh on the CPU. */
|
||||
DAG_EVAL_NEED_CPU_EVALUATED_MESH = (1 << 2),
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -134,6 +134,9 @@ void DEG_add_collection_geometry_relation(struct DepsNodeHandle *node_handle,
|
|||
void DEG_add_collection_geometry_customdata_mask(struct DepsNodeHandle *node_handle,
|
||||
struct Collection *collection,
|
||||
const struct CustomData_MeshMasks *masks);
|
||||
void DEG_add_collection_geometry_special_eval_flag(struct DepsNodeHandle *node_handle,
|
||||
struct Collection *collection,
|
||||
uint32_t flag);
|
||||
void DEG_add_simulation_relation(struct DepsNodeHandle *node_handle,
|
||||
struct Simulation *simulation,
|
||||
const char *description);
|
||||
|
|
|
@ -1312,6 +1312,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id,
|
|||
add_relation(target_transform_key, constraint_op_key, cti->name);
|
||||
add_relation(target_geometry_key, constraint_op_key, cti->name);
|
||||
add_customdata_mask(ct->tar, DEGCustomDataMeshMasks::MaskVert(CD_MASK_MDEFORMVERT));
|
||||
add_special_eval_flag(&ct->tar->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
else if (con->type == CONSTRAINT_TYPE_SHRINKWRAP) {
|
||||
bShrinkwrapConstraint *scon = (bShrinkwrapConstraint *)con->data;
|
||||
|
@ -1319,6 +1320,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id,
|
|||
/* Constraints which requires the target object surface. */
|
||||
ComponentKey target_key(&ct->tar->id, NodeType::GEOMETRY);
|
||||
add_relation(target_key, constraint_op_key, cti->name);
|
||||
add_special_eval_flag(&ct->tar->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
|
||||
/* Add dependency on normal layers if necessary. */
|
||||
if (ct->tar->type == OB_MESH && scon->shrinkType != MOD_SHRINKWRAP_NEAREST_VERTEX) {
|
||||
|
@ -2019,10 +2021,13 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene)
|
|||
add_relation(object_transform_simulation_init_key,
|
||||
rb_simulate_key,
|
||||
"Object Transform -> Rigidbody Sim Eval");
|
||||
|
||||
/* Geometry must be known to create the rigid body. RBO_MESH_BASE
|
||||
* uses the non-evaluated mesh, so then the evaluation is
|
||||
* unnecessary. */
|
||||
if (rigidbody_object_depends_on_evaluated_geometry(object->rigidbody_object)) {
|
||||
eRigidBody_MeshSource mesh_source = rigidbody_object_depends_on_evaluated_geometry(
|
||||
object->rigidbody_object);
|
||||
if (mesh_source != RBO_MESH_BASE) {
|
||||
/* NOTE: We prefer this relation to be never killed, to avoid
|
||||
* access partially evaluated mesh from solver. */
|
||||
ComponentKey object_geometry_key(&object->id, NodeType::GEOMETRY);
|
||||
|
@ -2030,6 +2035,10 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene)
|
|||
rb_simulate_key,
|
||||
"Object Geom Eval -> Rigidbody Sim Eval",
|
||||
RELATION_FLAG_GODMODE);
|
||||
/* Request the CPU mesh when necessary. */
|
||||
if (mesh_source == RBO_MESH_FINAL) {
|
||||
add_special_eval_flag(&object->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
}
|
||||
|
||||
/* Final transform is whatever the solver gave to us. */
|
||||
|
|
|
@ -105,17 +105,16 @@ Relation *DepsgraphRelationBuilder::add_node_handle_relation(const KeyType &key_
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static inline bool rigidbody_object_depends_on_evaluated_geometry(const RigidBodyOb *rbo)
|
||||
static inline eRigidBody_MeshSource rigidbody_object_depends_on_evaluated_geometry(
|
||||
const RigidBodyOb *rbo)
|
||||
{
|
||||
if (rbo == nullptr) {
|
||||
return false;
|
||||
return RBO_MESH_BASE;
|
||||
}
|
||||
if (ELEM(rbo->shape, RB_SHAPE_CONVEXH, RB_SHAPE_TRIMESH)) {
|
||||
if (rbo->mesh_source != RBO_MESH_BASE) {
|
||||
return true;
|
||||
}
|
||||
return static_cast<eRigidBody_MeshSource>(rbo->mesh_source);
|
||||
}
|
||||
return false;
|
||||
return RBO_MESH_BASE;
|
||||
}
|
||||
|
||||
template<typename KeyTo>
|
||||
|
|
|
@ -122,6 +122,19 @@ void DEG_add_collection_geometry_customdata_mask(DepsNodeHandle *node_handle,
|
|||
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
|
||||
}
|
||||
|
||||
void DEG_add_collection_geometry_special_eval_flag(struct DepsNodeHandle *node_handle,
|
||||
struct Collection *collection,
|
||||
uint32_t flag)
|
||||
{
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
|
||||
DEG_add_special_eval_flag(node_handle, &ob->id, flag);
|
||||
if (ob->type == OB_EMPTY && ob->instance_collection != nullptr) {
|
||||
DEG_add_collection_geometry_special_eval_flag(node_handle, ob->instance_collection, flag);
|
||||
}
|
||||
}
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
|
||||
}
|
||||
|
||||
void DEG_add_simulation_relation(DepsNodeHandle *node_handle,
|
||||
Simulation *simulation,
|
||||
const char *description)
|
||||
|
|
|
@ -140,6 +140,10 @@ void DEG_add_forcefield_relations(DepsNodeHandle *handle,
|
|||
DEG_add_object_pointcache_relation(handle, relation->ob, DEG_OB_COMP_GEOMETRY, name);
|
||||
}
|
||||
|
||||
if (ELEM(relation->pd->shape, PFIELD_SHAPE_POINTS)) {
|
||||
DEG_add_special_eval_flag(handle, &relation->ob->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
|
||||
/* Smoke flow relations. */
|
||||
if (relation->pd->forcefield == PFIELD_FLUIDFLOW && relation->pd->f_source != nullptr) {
|
||||
DEG_add_object_pointcache_relation(
|
||||
|
|
|
@ -209,6 +209,7 @@ static void updateDepsgraph(GpencilModifierData *md,
|
|||
DEG_add_object_relation(ctx->node, mmd->target, DEG_OB_COMP_TRANSFORM, "Shrinkwrap Modifier");
|
||||
DEG_add_object_relation(ctx->node, mmd->target, DEG_OB_COMP_GEOMETRY, "Shrinkwrap Modifier");
|
||||
DEG_add_customdata_mask(ctx->node, mmd->target, &mask);
|
||||
DEG_add_special_eval_flag(ctx->node, &mmd->target->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
if (mmd->shrink_type == MOD_SHRINKWRAP_TARGET_PROJECT) {
|
||||
DEG_add_special_eval_flag(ctx->node, &mmd->target->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY);
|
||||
}
|
||||
|
@ -219,6 +220,7 @@ static void updateDepsgraph(GpencilModifierData *md,
|
|||
DEG_add_object_relation(
|
||||
ctx->node, mmd->aux_target, DEG_OB_COMP_GEOMETRY, "Shrinkwrap Modifier");
|
||||
DEG_add_customdata_mask(ctx->node, mmd->aux_target, &mask);
|
||||
DEG_add_special_eval_flag(ctx->node, &mmd->aux_target->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
if (mmd->shrink_type == MOD_SHRINKWRAP_TARGET_PROJECT) {
|
||||
DEG_add_special_eval_flag(
|
||||
ctx->node, &mmd->aux_target->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY);
|
||||
|
|
|
@ -82,10 +82,12 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
if (amd->start_cap != nullptr) {
|
||||
DEG_add_object_relation(
|
||||
ctx->node, amd->start_cap, DEG_OB_COMP_GEOMETRY, "Array Modifier Start Cap");
|
||||
DEG_add_special_eval_flag(ctx->node, &amd->start_cap->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
if (amd->end_cap != nullptr) {
|
||||
DEG_add_object_relation(
|
||||
ctx->node, amd->end_cap, DEG_OB_COMP_GEOMETRY, "Array Modifier End Cap");
|
||||
DEG_add_special_eval_flag(ctx->node, &amd->end_cap->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
if (amd->curve_ob) {
|
||||
DEG_add_object_relation(
|
||||
|
|
|
@ -108,12 +108,15 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object != nullptr) {
|
||||
DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
|
||||
DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_GEOMETRY, "Boolean Modifier");
|
||||
DEG_add_special_eval_flag(ctx->node, &bmd->object->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
|
||||
Collection *col = bmd->collection;
|
||||
|
||||
if ((bmd->flag & eBooleanModifierFlag_Collection) && col != nullptr) {
|
||||
DEG_add_collection_geometry_relation(ctx->node, col, "Boolean Modifier");
|
||||
DEG_add_collection_geometry_special_eval_flag(
|
||||
ctx->node, col, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
/* We need own transformation as well. */
|
||||
DEG_add_depends_on_transform_relation(ctx->node, "Boolean Modifier");
|
||||
|
|
|
@ -124,6 +124,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
DEG_add_object_relation(
|
||||
ctx->node, dtmd->ob_source, DEG_OB_COMP_GEOMETRY, "DataTransfer Modifier");
|
||||
DEG_add_customdata_mask(ctx->node, dtmd->ob_source, &cddata_masks);
|
||||
DEG_add_special_eval_flag(ctx->node, &dtmd->ob_source->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
|
||||
if (dtmd->flags & MOD_DATATRANSFER_OBSRC_TRANSFORM) {
|
||||
DEG_add_object_relation(
|
||||
|
|
|
@ -66,6 +66,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
ctx->node, mvmd->object, DEG_OB_COMP_GEOMETRY, "Mesh to Volume Modifier");
|
||||
DEG_add_object_relation(
|
||||
ctx->node, mvmd->object, DEG_OB_COMP_TRANSFORM, "Mesh to Volume Modifier");
|
||||
DEG_add_special_eval_flag(ctx->node, &mvmd->object->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -156,6 +156,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
if (mmd->object != NULL) {
|
||||
DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Mesh Deform Modifier");
|
||||
DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_GEOMETRY, "Mesh Deform Modifier");
|
||||
DEG_add_special_eval_flag(ctx->node, &mmd->object->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
/* We need own transformation as well. */
|
||||
DEG_add_depends_on_transform_relation(ctx->node, "Mesh Deform Modifier");
|
||||
|
|
|
@ -261,6 +261,8 @@ static void add_collection_relation(const ModifierUpdateDepsgraphContext *ctx,
|
|||
{
|
||||
DEG_add_collection_geometry_relation(ctx->node, &collection, "Nodes Modifier");
|
||||
DEG_add_collection_geometry_customdata_mask(ctx->node, &collection, &dependency_data_mask);
|
||||
DEG_add_collection_geometry_special_eval_flag(
|
||||
ctx->node, &collection, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
|
||||
static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Object &object)
|
||||
|
@ -273,6 +275,7 @@ static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Objec
|
|||
else if (DEG_object_has_geometry_component(&object)) {
|
||||
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier");
|
||||
DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask);
|
||||
DEG_add_special_eval_flag(ctx->node, &object.id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,6 +163,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
DEG_add_object_relation(ctx->node, smd->target, DEG_OB_COMP_TRANSFORM, "Shrinkwrap Modifier");
|
||||
DEG_add_object_relation(ctx->node, smd->target, DEG_OB_COMP_GEOMETRY, "Shrinkwrap Modifier");
|
||||
DEG_add_customdata_mask(ctx->node, smd->target, &mask);
|
||||
DEG_add_special_eval_flag(ctx->node, &smd->target->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
if (smd->shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) {
|
||||
DEG_add_special_eval_flag(ctx->node, &smd->target->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY);
|
||||
}
|
||||
|
@ -173,6 +174,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
DEG_add_object_relation(
|
||||
ctx->node, smd->auxTarget, DEG_OB_COMP_GEOMETRY, "Shrinkwrap Modifier");
|
||||
DEG_add_customdata_mask(ctx->node, smd->auxTarget, &mask);
|
||||
DEG_add_special_eval_flag(ctx->node, &smd->auxTarget->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
if (smd->shrinkType == MOD_SHRINKWRAP_TARGET_PROJECT) {
|
||||
DEG_add_special_eval_flag(ctx->node, &smd->auxTarget->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY);
|
||||
}
|
||||
|
|
|
@ -242,14 +242,14 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
|
|||
/* Delay evaluation to the draw code if possible, provided we do not have to apply the modifier.
|
||||
*/
|
||||
if ((ctx->flag & MOD_APPLY_TO_BASE_MESH) == 0) {
|
||||
Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
|
||||
const bool is_render_mode = (ctx->flag & MOD_APPLY_RENDER) != 0;
|
||||
/* Same check as in `DRW_mesh_batch_cache_create_requested` to keep both code coherent. The
|
||||
* difference is that here we do not check for the final edit mesh pointer as it is not yet
|
||||
* assigned at this stage of modifier stack evaluation. */
|
||||
const bool is_editmode = (mesh->edit_mesh != nullptr);
|
||||
const int required_mode = BKE_subsurf_modifier_eval_required_mode(is_render_mode, is_editmode);
|
||||
if (BKE_subsurf_modifier_can_do_gpu_subdiv(scene, ctx->object, mesh, smd, required_mode)) {
|
||||
if (BKE_subsurf_modifier_can_do_gpu_subdiv(
|
||||
ctx->depsgraph, ctx->object, mesh, smd, required_mode)) {
|
||||
subdiv_cache_mesh_wrapper_settings(ctx, mesh, smd, runtime_data);
|
||||
return result;
|
||||
}
|
||||
|
@ -425,8 +425,10 @@ static void panel_draw(const bContext *C, Panel *panel)
|
|||
SubsurfModifierData *smd = static_cast<SubsurfModifierData *>(ptr->data);
|
||||
Object *ob = static_cast<Object *>(ob_ptr.data);
|
||||
const Mesh *mesh = static_cast<const Mesh *>(ob->data);
|
||||
if (BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(smd, mesh)) {
|
||||
uiItemL(layout, "Autosmooth or custom normals detected, disabling GPU subdivision", ICON_INFO);
|
||||
if (BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(depsgraph, ob, mesh, smd)) {
|
||||
uiItemL(layout,
|
||||
"Disabling GPU subdivision due to autosmooth, custom normals or dependencies",
|
||||
Hans Goudey
commented
I'm not sure an optimization like this deserves a UI label. If there is a way to show whether GPU subdivision is being used, it should probably be more subtle IMO. I'm not sure an optimization like this deserves a UI label. If there is a way to show whether GPU subdivision is being used, it should probably be more subtle IMO.
Alexander Gavrilov
commented
Well, I wasn't the one who put this here, I just change the text to reflect my additions ;) Well, I wasn't the one who put this here, I just change the text to reflect my additions ;)
Kévin Dietrich
commented
Custom normals are incompatible with subdivision surfaces. The fact that GPU subdivision is disabled in the presence of such normals is to start a differentiation between workflows with custom shading and workflows with subdivision surfaces. See #68891 and #68893. This is what this UI label is for. Essentially to put in users mind that subdivision and custom normals should be different workflows. It only affects GPU subdivision as it is easier/more acceptable to prevent new features from introducing "bad" workflows than breaking compatibility. Custom normals are incompatible with subdivision surfaces. The fact that GPU subdivision is disabled in the presence of such normals is to start a differentiation between workflows with custom shading and workflows with subdivision surfaces. See #68891 and #68893. This is what this UI label is for. Essentially to put in users mind that subdivision and custom normals should be different workflows. It only affects GPU subdivision as it is easier/more acceptable to prevent new features from introducing "bad" workflows than breaking compatibility.
Alexander Gavrilov
commented
If you really mean this, rather than just simplifying the phrase 'the GPU implementation of subdivision surfaces', I think the NPR people will very much beg to differ. In fact, the subdivision surface modifier specifically has an option to subdivide and interpolate custom normals. In my view the purpose of the message is to let the user know when the GPU optimization won't be used, so they can be aware and plan accordingly. IIRC something similar happened in 2.7* versions. In #104441 I even added a message tracking the internal state when it is actually being used for feedback. > Custom normals are incompatible with subdivision surfaces. ... Essentially to put in users mind that subdivision and custom normals should be different workflows.
If you really mean this, rather than just simplifying the phrase 'the *GPU implementation* of subdivision surfaces', I think the NPR people will *very much* beg to differ. In fact, the subdivision surface modifier specifically has an option to subdivide and interpolate custom normals.
In my view the purpose of the message is to let the user know when the GPU optimization won't be used, so they can be aware and plan accordingly. IIRC something similar happened in 2.7* versions. In #104441 I even added a message tracking the internal state when it is actually being used for feedback.
|
||||
ICON_INFO);
|
||||
}
|
||||
else if (Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob)) {
|
||||
if (ModifierData *md_eval = BKE_modifiers_findby_name(ob_eval, smd->modifier.name)) {
|
||||
|
|
|
@ -274,6 +274,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
if (smd->target != nullptr) {
|
||||
DEG_add_object_relation(
|
||||
ctx->node, smd->target, DEG_OB_COMP_GEOMETRY, "Surface Deform Modifier");
|
||||
DEG_add_special_eval_flag(ctx->node, &smd->target->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -381,6 +381,8 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
wmd->proximity_mode == MOD_WVG_PROXIMITY_GEOMETRY) {
|
||||
DEG_add_object_relation(
|
||||
ctx->node, wmd->proximity_ob_target, DEG_OB_COMP_GEOMETRY, "WeightVGProximity Modifier");
|
||||
DEG_add_special_eval_flag(
|
||||
ctx->node, &wmd->proximity_ob_target->id, DAG_EVAL_NEED_CPU_EVALUATED_MESH);
|
||||
}
|
||||
need_transform_relation = true;
|
||||
}
|
||||
|
|
I can understand why paint mode needs the mesh, but from what I remember about sculpt mode, it doesn't seem to. What am I missing there?
Anything that calls
sculpt_update_object
(e.g. viaBKE_sculpt_update_object_after_eval
) would access the mesh. It is possible that code retrieves the mesh even when it doesn't need it and can be improved, but that is probably out of the scope of this patch.One thing though, this code here probably should check
DEG_is_active
too - the location calling toBKE_sculpt_update_object_after_eval
does check it.