Giter Site home page Giter Site logo

audulus / vger Goto Github PK

View Code? Open in Web Editor NEW
247.0 6.0 15.0 4.82 MB

2D GPU renderer for dynamic UIs

License: MIT License

Swift 3.26% Objective-C 9.23% C 43.92% C++ 6.34% Metal 2.26% Objective-C++ 34.77% Shell 0.23%
macos ios objective-c swift metal vector-graphics 2d-graphics c vger nanovg

vger's Introduction

vger

build status Swift Package Manager (SPM) compatible

vger is a vector graphics renderer which renders a limited set of primitives, but does so almost entirely on the GPU. Works on iOS and macOS. API is plain C. Rust port is here.

demo

Each primitive can be filled with a solid color, gradient, or texture. vger renders primitives as instanced quads, with most of the calculations done in the fragment shader.

Here's an early screenshot from vger in use for Audulus:

Here's it rendering that svg tiger (the cubic curves are converted to quadratic by a lousy method, and I've omitted the strokes):

Why?

I was previously using nanovg for Audulus, which was consuming too much CPU for the immediate-mode UI. nanovg is certainly more full featured, but for Audulus, vger maintains 120fps while nanovg falls to 30fps on my 120Hz iPad because of CPU-side path tessellation, and other overhead. vger renders analytically without tessellation, leaning heavily on the fragment shader.

vger isn't cross-platform (just iOS and macOS), but the API is simple enough that it could be ported fairly easily. If Audulus goes cross-platform again, I will port vger to vulkan or wgpu.

How it works

vger draws a quad for each primitive and computes the actual primitive shape in the fragment function. For path fills, vger splits paths into horizontal slabs (see vgerPathScanner) to reduce the number of tests in the fragment function.

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.

Status

  • ✅ Quadratic bezier strokes
  • ✅ Round Rectangles
  • ✅ Circles
  • ✅ Line segments (need square ends for Audulus)
  • ✅ Arcs
  • ✅ Text (Audulus only uses one font, but could add support for more if anyone is interested)
  • ✅ Multi-line text
  • ✅ Path Fills.

Installation

To add vger to your Xcode project, select File -> Swift Packages -> Add Package Depedancy. Enter https://github.com/audulus/vger for the URL. Check the use branch option and enter main.

Usage

See vger.h for the complete API. You can get a good sense of the usage by looking at these tests.

Vger has a C interface and can be used from C, C++, ObjC, or Swift. vgerEncode must be called from either ObjC or Swift since it takes a MTLCommandBuffer.

See the demo app for an example of using vger in a iOS/macOS SwiftUI app. vger includes VgerView to make it really easy to use Vger within SwiftUI:

import SwiftUI
import vger      // C/C++/ObjC interface.
import vgerSwift // Swift nicities.

struct HelloView: View {

    let cyan = SIMD4<Float>(0,1,1,1)

    var body: some View {
        VgerView(renderCallback: { vger in
            vgerText(vger, "Hello world. This is V'Ger.", cyan, 0)
        })
    }
}

vger's People

Contributors

aure avatar finestructure avatar wtholliday avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

vger's Issues

missing vgerStroke function like vgerFill

draw multiple bezier lines, use vgerStrokeBezier(vgerContext vg, vgerBezierSegment s, float width, vgerPaintIndex paint)

when paint color is transparent,start/end point draw more times.

let linepaint2 = vgerColorPaint(vger, .init(x: 0, y: 1, z: 0, w: 0.5))
let linepaint3 = vgerColorPaint(vger, .init(x: 0, y: 0, z: 1, w: 0.5))

        vgerStrokeBezier(vger, vgerBezierSegment(a:
                .init(x: 240, y: 360),
                 b: .init(x: 260, y: 350),
                 c: .init(x: 280, y: 450)), 20.0, linepaint2)
        vgerStrokeBezier(vger, vgerBezierSegment(a:
                .init(x: 280, y: 450),
                 b: .init(x: 285, y: 490),
                 c: .init(x: 320, y: 490)), 20.0, linepaint2)
        vgerStrokeBezier(vger, vgerBezierSegment(a:
                .init(x: 320, y: 490),
                 b: .init(x: 325, y: 550),
                 c: .init(x: 340, y: 650)), 20.0, linepaint3)

Screenshot 2023-06-18 at 11 26 04

when render small biezer line , the display is very weird

I run the demo, and add some code in DemoView

`

    let bezPaint = vgerLinearGradient(vger, .init(x: 50, y: 450), .init(x: 100, y: 450), cyan, magenta, 0.0)
    // draw small biezer line 
    vgerStrokeBezier(vger, vgerBezierSegment(
        a: .init(x: 231, y: 81),
        b: .init(x: 233, y: 86),
        c: .init(x: 237 , y: 98  )), 4.0, bezPaint)
    
    vgerStrokeBezier(vger, vgerBezierSegment(a: .init(x: 50, y: 450), b: .init(x: 100, y: 450), c: .init(x: 100, y: 500)), 1.0, bezPaint)
    
    
    
    textAt(vger, 150, 450, "Quadratic Bezier stroke")

`

Simulator Screen Shot - iPad Air (5th generation) - 2023-06-17 at 22 33 36

GPU path "scanning"

Currently to render path fills efficiently, vger "scans" the path (see vgerPathScanner) and divides it into horizontal slabs on the CPU. This is the most significant CPU expense for rendering SVGs.

Another option would be to generate this acceleration information on the GPU. A thread group would process a single path and compute a grid, where each square (or tile) contains a list of the relevant segment indices.

In the current CPU implementation, a detailed path can have many slabs:

image

This is challenging for the GPU since each path would need handling at a different resolution.

vertical centering of text isn't correct

Running testTextAlign yields:

image

The text is lower than it should be relative to the magenta dot which is the origin. Seems that alignOffset is returning the wrong offset along the y-axis.

My understanding of the typographic bounds is based on this:

from here

Vertical center should be at (ascent - descent)/2. Thus offset to move it down to the x-axis would be (descent - ascent) / 2.

tile-based rendering

I've implemented an experimental tile-based pipeline.

How it works

Instead of rendering primitives, the fragment function (vger_tile_fragment) adds commands to Tiles. I use raster_order_group to ensure the commands are entered in draw order (no need to deal with atomic operations or sorting :). Each tile is 16x16 pixels and is computed using a kernel (vger_tile_render).

Issues

  • Tile overflow. Tiles are fixed size. What happens when too many commands are added to a tile? Could use a linked list for tiles instead of fixed buffers. For now I've put off this problem by not storing path cvs in the tile buffer.
  • Transformations. Bezier paths can be transformed by transforming their control points, but what about other prims? Would the prim command for round-rect need an inverse transform to get back to local space to compute the df? And then what about AA, since df's aren't preserved by affine transformations? Or should vger convert the other prims to paths? Where should that conversion happen?
  • Small complex paths: a single glyph might fit into a 16x16 tile, which would mean each tile pixel would be tested against all segments of the glyph. The slab-based renderer wouldn't suffer from this issue, as the glyph would still be divided into slabs to minimize tests.

An alternative?

The tile rendering is just to speed up paths without using any CPU. Primitives such as rounded rectangles are so cheap they can be rendered well using the standard GPU pipeline. Could some GPU computation be done for paths to speed up their rendering while still using the normal pipeline? This would probably involve computing slabs for each path on the GPU.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.