8 ms·
Vector graphics on GPU
- larodi 10mo agoReally, inst there anything which comes Slug-level of capabilities and is not super expensive?
- coffeeaddict1 10mo agoVello [0] might suit you although it's not production grade yet. [0] https://github.com/linebender/vello https://github.com/linebender/vello
- miguel_martin 10mo agoJust use blend2d - it is CPU only but it is plenty fast enough. Cache the rasterization to a texture if needed. Alternatively, see blaze by the same author as this article: https://gasiulis.name/parallel-rasterization-on-cpu/ https://gasiulis.name/parallel-rasterization-on-cpu/
- reallynattu 10mo agoThorVG might be worth a look - open source (MIT), ~150KB core, GPU backends (WebGPU, OpenGL). We are using it as official dotLottie runtimes, now a Linux Foundation project. Handles SVG, Lottie, fonts, effects. https://github.com/thorvg/thorvg/ https://github.com/thorvg/thorvg/
- coffeeaddict1 10mo agoIn terms of performance, it's quite far from something like Blend2D or Vello though.
- hermet 10mo agoBlend2D is a CPU-only rendering engine, so I don't think it's a fair comparison to ThorVG. If we're talking about CPU rendering, ThorVG is faster than Skia. (no idea about Blend2d) But at high resolutions, CPU rendering has serious limitations anyway. Blend2D is still more of an experimental project that JIT kills the compatiblity and Vello is not yet production-ready and webgpu only. No point of arguing fast today if it's not usable in real-world scenarios.
- Asm2D 10mo agoHow JIT kills compatibility if it's only enabled on x86 and aaarch64? You can compile Blend2D without it and it would just work. So no, it doesn't kill any compatibility - it only shows a different approach. BTW GPU-only renderers suck, and many renderers that have GPU and CPU engines suck when GPU is not available or have bugs. Strong CPU rendering performance is just necessary for any kind of library if you want true compatibility across various platforms. I have seen many many times broken rendering on GPU without any ability to switch to CPU. And the biggest problem is that more exotic HW you run it on, less chance that somebody would be able to fix it (talking about GPUs).
- hermet 10mo agoCheck this out: https://thorvg-perf-test.vercel.app/ https://thorvg-perf-test.vercel.app/ https://www.youtube.com/watch?v=jdnnzmtHy9k https://www.youtube.com/watch?v=jdnnzmtHy9k
- badlibrarian 10mo agoAuthor uses a lot of odd, confusing terminology and brings CPU baggage to the GPU creating the worst of both worlds. Shader hacks and CPU-bound partitioning and choosing the Greek letter alpha to be your accumulator in a graphics article? Oh my. NV_path_rendering solved this in 2011. https://developer.nvidia.com/nv-path-rendering https://developer.nvidia.com/nv-path-rendering It never became a standard but was a compile-time option in Skia for a long time. Skia of course solved this the right way. https://skia.org/ https://skia.org/
- bsder 10mo agoWhile the author doesn't seem to be aware of state of the art in the field, vector rendering is absolute NOT a solved problem whether on CPU or GPU. Vello by Raph Levien seems to be a nice combination of what is required to pull this off on GPUs. https://www.youtube.com/watch?v=_sv8K190Zps https://www.youtube.com/watch?v=_sv8K190Zps
- lukan 10mo agoYeah, I have high hopes for Vello to take off. I could throw away lots of hacks and caching and whatnot if I could do fast vector rendering reliable on the GPU. I think Rive also does vector rendering on the GPU https://rive.app/renderer https://rive.app/renderer But it is not really meant (yet?) as a general graphics libary, but just a renderer for the rive design tools.
- pier25 10mo agoAFAIK you can use the Rive renderer in your C++ app. http://github.com/rive-app/rive-runtime http://github.com/rive-app/rive-runtime
- bean469 10mo ago> While the author doesn't seem to be aware of state of the art in the field The blog post is from 2022, though
- sirwhinesalot 10mo ago
- virtualritz 10mo agoUnless I miss something I think that this describes box filtering. It should probably mention that that this is only sufficient for some use cases but not for high quality ones. E.g. if you were to use this e.g. for rendering font glyphs into something like a static image (or a slow rolling title/credits) you probably want a higher quality filter.
- jstimpfle 10mo agoWhat type of filter do you mean? Unless I'm misunderstanding/missing something, the approach described doesn't go into the details of how coverage is computed. If the input image is only simple lines whose coverage can be correctly computed (don't know how to do this for curves?) then what's missing? I'd be interested how feasible complete 2D UIs using dynamically GPU rendered vector graphics are. I've played with vector rendering in the past, using a pixel shader that more or less implemented the method described in the OP. Could render the ghost script tiger at good speeds (like 1-digit milliseconds at 4K IIRC), but there is always an overhead to generating vector paths, sampling them into line segments, dispatching them etc... Building a 2D UI based on optimized primitives instead, like axis-aligned rects and rounded rects, mostly will always be faster, obviously. Text rendering typically adds pixel snapping, possibly using byte code interpreter, and often adds sub-pixel rendering.
- jlokier 10mo ago> If the input image is only simple lines whose coverage can be correctly computed (don't know how to do this for curves?) then what's missing? Computing pixel coverage accurately isn't enough for the best results. Using it as the alpha channel for blending forground over background colour is the same thing as sampling a box filter applied to the underlying continuous vector image. But often a box filter isn't ideal. Pixels on the physical screen have a shape and non-uniform intensity across their surface. RGB sub-pixels (or other colour basis) are often at different positions, and the perceptual luminance differs between sub-pixels in addition to the non-uniform intensity. If you don't want to tune rendering for a particular display, there are sometimes still improvements from using a non-box filter An alternative is to compute the 2D integral of a filter kernel over the coverage area for each pixel. If the kernel has separate R, G, B components, to account for sub-pixel geometry, then you may require another function to optimise perceptual luminance while minimising colour fringing on detailed geometries. Gamma correction helps, and fortunately that's easily combined with coverage. For example, slow rolling tile/credits will shimmer less at the edges if gamme is applied correctly. However, these days with Retina/HiDPI-style displays, these issues are reduced. For example, MacOS removed sub-pixel anti-aliasing from text rendering in recent years, because they expect you to use a Retina display, and they've decided regular whole-pixel coverage anti-aliasing is good enough on those.
- masswerk 10mo agoMay require "(2022)" in the title.
- xattt 10mo agoTangential, but was this not the goal of Quartz 2D? The idea of everyday things running on the GPU seemed very attractive. There is some context in this 13-year-old discussion: https://news.ycombinator.com/item?id=5345905#5346541 https://news.ycombinator.com/item?id=5345905#5346541 I am curious if the equation of CPU-determined graphics being faster than being done on the GPU has changed in the last decade. Did Quartz 2D ever become enabled on macOS?
- kllrnohj 10mo agoWhen things like this (or Vello or piet-gpu or etc...) talk about "vector graphics on GPU" they are near exclusively talking only about essentially a full solve solution. A generic solution that handles fonts and svgs and arbitrarily complex paths with strokes and fills and the whole shebang. These are great goals, but also largely inconsequential with nearly all UI designs. The majority of systems today (like skia) are hybrids. Things like simple shapes (eg, round rects) have analytical shaders on the GPU and complex paths (like fronts) are just done on the CPU once and cached on the GPU in a texture. It's a very robust, fast approach to the wholistic problem, at the cost of not being as "clean" of a solution like a pure GPU renderer would be.
- jacobp100 10mo ago> I am curious if the equation of CPU-determined graphics being faster than being done on the GPU has changed in the last decade If you look at Blend2D (a CPU rasterizer), they seem to outperform every other rasterizer including GPU-based ones - according to their own benchmarks at least
- Asm2D 10mo agoBlend2D doesn't benchmark against GPU renderers - the benchmarking page compares CPU renderers. I have seen comparisons in the past, but it's pretty difficult to do a good CPU vs GPU benchmarking.
- miguel_martin 10mo agoBlaze outperforms Blend2D - by the same author as the article: https://gasiulis.name/parallel-rasterization-on-cpu/ https://gasiulis.name/parallel-rasterization-on-cpu/ - but to be fair, Blend2D is really fast.
- nubskr 10mo agoTurns out the best GPU optimization is just being too scared of graphics drivers to do the fancy stuff, 10-15x faster and you can actually debug it.
- jayd16 10mo agoSo without blowing up the traditional shader pipeline, why is it not trivial to add a path stage as an alternative to the vertex stage? It seems like GPUs and shader language could implement a standard way to turn vector paths into fragments and keep the rest of the pipeline. In fact, you could likely use the geometry stage to create arbitrarily dense vertices based on path data passed to the shader without needing any new GPU features. Why is this not done? Is the CPU render still faster than these options?
- exDM69 10mo ago> why is it not trivial to add a path stage as an alternative to the vertex stage? Because paths, unlike triangles are not fixed size or have screen space locality. Paths consist of multiple contours of segments, typically cubic bezier curves and a winding rule. You can't draw one segment out of a contour on the screen and continue to the next one, let alone do them in parallel. A vertical line segment on the left hand side going bottom to top of your screen will make every pixel to the right of it "inside" the path, but if there's another line segment going top to bottom somewhere the pixel and it's outside again. You need to evaluate the winding rule for every curve segment on every pixel and sum it up. By contrast, all the pixels inside the triangle are also inside the bounding box of the triangle and the inside/outside test for a pixel is trivially simple. There are at least four popular approaches to GPU vector graphics: 1) Loop-Blinn: Use CPU to tessellate the path to triangles on the inside and on the edges of the paths. Use a special shader with some tricks to evaluate a bezier curve for the triangles on the edges. 2) Stencil then cover: For each line segment in a tessellated curve, draw a rectangle that extends to the left edge of the contour and use two sided stencil function to add +1 or -1 to the stencil buffer. Draw another rectangle on top of the whole path and set the stencil test to draw only where the stencil buffer is non-zero (or even/odd) according to the winding rule. 3) Draw a rectangle with a special shader that evaluates all the curves in a path, and use a spatial data structure to skip some. Useful for fonts and quadratic bezier curves, not full vector graphics. Much faster than the other methods for simple and small (pixel size) filled paths. Example: Lengyel's method / Slug library. 4) Compute based methods such as the one in this article or Raph Levien's work: use a grid based system with tessellated line segments to limit the number of curves that have to be evaluated per pixel. Now this is only filling paths, which is the easy part. Stroking paths is much more difficult. Full SVG support has both and much more. > In fact, you could likely use the geometry stage to create arbitrarily dense vertices based on path data passed to the shader without needing any new GPU features. Geometry shaders are commonly used with stencil-then-cover to avoid a CPU preprocessing step. But none of the GPU geometry stages (geometry, tessellation or mesh shaders) are powerful enough to deal with all the corner cases of tessellating vector graphics paths, self intersections, cusps, holes, degenerate curves etc. It's not a very parallel friendly problem. > Why is this not done? As I've described here: all of these ideas have been done with varying degrees of success. > Is the CPU render still faster than these options? No, the fastest methods are a combination of CPU preprocessing for the difficult geometry problems and GPU for blasting out the pixels.
- deleted 10mo ago[deleted]
- Dwedit 10mo agoAccording to the page here: https://www.humus.name/index.php?page=News&ID=228 https://www.humus.name/index.php?page=News&ID=228 The best way to draw a circle on a GPU is to start with a large triangle, and keep adding additional triangles on the edges until you've reached the point where you do not need to add any more triangles (smaller than a pixel)
- jesse__ 10mo agoI'd put money on that the best way is actually to draw a quad, or single triangle, and draw the circle as a SDF in the fragment shader
- dahart 10mo agoFwiw, that’s not the best way to draw a circle in general, the test shows it’s the fastest way to tessellate a circle, among the methods the author tried, and using a specific GPU. You don’t have to use triangles to draw a circle, and the author didn’t try all possible tessellations, and the author there didn’t compare perf against any other method (a shader, for example), and the also didn’t investigate accuracy. Their fast method might have numerical accuracy issues with thin sliver triangles at some point.
- Lichtso 10mo ago> but [analytic anti-aliasing (aaa)] also has much better quality than what can be practically achieved with supersampling What this statement is missing is that aaa coverage is immediately resolved, while msaa coverage is resolved later in a separate step with extra data being buffered in between. This is important because msaa is unbiased while aaa is biased towards too much coverage once two paths partially cover the same pixel. In other words aaa becomes incorrect once you draw overlapping or self-intersecting paths. Think about drawing the same path over and over at the same place: aaa will become darker with every iteration, msaa is idempotent and will not change further after the first iteration. Unfortunately, this is a little known fact even in the exquisite circles of 2D vector graphics people, often presenting aaa as the silver bullet, which it is not.
- jesse__ 10mo agoInterestingly they do not cite calculating a signed distance to the surface of the shape as an approach to doing AA, as described in the Valve paper [1]. I suppose this is more targeted at offline baking, but given they're suggesting iterating every curve at every pixel, I'm not sure why you wouldn't. [1] https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007...
- reallynattu 10mo agoFor anyone looking at this space: ThorVG is worth checking out. Open-source vector engine with GPU backends (WebGPU, OpenGL), runs on microcontrollers to browsers. Now a Linux Foundation project. https://github.com/thorvg/thorvg https://github.com/thorvg/thorvg (Disclosure: CTO at LottieFiles, we build and maintain ThorVG in-house, with community contributions from individuals and companies like Canva)
- erichocean 10mo agoHow does ThorVG's GPU implementation compare to Impeller (Flutter's new-ish GPU rendering backend)?