From 7d6b55ab3fc603446308a5c4fd45622a690fd976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Tue, 7 Oct 2025 11:07:19 +0200 Subject: [PATCH] Anim: remove "Render Viewport Keyframes" operator Remove the "Render Viewport Keyframes" mode of the `RENDER_OT_opengl` operator. This was implemented 6 years ago to avoid camera strobing when animating on 2s, and was (AFAIK) little used after that initial need (mostly because of usability issues and limited functionality). The Fix to Camera operator deals with this at an animation level; that's a better solution, as it also works for regular (non-viewport) renders. See #146749 Pull Request: https://projects.blender.org/blender/blender/pulls/147463 --- .../blender/editors/render/render_opengl.cc | 210 +----------------- 1 file changed, 2 insertions(+), 208 deletions(-) diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 410eb6f5408..36789fd2976 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -127,9 +127,6 @@ struct OGLRender : public RenderJobBase { int totvideos = 0; - /* For only rendering frames that have a key in animation data. */ - BLI_bitmap *render_frames = nullptr; - /* quick lookup */ int view_id = 0; @@ -520,183 +517,6 @@ static void screen_opengl_render_apply(OGLRender *oglrender) } } -static void gather_frames_to_render_for_adt(const OGLRender *oglrender, const AnimData *adt) -{ - if (adt == nullptr || adt->action == nullptr) { - return; - } - - Scene *scene = oglrender->scene; - int frame_start = PSFRA; - int frame_end = PEFRA; - - for (const FCurve *fcu : blender::animrig::legacy::fcurves_for_assigned_action(adt)) { - if (fcu->driver != nullptr || fcu->fpt != nullptr) { - /* Drivers have values for any point in time, so to get "the keyed frames" they are - * useless. Same for baked FCurves, they also have keys for every frame, which is not - * useful for rendering the keyed subset of the frames. */ - continue; - } - - bool found = false; /* Not interesting, we just want a starting point for the for-loop. */ - int key_index = BKE_fcurve_bezt_binarysearch_index( - fcu->bezt, frame_start, fcu->totvert, &found); - for (; key_index < fcu->totvert; key_index++) { - BezTriple *bezt = &fcu->bezt[key_index]; - /* The frame range to render uses integer frame numbers, and the frame - * step is also an integer, so we always render on the frame. */ - int frame_nr = round_fl_to_int(bezt->vec[1][0]); - - /* (frame_nr < frame_start) cannot happen because of the binary search above. */ - BLI_assert(frame_nr >= frame_start); - if (frame_nr > frame_end) { - break; - } - BLI_BITMAP_ENABLE(oglrender->render_frames, frame_nr - frame_start); - } - } -} - -static void gather_frames_to_render_for_grease_pencil(const OGLRender *oglrender, - const bGPdata *gp) -{ - if (gp == nullptr) { - return; - } - - Scene *scene = oglrender->scene; - int frame_start = PSFRA; - int frame_end = PEFRA; - - LISTBASE_FOREACH (const bGPDlayer *, gp_layer, &gp->layers) { - LISTBASE_FOREACH (const bGPDframe *, gp_frame, &gp_layer->frames) { - if (gp_frame->framenum < frame_start || gp_frame->framenum > frame_end) { - continue; - } - BLI_BITMAP_ENABLE(oglrender->render_frames, gp_frame->framenum - frame_start); - } - } -} - -static int gather_frames_to_render_for_id(LibraryIDLinkCallbackData *cb_data) -{ - ID **id_p = cb_data->id_pointer; - if (*id_p == nullptr) { - return IDWALK_RET_NOP; - } - ID *id = *id_p; - - ID *self_id = cb_data->self_id; - const LibraryForeachIDCallbackFlag cb_flag = cb_data->cb_flag; - if (cb_flag == IDWALK_CB_LOOPBACK || id == self_id) { - /* IDs may end up referencing themselves one way or the other, and those - * (the self_id ones) have always already been processed. */ - return IDWALK_RET_STOP_RECURSION; - } - - OGLRender *oglrender = static_cast(cb_data->user_data); - - /* Whitelist of datablocks to follow pointers into. */ - const ID_Type id_type = GS(id->name); - switch (id_type) { - /* Whitelist: */ - case ID_ME: /* Mesh */ - case ID_CU_LEGACY: /* Curve */ - case ID_MB: /* MetaBall */ - case ID_MA: /* Material */ - case ID_TE: /* Tex (Texture) */ - case ID_IM: /* Image */ - case ID_LT: /* Lattice */ - case ID_LA: /* Light */ - case ID_CA: /* Camera */ - case ID_KE: /* Key (shape key) */ - case ID_VF: /* VFont (Vector Font) */ - case ID_TXT: /* Text */ - case ID_SPK: /* Speaker */ - case ID_SO: /* Sound */ - case ID_AR: /* bArmature */ - case ID_NT: /* bNodeTree */ - case ID_PA: /* ParticleSettings */ - case ID_MC: /* MovieClip */ - case ID_MSK: /* Mask */ - case ID_LP: /* LightProbe */ - case ID_CV: /* Curves */ - case ID_PT: /* PointCloud */ - case ID_VO: /* Volume */ - break; - - /* Blacklist: */ - case ID_SCE: /* Scene */ - case ID_LI: /* Library */ - case ID_OB: /* Object */ - case ID_WO: /* World */ - case ID_SCR: /* Screen */ - case ID_GR: /* Group */ - case ID_AC: /* bAction */ - case ID_BR: /* Brush */ - case ID_WM: /* WindowManager */ - case ID_LS: /* FreestyleLineStyle */ - case ID_PAL: /* Palette */ - case ID_PC: /* PaintCurve */ - case ID_CF: /* CacheFile */ - case ID_WS: /* WorkSpace */ - /* Only follow pointers to specific datablocks, to avoid ending up in - * unrelated datablocks and exploding the number of blocks we follow. If the - * frames of the animation of certain objects should be taken into account, - * they should have been selected by the user. */ - return IDWALK_RET_STOP_RECURSION; - - /* Special cases: */ - case ID_GD_LEGACY: /* bGPdata, (Grease Pencil) */ - /* In addition to regular ID's animdata, GreasePencil uses a specific frame-based animation - * system that requires specific handling here. */ - gather_frames_to_render_for_grease_pencil(oglrender, (bGPdata *)id); - break; - case ID_GP: - /* TODO: gather frames. */ - break; - } - - AnimData *adt = BKE_animdata_from_id(id); - gather_frames_to_render_for_adt(oglrender, adt); - - return IDWALK_RET_NOP; -} - -/** - * Collect the frame numbers for which selected objects have keys in the animation data. - * The frames ares stored in #OGLRender.render_frames. - * - * Note that this follows all pointers to ID blocks, only filtering on ID type, - * so it will pick up keys from pointers in custom properties as well. - */ -static void gather_frames_to_render(bContext *C, OGLRender *oglrender) -{ - Scene *scene = oglrender->scene; - int frame_start = PSFRA; - int frame_end = PEFRA; - - /* Will be freed in screen_opengl_render_end(). */ - oglrender->render_frames = BLI_BITMAP_NEW(frame_end - frame_start + 1, - "OGLRender::render_frames"); - - /* The first frame should always be rendered, otherwise there is nothing to write to file. */ - BLI_BITMAP_ENABLE(oglrender->render_frames, 0); - - CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { - ID *id = &ob->id; - - /* Gather the frames from the object animation data. */ - AnimData *adt = BKE_animdata_from_id(id); - gather_frames_to_render_for_adt(oglrender, adt); - - /* Gather the frames from linked data-blocks (materials, shape-keys, etc.). */ - BKE_library_foreach_ID_link( - nullptr, id, gather_frames_to_render_for_id, oglrender, IDWALK_RECURSE); - } - CTX_DATA_END; -} - static bool screen_opengl_render_init(bContext *C, wmOperator *op) { /* new render clears all callbacks */ @@ -717,7 +537,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) int sizex, sizey; bool is_view_context = RNA_boolean_get(op->ptr, "view_context"); const bool is_animation = RNA_boolean_get(op->ptr, "animation"); - const bool is_render_keyed_only = RNA_boolean_get(op->ptr, "render_keyed_only"); const bool is_write_still = RNA_boolean_get(op->ptr, "write_still"); const eImageFormatDepth color_depth = static_cast( (is_animation) ? (eImageFormatDepth)scene->r.im_format.depth : R_IMF_CHAN_DEPTH_32); @@ -857,10 +676,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) oglrender->win = win; if (is_animation) { - if (is_render_keyed_only) { - gather_frames_to_render(C, oglrender); - } - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { oglrender->task_pool = BLI_task_pool_create_background_serial(oglrender, TASK_PRIORITY_HIGH); } @@ -904,8 +719,6 @@ static void screen_opengl_render_end(OGLRender *oglrender) oglrender->task_pool = nullptr; } - MEM_SAFE_FREE(oglrender->render_frames); - if (!oglrender->movie_writers.is_empty()) { if (BKE_imtype_is_movie(oglrender->scene->r.im_format.imtype)) { for (MovieWriter *writer : oglrender->movie_writers) { @@ -1211,12 +1024,8 @@ static bool screen_opengl_render_anim_step(OGLRender *oglrender) BKE_scene_camera_switch_update(scene); } - if (oglrender->render_frames == nullptr || - BLI_BITMAP_TEST_BOOL(oglrender->render_frames, scene->r.cfra - PSFRA)) - { - /* render into offscreen buffer */ - screen_opengl_render_apply(oglrender); - } + /* render into offscreen buffer */ + screen_opengl_render_apply(oglrender); /* save to disk */ rr = RE_AcquireResultRead(oglrender->re); @@ -1393,13 +1202,6 @@ static std::string screen_opengl_render_get_description(bContext * /*C*/, if (!RNA_boolean_get(ptr, "animation")) { return ""; } - - if (RNA_boolean_get(ptr, "render_keyed_only")) { - return TIP_( - "Render the viewport for the animation range of this scene, but only render keyframes of " - "selected objects"); - } - return TIP_("Render the viewport for the animation range of this scene"); } @@ -1428,14 +1230,6 @@ void RENDER_OT_opengl(wmOperatorType *ot) "Render files from the animation range of this scene"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, - "render_keyed_only", - false, - "Render Keyframes Only", - "Render only those frames where selected objects have a key in their " - "animation data. Only used when rendering animation"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean( ot->srna, "sequencer", false, "Sequencer", "Render using the sequencer's OpenGL display"); RNA_def_property_flag(prop, PROP_SKIP_SAVE);