-
-
Notifications
You must be signed in to change notification settings - Fork 312
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 linecaps (GLMakie, CairoMakie) #2536
Conversation
@@ -10,6 +10,7 @@ in vec4 f_color; | |||
in vec2 f_uv; | |||
in float f_thickness; | |||
flat in uvec2 f_id; | |||
flat in int f_type; // TODO bad for performance |
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.
Given the comment in the scatter shader
uniform int shape; // shape is a uniform for now. Making them a in && using them for control flow is expected to kill performance |
I assume we should find a way around this?
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.
Did some benchmarking with
let
GLMakie.closeall()
ps = [2 * rand(Point2f) .- 1 for _ in 1:2000]
function myrenderloop(screen)
yield()
GLMakie.GLFW.SwapInterval(0)
GLMakie.pollevents(screen)
GLMakie.render_frame(screen)
GLMakie.GLFW.SwapBuffers(GLMakie.to_native(screen))
yield()
@time for _ in 1:10_000
GLMakie.pollevents(screen)
GLMakie.render_frame(screen)
GLMakie.GLFW.SwapBuffers(GLMakie.to_native(screen))
end
while GLMakie.isopen(screen) && !screen.stop_renderloop
GLMakie.pollevents(screen)
sleep(0.1)
yield()
end
return
end
scene = Scene()
linesegments!(scene, ps, linewidth = 10)
screen = GLMakie.Screen(renderloop = myrenderloop)
display(screen, scene)
end
I get the same ~5% CI got for this branch vs master.
I tried hacking together a two pass version which does the old lines in the first pass and caps in the second pass. This way we can swap out the flat in int f_type
for uniform int linecap
and uniform int render_pass
. This ends up being about 10% slower than master though, doing both passes. Doing just the line pass is 3-4% slower.
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 think that's ok for linecaps! Can we optionally disable it and get the 5% back for high perf use cases? We could also think about having a different recipe for that though 🤷 I think the default should make good lines, and performance should be the special case!
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.
Reducing the number of options in the fragment shader (5e206a9) helped a bit ... I think. I'm never sure with gpu benchmarking. When I tested it I got a bunch of benchmarks that were ~2% slower than master and then a bunch up to 5% slower 🤷
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 ran them again without background processes and I'm getting ~2% now (812ff3f)
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(display(fig))
|
@@ -269,7 +270,7 @@ function draw_atomic(screen::Screen, scene::Scene, @nospecialize(x::Lines)) | |||
data[:pattern] = ls | |||
else | |||
linewidth = gl_attributes[:thickness] | |||
data[:pattern] = ls .* (to_value(linewidth) * 0.25) | |||
data[:pattern] = ls * _mean(to_value(linewidth)) |
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 took this as somewhat of a shortcut for now, because linestyle
needs some cleanup anyway.
Example with variable linewidth:
GLMakie.closeall()
widths = [57.37044620677474, 27.847264587380923, 56.663525479541775, 47.302310563997985, 56.36164168328704, 43.014590186214285, 58.832167079498525, 27.208798997411307, 17.12575358453462, 45.95171757911098, 26.771344098755126, 35.85649823581235, 53.08232474276624, 27.217846025535966, 43.98443255191129, 37.389819316502695, 18.07005596531466, 59.569472267387056, 50.919270815270814, 36.74914882840677, 53.08834081120422, 45.44455789568474, 40.147375157549135, 22.166337026901253, 50.53203686576642, 15.238811525483367, 12.780634172815944, 12.980713770063876, 28.175981476856755, 37.542112019663485]
ys = [0.3672275443344566, 0.13769475910008522, 0.22445059716611304, 0.5883310148108587, 0.7893622240334566, 0.3129809823712394, 0.953606981656488, 0.2770419071274035, 0.3048558242752848, 0.6782285082882588, 0.5447836889282002, 0.19905515156197973, 0.19770277191975638, 0.537590947032379, 0.28027667604204765, 0.8287252657638743, 0.9336781225747905, 0.1371941303349402, 0.5973028266375866, 0.3561025405662749, 0.43500848119792546, 0.36410392908891054, 0.9117308440489152, 0.8008057243055526, 0.2349037873519133, 0.21294938231466431, 0.8334978136186971, 0.5097696792342837, 0.21488349759400982, 0.3434422610208452]
fig = Figure()
ax = Axis(fig[1, 1])
# p = linesegments!(ax, ys, linewidth = widths./10, linecap = 4, linecap_length = widths./10, color = :red)
# p = linesegments!(ax, ys, linewidth = widths./10, linecap = 4, linecap_length = widths./10, linestyle = :dot)
p = lines!(ax, ys, linewidth = widths./10, linecap = 0, linecap_length = widths, color = :red)
p = lines!(ax, ys, linewidth = widths./10, linecap = 0, linecap_length = widths, linestyle = :dash)
screen = display(GLMakie.Screen(), fig)
and pixel perfect results with constant linewidth (tested explicitly for linewidth = 50 on a straight line with lines and linesegments looks the same).
There is however some artifacting with :dash
that got more frequent but isn't new. From master:
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.
Oh right, I also changed the scaling with linewidth in lines!
(not here but in the shader) which makes :dash
shorter notably at large linestyles. It's now 3lw on, 3lw off like the pattern requests. master has weird scaling, maybe quadratic? For linewidth 50, it seems to be about 40.1lw on, 40.1lw off. For linewidth 10 about 10.5lw on 10.5lw off
lines([0, 0], linewidth = 50, linestyle = :dash)
Missing reference imagesFound 1 new images without existing references. |
Missing reference imagesFound 1 new images without existing references. |
Missing reference imagesFound 1 new images without existing references. |
https://discourse.julialang.org/t/y-axis-in-cairomakie-jl-not-vertical/93165 made me realize that a pixel offset attribute for lines is probably less niche than I first thought, so I'm reworking @jkrumbiegel This would allow you to nudge the start and endpoints of a line around by some number of pixel. So for an axis you could make ticks start 1 frame thickness later, and shorten/lengthen some of the frame lines by one thickness to avoid overlap. Or for 2d arrows you could use this to stop the line before the marker. |
because while offsets work line caps and joints don't
Missing reference imagesFound 2 new images without existing references. |
Missing reference imagesFound 2 new images without existing references. |
Missing reference imagesFound 2 new images without existing references. |
This seems to be mostly display time, not render time. Running render-N-frames check returns (1.7% min, 8.5% max, 1.8% mean, 1.3% median) more time spend on rendering here. (I'm now running 30 timings at 1k frames and filter spikes > 20% mean.) I guess this spike in display time isn't really that surprising with me adding another buffer to upload. Code```julia let for _ in 1:30 GLMakie.closeall() ps = [2 * rand(Point2f) .- 1 for _ in 1:2000]
end using Statistics function remove_spikes(ts, threshold = 0.2)
|
Missing reference imagesFound 2 new images without existing references. |
In Cairo linecaps are applied to dashes as well. It's set up to take space away from the "off" part of dashes. I.e. (with pattern being the vector of float we generate from a linestyle)
I've adjusted the linestyle lengths so that caps take space from the "on" part of a dash/dot instead of the "off" part. So it's not:
|
Missing reference imagesFound 2 new images without existing references. |
What do we do with this pr after #2666 got merged? |
Probably kill it and restart it. Most of this would need to be redone either way. I've also been thinking it would be good to match Cairo linecaps in GLMakie. They put them on each dash/dot, this pr only did lin start/end. |
Ok, makes sense! I'll close it then ;) |
Description
This pr aims to add linecaps to
lines!
andlinesegments!
in GLMakie.Current prototype (second commit) should render caps for lines (triangular, rectangular or circular depending on the values in the shader.)
(Debug) Example for round line caps:
TODO
lines!
(closes allow for different line widths in lines #1541)reuse line vertices for linecapsthis isn't easily possible since different line caps use different uvs...closes #1119, #2205
Also related: #871
Type of change
Checklist
Added unit tests for new algorithms, conversion methods, etc.