Frame change handler no longer entered when playback stops #109218

Closed
opened 2023-06-21 23:08:06 +02:00 by Julieta Riley · 21 comments

System Information
Operating system: Windows-10-10.0.19044-SP0 64 Bits
Graphics card: NVIDIA GeForce GTX 1070/PCIe/SSE2 NVIDIA Corporation 4.5.0 NVIDIA 535.98

Blender Version
Broken: version: 4.0.0 Alpha, branch: main, commit date: 2023-06-19 17:26, hash: cedec09d0594
Worked: (3.6 below build)
image

Well, sort of intentional, but caused by d8388ef36a

Short description of error
Frame change handler is not being entered when playback ends

Exact steps for others to reproduce the error

  1. Open attached blend file in 3.6 (make sure you're using the same has as me, because later builds of 3.6 also have the bug)
  2. go to the script workspace and run the script
  3. open the console
  4. change the frame manually with the arrow key or mouse
  5. take a note of the output
  6. press play twice to start and stop playback, aim for 3 or so frames to keep the console output short
  7. take a note of the output
  8. open blender 4.0 or later build of 3.6 and load the same file
  9. repeat steps 2 to 7
  10. compare outputs

In later builds the handler doesn't output the final message due to the handler not being triggered when playback ends. Manual frame change works the same as it always has. Triggering the handler is necessary on playback stopping as it provides an opportunity to clear variables etc (unless a playback stop handler has been introduced and this new behaviour is intentional?)

3.6 manual frame change output:

handler entered
frame change handler.  frame = 45.

manual frame change

3.6 playback output (notice how frame 48 is triggered twice, once during playback and once after stopping):

handler entered
frame change handler.  frame = 46.
playing

handler entered
frame change handler.  frame = 47.
playing

handler entered
frame change handler.  frame = 48.
playing

handler entered
frame change handler.  frame = 48.

playback just stopped

4.0 manual frame change output:

handler entered
frame change handler.  frame = 45.

manual frame change

4.0 playback frame change output:

handler entered
frame change handler.  frame = 46.
playing

handler entered
frame change handler.  frame = 47.
playing

handler entered
frame change handler.  frame = 48.
playing


notice that 'playback just stopped' was missed due to the handler not being executed on playback stop.

**System Information** Operating system: Windows-10-10.0.19044-SP0 64 Bits Graphics card: NVIDIA GeForce GTX 1070/PCIe/SSE2 NVIDIA Corporation 4.5.0 NVIDIA 535.98 **Blender Version** Broken: version: 4.0.0 Alpha, branch: main, commit date: 2023-06-19 17:26, hash: `cedec09d0594` Worked: (3.6 below build) ![image](/attachments/d98c5b12-4bab-48f8-8580-2a53cb95d064) Well, sort of intentional, but caused by d8388ef36a1daaf72c10ebbcb4538c99d74c6bd0 **Short description of error** Frame change handler is not being entered when playback ends **Exact steps for others to reproduce the error** 1. Open attached blend file in 3.6 (make sure you're using the same has as me, because later builds of 3.6 also have the bug) 2. go to the script workspace and run the script 3. open the console 4. change the frame manually with the arrow key or mouse 5. take a note of the output 6. press play twice to start and stop playback, aim for 3 or so frames to keep the console output short 7. take a note of the output 8. open blender 4.0 or later build of 3.6 and load the same file 9. repeat steps 2 to 7 10. compare outputs In later builds the handler doesn't output the final message due to the handler not being triggered when playback ends. Manual frame change works the same as it always has. Triggering the handler is necessary on playback stopping as it provides an opportunity to clear variables etc (unless a playback stop handler has been introduced and this new behaviour is intentional?) 3.6 manual frame change output: ``` handler entered frame change handler. frame = 45. manual frame change ``` 3.6 playback output (notice how frame 48 is triggered twice, once during playback and once after stopping): ``` handler entered frame change handler. frame = 46. playing handler entered frame change handler. frame = 47. playing handler entered frame change handler. frame = 48. playing handler entered frame change handler. frame = 48. playback just stopped ``` 4.0 manual frame change output: ``` handler entered frame change handler. frame = 45. manual frame change ``` 4.0 playback frame change output: ``` handler entered frame change handler. frame = 46. playing handler entered frame change handler. frame = 47. playing handler entered frame change handler. frame = 48. playing ``` notice that 'playback just stopped' was missed due to the handler not being executed on playback stop.
Julieta Riley added the
Type
Report
Status
Needs Triage
Priority
Normal
labels 2023-06-21 23:08:07 +02:00
Member

Hi, thanks for the report. Can confirm
Broke between 54e397cc78 - 654cfc06c5
@lichtwerk hi, can you check further?

Hi, thanks for the report. Can confirm Broke between 54e397cc7830 - 654cfc06c591 @lichtwerk hi, can you check further?
Member

Yep, will check

Yep, will check
Member

Well, sort of intentional, but caused by d8388ef36a

CC @JacquesLucke

We could of course (easily) add playback PRE/POST handlers if we want to keep the current situation that avoids the extra depsgraph evaluation when stopping animation playback [can post a PR for this is this is desired].

Well, sort of intentional, but caused by d8388ef36a1daaf72c10ebbcb4538c99d74c6bd0 CC @JacquesLucke We could of course (easily) add playback PRE/POST handlers if we want to keep the current situation that avoids the extra depsgraph evaluation when stopping animation playback [can post a PR for this is this is desired].
Member

PR for such handlers is in #109232

With those, you can then change the script to this:

import bpy

skip_all_frame_change_handlers = False
playing = None

def update_on_frame_change(scene):
    print('\nhandler entered')
    global skip_all_frame_change_handlers
    global playing
    
    if skip_all_frame_change_handlers:
        print('skipping repeat handler execution for same frame')
        return
    
    print(f'frame change handler.  frame = {scene.frame_current}.')
    
    #make sure only enters frame change handler once.
    skip_all_frame_change_handlers = True

    if bpy.context.screen.is_animation_playing and not bpy.context.screen.is_scrubbing:        
        print('playing')
        playing = True
    else:
        #playback just finished or use change frame manually with arrow keys or mouse        
        if playing is None:
            #it's a manual frame change
            print('\nmanual frame change')
        print('\n')
    
    #ensure at the end of each frame the skip variable is set back to None so that the handler wont be skipped on new frames
    skip_all_frame_change_handlers = None

def playback_pre(scene):
    print('\nplayback just started')
    playing = True

def playback_post(scene):
    print('\nplayback just stopped')
    playing = None

    #ensure at the end of each frame the skip variable is set back to None so that the handler wont be skipped on new frames
    skip_all_frame_change_handlers = None



def register_handler():
    if update_on_frame_change not in bpy.app.handlers.frame_change_post:
        bpy.app.handlers.frame_change_post.append(update_on_frame_change)
    if playback_pre not in bpy.app.handlers.playback_pre:
        bpy.app.handlers.playback_pre.append(playback_pre)
    if playback_post not in bpy.app.handlers.playback_post:
        bpy.app.handlers.playback_post.append(playback_post)

def unregister_handler():
    if update_on_frame_change in bpy.app.handlers.frame_change_post:
        bpy.app.handlers.frame_change_post.remove(update_on_frame_change)
    if playback_pre in bpy.app.handlers.playback_pre:
        bpy.app.handlers.playback_pre.remove(playback_pre)        
    if playback_post in bpy.app.handlers.playback_post:
        bpy.app.handlers.playback_post.remove(playback_post)

if __name__ == "__main__":
    register_handler()
PR for such handlers is in #109232 With those, you can then change the script to this: ```Py import bpy skip_all_frame_change_handlers = False playing = None def update_on_frame_change(scene): print('\nhandler entered') global skip_all_frame_change_handlers global playing if skip_all_frame_change_handlers: print('skipping repeat handler execution for same frame') return print(f'frame change handler. frame = {scene.frame_current}.') #make sure only enters frame change handler once. skip_all_frame_change_handlers = True if bpy.context.screen.is_animation_playing and not bpy.context.screen.is_scrubbing: print('playing') playing = True else: #playback just finished or use change frame manually with arrow keys or mouse if playing is None: #it's a manual frame change print('\nmanual frame change') print('\n') #ensure at the end of each frame the skip variable is set back to None so that the handler wont be skipped on new frames skip_all_frame_change_handlers = None def playback_pre(scene): print('\nplayback just started') playing = True def playback_post(scene): print('\nplayback just stopped') playing = None #ensure at the end of each frame the skip variable is set back to None so that the handler wont be skipped on new frames skip_all_frame_change_handlers = None def register_handler(): if update_on_frame_change not in bpy.app.handlers.frame_change_post: bpy.app.handlers.frame_change_post.append(update_on_frame_change) if playback_pre not in bpy.app.handlers.playback_pre: bpy.app.handlers.playback_pre.append(playback_pre) if playback_post not in bpy.app.handlers.playback_post: bpy.app.handlers.playback_post.append(playback_post) def unregister_handler(): if update_on_frame_change in bpy.app.handlers.frame_change_post: bpy.app.handlers.frame_change_post.remove(update_on_frame_change) if playback_pre in bpy.app.handlers.playback_pre: bpy.app.handlers.playback_pre.remove(playback_pre) if playback_post in bpy.app.handlers.playback_post: bpy.app.handlers.playback_post.remove(playback_post) if __name__ == "__main__": register_handler() ```
Author

Thanks @lichtwerk it's great that you've added those additional handlers. Our addon uses version checking to make sure it works for 2.93 to currently available builds, could you confirm what I need to check for below:

if bpy.app.version >= (?,?,?): 

p.s Do you think you could also get your compositor handlers finally committed to master too? And on a related note, we currently have to call render.render() to force the playhead to wait for the compositor to finish processing before moving to the next frame, would it be easy for you to add a 'wait_for_comp' property to the playback operator that can be enabled from your new playback_pre handler? That way we can avoid render.render() in that case.

Thanks @lichtwerk it's great that you've added those additional handlers. Our addon uses version checking to make sure it works for 2.93 to currently available builds, could you confirm what I need to check for below: ``` if bpy.app.version >= (?,?,?): ``` p.s Do you think you could also get your compositor handlers finally committed to master too? And on a related note, we currently have to call render.render() to force the playhead to wait for the compositor to finish processing before moving to the next frame, would it be easy for you to add a 'wait_for_comp' property to the playback operator that can be enabled from your new playback_pre handler? That way we can avoid render.render() in that case.
Member

Well the playback handlers are not committed to yet (these wont go into 3.6 [too late], so will be 4.0 if all goes well -- there is still one approval waiting -- atm, it would be (4, 0, 0)).

Regarding the compositing handlers : these should be in since 3.3?

Regarding 'wait_for_comp' : not sure if this is the best solution (havent thought about this / looked into this to a degree that it would make sense to answer at this point), but maybe it would be possible to use the available callbacks then to implement some kind of "own" playback? (use playback_pre to actually stop playback, advance frames via API -- after compositing finishes -- and so on and so forth, but intercepting spacebar or pressing stop playback buttons would all make this hairy as well... thinking about it further.... assume for now the render.render() is still the way to go...)

Well the playback handlers are not committed to yet (these wont go into 3.6 [too late], so will be 4.0 if all goes well -- there is still one approval waiting -- atm, it would be `(4, 0, 0)`). Regarding the compositing handlers : these should be in since 3.3? Regarding 'wait_for_comp' : not sure if this is the best solution (havent thought about this / looked into this to a degree that it would make sense to answer at this point), but maybe it would be possible to use the available callbacks then to implement some kind of "own" playback? (use playback_pre to actually stop playback, advance frames via API -- after compositing finishes -- and so on and so forth, but intercepting spacebar or pressing stop playback buttons would all make this hairy as well... thinking about it further.... assume for now the render.render() is still the way to go...)
Author

@lichtwerk that's great thanks. Will I be notified automatically here when it's committed?

Oh wow, OK I hadn't realised it was in master yet, but I just read the commit and noticed I've already been using the 'bpy.app.is_job_running('COMPOSITE')' part of that commit, so yes it must be there thanks.

I did consider stopping playback and advancing frame by frame, but I wasn't sure what that might break, as I recall some things don't get updated the same when advancing frame by frame compared to playback. For example we used to hold down the right arrow instead of playback to get much faster fps if the scene contained rigged characters (presumably something wasn't getting updated (although no difference was perceivable in the viewport oddly)).

@lichtwerk that's great thanks. Will I be notified automatically here when it's committed? Oh wow, OK I hadn't realised it was in master yet, but I just read the commit and noticed I've already been using the 'bpy.app.is_job_running('COMPOSITE')' part of that commit, so yes it must be there thanks. I did consider stopping playback and advancing frame by frame, but I wasn't sure what that might break, as I recall some things don't get updated the same when advancing frame by frame compared to playback. For example we used to hold down the right arrow instead of playback to get much faster fps if the scene contained rigged characters (presumably something wasn't getting updated (although no difference was perceivable in the viewport oddly)).
Member

@Julieta-Riley : for the playback handlers : best subscribe in #109232 to make sure you get notified (since that is probably not regarded the "fix" for this issue here)

@Julieta-Riley : for the playback handlers : best subscribe in https://projects.blender.org/blender/blender/pulls/109232 to make sure you get notified (since that is probably not regarded the "fix" for this issue here)
Author

Hi @lichtwerk thanks. Could you confirm that which of the daily builds that commit will affect? Still just 4.0 upwards? Is there a way for me to tell in future which daily builds are affected by a particular commit by looking at the commit page?

Hi @lichtwerk thanks. Could you confirm that which of the daily builds that commit will affect? Still just 4.0 upwards? Is there a way for me to tell in future which daily builds are affected by a particular commit by looking at the commit page?
Member

Usually the day after (the daily builds run once a day [around midnight CET]-- if not kicked manually which sometimes happens, too)
And yep, only 4.0...
Upcoming 3.6 will be LTS, we could try checking if this could/should be backported if there are no other ways of fixing the issues you have for 3.6

Usually the day after (the daily builds run once a day [around midnight CET]-- if not kicked manually which sometimes happens, too) And yep, only 4.0... Upcoming 3.6 will be LTS, we could try checking if this could/should be backported if there are no other ways of fixing the issues you have for 3.6
Author

Thanks, yes it would be great if it could be backported to the first build that jacque'ss update broke the current functionality. The reason is our addon has one version which is compatible from 2.93 all the way to 3.6 currently, so it would be great if we could just add a check such as:

def register_handler():
    if update_on_frame_change not in bpy.app.handlers.frame_change_post:
        bpy.app.handlers.frame_change_post.append(update_on_frame_change)
    if bpy.app.version >= (3,6,?):
        if playback_pre not in bpy.app.handlers.playback_pre:
            bpy.app.handlers.playback_pre.append(playback_pre)
        if playback_post not in bpy.app.handlers.playback_post:
            bpy.app.handlers.playback_post.append(playback_post)
      
Thanks, yes it would be great if it could be backported to the first build that jacque'ss update broke the current functionality. The reason is our addon has one version which is compatible from 2.93 all the way to 3.6 currently, so it would be great if we could just add a check such as: ```py def register_handler(): if update_on_frame_change not in bpy.app.handlers.frame_change_post: bpy.app.handlers.frame_change_post.append(update_on_frame_change) if bpy.app.version >= (3,6,?): if playback_pre not in bpy.app.handlers.playback_pre: bpy.app.handlers.playback_pre.append(playback_pre) if playback_post not in bpy.app.handlers.playback_post: bpy.app.handlers.playback_post.append(playback_post) ````
Member

I think #109232 is the right solution. I don't see justification for running the frame-change handler if the frame hasn't actually changed, just because playback stopped. I consider that old behavior to be a bug.

I wouldn't mind if we backport the playback_pre and playback_post handlers to 3.6. The change seems simple enough and shouldn't break anything else.

I think #109232 is the right solution. I don't see justification for running the frame-change handler if the frame hasn't actually changed, just because playback stopped. I consider that old behavior to be a bug. I wouldn't mind if we backport the `playback_pre` and `playback_post` handlers to 3.6. The change seems simple enough and shouldn't break anything else.
Member

I have added f8981b6492 to #109399 now.

That would leave 3.6.0 "broken" until 3.6.1 comes out.
If we are all fine with this, I think it we could close this report, but will leave up to @JacquesLucke to do so

I have added f8981b6492 to #109399 now. That would leave 3.6.0 "broken" until 3.6.1 comes out. If we are all fine with this, I think it we could close this report, but will leave up to @JacquesLucke to do so
Member

Thanks.
3.6.0 will be broken at least until 3.6.1 comes out either way, so sounds good to me.

Thanks. 3.6.0 will be broken at least until 3.6.1 comes out either way, so sounds good to me.
Blender Bot added
Status
Archived
and removed
Status
Confirmed
labels 2023-06-28 09:47:10 +02:00

👍 thanks for handling this folks!

👍 thanks for handling this folks!
Author

Thanks. I understand the 3.6 version available on the main blender website won't be updated, but will the 3.6.0 daily builds have this new handler?

Thanks. I understand the 3.6 version available on the main blender website won't be updated, but will the 3.6.0 daily builds have this new handler?
Author

@lichtwerk is there any chance this could also be added to the 3.6.0 daily builds? We have a few thousand users, and it would be great if we didn't have to tell them we can't support 3.6.0

@lichtwerk is there any chance this could also be added to the 3.6.0 daily builds? We have a few thousand users, and it would be great if we didn't have to tell them we can't support 3.6.0

Instead of checking an explicit version of Blender, you can also do 'feature probing':

if hasattr(bpy.app.handlers, 'playback_pre'):
    if playback_pre not in bpy.app.handlers.playback_pre:
        bpy.app.handlers.playback_pre.append(playback_pre)
    if playback_post not in bpy.app.handlers.playback_post:
        bpy.app.handlers.playback_post.append(playback_post)

That way the code works regardless of which exact version the feature lands in.

Instead of checking an explicit version of Blender, you can also do 'feature probing': ```python if hasattr(bpy.app.handlers, 'playback_pre'): if playback_pre not in bpy.app.handlers.playback_pre: bpy.app.handlers.playback_pre.append(playback_pre) if playback_post not in bpy.app.handlers.playback_post: bpy.app.handlers.playback_post.append(playback_post) ``` That way the code works regardless of which exact version the feature lands in.
Author

@dr.sybren yep, that's what we've done, but the issue is that in some 3.6.0 builds 'bpy.app.handlers.frame_change_post' is no longer entered when playback stops, and also the new 'animation_playback_post' is not available (it's only in 3.6.1 onwards).

This means there's no way to handle playback stopping via handlers in 3.6.0.

My boss has made a workaround with timers for now, he says it's a bit messy, but it works. We were a little surprised that this change to the API was made with no warning and no alternative provided via the API.

@dr.sybren yep, that's what we've done, but the issue is that in some 3.6.0 builds 'bpy.app.handlers.frame_change_post' is no longer entered when playback stops, and also the new 'animation_playback_post' is not available (it's only in 3.6.1 onwards). This means there's no way to handle playback stopping via handlers in 3.6.0. My boss has made a workaround with timers for now, he says it's a bit messy, but it works. We were a little surprised that this change to the API was made with no warning and no alternative provided via the API.
Member

I can do backports to 3.6 LTS tomorrow (meaning there will be 3.6.1 release candidate builds on builder.blender.org the day after tomorrow). 3.6.1 official will probably be out July 18th.

I can do backports to 3.6 LTS tomorrow (meaning there will be 3.6.1 release candidate builds on builder.blender.org the day after tomorrow). 3.6.1 official will probably be out July 18th.
Member

I can do backports to 3.6 LTS tomorrow (meaning there will be 3.6.1 release candidate builds on builder.blender.org the day after tomorrow). 3.6.1 official will probably be out July 18th.

Done

> I can do backports to 3.6 LTS tomorrow (meaning there will be 3.6.1 release candidate builds on builder.blender.org the day after tomorrow). 3.6.1 official will probably be out July 18th. Done
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
5 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#109218
No description provided.