Skip to content
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

SpecApi #3281

Merged
merged 62 commits into from Oct 28, 2023
Merged

SpecApi #3281

merged 62 commits into from Oct 28, 2023

Conversation

SimonDanisch
Copy link
Member

@SimonDanisch SimonDanisch commented Oct 6, 2023

Similar to PlotSpec, just for having Observable{Figure}

  • Fixes lots of memory leaks from delete!(block) and delete!(ax/scene, plot), especially for WGLMakie
  • Also fixes large performance bugs with creating plots (e.g. bbox(::BezierPath) is really expensive and was run many times for markersize=large_array. See Poor performance of scatter with non-scalar markersize #3307
  • Adds docs & tests for PlotSpec & BlockSpec
  • Fixes previous compile time regressions and found a few improvements while searching for regressions
  • Refactors plot creation API a bit, to make things like convert_arguments(...)::FigureSpec easier to integrate and removes double convert_arguments from previous implementation. This should also make Unit support for Axes & Recipes, a.k.a axis converts #3226 easier!
  • Moves cycler from Axis to Scene, so that Recipes and PlotSpec work with cycling, and cycling is applied more consistently
  • Moves update tracking for the GLMakie on_demand_renderloop to conversion pipeline, so that much less observables for tracking are created (more than halfs the size of objects created for each plot)
  • Lays the groundwork for a very lightweight and interactive AlgebraOfGraphics

Examples from the docs:

using GLMakie, DelimitedFiles, FileIO
import Makie.SpecApi as S
GLMakie.activate!() # hide
volcano = readdlm(Makie.assetpath("volcano.csv"), ',', Float64)
brain = load(assetpath("brain.stl"))
r = LinRange(-1, 1, 100)
cube = [(x .^ 2 + y .^ 2 + z .^ 2) for x = r, y = r, z = r]

ax1 = S.Axis(; title="Axis 1", plots=map(x -> S.density(x * randn(200) .+ 3x, color=:y), 1:5))
ax2 = S.Axis(; title="Axis 2", plots=[S.contourf(volcano; colormap=:inferno)])
ax3 = S.Axis3(; title="Axis3", plots=[S.mesh(brain, colormap=:Spectral, color=[tri[1][2] for tri in brain for i in 1:3])])
ax4 = S.Axis3(; plots=[S.contour(cube, alpha=0.5)])


spec_array = S.Figure([ax1, ax2]);
spec_matrix = S.Figure([ax1 ax2; ax3 ax4]);
f = Figure(; resolution=(1000, 500))
plot(f[1, 1], spec_array)
plot(f[1, 2], spec_matrix)
f
image
import Makie.SpecApi as S
struct PlotGrid
    nplots::Tuple{Int,Int}
end

Makie.used_attributes(::Type{<:AbstractPlot}, ::PlotGrid) = (:color,)
function Makie.convert_arguments(::Type{<:AbstractPlot}, obj::PlotGrid; color=:black)
    f = S.Figure(; fontsize=30)
    for i in 1:obj.nplots[1]
        for j in 1:obj.nplots[2]
            ax = S.Axis(f[i, j])
            S.lines!(ax, cumsum(randn(1000)); color=color)
        end
    end
    return f
end

f = Figure()
plot(f[1, 1], PlotGrid((1, 1)); color=Cycled(1))
plot(f[1, 2], PlotGrid((2, 2)); color=Cycled(2))
f
image
struct LineScatter
    show_lines::Bool
    show_scatter::Bool
    kw::Dict{Symbol,Any}
end
LineScatter(lines, scatter; kw...) = LineScatter(lines, scatter, Dict{Symbol,Any}(kw))

function Makie.convert_arguments(::Type{<:AbstractPlot}, obj::LineScatter, data...)
    plots = PlotSpec[]
    if obj.show_lines
        push!(plots, S.lines(data...; obj.kw...))
    end
    if obj.show_scatter
        push!(plots, S.scatter(data...; obj.kw...))
    end
    return plots
end

f = Figure()
ax = Axis(f[1, 1])
# Can be plotted into Axis, since it doesn't create its own axes like FigureSpec
plot!(ax, LineScatter(true, true; markersize=20, color=1:4), 1:4)
plot!(ax, LineScatter(true, false; color=:darkcyan, linewidth=3), 2:4)
f
image
struct MySimulation
    plottype::Symbol
    arguments::AbstractVector
end

function Makie.convert_arguments(::Type{<:AbstractPlot}, sim::MySimulation)
    return map(enumerate(sim.arguments)) do (i, data)
        return PlotSpec(sim.plottype, data)
    end
end
f = Figure()
s = Slider(f[1, 1], range=1:10)
m = Menu(f[1, 2], options=[:scatter, :lines, :barplot])
sim = lift(s.value, m.selection) do n_plots, p
    args = [cumsum(randn(100)) for i in 1:n_plots]
    return MySimulation(p, args)
end

ax, pl = plot(f[2, :], sim)
on(sim; priority=-1) do s
    autolimits!(ax)
end
tight_ticklabel_spacing!(ax)
f

blockspec2

@SimonDanisch SimonDanisch changed the base branch from master to sd/beta-20 October 6, 2023 09:06
@MakieBot
Copy link
Collaborator

MakieBot commented Oct 6, 2023

Compile Times benchmark

Note, 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(display(fig))
using create display create display
GLMakie 4.50s (4.47, 4.55) 0.03+- 397.49ms (392.92, 402.60) 4.31+- 776.08ms (760.73, 831.40) 25.81+- 9.85ms (9.78, 9.93) 0.06+- 115.30ms (113.75, 117.44) 1.18+-
master 4.56s (4.53, 4.58) 0.02+- 481.60ms (474.39, 486.31) 4.37+- 1.36s (1.35, 1.37) 0.01+- 9.46ms (9.42, 9.51) 0.03+- 153.26ms (152.07, 155.53) 1.11+-
evaluation -1.26%, -0.06s faster ✓ (-2.51d, 0.00p, 0.02std) -21.16%, -84.12ms faster✅ (-19.38d, 0.00p, 4.34std) -75.13%, -583.06ms faster✅ (-30.78d, 0.00p, 16.49std) +3.95%, 0.39ms slower X (7.81d, 0.00p, 0.05std) -32.93%, -37.97ms faster✅ (-33.11d, 0.00p, 1.15std)
CairoMakie 3.94s (3.91, 3.98) 0.02+- 417.68ms (413.59, 422.37) 2.94+- 388.51ms (382.58, 392.85) 3.53+- 9.99ms (9.89, 10.13) 0.09+- 5.03ms (4.75, 5.36) 0.20+-
master 3.90s (3.88, 3.92) 0.02+- 495.40ms (488.83, 503.91) 5.61+- 522.48ms (519.46, 526.20) 2.34+- 9.67ms (9.62, 9.78) 0.06+- 4.79ms (4.51, 5.12) 0.21+-
evaluation +0.82%, 0.03s slower X (1.58d, 0.01p, 0.02std) -18.61%, -77.72ms faster✅ (-17.35d, 0.00p, 4.28std) -34.48%, -133.97ms faster✅ (-44.75d, 0.00p, 2.93std) +3.20%, 0.32ms slower X (4.23d, 0.00p, 0.07std) +4.79%, 0.24ms invariant (1.16d, 0.05p, 0.21std)
WGLMakie 4.74s (4.72, 4.79) 0.02+- 683.12ms (666.68, 739.20) 25.29+- 10.37s (10.17, 10.52) 0.15+- 14.31ms (12.50, 15.20) 0.97+- 823.66ms (790.14, 863.70) 26.24+-
master 4.82s (4.78, 4.85) 0.03+- 1.77s (1.75, 1.79) 0.01+- 11.07s (10.95, 11.19) 0.10+- 13.41ms (12.85, 13.76) 0.32+- 809.85ms (754.45, 841.38) 27.61+-
evaluation -1.55%, -0.07s faster ✓ (-2.95d, 0.00p, 0.02std) -158.46%, -1082.5ms faster✅ (-53.38d, 0.00p, 19.40std) -6.72%, -0.7s faster✅ (-5.63d, 0.00p, 0.12std) +6.31%, 0.9ms noisy🤷‍♀️ (1.25d, 0.05p, 0.65std) +1.68%, 13.81ms invariant (0.51d, 0.36p, 26.92std)

Comment on lines +205 to 214
function lift_convert(key, value, plot, screen)
return lift_convert_inner(value, Key{key}(), Key{Makie.plotkey(plot)}(), plot, screen)
end

function lift_convert_inner(value, key, plot_key, plot)
function lift_convert_inner(value, key, plot_key, plot, screen)
return lift(plot, value) do value
screen.requires_update = true
return convert_attribute(value, key, plot_key)
end
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can tell all plot update tracking comes from this now? In that case I think it misses updates to camera matrices and lighting, because those get added to a render object from scene data. Though those should probably also be handled in screen.jl?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, didn't notice that, since enough is usually going on that it seems to be enough for most plots to rely on those updates...
Yes, I think we should add that here:
https://github.com/MakieOrg/Makie.jl/blob/master/GLMakie/src/screen.jl#L438

@SimonDanisch
Copy link
Member Author

SimonDanisch commented Oct 27, 2023

A few Benchmarks:

using GLMakie, WGLMakie

function test(ax, last_pl, n=500)
    for i in 1:n
        delete!(ax, last_pl)
        last_pl = scatter!(ax, rand(1000); color=rand(1000), markersize=10, colormap=:viridis)
    end
end

f, ax, pl = scatter(rand(1000); color=rand(1000), markersize=10, colormap=:viridis)
fsize = Base.summarysize(f) / 10^6
@time test(ax, pl, 500)

function test2(f, ax, n=100)
    for i in 1:n
        delete!(ax)
        ax, pl = scatter(f[1, 1], rand(1000); color=rand(1000), markersize=10, colormap=:viridis)
        yield()
    end
end
f, ax, pl = scatter(rand(1000); color=rand(1000), markersize=10, colormap=:viridis)
fsize = Base.summarysize(f) / 10^6
@time test2(f, ax)
fsize2 = Base.summarysize(f) / 10^6

PR

test1

GLMakie:0.6s, 0.475mb, 0.475mb
WGLMakie:3.5s, 7.44mb, 7.45mb

test2

GLMakie: 2.4s, 8.81mb, 12.1mb
WGLMakie:9.0s, 8.3mb, 9.1mb

Tagged

test1

GLMakie:2.5s, 9.5mb, 20.4mb
WGLMakie:3.5s, 7.2mb, 17.7mb

test2

GLMakie: 3.37s, 19mb, 37mb
WGLMakie:9.3s, 7.5mb, 39mb

@SimonDanisch SimonDanisch merged commit f77e9e4 into sd/beta-20 Oct 28, 2023
15 checks passed
@SimonDanisch SimonDanisch deleted the sd/blockspec branch October 28, 2023 14:20
@ffreyer ffreyer mentioned this pull request Oct 28, 2023
16 tasks
SimonDanisch added a commit that referenced this pull request Nov 17, 2023
Continues #2831 !
Still needs to check, if I rebased correctly and didn't incorrectly
apply some of the changes!

## Merged PRs
- #2598
- #2746
- #2346
- #2544
- #3082
- #2868
- #3062
- #3106
- #3281
- #3246

## TODOS

- [x] fix flaky test `@test GLMakie.window_size(screen.glscreen) ==
scaled(screen, (W, H))`
- [x] Merge axis type inferences from #2220 
- [x] Test on different resolution screens, IJulia, Pluto, VSCode,
Windowed
- [x] rebase to only have merge commits from the PRs 
- [x] investigate unexpected speed ups
- [x] reset camera settings from tests
- [ ] check doc image generation
- [x] rethink default near/far in Camera3D (compatability with OIT)
- [x] merge #3246
- [x] fix WGLMakie issues/tests:
- [x] fix line depth issues (see tests: ~~hexbin colorrange~~ (not new),
LaTeXStrings in Axis3, Axis3 axis reversal)
  - [x] fix lighting of surface with nan points (fixed in #3246)
- ~~volume/3D contour artifacts (see 3D Contour with 2D contour
slices)~~ not new
  - ~~artifacting in "colorscale (lines)"~~ not new
- [x] GLMakie:
  - [x] slight outline in "scatter image markers" test
  - ~~clipping/z-fighting in "volume translated"~~ not new
- [x] CairoMakie:
  -  ~~Artfiacting in `colorscale (lines)"~~ not new
  - ~~markersize in "scatter rotations" changed?~~ not new
  - ~~color change in "colorscale (poly)"~~ not new
  - ~~transparency/render order of "OldAxis + Surface"~~ not new
  - ~~render order in "Merged color mesh"~~ not new
  - ~~render order of "Surface + wireframe + contour"~~ not new
- [x] Check "SpecApi in convert_arguments" (colors swapped?)


## Fixes the following errors

- fixes #2721 via #2746
- fixes #1600 via #2746
- fixes #1236 via #2746
- fixes MakieOrg/GeoMakie.jl#133 via #2598
- closes #2522
- closes #3239 via #3246
- fixes #3238 via #3246
- fixes #2985 via #3246
- fixes #3307 via #3281
@SimonDanisch SimonDanisch changed the title Blockspec SpecApi Nov 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants