Commit: 7faa8c84b35397dee81532c235ebda487d71a8b7 Author: Alexander Gavrilov Date: Wed Jan 25 11:34:58 2023 +0200 Branches: temp-angavrilov https://developer.blender.org/rB7faa8c84b35397dee81532c235ebda487d71a8b7
Subdivision Surface: fix a serious performance hit when mixing CPU & GPU. Subdivision surface efficiency relies on caching pre-computed topology data for evaluation between frames. However, while rBeed45d2a239a introduced a second GPU subdiv evaluator type, it still only kept one slot for caching this runtime data per mesh. The result is that if the mesh is also needed on CPU, for instance due to a modifier on a different object (e.g. shrinkwrap), the two evaluators are used at the same time and fight over the single slot. This causes the topology data to be discarded and recomputed twice per frame. Since avoiding duplicate evaluation is a complex task, this fix simply adds a second separate cache slot for the GPU data, so that the cost is simply running subdivision twice, not recomputing topology twice. To help diagnostics, I also add a message to show when GPU evaluation is actually used to the modifier panel. Two frame counters are used to suppress flicker in the UI panel. Differential Revision: https://developer.blender.org/D17117 =================================================================== M source/blender/blenkernel/BKE_subdiv_modifier.h M source/blender/blenkernel/intern/mesh_wrapper.cc M source/blender/blenkernel/intern/subdiv_modifier.cc M source/blender/modifiers/intern/MOD_subsurf.cc =================================================================== diff --git a/source/blender/blenkernel/BKE_subdiv_modifier.h b/source/blender/blenkernel/BKE_subdiv_modifier.h index d65df26da77..dd01c4ffd07 100644 --- a/source/blender/blenkernel/BKE_subdiv_modifier.h +++ b/source/blender/blenkernel/BKE_subdiv_modifier.h @@ -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; diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc index 9c8c36cacb8..3b036274477 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.cc +++ b/source/blender/blenkernel/intern/mesh_wrapper.cc @@ -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); } diff --git a/source/blender/blenkernel/intern/subdiv_modifier.cc b/source/blender/blenkernel/intern/subdiv_modifier.cc index 3221e43d4e6..60d55af215c 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.cc +++ b/source/blender/blenkernel/intern/subdiv_modifier.cc @@ -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) diff --git a/source/blender/modifiers/intern/MOD_subsurf.cc b/source/blender/modifiers/intern/MOD_subsurf.cc index 75377f00bb3..7f6a9696b56 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.cc +++ b/source/blender/modifiers/intern/MOD_subsurf.cc @@ -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_ERROR); + } + else { + uiItemL(layout, "Using GPU subdivision", ICON_INFO); + } + } + } + } + } modifier_panel_end(layout, ptr); } _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs