From c48423256d3aadac09fbddf17d2c1dc44902ae25 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 16 Jun 2025 17:02:25 +0200 Subject: [PATCH] Draw: Remove mode transfer overhead when inactive Currently the mode transfer overlay has to check whether it should draw for every single object. For scenes with many instances even that small amount of work per object can be significant. I observed the overlay taking 1.8% of the samples in a profile. This commit removes the mode transfer timer and stores it in a global map instead. Besides the benefit of removing 8 bytes per object, the main improvement is that it's now trivial to check whether the overlay can be completely disabled. Pull Request: https://projects.blender.org/blender/blender/pulls/140374 --- source/blender/blenkernel/BKE_object_types.hh | 3 - source/blender/draw/CMakeLists.txt | 1 + .../engines/overlay/overlay_mode_transfer.cc | 34 +++++++++ .../engines/overlay/overlay_mode_transfer.hh | 69 ++++--------------- source/blender/editors/include/ED_object.hh | 8 +++ source/blender/editors/object/object_modes.cc | 41 ++++++++++- 6 files changed, 96 insertions(+), 60 deletions(-) create mode 100644 source/blender/draw/engines/overlay/overlay_mode_transfer.cc diff --git a/source/blender/blenkernel/BKE_object_types.hh b/source/blender/blenkernel/BKE_object_types.hh index c4d465a4ade..fb39bd853d2 100644 --- a/source/blender/blenkernel/BKE_object_types.hh +++ b/source/blender/blenkernel/BKE_object_types.hh @@ -56,9 +56,6 @@ struct ObjectRuntime { */ char is_data_eval_owned = false; - /** Start time of the mode transfer overlay animation. */ - double overlay_mode_transfer_start_time = 0.0f; - /** * The bounding box of the object's evaluated geometry in the active dependency graph. The bounds * are copied back to the original object for the RNA API and for display in the interface. diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 7b8b8e72c16..24b9453917e 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -146,6 +146,7 @@ set(SRC engines/overlay/overlay_armature.cc engines/overlay/overlay_engine.cc engines/overlay/overlay_instance.cc + engines/overlay/overlay_mode_transfer.cc engines/overlay/overlay_shader.cc engines/overlay/overlay_shape.cc diff --git a/source/blender/draw/engines/overlay/overlay_mode_transfer.cc b/source/blender/draw/engines/overlay/overlay_mode_transfer.cc new file mode 100644 index 00000000000..bd7638de6c7 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_mode_transfer.cc @@ -0,0 +1,34 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "ED_object.hh" + +#include "overlay_mode_transfer.hh" + +namespace blender::draw::overlay { + +void ModeTransfer::begin_sync(Resources &res, const State &state) +{ + object_factors_ = ed::object::mode_transfer_overlay_current_state(); + + enabled_ = state.is_space_v3d() && !res.is_selection() && !object_factors_.is_empty(); + + if (!enabled_) { + /* Not used. But release the data. */ + ps_.init(); + return; + } + + UI_GetThemeColor3fv(TH_VERTEX_SELECT, flash_color_); + srgb_to_linearrgb_v4(flash_color_, flash_color_); + + ps_.init(); + ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_WRITE_DEPTH, + state.clipping_plane_count); + ps_.shader_set(res.shaders->uniform_color.get()); + ps_.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf); + ps_.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf); +} + +} // namespace blender::draw::overlay diff --git a/source/blender/draw/engines/overlay/overlay_mode_transfer.hh b/source/blender/draw/engines/overlay/overlay_mode_transfer.hh index 9a700784498..6b2a9c97ed4 100644 --- a/source/blender/draw/engines/overlay/overlay_mode_transfer.hh +++ b/source/blender/draw/engines/overlay/overlay_mode_transfer.hh @@ -8,7 +8,7 @@ #pragma once -#include "BLI_time.h" +#include "BLI_map.hh" #include "BKE_paint.hh" @@ -27,38 +27,12 @@ class ModeTransfer : Overlay { private: PassSimple ps_ = {"ModeTransfer"}; + Map object_factors_; + float4 flash_color_; - double current_time_ = 0.0; - - /* True if any object used was synced using this overlay. */ - bool any_animated_ = false; - public: - void begin_sync(Resources &res, const State &state) final - { - enabled_ = state.is_space_v3d() && !res.is_selection(); - - if (!enabled_) { - /* Not used. But release the data. */ - ps_.init(); - return; - } - - UI_GetThemeColor3fv(TH_VERTEX_SELECT, flash_color_); - srgb_to_linearrgb_v4(flash_color_, flash_color_); - - current_time_ = BLI_time_now_seconds(); - - ps_.init(); - ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_WRITE_DEPTH, - state.clipping_plane_count); - ps_.shader_set(res.shaders->uniform_color.get()); - ps_.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf); - ps_.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf); - - any_animated_ = false; - } + void begin_sync(Resources &res, const State &state) final; void object_sync(Manager &manager, const ObjectRef &ob_ref, @@ -69,6 +43,11 @@ class ModeTransfer : Overlay { return; } + const std::optional alpha_opt = object_factors_.lookup_try_as(ob_ref.object->id.name); + if (!alpha_opt) { + return; + } + const bool renderable = DRW_object_is_renderable(ob_ref.object); const bool draw_surface = (ob_ref.object->dt >= OB_WIRE) && (renderable || (ob_ref.object->dt == OB_WIRE)); @@ -76,11 +55,8 @@ class ModeTransfer : Overlay { return; } - const float time = current_time_ - ob_ref.object->runtime->overlay_mode_transfer_start_time; - const float alpha = alpha_from_time_get(time); - if (alpha == 0.0f) { - return; - } + constexpr float flash_alpha = 0.25f; + const float alpha = *alpha_opt * flash_alpha; ps_.push_constant("ucolor", float4(flash_color_.xyz() * alpha, alpha)); @@ -99,8 +75,6 @@ class ModeTransfer : Overlay { ps_.draw(geom, manager.unique_handle(ob_ref)); } } - - any_animated_ = true; } void draw(Framebuffer &framebuffer, Manager &manager, View &view) final @@ -112,25 +86,8 @@ class ModeTransfer : Overlay { GPU_framebuffer_bind(framebuffer); manager.submit(ps_, view); - if (any_animated_) { - /* Request redraws until the object fades out. */ - DRW_viewport_request_redraw(); - } - } - - private: - static constexpr float flash_length = 0.55f; - static constexpr float flash_alpha = 0.25f; - - static float alpha_from_time_get(const float anim_time) - { - if (anim_time > flash_length) { - return 0.0f; - } - if (anim_time < 0.0f) { - return 0.0f; - } - return (1.0f - (anim_time / flash_length)) * flash_alpha; + /* Request redraws until the object fades out (enabled_ will be reset to false). */ + DRW_viewport_request_redraw(); } }; diff --git a/source/blender/editors/include/ED_object.hh b/source/blender/editors/include/ED_object.hh index dbd9700cacf..23eae04cd28 100644 --- a/source/blender/editors/include/ED_object.hh +++ b/source/blender/editors/include/ED_object.hh @@ -11,6 +11,7 @@ #include #include "BLI_compiler_attrs.h" +#include "BLI_map.hh" #include "BLI_math_matrix_types.hh" #include "BLI_string_ref.hh" #include "BLI_vector.hh" @@ -457,6 +458,13 @@ Object *object_in_mode_from_index(const Scene *scene, eObjectMode mode, int index); +/** + * Retrieve the alpha factors of the currently active mode transfer overlay animations. The key is + * the object ID name to prevent possible storage of stale pointers and because the #session_uid + * isn't available on evaluated objects. + */ +Map mode_transfer_overlay_current_state(); + /* `object_modifier.cc` */ enum { diff --git a/source/blender/editors/object/object_modes.cc b/source/blender/editors/object/object_modes.cc index 89a7d236c6b..fa8ad39744a 100644 --- a/source/blender/editors/object/object_modes.cc +++ b/source/blender/editors/object/object_modes.cc @@ -408,11 +408,50 @@ static void object_transfer_mode_reposition_view_pivot(ARegion *region, ups->last_stroke_valid = true; } +constexpr float mode_transfer_flash_length = 0.55f; + +static auto &mode_transfer_overlay_start_times() +{ + static Map map; + return map; +} + +static float alpha_from_time_get(const float anim_time) +{ + if (anim_time < 0.0f) { + return 0.0f; + } + return (1.0f - (anim_time / mode_transfer_flash_length)); +} + +Map mode_transfer_overlay_current_state() +{ + const double now = BLI_time_now_seconds(); + + /* Protect against possible concurrent access from multiple renderers or viewports. */ + static Mutex mutex; + std::scoped_lock lock(mutex); + + /* Remove finished animations form the global map. */ + Map &start_times = mode_transfer_overlay_start_times(); + start_times.remove_if( + [&](const auto &item) { return (now - item.value) > mode_transfer_flash_length; }); + + Map factors; + for (const auto &item : start_times.items()) { + const float alpha = alpha_from_time_get(now - item.value); + if (alpha > 0.0f) { + factors.add_new(item.key, alpha); + } + } + return factors; +} + static void object_overlay_mode_transfer_animation_start(bContext *C, Object *ob_dst) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob_dst_eval = DEG_get_evaluated(depsgraph, ob_dst); - ob_dst_eval->runtime->overlay_mode_transfer_start_time = BLI_time_now_seconds(); + mode_transfer_overlay_start_times().add_as(ob_dst_eval->id.name, BLI_time_now_seconds()); } static bool object_transfer_mode_to_base(bContext *C,