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

[GLMakie] Cleanup linestyles #2666

Merged
merged 45 commits into from
Mar 14, 2023
Merged

[GLMakie] Cleanup linestyles #2666

merged 45 commits into from
Mar 14, 2023

Conversation

ffreyer
Copy link
Collaborator

@ffreyer ffreyer commented Feb 10, 2023

Description

Dots and dashes look pretty bad in GLMakie. This pr is supposed to clean them up. Here's an example test case:

scene = Scene(resolution = (600, 600))
ps = [0.8 * Point2f(cos(x), sin(x)) for x in range(0, pi, length=21)]
push!(ps, 
    Point2f(-0.3, 0),
    Point2f(-0.3, 0.4),
    Point2f(-0.05, 0.4),
    Point2f(0.0, 0.0),
    Point2f(0.05, 0.4),
    Point2f(0.3, 0.4),
    Point2f(0.3, 0.0),
    Point2f(0.8, 0.0)
)
lines!(scene, ps, linestyle = :dot, linewidth = 25)
s = scatterlines!(scene, p.converted[1], color = :blue, markersize = 10)
screen = display(scene)

On master:

Screenshot from 2023-02-10 16-14-51

On this branch (70ce058)

Screenshot from 2023-02-20 20-39-03

Corner behavior (0a915ed)

Previous Version From 70ce058
prototype.mp4
prototype.mp4

Change Summary

For the second version of this I allowed line segments to resize themselves to conform to the line pattern. The code now does the following for each line segment of a line:

  1. Find the the left most end of a drawn part of a pattern (on-to-off) within (p1 + 0.5 * linewidth, Inf) and save start of it (off-to-on) as start.
  2. If start is to the left of p1 - 0.5 * linewidth we draw a normal line join (truncated or not depending on angle). In this case the the "on" part of the pattern should generally be enough to cover the whole join (except for non-truncated joins with sharper than 90° angles - TODO)
  3. If start is to the right of p1 - 0.5 * linewidth we extend the line to include that part of the pattern.

We go through an analogously set of steps for the end of the line segment.

To force anti-aliasing of potentially cut of parts of a pattern, I added f_uv_minmax. It acts as a limit for f_uv.x which introduces another pair of anti-aliasing edges and f_uv_minmax.x and f_uv_minmax.y.

Like before this second version also requires conversions to screen space for patterned lines. Without that, continuity between one segment and the next is not given.

Performance should be roughly the same as the last version. Maybe slightly faster without linestyles and a bit slower with them.

Type of change

Delete options that do not apply:

  • Bug fix
  • New feature (per vertex linestyle)

Checklist

  • Added an entry in NEWS.md (for new features and breaking changes)
  • Added or changed relevant sections in the documentation
  • Added unit tests for new algorithms, conversion methods, etc.
  • Added reference image tests for new plotting functions, recipes, visual options, etc.

Other TODO's:

  • add a fast-path for solid lines (Specifically reversing the data -> screen space transformations that run on the CPU in this pr. Since solid linestyles already use a modified shader this should be able to optimize freely here without making the pr breaking.)
  • maybe rework linestyles some more to allow linestyle = (sdf, [0, 1, 2]) or maybe even (marker, [0, 1, 2])
  • fix bad non-truncated corner at < 90° angle
  • check on vertex generation for mixed pattern (looks like there might be some padding missing?)
Old Version

On this branch (first commit)

Screenshot from 2023-02-10 16-13-00

(The first commit combines a lot of commits I made in branch coming off the linecaps pr. That included a lot of debug code which I wanted to clean out)

This addresses a few of the points raised in #2548:

  1. Fix/Add anti-aliasing in the direction of a line (i.e. at line starts, ends, pattern edges (?))
  2. Fix/remove artifacts that sometimes appear in patterned lines nearby "on"/"off" transitions.
  3. Fix spacing issues

And also:

  • adds the per-vertex linewidth from add linecaps (GLMakie, CairoMakie) #2536
  • fix the mixing between rectangular and sheered rectangular dots (etc) on straight segments
  • complete the AA border around truncated miter joins
  • add logic to avoid having patterns switch between "on" and "off" in corners

Changelog

Apply transformations on CPU

The transformations for positions from data space to pixel space have been moved to the CPU (in drawing_primitives.jl). As far as I can tell this is necessary to get the correct cumulative pixel scale lengths of each line vertex, i.e. lastlen. Before, these lengths were calculated in data space and transformed in the shader. This doesn't work out in the end, as aspect ratio, perspective projection, rotation and scale transformations all influence how data space and pixel space lengths relate. Getting them wrong causes jumps in the pattern progression from one line segment to the next.

Fixing dot/dash shapes

On master patterned lines often generate dots/dashes which look like an overlapping rectangle and triangle. This is mainly caused by not considering extrusions to vertices when calculating uv coorinates. On master we have:

void emit_vertex(vec2 position, vec2 uv, int index, float ratio)
{
    vec4 inpos  = gl_in[index].gl_Position;
    f_uv        = vec2((g_lastlen[index] * ratio) / pattern_length / (thickness+4) / 2.0, uv.y);
    f_color     = g_color[index];
    gl_Position = vec4((position/resolution)*inpos.w, inpos.z, inpos.w);
    f_id        = g_id[index];
    f_thickness = thickness;
    EmitVertex();
}

Ignoring the scaling factors, this puts uv.x at g_lastlen[index], i.e. the cumulative length related to the base vertex position of the emitted vertex. For shallow joins these vertices are however moved in (or against) the direction of the line to make it continuous. Since uv.x does not reflect this, the interpolated index becomes inconsistent at the top and bottom end of the line. This is fixed by

// for line sections
void emit_vertex(vec2 position, float v, int index, vec2 line_unit, vec2 line_pos)
{
    vec4 inpos = gl_in[index].gl_Position;
    // adjusts u values to extrusions made in vertex generation (e.g. to close lines)
    float vertex_offset = dot(position - line_pos, line_unit);

    f_uv        = vec2(0.5 * (g_lastlen[index] + vertex_offset) / pattern_length, v);
    f_color     = g_color[index];
    gl_Position = vec4(position / resolution, inpos.z, inpos.w);
    f_id        = g_id[index];
    f_thickness = g_thickness[index];
    EmitVertex();
}

which considers the offset between the expected start/end of a line line_pos and the given start/end position in the direction of the line line_pos.

Pattern Scale

I don't really understand what the code was trying to achieve before. Now the steps go like this:

  1. In drawing_primitves.jl we have data[:pattern] = linestyle * mean(linewidth) which scales a pattern in units of linewidth up to pixel units.
  2. In lines.jl this is converted to to a texture mostly as before. The only change here is to make sure the texture smoothly repeats by generating an extra point that is removed. This fixes artifacts that appear close the start and of a dot/dash. (More specifically without this you get something like -2 -1 0 | 0 1 2 as you repeat the texture, which generates a messy AA border.)
  3. In the shader we have the cumulative pixel scale length of the line to use as an index for the pattern. This is transformed such that 0 .. 0.5pattern_length becomes 0 .. 1 where the 0.5 comes from an incomplete transformation from clip space (-1 .. 1) to "pixel space" (-width .. width).

Anti-aliasing of truncated miter join

To correctly anti-alias the truncated miter join adding just a triangle is not sufficient. Here's an example to illustrate the problem:

Screenshot from 2023-02-11 18-09-05

The black parts indicate the line segments and truncated miter without AA and the AA border for line segments is drawn in blue. Simply using the line segment vertices with AA leads to the red miter join, which obviously has less space to anti-alias as the lines. To give them enough space we add the orange rectangle below.

Note that extruding the red triangle outwards further also works in this case, but if the lines become close to (anti-) parallel the extrusion necessary goes to infinity. The rectangle on the other hand can have a fixed height of AA_THICKNESS with a width <= the linewidth.

Other anti-aliasing & avoiding disjointed patterns

This is basically the "everything else". First of all I got rid of the dFdx reliant aastep methods in lines.frag and copied over the methods from the scatter shader. This is mostly because I found the old methods hard to manipulate and reason with, as they pull in information from surrounding pixels.

Beyond that I added vec4 f_uv_minmax to overwrite what is sampled from patterns. It's interpretation follows from

xy.x = ifelse(f_uv.x <= f_uv_minmax.x, (f_uv.x - f_uv_minmax.y) * pattern_length, xy.x);
xy.x = ifelse(f_uv.x >= f_uv_minmax.z, (f_uv_minmax.w - f_uv.x) * pattern_length, xy.x);

Consider a line starting at (0, 0) going to (1, 0) (i.e. just extending in +x direction)

  1. If uv.x is smaller than f_uv_minmax.x, i.e. if we are to the left of that value, then we set the signed distance xy.x to be f_uv.x - f_uv_minxmax.y. This means we have a off -> on transition at f_uv_minmax.y.
  2. Here we do the same thing on the other end of the line. If we are right of f_uv_minmax we create a on -> off transition at f_uv_minmax.w.

This is used throughout the geometry shaders:

  • For truncated miter joins, this is used to circumvent the usual pattern fetches and to turn the join on or off.
  • For line segments this is used to keep them from drawing parts of the line join until those can be drawn fully. This avoid creating discontinuous shapes and instead gives those cut of squares you see in the new example above.
  • For line starts/ends it used to ensure anti-aliasing by placing a manual edge in the necessary places.

Type of change

Delete options that do not apply:

  • Bug fix
  • New feature (per vertex linestyle)

Checklist

  • y Added an entry in NEWS.md (for new features and breaking changes)
  • y Added or changed relevant sections in the documentation
  • Added unit tests for new algorithms, conversion methods, etc.
  • y Added reference image tests for new plotting functions, recipes, visual options, etc.

Other TODO's:

  • y add a fast-path for solid lines (Specifically reversing the data -> screen space transformations that run on the CPU in this pr. Since solid linestyles already use a modified shader this should be able to optimize freely here without making the pr breaking.)
  • "We should add a way to draw closed lines" from GLMakie lines TODO #2548 should be easy to set up so why not add it here Skipping this here because it's not quite as easy as I thought.
  • n maybe tweak corner behavior more
  • y fix lines with linestyle = nothing
  • y fix linesegments
  • y avoid reducing line length to apply AA
  • cleanup uv coordinates to be consistently in pixel or (0, 1) space I think it's better to leave it as it is for performance, because otherwise we need to add extra conversions in the fragment shader.
  • thickness is currently the full linewidth but mostly used as half of it. Maybe change this? Similar situation. We need the full width in a bunch of places, and the half width in a bunch of others. In the end we don't really simplify things by changing this.

- per point linewidth
- fix pattern deformation (merging of rectangle and parallelogram)
- fix artifacting at pattern edge
- cleanup joins
- cleanup anti-aliasing
@ffreyer ffreyer added the GLMakie This relates to GLMakie.jl, the OpenGL backend for Makie. label Feb 10, 2023
@MakieBot
Copy link
Collaborator

MakieBot commented Feb 10, 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 37.02s (36.56, 37.52) 0.37+- 17.62s (17.14, 18.19) 0.47+- 15.96s (15.60, 16.47) 0.30+- 10.11ms (9.95, 10.54) 0.21+- 114.01ms (112.55, 115.86) 1.35+-
master 37.00s (36.64, 37.64) 0.33+- 17.63s (17.18, 18.46) 0.42+- 15.92s (15.49, 16.73) 0.46+- 10.10ms (9.86, 10.22) 0.13+- 115.70ms (113.66, 117.82) 1.49+-
evaluation +0.08%, 0.03s invariant (0.08d, 0.88p, 0.35std) -0.03%, -0.0s invariant (-0.01d, 0.99p, 0.45std) +0.23%, 0.04s invariant (0.10d, 0.86p, 0.38std) +0.06%, 0.01ms invariant (0.04d, 0.95p, 0.17std) -1.48%, -1.68ms faster ✓ (-1.18d, 0.05p, 1.42std)
CairoMakie 34.00s (33.82, 34.21) 0.16+- 17.04s (16.79, 17.44) 0.25+- 2.53s (2.48, 2.58) 0.04+- 10.56ms (10.32, 10.71) 0.15+- 5.16ms (4.86, 5.40) 0.20+-
master 33.68s (33.52, 33.95) 0.17+- 17.10s (16.82, 17.50) 0.28+- 2.55s (2.49, 2.59) 0.03+- 10.54ms (10.38, 10.63) 0.10+- 5.14ms (5.04, 5.29) 0.09+-
evaluation +0.93%, 0.32s slower X (1.89d, 0.00p, 0.17std) -0.31%, -0.05s invariant (-0.20d, 0.72p, 0.26std) -0.70%, -0.02s invariant (-0.53d, 0.34p, 0.03std) +0.23%, 0.02ms invariant (0.20d, 0.72p, 0.12std) +0.33%, 0.02ms invariant (0.11d, 0.84p, 0.14std)
WGLMakie 51.25s (50.35, 52.16) 0.60+- 24.83s (23.25, 25.85) 0.80+- 24.91s (23.42, 25.94) 0.85+- 16.91ms (15.38, 18.33) 1.13+- 2.10s (1.98, 2.20) 0.07+-
master 51.61s (50.48, 52.30) 0.62+- 25.27s (24.71, 25.99) 0.50+- 25.14s (24.36, 25.79) 0.52+- 16.80ms (15.44, 17.82) 0.84+- 2.09s (1.98, 2.17) 0.07+-
evaluation -0.71%, -0.36s invariant (-0.59d, 0.29p, 0.61std) -1.77%, -0.44s invariant (-0.66d, 0.25p, 0.65std) -0.93%, -0.23s invariant (-0.33d, 0.55p, 0.69std) +0.62%, 0.1ms invariant (0.11d, 0.85p, 0.99std) +0.72%, 0.02s invariant (0.22d, 0.69p, 0.07std)

@ffreyer
Copy link
Collaborator Author

ffreyer commented Feb 10, 2023

I'm quite surprised that the benchmark isn't complaining about this. I thought moving the data -> pixel space calculation to the CPU would surely trigger it.

Anyway while working on this pr I also noticed that Cairo(Makie) handles corners with patterned lines differently. Take this for example:

Screenshot from 2023-02-10 18-34-01

In this pr I'm currently cutting of the dot to avoid creating weird shapes at the corner. (I'm not quite sure what happens anymore. I think you get an additional triangle on the right segment up to the edge of the marker and another one at the tip of the other segment. Might also have AA issues.)

What Cairo does instead is that it picks one segment to place the dot on, i.e. the right one as it has more of the dot, and extends that segment to draw the full marker there. That is also something we should be able to do in GLMakie. It would require reworking the pattern internals in GLMakie, but I don't think it would be breaking. It would probably simplify the shader in the end, it should also allow use generic markers in linestyles and it would allow us to match the linecap behavior from Cairo.

The problem I see with Cairos approach is that it might become a bit misleading in terms of where a corner is. Specifically with long dashes a segment might extend significantly past a corner where the current approach would bend the dash around the corner. Though dashed and dotted lines kind of have this problem anyway. If the corner is in a gap it is always hard to figure out where exactly the corner is.

@ffreyer
Copy link
Collaborator Author

ffreyer commented Feb 10, 2023

What Cairo does instead is that it picks one segment to place the dot on, i.e. the right one as it has more of the dot, and extends that segment to draw the full marker there.

Now that I'm trying to reproduce it I'm not getting this kind of behavior anymore. I could swear Cairo did behave like that though... Now it seems to be doing some of the things I tried to actively avoid in GLMakie, i.e. mixing dots from different line segments to create bends, gaps or step-like shapes:

GLMakie | CairoMakie
Screenshot from 2023-02-10 23-16-33

CairoMakie generating a small gap:
Screenshot from 2023-02-10 23-21-16

The branch was still based of the linecaps pr when I looked, so it was probably a result of those size adjustments.

@ffreyer
Copy link
Collaborator Author

ffreyer commented Feb 11, 2023

I added a fast path now which moves the data space -> pixel space transformations back into the shader and skips a bunch of steps. Rendering 1k frames of a line with 1k points as fast as possible takes

  • 0.174 - 0.18s on master
  • 0.174 - 0.18s with fast path
  • 1.0 - 1.05s with slow path(That's about 5.8x slower)

So as of now this pr makes patterned lines significantly slower but leaves solid lines as fast as before. The extra cost comes from running the data -> pixel space transformations on the CPU. Since that triggers on projectionview it's likely worse in practice than what this benchmark suggests. But without this the patterns won't be continuous.

@jkrumbiegel
Copy link
Member

I think it's completely fine to make line styles slower to make them look good. Because to me, fast but bad quality is much worse than slower but good quality.

People concerned about maximum performance can always use solid lines as a fallback.

@ffreyer
Copy link
Collaborator Author

ffreyer commented Feb 12, 2023

Yes I agree. It stings a little that it's that much of a difference, but it's the price you have to pay to get clean linestyles.

What's perhaps a little upside is that it's a pretty self contained problem. Pretty much the entire problem is that the 4x4 matrix M sits inside the cumulative distance calculation cumsum([norm(M * p) for p in ps]). If we can find a way to measure distance where the summation happens before M is applied then that should solve the performance loss here. I think a 3x3 matrix would be good enough for this as well, as it would only miss perspective projection.

@SimonDanisch
Copy link
Member

This is really cool!! I agree, that the default shouldn't worry too much about performance, while we still have an option we can use for high performance use cases!

I've been wanting to separate 2d + 3d shaders for lines + scatter, since the 2d problem is much easier to solve, and has a much higher need for quality... Maybe we can move more into that direction?

@ffreyer
Copy link
Collaborator Author

ffreyer commented Feb 12, 2023

I don't think reducing this to 2D would be that much easier. We would still need to do conversion on the CPU since rotations (transformations) and scaling (camera, resolution, transformations) are still relevant. For positions we would still need a 4x4 matrix (or 3x4?) but I guess for just line lengths we could drop down to 2x2. That seems to be ~3.3x as expensive than master (~0.6s) based on a quick benchmark.
The shader wouldn't really change either, since it's all in pixel space anyway.

@ffreyer
Copy link
Collaborator Author

ffreyer commented Feb 14, 2023

I'm going through tests manually to look for any bad behavior that's not showing up in CI and noticed that

c = lines!(scene, Circle(Point2f(0.1, 0.5), 0.1f0), color=:red, offset=Vec3f(0, 0, 1))

is practically invisible (in refimages too). This line seems to be from pre-MakieGallery. Should we remove it?

For reference, it just peeks out a little bit like this:
Screenshot from 2023-02-14 20-18-58

@jkrumbiegel
Copy link
Member

I think so, if it's not even visible. Doesn't seem to be a critical functionality check either

@ffreyer
Copy link
Collaborator Author

ffreyer commented Feb 14, 2023

I enabled the linestyle test, but there is a difference in how CairoMakie and GLMakie interpret vector linestyles. CairoMakie seems to interpret it as state switches, i.e. [1, 2, 4, 5] becomes

pen down (init)
1 move (2-1) * linewidth
swap state (pen up)
2 move (4-2) * linewidth
swap state (pen down)
3 move (5-4) * linewidth
swap state (pen up)
(repeat but state inverted)

1 move (2-1) * linewidth
swap state (pen down)
2 move (4-2) * linewidth
swap state (pen up)
3 move (5-4) * linewidth
swap state(pen down)
(repeat with initial state)
...

or "draw 1 - gap 2 - draw 1 - gap 1 - draw 2 - gap 1", while in it just repeats one cycle: "draw 1 - gap 2 - draw 1".

We need to pick which one of these two we want.

(The top one in these is the [1, 2, 4, 5] pattern)

GLMakie

Screenshot from 2023-02-14 18-39-48

CairoMakie

linestyles_Cairo

@github-actions
Copy link
Contributor

Missing reference images

Found 1 new images without existing references.
Upload new reference images before merging this PR.

@jkrumbiegel
Copy link
Member

This doesn't look quite right no? image

@github-actions
Copy link
Contributor

Missing reference images

Found 1 new images without existing references.
Upload new reference images before merging this PR.

@ffreyer
Copy link
Collaborator Author

ffreyer commented Feb 25, 2023

What about it? The square not bending at the edge is something I did deliberately.

Within the section marked in red here
Screenshot from 2023-02-25 20-49-13
it's hard to reason over what section of the pattern should be displayed. Looking from the center of the line you'd want to go from corner_px_stride - delta to corner_px_stride + delta. You can set that up for the top part of the line, but not the bottom because there you only have one point to work with - so no space to interpolate.

The way I set it up is, that a line can be elongated by up to half a linewidth in either direction to fit an on/off transition of the pattern. If more space is requires there pattern will be bend around a corner. If the line ends, the pattern will be cut off right at the edge.

I've tried a couple more things, but the only one worth mentioning is what I've shown in the first comments. There I avoided partially filling corners by squishing and stretching pattern section near them. Imo that looks much worse. Having a dot |#| be cut off to just a line | looks like an artifact to me.

I'm also looking ahead a bit with this - the new version of the shader should be able to handle 2d patterns like scatter makers. Having them bend around a corner could be quite annoying - it would cause something like a circle to distort. Right now it wouldn't (as long as the markers width <= height)

This is how it looks in CairoMakie btw
Screenshot from 2023-02-25 21-18-16

@ffreyer
Copy link
Collaborator Author

ffreyer commented Feb 25, 2023

I should probably add the line visualization I made at some point along the way here:
https://gist.github.com/ffreyer/4008c0aec6bd5b62884e9bdab545e939

It's also a good thing I looked at this again because the offset between line dot and circle here

Screenshot from 2023-02-25 20-49-13

was another little error. I applied some scaling in the wrong place which caused line sections to be too small. 3742e95 fixed that. (I checked with 1560 pixels which is 52 dots at linewidth 10.)

@SimonDanisch
Copy link
Member

Can we in some way preserve all of the great debug examples you have shown here? They look like they took some time to create and pretty educational as well ;)

@ffreyer
Copy link
Collaborator Author

ffreyer commented Feb 25, 2023

I added the half circle example and the animation in the gist now. The light gray backgrounds still have code in the fragment shader similar to the scatter shader.

The geom shader also has a ton of sketches in it, probably too many

@github-actions
Copy link
Contributor

Missing reference images

Found 1 new images without existing references.
Upload new reference images before merging this PR.

@EdsterG
Copy link
Contributor

EdsterG commented Feb 26, 2023

Out of curiosity, what's the technical reason for WGLMakie's lack of support for line styles?

@ffreyer
Copy link
Collaborator Author

ffreyer commented Feb 26, 2023

Out of curiosity, what's the technical reason for WGLMakie's lack of support for line styles?

I think WGLMakie doesn't have as many people helping, so it tends to lag behind.

I noticed another small problem - the anti-aliasing for (solid) lines at the start and end is a little off. Seems to be linewidth dependent too.

@github-actions
Copy link
Contributor

Missing reference images

Found 1 new images without existing references.
Upload new reference images before merging this PR.

@SimonDanisch
Copy link
Member

Out of curiosity, what's the technical reason for WGLMakie's lack of support for line styles?

WebGL doesn't support geometry shader, and the line caps + connections are easiest implemented with a geometry shader...
So I couldn't just re-use the GLMakie shader and writing a new one from scratch was too time consuming so I only implemented line segments back in the days...
I wonder how hard it would be to fake a geometry shader by instancing a geometry with 9 vertices and just discarding the unneeded vertices ... Not sure how easy that is, since one of the advantages of geometry shaders is also that you can emit all vertices in one go, so shared state between those vertices is easier (which makes it easier to connect things)... But maybe there's a trick simple enough trick to share the state 🤷

@github-actions
Copy link
Contributor

Missing reference images

Found 1 new images without existing references.
Upload new reference images before merging this PR.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2023

Missing reference images

Found 1 new images without existing references.
Upload new reference images before merging this PR.

@SimonDanisch
Copy link
Member

Thank you, this is really great!! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
GLMakie This relates to GLMakie.jl, the OpenGL backend for Makie.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants