Subdivision Surface: fix a serious performance hit when mixing CPU & GPU. #104441
@ -32,8 +32,13 @@ typedef struct SubsurfRuntimeData {
|
||||
SubdivSettings settings;
|
||||
|
||||
/* Cached subdivision surface descriptor, with topology and settings. */
|
||||
struct Subdiv *subdiv;
|
||||
bool set_by_draw_code;
|
||||
struct Subdiv *subdiv_cpu;
|
||||
struct Subdiv *subdiv_gpu;
|
||||
|
||||
/* Recent usage markers for UI diagnostics. To avoid UI flicker due to races
|
||||
* between evaluation and UI redraw, they are set to 2 when an evaluator is used,
|
||||
* and count down every frame. */
|
||||
char used_cpu, used_gpu;
|
||||
|
||||
/* Cached mesh wrapper data, to be used for GPU subdiv or lazy evaluation on CPU. */
|
||||
bool has_gpu_subdiv;
|
||||
|
@ -350,7 +350,7 @@ static Mesh *mesh_wrapper_ensure_subdivision(Mesh *me)
|
||||
BKE_mesh_calc_normals_split(subdiv_mesh);
|
||||
}
|
||||
|
||||
if (subdiv != runtime_data->subdiv) {
|
||||
if (subdiv != runtime_data->subdiv_cpu && subdiv != runtime_data->subdiv_gpu) {
|
||||
BKE_subdiv_free(subdiv);
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,8 @@ bool BKE_subsurf_modifier_runtime_init(SubsurfModifierData *smd, const bool use_
|
||||
* was already allocated. */
|
||||
if (runtime_data) {
|
||||
runtime_data->settings = settings;
|
||||
|
||||
runtime_data->used_cpu = runtime_data->used_gpu = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -162,15 +164,18 @@ Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(SubsurfRuntimeData *runtim
|
||||
const Mesh *mesh,
|
||||
const bool for_draw_code)
|
||||
{
|
||||
if (runtime_data->subdiv && runtime_data->set_by_draw_code != for_draw_code) {
|
||||
BKE_subdiv_free(runtime_data->subdiv);
|
||||
runtime_data->subdiv = nullptr;
|
||||
if (for_draw_code) {
|
||||
runtime_data->used_gpu = 2; /* countdown in frames */
|
||||
|
||||
return runtime_data->subdiv_gpu = BKE_subdiv_update_from_mesh(
|
||||
runtime_data->subdiv_gpu, &runtime_data->settings, mesh);
|
||||
}
|
||||
else {
|
||||
runtime_data->used_cpu = 2;
|
||||
|
||||
return runtime_data->subdiv_cpu = BKE_subdiv_update_from_mesh(
|
||||
runtime_data->subdiv_cpu, &runtime_data->settings, mesh);
|
||||
}
|
||||
Subdiv *subdiv = BKE_subdiv_update_from_mesh(
|
||||
runtime_data->subdiv, &runtime_data->settings, mesh);
|
||||
runtime_data->subdiv = subdiv;
|
||||
runtime_data->set_by_draw_code = for_draw_code;
|
||||
return subdiv;
|
||||
}
|
||||
|
||||
int BKE_subsurf_modifier_eval_required_mode(bool is_final_render, bool is_edit_mode)
|
||||
|
@ -100,8 +100,11 @@ static void freeRuntimeData(void *runtime_data_v)
|
||||
return;
|
||||
}
|
||||
SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)runtime_data_v;
|
||||
if (runtime_data->subdiv != nullptr) {
|
||||
BKE_subdiv_free(runtime_data->subdiv);
|
||||
if (runtime_data->subdiv_cpu != nullptr) {
|
||||
BKE_subdiv_free(runtime_data->subdiv_cpu);
|
||||
}
|
||||
if (runtime_data->subdiv_gpu != nullptr) {
|
||||
BKE_subdiv_free(runtime_data->subdiv_gpu);
|
||||
}
|
||||
MEM_freeN(runtime_data);
|
||||
}
|
||||
@ -227,6 +230,15 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
|
||||
|
||||
SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)smd->modifier.runtime;
|
||||
|
||||
/* Decrement the recent usage counters. */
|
||||
if (runtime_data->used_cpu) {
|
||||
runtime_data->used_cpu--;
|
||||
}
|
||||
|
||||
if (runtime_data->used_gpu) {
|
||||
runtime_data->used_gpu--;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
@ -273,7 +285,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
|
||||
CustomData_set_layer_flag(&result->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
|
||||
}
|
||||
// BKE_subdiv_stats_print(&subdiv->stats);
|
||||
if (subdiv != runtime_data->subdiv) {
|
||||
if (subdiv != runtime_data->subdiv_cpu && subdiv != runtime_data->subdiv_gpu) {
|
||||
BKE_subdiv_free(subdiv);
|
||||
}
|
||||
return result;
|
||||
@ -305,7 +317,7 @@ static void deformMatrices(ModifierData *md,
|
||||
return;
|
||||
}
|
||||
BKE_subdiv_deform_coarse_vertices(subdiv, mesh, vertex_cos, verts_num);
|
||||
if (subdiv != runtime_data->subdiv) {
|
||||
if (subdiv != runtime_data->subdiv_cpu && subdiv != runtime_data->subdiv_gpu) {
|
||||
BKE_subdiv_free(subdiv);
|
||||
}
|
||||
}
|
||||
@ -409,12 +421,29 @@ static void panel_draw(const bContext *C, Panel *panel)
|
||||
|
||||
uiItemR(layout, ptr, "show_only_control_edges", 0, nullptr, ICON_NONE);
|
||||
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
SubsurfModifierData *smd = static_cast<SubsurfModifierData *>(ptr->data);
|
||||
const Object *ob = static_cast<const Object *>(ob_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);
|
||||
}
|
||||
else if (Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob)) {
|
||||
if (ModifierData *md_eval = BKE_modifiers_findby_name(ob_eval, smd->modifier.name)) {
|
||||
if (md_eval->type == eModifierType_Subsurf) {
|
||||
SubsurfRuntimeData *runtime_data = (SubsurfRuntimeData *)md_eval->runtime;
|
||||
|
||||
if (runtime_data && runtime_data->used_gpu) {
|
||||
if (runtime_data->used_cpu) {
|
||||
uiItemL(layout, "Using both CPU and GPU subdivision", ICON_INFO);
|
||||
angavrilov marked this conversation as resolved
|
||||
}
|
||||
else {
|
||||
uiItemL(layout, "Using GPU subdivision", ICON_INFO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modifier_panel_end(layout, ptr);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user
Leave out the exclamation mark, we should generally not use them in UI text unless it's something really critical.
Also this is not really an error, at most a warning but even then would still go with ICON_INFO.
It's not something users are able to fix in generally, some setup just require it with our current implementation.