Refactor wgpu.gui for scheduling
Refs
- This finally addresses the main points in wgpu for https://github.com/pygfx/pygfx/issues/495.
- Closes #355
- closes #471
Summary
Canvas
- Remove
WgpuAutoGui, since its no simply a part ofWgpuCanvasBase - Implementation of
WgpuCanvasBaseis cleaner because it moves events and scheduling out.
Events
- Event handling logic is moved into a separate class.
- The event handling logic can merge events by itself (Canvas implementations don't have to do this).
- Events are queued until they are explicitly flushed (by the scheduling mechanics).
- EventType enum!
Scheduling
- The
WgpuCanvasBaseimplements a scheduling mechanic (Canvas implementations no longer have to do this). - Multiple update modes are implemented: fastest, continuous, ondemand, manual.
- The
max_fpsapplies (only) to continuous and ondemand mode. - New
force_draw()method for a blocking render call. Note that this still passes through the paint event on Qt, to avoid flicker.
Gui modules
-
run()andcall_later()functions are replaced with aloopobject that has both as a method.
Design decisions
- We assume that users use the event-loop corresponding to the canvas, and don't use two GUI systems at the same time (e.g. Qt with glfw). We can accommodate for this, but it will complicate the code and feels like a bad idea in general.
- Do we keep
canvas.add_event_handler()or do we switch tocanvas.events.add_handler()orcanvas.events.connect()?- I like
canvas.add_event_handler()as it feels flat. That waycanvas._eventsstays an implementation detail.
- I like
- Not sure yet about the
canvas.loopprop. - How to allow users to control scheduling, like setting mode or max_fps?
- Proposal: use
canvas.scheduler.max_fps,canvas.scheduler.mode, etc.
- Proposal: use
API changes
-
canvas.add_event_handler()? - New
canvas.force_draw(). - New
canvas.loopproperty? - Replaced
from wgpu.gui.xx import WgpuCanvas, runwithfrom wgpu.gui.xx import WgpuCanvas, loop - The glfw canvas is more friendly on your laptop battery (thanks to a proper event loop).
- The
WgpuAutoGuimixin class is removed.
Tasks
- [x] Update base canvas.
- [x] Take threading into account.
- [x] Update glfw gui
- [x] Update qt gui
- [ ] Update wx gui
- [ ] Update jupyter gui
- [x] Update offscreen gui
- [x] Don't draw when minimized.
- [x] Update docs
- [x] Show/test force_draw in an example.
- [x] Can we make 'ondemand' with calling
request_draw()in the draw function equal 'continuous'.
After this PR
- Handle sigint.
- Animate events.
- Allow
draw_functo be an asyn function. - Can we move
present()out ofCanvasContext()? - Register the draw function as a draw event handler instead of with
request_draw()? - Allow requesting a draw by setting a field in the event object?
- Tracking statistics (track time spent on different things). Plus visualizing them.
I've been working on this for more than a week. This is hard, but it's beginning to take shape now.
This implements (amongst other things) the ideas presented in http://gameprogrammingpatterns.com/game-loop.html
As robert says:
In addition to that, we also gave multiple different GUI systems and event loops.
Do you own the game loop, or does the platform?
In our case that actually depends 😅 The scheduling code assumes it runs on an externally provided eventloop. For glfw we do provide the loop, but the scheduling logic does not know this.
How to allow users to control scheduling, like setting mode or max_fps?
- Proposal: use
canvas.scheduler.max_fps,canvas.scheduler.mode, etc.
Does scheduler need to be a prop on canvas? Can it live on its own?
Does scheduler need to be a prop on canvas? Can it live on its own?
Yeah, I'd rather hide it. So you mean the prop can be something like canvas.update_mode?
Does scheduler need to be a prop on canvas? Can it live on its own?
Yeah, I'd rather hide it. So you mean the prop can be something like
canvas.update_mode?
I meant, it could be just scheduler, instead of canvas.scheduler :)
Does scheduler need to be a prop on canvas? Can it live on its own?
Yeah, I'd rather hide it. So you mean the prop can be something like
canvas.update_mode?I meant, it could be just
scheduler, instead ofcanvas.scheduler:)
There's no point for a scheduler without a canvas. It's really a little helper that I preferably hide from the user.
Proposal: use canvas.scheduler.max_fps, canvas.scheduler.mode, etc.
These args can be passed when the canvas is instantiated. For now, we can leave it at that (no changing these at runtime).
Ok, this is almost done. All backends are working and tested on different platforms. I'm going to do a round of self-review and cleanup.
I think we will never merge this here, but in the new repo: #627
This is ready for review. It does not actually break compatibility so much. You can run pygfx examples with it.
When this pr is approved, I'll re-recreate the pr at rendercanvas so we can apply it there.
Merged in rendercanvas.