10 ms·
Vector graphics on GPU
- Pulcinella 4y agoI’m glad it seems more and more people are looking into rendering vector graphics on the GPU. Has anyone done any image comparisons between CPU vs GPU rendering. I would be worried about potential quality and rendering issues of a GPU rendered image vs a CPU rendered reference image.
- jstimpfle 4y agoThere's nothing to worry about. You can do the same things on the GPU as on the CPU. The tricky part is finding a good way to distribute the work on many small cores.
- TobTobXX 4y agoShouldn't a GPU render (given a correct algorithm implementation) be more correct in environments where zooming and sub-pixel movements are common (eg. browsers)? The GPU runs the mathemarical computations every frame for the exact pixel dimensions while the CPU may often use techniques like upscaling.
- Pulcinella 4y agoI would at least think it would be faster/smoother during operations like zooming. My concern was about precision of math operations on the GPU and potential differences between GPU vendors (or even different models of GPUs from the same vendor).
- deleted 4y ago[deleted]
- moonchild 4y agoThe interesting primitives are: add mul fma sqrt. All of these are mandated by ieee-754 to be correctly rounded. While gpus have been found to cut corners in the past, I wouldn't worry too much about it.
- slabity 4y agoI don't want to be the guy that doesn't read the entire article, but the first sentence surprised me quite a bit: > Despite vector graphics being used in every computer with a screen connected to it, rendering of vector shapes and text is still mostly a task for the CPU. Do modern vector libraries really not use the GPU? One of the very first things I did when learning Vulkan was to use a fragment shader to draw a circle inside a square polygon. I always assumed that we've been using the GPU for pretty much any sort of vector rasterization, whether it was bezier curves or font rendering.
- amelius 4y agoYes, it's like the article is trying to ignore why GPUs were invented in the first place.
- TazeTSchnitzel 4y agoThere's definitely a lot of code out there that still does this only on the CPU, but the optimized implementations used in modern OSes, browsers and games won't.
- ygra 4y agoA few of the previous approaches are mentioned in Other work near the end. And from reading a few articles on the topic I got the impression that, yes, drawing a single shape in a shader seems almost trivial, vector graphics in general means mostly what PostScript/PDF/SVG are capable of these days. This means you don't just need filled shapes, but also strokes (and stroking in itself is a quite complicated problem), including dashed lines, line caps, etc. Gradients, image fills, blending modes are probably on the more trivial end, since I think those can all be easily solved in shaders.
- bXVsbGVy 4y agoIn SVG, strokes has a separated specification only for them [1]. The specification has images that highlights some of the challenges. [1] https://svgwg.org/specs/strokes/ https://svgwg.org/specs/strokes/
- 4y ago
- jfmc 4y agoDidn't Chrome (and probably others) added GPU accelerated CSS and SVG (i.e., vector graphics) 10 years ago? https://www.tomshardware.com/news/google-chrome-browser-gpu-acceleration,14384.html https://www.tomshardware.com/news/google-chrome-browser-gpu-...
- pier25 4y agoYeah I was also under the impression that Skia had GPU acceleration. Same with the FF Rust renderer (sorry don't remember the name).
- bXVsbGVy 4y agoBased on a quick test I just ran, it seems chrome does and firefox doesn't.
- jahewson 4y agoNot exactly - the article you link to is about SVG/CSS filters, not path drawing. Modern Chrome (skia) supports accelerated path drawing but only some of the work is offloaded to the GPU. In even older Chrome the GPU was used for compositing bitmaps of already-rendered layers.
- UncleEntity 4y agoThere’s a OpenGL-ish gpu graphics library (who’s name I can’t currently remember) that’s in mesa, but not built by default in most distros, and IIRC is also supported on raspberrypi. I played with it a bit, wrote a python wrapper for it, borked a fedora install trying to get real gpu support, fun times all around. Seems nobody cares about an accelerated vector graphics library.
- genpfault 4y agohttps://en.wikipedia.org/wiki/OpenVG https://en.wikipedia.org/wiki/OpenVG Mesa removed support in 2015: https://docs.mesa3d.org/relnotes/10.6.0.html https://docs.mesa3d.org/relnotes/10.6.0.html > Removed OpenVG support.
- pcwalton 4y agoThis is essentially how Pathfinder works, with 16x16 tiles instead of 32x32. It also has a pure-GPU mode that does tile setup on GPU instead of CPU, which is a nice performance boost, though a lot more complex. Note that if you're doing the setup on CPU, the work can be parallelized across cores and I highly recommend this for large scenes. The main difference, which isn't large, is that Pathfinder draws the tiles directly instead of drawing a large canvas-size quad and doing the lookup in the fragment shader. When I originally checked, Slug works in a similar way but doesn't do tiling, so it has to process more edges per scanline than Pathfinder or piet-gpu. Slug has gone through a lot of versions since then though and I wouldn't be surprised if they added tiling later.
- moonchild 4y agoI thought pathfinder is scanline-oriented, using techniques similar to http://kunzhou.net/zjugaps/pathrendering/ http://kunzhou.net/zjugaps/pathrendering/ Tiling doesn't work too well under domain transforms--3d environments, dynamic zoom, etc. That's why I am betting on high-quality space partitioning. Slug space partitioning is not amazing; I believe it still processes O(n) curves per fragment in a horizontal band.
- jstimpfle 4y agoYou are literally talking to the Pathfinder author. I just implemented basically the same tiling mechanism, it works OK for me, I can already do about 1000 line segments, covering ~10000 tiles (multiply counting tiles that are touched by different polygons) in about 1ms on CPU and GPU each. Meaning the work is already quite well divided for my test cases. This is end to end without any caching. For static scenes doing world space partitioning is an idea that I considered, but for now I'm trying to optimize more in order to see what the limits of this basic approach are.
- pcwalton 4y ago> You are literally talking to the Pathfinder author. No worries, I think they know :) And yeah, I considered doing fancier partitioning, but really toward the end (before work stopped due to layoffs) I was pushing on shipping, which meant deprioritizing fancy partitioning in favor of implementing all the bells and whistles of SVG. I certainly believe that you could get better results with more sophisticated acceleration structures, especially in cases like texturing 3D meshes with vector textures.
- pyrolistical 4y agoThis seems like a silly way to do vector graphics in a shader. What I've done in the past is representing the shape as a https://en.wikipedia.org/wiki/Signed_distance_function https://en.wikipedia.org/wiki/Signed_distance_function to allow each pixel to figure out if it is inside or outside the shape. This avoids the need to figure out the winding. Anti-aliasing is implemented as a linear interpolation for values near zero. This also allows you to control the "thickness" of the shape boundary. The edge become mores blurry if you increase the lerp length. Shader toy demo https://www.shadertoy.com/view/sldyRj https://www.shadertoy.com/view/sldyRj
- Guzba 4y agoSDF is cool but not a generally good solution to GPU vector graphics. It only works for moderate scaling up before looking bad, the CPU prep figuring out the data the GPU needs takes far longer than just rasterizing on CPU would, etc. It's great as a model for games where there are many renders as world position changes but that's about it.
- johndough 4y ago> It only works for moderate scaling up before looking bad That problem has been solved by multi-channel signed distance fields https://github.com/Chlumsky/msdfgen https://github.com/Chlumsky/msdfgen
- cyber_kinetist 4y agoThe decomposition method used by msdfgen works well enough for fonts, but I'm not sure if it would work for more complex vector drawings. Probably would need much more than 3 color channels (= decompositions) to render something like the tiger SVG...
- tayistay 4y agoSeems like you're thinking of precomputed SDFs that are stored in a texture. They don't have to be :) (for example, my little library: https://github.com/audulus/vger https://github.com/audulus/vger)
- xawsi 4y agoI don't get it. Is this really a problem in 2022? WPF does this since back in 2006! https://en.wikipedia.org/wiki/Windows_Presentation_Foundation https://en.wikipedia.org/wiki/Windows_Presentation_Foundatio...
- stefanfisk 4y agoFrom what I remember, WPF basically does tessellation on the CPU and then sends draw lists to the GPU. I could certainly be wrong though.
- tasty_freeze 4y ago8 or 9 years ago I had need to rasterize SVG for a program I had written back then and looked into gpu vs cpu, but a software rasterizer ended up being fast enough for my needs and was simpler, so I didn't dig any further. At the time I looked at an nvidia rendering extension, which was described in this 2012 paper: https://developer.nvidia.com/gpu-accelerated-path-rendering https://developer.nvidia.com/gpu-accelerated-path-rendering In addition to the paper, the linked page has links to a number of youtube demos. That was 10 years ago, so I have no idea if that is still a good way to do it or if it has been superseded.
- samlittlewood 4y agoThe best GPU vector rendering library I have seen is https://sluglibrary.com/ https://sluglibrary.com/. The principal use case is fonts, but it appears that the underlying mechanism can be used for any vector graphics.
- tayistay 4y agoI think the issue with slug is that it requires a fair amount of pre-computation. So it's great for its use case: rendering glyphs, especially on surfaces in games. I do something a bit like slug, but I'm sure slower, since slug is very optimized. (https://github.com/audulus/vger https://github.com/audulus/vger)
- Lichtso 4y agoA post about vector graphics and the word "stroke" appears zero times ... > Much better approach for vector graphics is analytic anti-aliasing. And it turns out, it is not just almost as fast as a rasterizer with no anti-aliasing, but also has much better quality than what can be practically achieved with supersampling. > Go over all segments in shape and compute area and cover values continuously adding them to alpha value. This is approach is called "coverage to alpha". The author will be surprised to learn about the problem of coverage to alpha conflation artifacts. E.g. if you draw two shapes of exactly the same geometry over each other, but with different colors. The correct result includes only the color of the last shape, but with "coverage to alpha" you will get a bit of color bleeding around the edges (the conflation artifacts). I think Guzba also gives other examples in this thread. Also, they did not mention the other hard problems like stroke offsetting, nested clipping and group opacity etc. Here, IMO a good blog post about the hard problems of getting alpha blending and coverage right: https://ciechanow.ski/alpha-compositing/ https://ciechanow.ski/alpha-compositing/
- flakiness 4y agoDoes this mean it is mostly done in the fragment shader and there is no tessellation, like how Bezier patches are rendered in 3D land? That's quite different from what I thought I knew.
- jayd16 4y agoIs there no built in GPU path draw command? Seems like it would be similar (although not identical) to what the GPU does for vertices visibility. Especially when you consider what tile based renderers do for determining whether a triangle fully covers a tile (allowing rejection of any other draw onto that tile) it seems like GPUs could have built in support for 'inside a path or outside a path.' Even just approximating with triangles as a pre-pass seems faster than the row based method in the post. Are arbitrary paths just too complex for this kind of optimization?
- pyrolistical 4y agoDepends on your definition of a path. If what you mean is something like a SVG path, then consider the path is defined as a series of command that describe the shape. See https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Pa... From my understanding there is no closed form solution to arbitrary paths defined in that way. So the only way to figure out what the shape looks like, and to figure out if a point is inside or outside, you would need to run all the commands that form the path.
- Lichtso 4y agoMeet "Wavelet Rasterization" by J. Manson and S. Schaefer, from the paper: > Because we evaluate wavelet coefficients through line integrals in 2D, we are able to derive analytic solutions for polygons that have Bézier curve boundaries of any order, and we provide solutions for quadratic and cubic curves.
- moonchild 4y agoNV_path_rendering https://developer.nvidia.com/nv-path-rendering https://developer.nvidia.com/nv-path-rendering
- interroboink 4y ago> ... seems faster than the row based method in the post. But the row-based method in the post is not what they describe doing on the GPU version of the algorithm. The row-based method is their initial CPU-style version. The GPU version handles each pixel in isolation, checking it against the relevant shape(s). At least, if I understand things correctly (: As far as I can tell, the approach described here is probably similar to what a built-in "draw path" command would do. Checking if something is inside a triangle is just extremely easy (no concavity, for instance) and common, and more complex operations are left up to shader developers — why burn that special-case stuff into silicon?
- pbsurf 4y agoGPU vector graphics library I released a few years ago: https://github.com/styluslabs/nanovgXC https://github.com/styluslabs/nanovgXC - basically a new backend for nanovg that supports arbitrary paths. Coverage calculation (for analytic antialiasing) is explained a bit here: https://github.com/styluslabs/nanovgXC/blob/master/src/nanovg_gl.h#L484 https://github.com/styluslabs/nanovgXC/blob/master/src/nanov...
- coffeeaddict1 4y agoInteresting, what's the license for this library?
- jstimpfle 4y agoWhat I don't understand - why is there this "cover table" with precomputed per-cell-row coverage? I.e. why is the cover table computed per-cell-row when the segments are specialized per-cell? There is the paper "ravg.pdf" that gets by with basically one "cover" integer per tile, plus some artificial fixup segments that I believe are needed even in the presence of such a cover table. I'm probably missing something, someone who is deeper in the topic please enlighten me?
- moonchild 4y ago> a very good performance optimization is not to try to reject segments on the X axis in the shader. Rejecting segments which are below or above current pixel boundaries is fine. Rejecting segments which are to the right of the current pixel will most likely increase shader execution time. Thread groups are generally rectangular IME--nv is 8x4, others 8x8. So it doesn't make sense to distinguish X from Y in this respect. But yes, you do want a strategy for dealing with 'branch mispredictions'. Buffering works, and is applicable to the cpu too.
- grishka 4y agoA possibly dumb question. GPUs are really, really good at rendering triangles. Millions of triangles per second good. Why not convert a vector path into a fine enough mesh of triangles/vertexes and make the GPU do all the rasterization from start to finish instead of doing it yourself in a pixel shader?
- jakearmitage 4y agoKind of what Slug does: http://sluglibrary.com/ http://sluglibrary.com/
- bsder 4y agoMark Kilgard from NVIDIA has been beating this drum for a couple decades. It's not simple. His latest paper is about how to handle stroking of cubic splines: https://arxiv.org/abs/2007.00308 https://arxiv.org/abs/2007.00308 He gives it as a talk, but you have to sign up with NVIDIA: https://developer.nvidia.com/siggraph/2020/video/sig03-vid https://developer.nvidia.com/siggraph/2020/video/sig03-vid
- Lichtso 4y agoAh, polar stroking, a really clever tick to sample a curves vertices where it matters and guarantee a uniform tangent angle in between the samples. I actually implemented that (but using complex numbers instead of angles) in my renderer.
- Jasper_ 4y agoYou can do that, except now you've moved the bulk of the work from the GPU to the CPU -- triangulation is tricky to parallelize. And GPUs are best at rendering large triangles -- small triangles are much trickier since you risk overdraw issues. Also, typical GPU triangle antialiasing like MSAAx16 only gives you 16 sample levels, which is far from the quality we want out of fonts and 2D shapes. We don't have textures inside the triangles in 2D like we do in 3D, so the quality of the silhouette matters far more. That said, this is what Direct2D does for everything except text.
- 4y ago
- bane 4y agoThe demoscene has been doing this for a while, I wonder if the basic techniques (doing it in a shader) are the same? https://youtu.be/-ZxPhDC-r3w https://youtu.be/-ZxPhDC-r3w
- jszymborski 4y agoOT, but anyone know the font used in the figures? They're awfully neat!
- misterprime 4y agoSlightly off-topic: I wonder if we'll ever get to the point with AI where we can input the linked article and get usable code out. I'll be up front: you can't give me that article as input and expect usable code as an output.
- tayistay 4y agoI've done a library for vector graphics on the GPU which works pretty well for my uses: https://github.com/audulus/vger https://github.com/audulus/vger and a rust version: https://github.com/audulus/vger-rs https://github.com/audulus/vger-rs (which powers my rust GUI library: https://github.com/audulus/rui https://github.com/audulus/rui) Here's the approach for rendering path fills. From the readme: > The bezier path fill case is somewhat original. To avoid having to solve quadratic equations (which has numerical issues), the fragment function uses a sort-of reverse Loop-Blinn. To determine if a point is inside or outside, vger tests against the lines formed between the endpoints of each bezier curve, flipping inside/outside for each intersection with a +x ray from the point. Then vger tests the point against the area between the bezier segment and the line, flipping inside/outside again if inside. This avoids the pre-computation of Loop-Blinn, and the AA issues of Kokojima. It works pretty well, and doesn't require as much preprocessing as the code in the article. Also doesn't require any GPU compute (though I do use GPU compute for some things). I think ultimately the approach in the article (essentially Piet-metal, aka tessellating and binning into tiles) will deliver better performance, and support more primitives, but at greater implementation complexity. I've tried the Piet-metal approach myself and it's tricky! I like the simpler Shadertoy/SDF inspired approach :)
- rnantes 4y agoNow if only chrome could do this on macOS. :( https://bugs.chromium.org/p/chromium/issues/detail?id=1305733 https://bugs.chromium.org/p/chromium/issues/detail?id=130573...
- 12thwonder 4y agoI think this article is more about alternative rasterization algorithm for 2D geometry. 'vector graphics' is misleading as vertices used in graphics APIs are vectors.