-
-
Notifications
You must be signed in to change notification settings - Fork 314
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add tick event #3948
Add tick event #3948
Conversation
Compile Times benchmarkNote, that these numbers may fluctuate on the CI servers, so take them with a grain of salt. All benchmark results are based on the mean time and negative percent mean faster than the base branch. Note, that GLMakie + WGLMakie run on an emulated GPU, so the runtime benchmark is much slower. Results are from running: using_time = @ctime using Backend
# Compile time
create_time = @ctime fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
display_time = @ctime Makie.colorbuffer(display(fig))
# Runtime
create_time = @benchmark fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
display_time = @benchmark Makie.colorbuffer(fig)
|
Closing #3163 would need using this to replace all time-dependent animations in Makie. I think that might just be the toggle switch animation, the textbox cursor animation and the axis reset timer after scrolling. |
Example from #3299 with this: begin
fig = Figure()
axs = [Axis(fig[i, j]) for i in 1:2, j in 1:2]
xlims = [[-1.2, 0], [0, 1.2]]
ylims = [[0, 1.2], [-1.2, 0]]
wavelengths = [
5 8
11 14
]
ω = 0.3
θs = range(0, 2π ; length = 1000)
for i in 1:2
for j in 1:2
ax = axs[i, j]
xlims!(ax, xlims[j])
ylims!(ax, ylims[i])
w = wavelengths[i, j]
ρs = @lift [sin(w * (θ + ω * $(events(fig).tick).count * 0.033)) for θ in θs]
xx = @lift $ρs .* cos.(θs)
yy = @lift $ρs .* sin.(θs)
lines!(ax, xx, yy)
end
end
fig
end
record(fig, "flower.mp4", 1:240) do i
end It's a little awkward that you have to use |
Why not both if both can be useful. For the animations in Edit: Ah I see, the animations would then probably look wrong in live mode. Maybe a keyword to |
I moved colorbuffer ticks to Makie record/save so they can synchronize to record framerates now. I'm not entirely sure if they are in the correct functions, but the tests I've written are passing at least. The ticks produced with WGLMakie are a bit of a mess though. On screen initialization there tends to be a burst of them, and during record the normal render loop is also running, producing ticks. Idk if there is anything we can do about that, maybe @SimonDanisch can have a look? |
Does this not work with enums? Also "Normals of a Cat" is failing due to changes in axis limits. Not idea where that is coming from. I don't see it locally, not even as a temporary state of the axis |
Normals of a cat has been flaky on CI for me too. Haven't yet tried to figure out what the problem is. |
docs/src/explanations/events.md
Outdated
|
||
For an interactive figure this will produce an animation synchronized with real time. Within `record` the tick times match up the set `framerate` such that the animation in the produced video matches up with real time. | ||
|
||
The only exception here is WGLMakie which currently runs the render loop during recording. Because of that `RegularRenderTick` and `OneTimeRenderTick` will mix during `record`, with the former being based on real time and the latter based on video time. If you do not restrict to `OneTimeRenderTick` here the resulting video will run faster than real time with `tick.delta_time` and may jump with `tick.time` and `tick.count`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could probably avoid this by eating up the regular render ticks during recording
cb = on(events(fig).tick, priority = typemax(Int)) do tick
return Consume(tick.state != Makie.OneTimeRenderTick)
end
# record ...
off(cb)
but it's not clear to me how to do that safely. My first thought would be to have the creation of VideoStream
start this and save
end this, but that doesn't seem save to me. I.e. VideoStream being created doesn't necessarily mean only recording takes place and you could save another video after the first save...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added that to record
and suggested doing this for the lower level functions in the docs for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I changed my mind and just attached the filtering to VideoStream and save/a finalizer. Mostly because otherwise the tick event order is a bit weird.
In the previous version the tick updates triggered just before colorbuffer()
. So the order of operations was:
# record(foo, ...)
foo()
tick_update()
colorbuffer()
You'd still get updates coming from on(..., tick)
before the render, but you couldn't use events(fig).tick
to get the current frames time or count in foo()
. I change things around a bit so that record
effectively becomes:
tick_update()
foo()
colorbuffer()
so you can. This makes the filtering more important though, because the update for tick N actually happens at the end of frame N-1 in the recordframe!()
call. (The first tick happens on VideoStream creation)
I also adjusted ticks so that the first tick is at count and time = 0.
With the current state of this pr you can set up an animation like this for example: angle = Observable(0.0)
pos = map(angle) do phi0
[Point2f(cos(phi + phi0), sin(phi + phi0)) for phi in range(0, 2pi, 101)[1:end-1]]
end
f, a, p = scatter(pos, color = 1:100, strokewidth = 1, strokecolor = :black)
scatter!(pos[][1], marker = Circle, markersize = 12, color = :transparent, strokewidth = 2, strokecolor = :red)
on(events(f).tick) do tick
angle[] = 0.5 * pi * tick.time
end
f and you will see 1 revolution every 4s independently of (set and real) frame rate. You can record this animation by just calling record: record(f, "test.mp4", 1:120, framerate = 30) do _
nothing
end This will generate a new set of ticks starting at count = t = 0 with time steps of If you then redisplay the figure, the interactive ticks will again be based on the renderloop. The first tick may have a long |
…into ff/render_tick
Just noting - the way record ticks work might be incompatible with #3883 |
Description
This pr adds
events.tick
which currently contains:This is more verbose than just a delta time mostly because of how
screen.render_tick
is used in GLMakie. Updates to it are grouped with event polling since we have mouse position updates depend on it. So it triggers whencolorbuffer
(i.e. save, record)Depending on what causes the tick update a different state flag is passed.event_delta_time
is always updated based on the time since the last tick.frame_delta_time
is always based on the last renderloop event (paused/skipped/drawn) or colorbuffer event (i.e. a draw for save or record) and set to 0 when the current event is not one of those. So it's the time between the last and current frame, even if plot insertions gets between frames.Currently the pr should be pretty functional, but I think we need to discuss what we want ticks to be on the user side.
(tick.count - first_count) / fps
to get the relevant delta_time.)Supersedes #3299
Related: #3163
Type of change
Checklist