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
This commit is contained in:
Hans Goudey
2025-06-16 17:02:25 +02:00
committed by Hans Goudey
parent efc2fd16a9
commit c48423256d
6 changed files with 96 additions and 60 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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<std::string, float, 1> 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<float> 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();
}
};

View File

@@ -11,6 +11,7 @@
#include <string>
#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<std::string, float, 1> mode_transfer_overlay_current_state();
/* `object_modifier.cc` */
enum {

View File

@@ -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<std::string, double> 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<std::string, float, 1> 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<std::string, double> &start_times = mode_transfer_overlay_start_times();
start_times.remove_if(
[&](const auto &item) { return (now - item.value) > mode_transfer_flash_length; });
Map<std::string, float, 1> 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,