Performance: Draw play head as an overlay

When playing back animations a playhead is updated in all the animation editors.
The drawing of the playhead is part of the drawing of the main region
`RGN_TYPE_WINDOW` that redraws the whole region.

This change will draw the play head and window scrollers when updating the
screen. This affects the Action editor, Timeline, Graph editor, NLA editor and
Sequence editor. There is noticeable speedup when using complex animation files.

Spring 02_020_A.anim.blend fps went from 11.8 to 12.5 when showing a timeline
and a action editor on a Ryzen 1700.

* When playing back animation the markers don't jump up/down when near the
  frame. This could be added back.

Reviewed By: Brecht van Lommel

Differential Revision: https://developer.blender.org/D8066
This commit is contained in:
Jeroen Bakker 2020-06-23 07:59:34 +02:00 committed by Jeroen Bakker
parent 87ceff3d1b
commit bbb2e0614f
Notes: blender-bot 2023-02-14 04:40:22 +01:00
Referenced by commit 1062649b5e, Fix T87041: Driver Editor not updated in realtime
Referenced by commit c371d699ca, Fix T84784: Time Remapping not displaying in the timeline
Referenced by issue #84784, Regression: Time Remapping not displaying in the timeline anymore (displays wrong frame)
Referenced by issue #81532, Timeline, Dope sheet, NLA Editor - slider covers button to reveal the sidebar
Referenced by issue #79619, Sequencer window not updating whilst playing animation
Referenced by issue #78191, Timeline lines change thickness
Referenced by issue #75124, ActionEditor: Keyframes Drawing
12 changed files with 196 additions and 57 deletions

View File

@ -142,6 +142,8 @@ typedef struct ARegionType {
void (*exit)(struct wmWindowManager *wm, struct ARegion *region);
/* draw entirely, view changes should be handled here */
void (*draw)(const struct bContext *C, struct ARegion *region);
/* Handler to draw overlays. This handler is called every draw loop. */
void (*draw_overlay)(const struct bContext *C, struct ARegion *region);
/* optional, compute button layout before drawing for dynamic size */
void (*layout)(const struct bContext *C, struct ARegion *region);
/* snap the size of the region (can be NULL for no snapping). */

View File

@ -92,7 +92,9 @@ static void draw_current_frame(const Scene *scene,
bool display_seconds,
const View2D *v2d,
const rcti *scrub_region_rect,
int current_frame)
int current_frame,
float sub_frame,
bool draw_line)
{
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
int frame_x = UI_view2d_view_to_region_x(v2d, current_frame);
@ -106,6 +108,23 @@ static void draw_current_frame(const Scene *scene,
float bg_color[4];
UI_GetThemeColorShade4fv(TH_CFRAME, -5, bg_color);
if (draw_line) {
/* Draw vertical line to from the bottom of the current frame box to the bottom of the screen.
*/
const float subframe_x = UI_view2d_view_to_region_x(v2d, current_frame + sub_frame);
GPU_line_width(2.0f);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformThemeColor(TH_CFRAME);
immBegin(GPU_PRIM_LINES, 2);
immVertex2f(pos, subframe_x, scrub_region_rect->ymax - box_padding);
immVertex2f(pos, subframe_x, 0.0f);
immEnd();
immUnbindProgram();
}
UI_draw_roundbox_corner_set(UI_CNR_ALL);
UI_draw_roundbox_3fv_alpha(true,
@ -135,6 +154,28 @@ static void draw_current_frame(const Scene *scene,
text_color);
}
void ED_time_scrub_draw_current_frame(const ARegion *region,
const Scene *scene,
bool display_seconds,
bool draw_line)
{
const View2D *v2d = &region->v2d;
GPU_matrix_push_projection();
wmOrtho2_region_pixelspace(region);
rcti scrub_region_rect;
get_time_scrub_region_rect(region, &scrub_region_rect);
draw_current_frame(scene,
display_seconds,
v2d,
&scrub_region_rect,
scene->r.cfra,
scene->r.subframe,
draw_line);
GPU_matrix_pop_projection();
}
void ED_time_scrub_draw(const ARegion *region,
const Scene *scene,
bool display_seconds,
@ -161,8 +202,6 @@ void ED_time_scrub_draw(const ARegion *region,
region, v2d, &numbers_rect, scene, display_seconds, TH_TEXT);
}
draw_current_frame(scene, display_seconds, v2d, &scrub_region_rect, scene->r.cfra);
GPU_matrix_pop_projection();
}

View File

@ -640,14 +640,14 @@ bool ANIM_remove_empty_action_from_animdata(struct AnimData *adt);
/* ---------- Current Frame Drawing ---------------- */
/* flags for Current Frame Drawing */
enum eAnimEditDraw_CurrentFrame {
typedef enum eAnimEditDraw_CurrentFrame {
/* plain time indicator with no special indicators */
/* DRAWCFRA_PLAIN = 0, */ /* UNUSED */
/* time indication in seconds or frames */
DRAWCFRA_UNIT_SECONDS = (1 << 0),
/* draw indicator extra wide (for timeline) */
DRAWCFRA_WIDE = (1 << 1),
};
} eAnimEditDraw_CurrentFrame;
/* main call to draw current-frame indicator in an Animation Editor */
void ANIM_draw_cfra(const struct bContext *C, struct View2D *v2d, short flag);

View File

@ -32,6 +32,11 @@ struct bContext;
struct bDopeSheet;
struct wmEvent;
void ED_time_scrub_draw_current_frame(const struct ARegion *region,
const struct Scene *scene,
bool display_seconds,
bool draw_vert_line);
void ED_time_scrub_draw(const struct ARegion *region,
const struct Scene *scene,
bool display_seconds,

View File

@ -4273,6 +4273,13 @@ static void SCREEN_OT_region_context_menu(wmOperatorType *ot)
*
* Animation Step.
* \{ */
static bool screen_animation_region_supports_time_follow(eSpace_Type spacetype,
eRegionType regiontype)
{
return (regiontype == RGN_TYPE_WINDOW &&
ELEM(spacetype, SPACE_SEQ, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) ||
(spacetype == SPACE_CLIP && regiontype == RGN_TYPE_PREVIEW);
}
static bool match_region_with_redraws(eSpace_Type spacetype,
eRegionType regiontype,
@ -4364,6 +4371,37 @@ static bool match_region_with_redraws(eSpace_Type spacetype,
return false;
}
static void screen_animation_region_tag_redraw(ScrArea *area,
ARegion *region,
const Scene *scene,
eScreen_Redraws_Flag redraws)
{
/* Do follow time here if editor type supports it */
if ((redraws & TIME_FOLLOW) &&
(screen_animation_region_supports_time_follow(area->spacetype, region->regiontype))) {
float w = BLI_rctf_size_x(&region->v2d.cur);
if (scene->r.cfra < region->v2d.cur.xmin) {
region->v2d.cur.xmax = scene->r.cfra;
region->v2d.cur.xmin = region->v2d.cur.xmax - w;
ED_region_tag_redraw(region);
return;
}
else if (scene->r.cfra > region->v2d.cur.xmax) {
region->v2d.cur.xmin = scene->r.cfra;
region->v2d.cur.xmax = region->v2d.cur.xmin + w;
ED_region_tag_redraw (region);
return;
}
}
if ((region->regiontype == RGN_TYPE_WINDOW) &&
(ELEM(area->spacetype, SPACE_GRAPH, SPACE_NLA, SPACE_ACTION, SPACE_SEQ))) {
/* No need to do a full redraw as the playhead is only updated. */
return;
}
ED_region_tag_redraw(region);
}
//#define PROFILE_AUDIO_SYNCH
static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
@ -4402,7 +4440,8 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv
}
if (scene_eval == NULL) {
/* Happens when undo/redo system is used during playback, nothing meaningful we can do here.
/* Happens when undo/redo system is used during playback, nothing meaningful we can do
* here.
*/
}
else if (scene_eval->id.recalc & ID_RECALC_AUDIO_SEEK) {
@ -4547,23 +4586,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv
}
if (redraw) {
ED_region_tag_redraw(region);
/* do follow here if editor type supports it */
if ((sad->redraws & TIME_FOLLOW)) {
if ((region->regiontype == RGN_TYPE_WINDOW &&
ELEM(area->spacetype, SPACE_SEQ, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) ||
(area->spacetype == SPACE_CLIP && region->regiontype == RGN_TYPE_PREVIEW)) {
float w = BLI_rctf_size_x(&region->v2d.cur);
if (scene->r.cfra < region->v2d.cur.xmin) {
region->v2d.cur.xmax = scene->r.cfra;
region->v2d.cur.xmin = region->v2d.cur.xmax - w;
}
else if (scene->r.cfra > region->v2d.cur.xmax) {
region->v2d.cur.xmin = scene->r.cfra;
region->v2d.cur.xmax = region->v2d.cur.xmin + w;
}
}
}
screen_animation_region_tag_redraw(area, region, scene, sad->redraws);
}
}
}

View File

@ -184,6 +184,11 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
short marker_flag = 0;
short cfra_flag = 0;
UI_view2d_view_ortho(v2d);
if (saction->flag & SACTION_DRAWTIME) {
cfra_flag |= DRAWCFRA_UNIT_SECONDS;
}
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
GPU_clear(GPU_COLOR_BIT);
@ -203,12 +208,6 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
draw_channel_strips(&ac, saction, region);
}
/* current frame */
if (saction->flag & SACTION_DRAWTIME) {
cfra_flag |= DRAWCFRA_UNIT_SECONDS;
}
ANIM_draw_cfra(C, v2d, cfra_flag);
/* markers */
UI_view2d_view_orthoSpecial(region, v2d, 1);
@ -237,6 +236,17 @@ static void action_main_region_draw(const bContext *C, ARegion *region)
/* scrubbing region */
ED_time_scrub_draw(region, scene, saction->flag & SACTION_DRAWTIME, true);
}
static void action_main_region_draw_overlay(const bContext *C, ARegion *region)
{
/* draw entirely, view changes should be handled here */
const SpaceAction *saction = CTX_wm_space_action(C);
const Scene *scene = CTX_data_scene(C);
View2D *v2d = &region->v2d;
/* scrubbing region */
ED_time_scrub_draw_current_frame(region, scene, saction->flag & SACTION_DRAWTIME, true);
/* scrollers */
UI_view2d_scrollers_draw(v2d, NULL);
@ -871,6 +881,7 @@ void ED_spacetype_action(void)
art->regionid = RGN_TYPE_WINDOW;
art->init = action_main_region_init;
art->draw = action_main_region_draw;
art->draw_overlay = action_main_region_draw_overlay;
art->listener = action_main_region_listener;
art->message_subscribe = saction_main_region_message_subscribe;
art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;

View File

@ -201,7 +201,6 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
bAnimContext ac;
View2D *v2d = &region->v2d;
float col[3];
short cfra_flag = 0;
/* clear and setup matrix */
UI_GetThemeColor3fv(TH_BACK, col);
@ -283,14 +282,6 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
immUnbindProgram();
}
if (sipo->mode != SIPO_MODE_DRIVERS) {
/* current frame */
if (sipo->flag & SIPO_DRAWTIME) {
cfra_flag |= DRAWCFRA_UNIT_SECONDS;
}
ANIM_draw_cfra(C, v2d, cfra_flag);
}
/* markers */
if (sipo->mode != SIPO_MODE_DRIVERS) {
UI_view2d_view_orthoSpecial(region, v2d, 1);
@ -315,6 +306,18 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
/* time-scrubbing */
ED_time_scrub_draw(region, scene, display_seconds, false);
}
static void graph_main_region_draw_overlay(const bContext *C, ARegion *region)
{
/* draw entirely, view changes should be handled here */
const SpaceGraph *sipo = CTX_wm_space_graph(C);
const Scene *scene = CTX_data_scene(C);
const bool draw_vert_line = sipo->mode != SIPO_MODE_DRIVERS;
View2D *v2d = &region->v2d;
/* scrubbing region */
ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME, draw_vert_line);
/* scrollers */
// FIXME: args for scrollers depend on the type of data being shown...
@ -853,6 +856,7 @@ void ED_spacetype_ipo(void)
art->regionid = RGN_TYPE_WINDOW;
art->init = graph_main_region_init;
art->draw = graph_main_region_draw;
art->draw_overlay = graph_main_region_draw_overlay;
art->listener = graph_region_listener;
art->message_subscribe = graph_region_message_subscribe;
art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;

View File

@ -259,13 +259,10 @@ static void nla_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_text_cache_draw(region);
}
UI_view2d_view_ortho(v2d);
/* current frame */
if (snla->flag & SNLA_DRAWTIME) {
cfra_flag |= DRAWCFRA_UNIT_SECONDS;
}
ANIM_draw_cfra(C, v2d, cfra_flag);
/* markers */
UI_view2d_view_orthoSpecial(region, v2d, 1);
@ -286,6 +283,17 @@ static void nla_main_region_draw(const bContext *C, ARegion *region)
UI_view2d_view_restore(C);
ED_time_scrub_draw(region, scene, snla->flag & SNLA_DRAWTIME, true);
}
static void nla_main_region_draw_overlay(const bContext *C, ARegion *region)
{
/* draw entirely, view changes should be handled here */
const SpaceNla *snla = CTX_wm_space_nla(C);
const Scene *scene = CTX_data_scene(C);
View2D *v2d = &region->v2d;
/* scrubbing region */
ED_time_scrub_draw_current_frame(region, scene, snla->flag & SNLA_DRAWTIME, true);
/* scrollers */
UI_view2d_scrollers_draw(v2d, NULL);
@ -614,6 +622,7 @@ void ED_spacetype_nla(void)
art->regionid = RGN_TYPE_WINDOW;
art->init = nla_main_region_init;
art->draw = nla_main_region_draw;
art->draw_overlay = nla_main_region_draw_overlay;
art->listener = nla_main_region_listener;
art->message_subscribe = nla_main_region_message_subscribe;
art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_ANIMATION | ED_KEYMAP_FRAMES;

View File

@ -2345,7 +2345,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW);
UI_view2d_view_restore(C);
ED_time_scrub_draw(region, scene, !(sseq->flag & SEQ_DRAWFRAMES), true);
UI_view2d_scrollers_draw(v2d, NULL);
/* Draw channel numbers. */
{
@ -2355,3 +2354,13 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
UI_view2d_draw_scale_y__block(region, v2d, &rect, TH_SCROLL_TEXT);
}
}
void draw_timeline_seq_display(const bContext *C, ARegion *region)
{
const Scene *scene = CTX_data_scene(C);
const SpaceSeq *sseq = CTX_wm_space_seq(C);
View2D *v2d = &region->v2d;
ED_time_scrub_draw_current_frame(region, scene, !(sseq->flag & SEQ_DRAWFRAMES), true);
UI_view2d_scrollers_draw(v2d, NULL);
}

View File

@ -43,6 +43,7 @@ struct wmOperator;
/* sequencer_draw.c */
void draw_timeline_seq(const struct bContext *C, struct ARegion *region);
void draw_timeline_seq_display(const struct bContext *C, struct ARegion *region);
void sequencer_draw_preview(const struct bContext *C,
struct Scene *scene,
struct ARegion *region,

View File

@ -521,6 +521,12 @@ static void sequencer_main_region_draw(const bContext *C, ARegion *region)
draw_timeline_seq(C, region);
}
/* Strip editing timeline. */
static void sequencer_main_region_draw_overlay(const bContext *C, ARegion *region)
{
draw_timeline_seq_display(C, region);
}
static void sequencer_main_region_listener(wmWindow *UNUSED(win),
ScrArea *UNUSED(area),
ARegion *region,
@ -865,6 +871,7 @@ void ED_spacetype_sequencer(void)
art->regionid = RGN_TYPE_WINDOW;
art->init = sequencer_main_region_init;
art->draw = sequencer_main_region_draw;
art->draw_overlay = sequencer_main_region_draw_overlay;
art->listener = sequencer_main_region_listener;
art->message_subscribe = sequencer_main_region_message_subscribe;
art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_ANIMATION;

View File

@ -69,6 +69,8 @@
#include "wm_surface.h"
#include "wm_window.h"
#include "UI_resources.h"
#ifdef WITH_OPENSUBDIV
# include "BKE_subsurf.h"
#endif
@ -127,6 +129,28 @@ static void wm_paintcursor_draw(bContext *C, ScrArea *area, ARegion *region)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Post Draw Region on display handlers
* \{ */
static void wm_region_draw_overlay(bContext *C, ScrArea *area, ARegion *region)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
/* Don't draw overlay with locked interface. Drawing could access scene data that another thread
* may be modifying. */
if (wm->is_interface_locked) {
return;
}
wmViewport(&region->winrct);
UI_SetTheme(area->spacetype, region->regiontype);
region->type->draw_overlay(C, region);
wmWindowViewport(win);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Internal Utilities
* \{ */
@ -749,25 +773,30 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
}
}
/* Draw paint cursors. */
if (wm->paintcursors.first) {
ED_screen_areas_iter (win, screen, area) {
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region->visible && region == screen->active_region) {
CTX_wm_area_set(C, area);
CTX_wm_region_set(C, region);
/* make region ready for draw, scissor, pixelspace */
wm_paintcursor_draw(C, area, region);
CTX_wm_region_set(C, NULL);
CTX_wm_area_set(C, NULL);
/* Draw overlays and paint cursors. */
ED_screen_areas_iter (win, screen, area) {
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
if (region->visible) {
const bool do_paint_cursor = (wm->paintcursors.first && region == screen->active_region);
const bool do_draw_overlay = (region->type && region->type->draw_overlay);
if (!(do_paint_cursor || do_draw_overlay)) {
continue;
}
CTX_wm_area_set(C, area);
CTX_wm_region_set(C, region);
if (do_draw_overlay) {
wm_region_draw_overlay(C, area, region);
}
if (do_paint_cursor) {
wm_paintcursor_draw(C, area, region);
}
CTX_wm_region_set(C, NULL);
CTX_wm_area_set(C, NULL);
}
}
wmWindowViewport(win);
}
wmWindowViewport(win);
/* Blend in overlapping area regions */
ED_screen_areas_iter (win, screen, area) {