summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chickadee/graphics/path.scm367
1 files changed, 122 insertions, 245 deletions
diff --git a/chickadee/graphics/path.scm b/chickadee/graphics/path.scm
index ca1b0be..34e7f3c 100644
--- a/chickadee/graphics/path.scm
+++ b/chickadee/graphics/path.scm
@@ -755,80 +755,30 @@
;;; Stroked path
;;;
+(define-geometry-type <stroke-vertex>
+ stroke-vertex-ref
+ stroke-vertex-set!
+ stroke-vertex-append!
+ (position vec2)
+ (texture vec2)
+ (length float))
+
;; TODO: Allow for multiple path styles to be rendered in a single
;; draw call. This will probably involve abusing textures to store
;; the per-path style info. We can cross that bridge if we ever need
;; the extra performance.
(define-record-type <stroked-path>
- (%make-stroked-path vertex-count index-count
- vertex-capacity index-capacity
- vertex-buffer index-buffer vertex-array)
+ (%make-stroked-path geometry)
stroked-path?
(blend-mode stroked-path-blend-mode set-stroked-path-blend-mode!)
(color stroked-path-color set-stroked-path-color!)
(width stroked-path-width set-stroked-path-width!)
(feather stroked-path-feather set-stroked-path-feather!)
(cap stroked-path-cap set-stroked-path-cap!)
- (vertex-count stroked-path-vertex-count set-stroked-path-vertex-count!)
- (index-count stroked-path-index-count set-stroked-path-index-count!)
- (vertex-capacity stroked-path-vertex-capacity set-stroked-path-vertex-capacity!)
- (index-capacity stroked-path-index-capacity set-stroked-path-index-capacity!)
- (vertex-buffer stroked-path-vertex-buffer)
- (index-buffer stroked-path-index-buffer)
- (vertex-array stroked-path-vertex-array))
-
-(define (resize-stroked-path-vertex-buffer! stroked-path capacity)
- (resize-buffer! (stroked-path-vertex-buffer stroked-path)
- (* capacity (* 5 4))) ; 5 floats per vertex
- (set-stroked-path-vertex-capacity! stroked-path capacity))
-
-(define (resize-stroked-path-index-buffer! stroked-path capacity)
- (resize-buffer! (stroked-path-index-buffer stroked-path)
- (* capacity 4)) ; 4 bytes per index
- (set-stroked-path-index-capacity! stroked-path capacity))
+ (geometry stroked-path-geometry))
(define (make-stroked-path)
- (let* ((vertex-buffer (make-buffer #f
- #:name "stroke vertex buffer"
- ;; Vertex layout:
- ;; - x, y
- ;; - u, v
- ;; - length of current path
- #:stride (* 5 4) ; 5 f32s
- #:usage 'stream))
- (index-buffer (make-buffer #f
- #:name "stroke index buffer"
- #:target 'index
- #:usage 'stream))
- (verts (make-buffer-view #:name "stroke vertices"
- #:buffer vertex-buffer
- #:type 'vec2
- #:component-type 'float))
- (tex (make-buffer-view #:name "stroke texcoords"
- #:buffer vertex-buffer
- #:type 'vec2
- #:component-type 'float
- #:offset 8))
- (lengths (make-buffer-view #:name "stroke lengths"
- #:buffer vertex-buffer
- #:type 'scalar
- #:component-type 'float
- #:offset 16))
- (index (make-buffer-view #:name "stroke index"
- #:buffer index-buffer
- #:type 'scalar
- #:component-type 'unsigned-int))
- (vertex-array (make-vertex-array #:indices index
- #:attributes `((0 . ,verts)
- (1 . ,tex)
- (2 . ,lengths))))
- (stroked-path (%make-stroked-path 0 0 0 0
- vertex-buffer
- index-buffer
- vertex-array)))
- (resize-stroked-path-vertex-buffer! stroked-path 128)
- (resize-stroked-path-index-buffer! stroked-path 128)
- stroked-path))
+ (%make-stroked-path (make-geometry <stroke-vertex> 32)))
;; Tesselation of stroked paths involves building rectangles composed
;; of 2 triangles for each line segment in the path. This
@@ -843,70 +793,41 @@
(set-stroked-path-width! stroked-path width)
(set-stroked-path-feather! stroked-path feather)
(set-stroked-path-cap! stroked-path cap)
- ;; Initialize counts.
- (set-stroked-path-vertex-count! stroked-path 0)
- (set-stroked-path-index-count! stroked-path 0)
;; Tesselate.
- (with-mapped-buffer (stroked-path-vertex-buffer stroked-path)
- (with-mapped-buffer (stroked-path-index-buffer stroked-path)
- (let ((points (compiled-path-points compiled-path))
- (offsets (compiled-path-offsets compiled-path))
- (counts (compiled-path-counts compiled-path))
- (path-count (compiled-path-count compiled-path))
- (padding (/ (ceiling (+ width (* feather 2.5))) 2.0)))
- (define (add-points first? lx ly rx ry distance)
- ;; Resize buffers, if necessary.
- (let ((vert-count (stroked-path-vertex-count stroked-path))
- (vert-capacity (stroked-path-vertex-capacity stroked-path))
- (index-count (stroked-path-index-count stroked-path))
- (index-capacity (stroked-path-index-capacity stroked-path)))
- (when (> (+ vert-count 2) vert-capacity)
- (resize-stroked-path-vertex-buffer! stroked-path
- (* vert-capacity 2))
- (map-buffer! (stroked-path-vertex-buffer stroked-path)))
- (when (> (+ index-count 6) index-capacity)
- (resize-stroked-path-index-buffer! stroked-path
- (* index-capacity 2))
- (map-buffer! (stroked-path-index-buffer stroked-path)))
- (let ((verts (buffer-data
- (stroked-path-vertex-buffer stroked-path)))
- (voffset (* vert-count 5))) ; 5 floats per vertex
- ;; Left:
- ;; Position
- (f32vector-set! verts voffset lx)
- (f32vector-set! verts (+ voffset 1) ly)
- ;; Distance from starting point
- (f32vector-set! verts (+ voffset 2) distance)
- ;; Distance from true line segment (used for antialising)
- (f32vector-set! verts (+ voffset 3) padding)
- ;; Right:
- ;; Position
- (f32vector-set! verts (+ voffset 5) rx)
- (f32vector-set! verts (+ voffset 6) ry)
- ;; Distance from starting point
- (f32vector-set! verts (+ voffset 7) distance)
- ;; Distance from true line segment
- (f32vector-set! verts (+ voffset 8) (- padding)))
- (set-stroked-path-vertex-count! stroked-path (+ vert-count 2))
- ;; On the first iteration we only have 2 points which is
- ;; not enough to create line segment geometry which
- ;; requires looking at the newest 2 points + the 2
- ;; previous points.
- (unless first?
- (let ((index (buffer-data
- (stroked-path-index-buffer stroked-path))))
- (u32vector-set! index index-count (- vert-count 1))
- (u32vector-set! index (+ index-count 1) (- vert-count 2))
- (u32vector-set! index (+ index-count 2) vert-count)
- (u32vector-set! index (+ index-count 3) (- vert-count 1))
- (u32vector-set! index (+ index-count 4) vert-count)
- (u32vector-set! index (+ index-count 5) (+ vert-count 1))
- (set-stroked-path-index-count! stroked-path (+ index-count 6))))))
- (define (set-length i length)
- (let ((verts (buffer-data (stroked-path-vertex-buffer stroked-path)))
- (voffset (* i 5 2)))
- (f32vector-set! verts (+ voffset 4) length)
- (f32vector-set! verts (+ voffset 9) length)))
+ (let ((points (compiled-path-points compiled-path))
+ (offsets (compiled-path-offsets compiled-path))
+ (counts (compiled-path-counts compiled-path))
+ (path-count (compiled-path-count compiled-path))
+ (padding (/ (ceiling (+ width (* feather 2.5))) 2.0))
+ (geometry (stroked-path-geometry stroked-path)))
+ (define (add-points first? lx ly rx ry distance)
+ (let ((vert-count (geometry-vertex-count geometry <stroke-vertex>)))
+ ;; Each vertex has the following data:
+ ;; - x
+ ;; - y
+ ;; - distance from starting point
+ ;; - distance from true line segment (used for antialising)
+ ;;
+ ;; First vertex is the left hand side, second is the right.
+ (stroke-vertex-append! geometry
+ (lx ly distance padding 0.0)
+ (rx ry distance (- padding) 0.0))
+ ;; On the first iteration we only have 2 points which is
+ ;; not enough to create line segment geometry which
+ ;; requires looking at the newest 2 points + the 2
+ ;; previous points.
+ (unless first?
+ (geometry-index-append! geometry
+ (- vert-count 1)
+ (- vert-count 2)
+ vert-count
+ (- vert-count 1)
+ vert-count
+ (+ vert-count 1)))))
+ (define (set-length i length)
+ (stroke-vertex-set! geometry length (* i 2) length)
+ (stroke-vertex-set! geometry length (+ (* i 2) 1) length))
+ (with-geometry geometry
(let path-loop ((i 0))
(when (< i path-count)
(let* ((count (u32vector-ref counts i))
@@ -1059,70 +980,37 @@
(when (<= k last)
(set-length k distance)
(length-loop (+ k 1)))))))
- (path-loop (+ i 1))))))))
+ (path-loop (+ i 1)))))))
;;;
;;; Filled path
;;;
+(define-geometry-type <fill-vertex>
+ fill-vertex-ref
+ fill-vertex-set!
+ fill-vertex-append!
+ (position vec2))
+
(define-record-type <filled-path>
- (%make-filled-path count stencil-vertex-count stencil-vertex-capacity
- stencil-vertex-buffer stencil-vertex-array
- quad-vertex-buffer quad-vertex-array)
+ (%make-filled-path count quad-geometry stencil-geometry)
filled-path?
(blend-mode filled-path-blend-mode set-filled-path-blend-mode!)
(color filled-path-color set-filled-path-color!)
(counts filled-path-counts set-filled-path-counts!)
(offsets filled-path-offsets set-filled-path-offsets!)
(count filled-path-count set-filled-path-count!)
- (stencil-vertex-count filled-path-stencil-vertex-count
- set-filled-path-stencil-vertex-count!)
- (stencil-vertex-capacity filled-path-stencil-vertex-capacity
- set-filled-path-stencil-vertex-capacity!)
- (stencil-vertex-buffer filled-path-stencil-vertex-buffer)
- (stencil-vertex-array filled-path-stencil-vertex-array)
- (quad-vertex-buffer filled-path-quad-vertex-buffer)
- (quad-vertex-array filled-path-quad-vertex-array))
-
-(define (resize-filled-path-stencil-vertex-buffer! filled-path capacity)
- (resize-buffer! (filled-path-stencil-vertex-buffer filled-path)
- (* capacity 8)) ; 2 f32s per vertex: x y
- (set-filled-path-stencil-vertex-capacity! filled-path capacity))
+ (stencil-geometry filled-path-stencil-geometry)
+ (quad-geometry filled-path-quad-geometry))
(define (make-filled-path)
- (let* ((quad-vertex-buffer (make-buffer #f
- #:length (* 8 4) ; 8 f32s
- #:name "fill quad vertices"
- #:usage 'stream))
- (quad-verts (make-buffer-view #:name "fill quad vertices"
- #:buffer quad-vertex-buffer
- #:type 'vec2
- #:component-type 'float))
- (quad-index (make-buffer-view #:name "fill quad index"
- #:type 'scalar
- #:component-type 'unsigned-int
- #:buffer (make-buffer (u32vector 0 2 3 0 1 2)
- #:name "fill quad index"
- #:target 'index)))
- (quad-vertex-array (make-vertex-array #:indices quad-index
- #:attributes `((0 . ,quad-verts))))
- (stencil-vertex-buffer (make-buffer #f
- #:name "fill stencil vertices"
- #:usage 'stream))
- (stencil-verts (make-buffer-view #:name "fill stencil vertices"
- #:buffer stencil-vertex-buffer
- #:type 'vec2
- #:component-type 'float))
- (stencil-vertex-array (make-vertex-array #:attributes `((0 . ,stencil-verts))
- #:mode 'triangle-fan))
- (filled-path (%make-filled-path 0 0 0
- stencil-vertex-buffer
- stencil-vertex-array
- quad-vertex-buffer
- quad-vertex-array)))
- (resize-filled-path-stencil-vertex-buffer! filled-path 128)
- filled-path))
+ (let* ((quad-geometry (make-geometry <fill-vertex> 4
+ #:index-capacity 6))
+ (stencil-geometry (make-geometry <fill-vertex> 32
+ #:index? #f
+ #:mode 'triangle-fan)))
+ (%make-filled-path 0 quad-geometry stencil-geometry)))
(define* (fill-path filled-path compiled-path #:key blend-mode color)
(let* ((points (compiled-path-points compiled-path))
@@ -1138,14 +1026,16 @@
;; GPU, though, and the center of the bounding box seems like
;; a sensible location.
(ref-x (rect-center-x bbox))
- (ref-y (rect-center-y bbox)))
+ (ref-y (rect-center-y bbox))
+ (quad-geometry (filled-path-quad-geometry filled-path))
+ (stencil-geometry (filled-path-stencil-geometry filled-path)))
;; Setup style.
(set-filled-path-color! filled-path color)
(set-filled-path-blend-mode! filled-path blend-mode)
;; Setup counts and offsets.
(set-filled-path-count! filled-path 0)
- (set-filled-path-stencil-vertex-count! filled-path 0)
(set-filled-path-count! filled-path path-count)
+ ;; TODO: Don't allocate each time.
(let ((bv (make-u32vector path-count)))
(let loop ((i 0))
(when (< i path-count)
@@ -1159,59 +1049,43 @@
(loop (+ i 1))))
(set-filled-path-offsets! filled-path bv))
;; Create geometry for the stencil buffer.
- (with-mapped-buffer (filled-path-stencil-vertex-buffer filled-path)
- (let loop ((i 0))
- (when (< i path-count)
- (let* ((count (u32vector-ref counts i))
- (first (u32vector-ref offsets i))
- (last (+ first count -1))
- (n (filled-path-stencil-vertex-count filled-path)))
- ;; Resize buffer, if necessary.
- (let ((capacity (filled-path-stencil-vertex-capacity filled-path)))
- (when (>= (+ n count 1) capacity)
- (let ((new-capacity (let cloop ((c (* capacity 2)))
- (if (> c (+ n count 1))
- c
- (cloop (* c 2))))))
- (resize-filled-path-stencil-vertex-buffer! filled-path
- new-capacity)
- (map-buffer! (filled-path-stencil-vertex-buffer filled-path)))))
- (let ((verts (buffer-data (filled-path-stencil-vertex-buffer filled-path)))
- (offset (* n 2)))
- ;; Build the triangle fan for the path. This geometry
- ;; will be used for a GPU-based implementation of the
- ;; non-zero rule:
- ;;
- ;; See: https://en.wikipedia.org/wiki/Nonzero-rule
- ;;
- ;; Add reference point as the basis for each triangle in
- ;; the fan.
- (f32vector-set! verts offset ref-x)
- (f32vector-set! verts (+ offset 1) ref-y)
- ;; Now simply copy all the points in the path into the
- ;; buffer. Each point is stored as 2 f32s, so 8 bytes per
- ;; point.
- (bytevector-copy! points (* first 8) verts (* (+ n 1) 8) (* count 8))
- (set-filled-path-stencil-vertex-count! filled-path (+ n count 1))))
- (loop (+ i 1)))))
+ (geometry-begin! stencil-geometry)
+ (let loop ((i 0))
+ (when (< i path-count)
+ (let* ((count (u32vector-ref counts i))
+ (first (u32vector-ref offsets i))
+ (last (+ first count -1)))
+ ;; Build the triangle fan for the path. This geometry
+ ;; will be used for a GPU-based implementation of the
+ ;; non-zero rule:
+ ;;
+ ;; See: https://en.wikipedia.org/wiki/Nonzero-rule
+ ;;
+ ;; Add reference point as the basis for each triangle in
+ ;; the fan.
+ (fill-vertex-append! stencil-geometry (ref-x ref-y))
+ ;; Now simply copy all the points in the path into the
+ ;; buffer.
+ (let inner ((i first))
+ (when (<= i last)
+ (fill-vertex-append! stencil-geometry
+ ((f32vector-ref points (* i 2))
+ (f32vector-ref points (+ (* i 2) 1))))
+ (inner (+ i 1)))))
+ (loop (+ i 1))))
+ (geometry-end! stencil-geometry)
;; Create simple quad covering the bounding box to be used for the
;; final render pass with stencil applied.
;;
;; TODO: A convex hull would result in less fragments to process.
- (with-mapped-buffer (filled-path-quad-vertex-buffer filled-path)
- (let ((verts (buffer-data (filled-path-quad-vertex-buffer filled-path)))
- (x1 (rect-x bbox))
- (y1 (rect-y bbox))
- (x2 (rect-right bbox))
- (y2 (rect-top bbox)))
- (f32vector-set! verts 0 x1)
- (f32vector-set! verts 1 y1)
- (f32vector-set! verts 2 x2)
- (f32vector-set! verts 3 y1)
- (f32vector-set! verts 4 x2)
- (f32vector-set! verts 5 y2)
- (f32vector-set! verts 6 x1)
- (f32vector-set! verts 7 y2)))))
+ (geometry-begin! quad-geometry)
+ (let ((x1 (rect-x bbox))
+ (y1 (rect-y bbox))
+ (x2 (rect-right bbox))
+ (y2 (rect-top bbox)))
+ (fill-vertex-append! quad-geometry (x1 y1) (x2 y1) (x2 y2) (x1 y2))
+ (geometry-index-append! quad-geometry 0 2 3 0 1 2))
+ (geometry-end! quad-geometry)))
;;;
@@ -1237,7 +1111,9 @@
(define* (draw-filled-path filled-path matrix)
(let ((counts (filled-path-counts filled-path))
(offsets (filled-path-offsets filled-path))
- (n (filled-path-count filled-path)))
+ (n (filled-path-count filled-path))
+ (quad-geometry (filled-path-quad-geometry filled-path))
+ (stencil-geometry (filled-path-stencil-geometry filled-path)))
(matrix4-mult! *mvp* matrix (current-projection))
;; Wireframe debug mode.
(when *debug?*
@@ -1245,7 +1121,7 @@
(let loop ((i 0))
(when (< i n)
(gpu-apply* (force path-shader)
- (filled-path-stencil-vertex-array filled-path)
+ (geometry-vertex-array stencil-geometry)
(u32vector-ref offsets i)
(u32vector-ref counts i)
#:mvp (current-projection)
@@ -1269,7 +1145,7 @@
(let loop ((i 0))
(when (< i n)
(gpu-apply* (force path-shader)
- (filled-path-stencil-vertex-array filled-path)
+ (geometry-vertex-array stencil-geometry)
(u32vector-ref offsets i)
(u32vector-ref counts i)
#:mvp *mvp*
@@ -1281,7 +1157,7 @@
(with-stencil-test stencil-cover-and-clear
(with-blend-mode (filled-path-blend-mode filled-path)
(gpu-apply (force path-shader)
- (filled-path-quad-vertex-array filled-path)
+ (geometry-vertex-array quad-geometry)
#:mvp *mvp*
#:mode 0
#:color (filled-path-color filled-path)))))))
@@ -1291,23 +1167,24 @@
(define* (draw-stroked-path stroked-path matrix)
(matrix4-mult! *mvp* matrix (current-projection))
(with-blend-mode (stroked-path-blend-mode stroked-path)
- (gpu-apply* (force path-shader)
- (stroked-path-vertex-array stroked-path)
- 0
- (stroked-path-index-count stroked-path)
- #:mvp *mvp*
- #:color (stroked-path-color stroked-path)
- #:mode 1
- #:feather (stroked-path-feather stroked-path)
- #:stroke-cap (match (stroked-path-cap stroked-path)
- (#f 0) ; no cap
- ('butt 1)
- ('square 2)
- ('round 3)
- ('triangle-out 4)
- ('triangle-in 5)
- (x (error "unsupported line cap style" x)))
- #:stroke-width (stroked-path-width stroked-path))))
+ (let ((geometry (stroked-path-geometry stroked-path)))
+ (gpu-apply* (force path-shader)
+ (geometry-vertex-array geometry)
+ 0
+ (geometry-index-count geometry)
+ #:mvp *mvp*
+ #:color (stroked-path-color stroked-path)
+ #:mode 1
+ #:feather (stroked-path-feather stroked-path)
+ #:stroke-cap (match (stroked-path-cap stroked-path)
+ (#f 0) ; no cap
+ ('butt 1)
+ ('square 2)
+ ('round 3)
+ ('triangle-out 4)
+ ('triangle-in 5)
+ (x (error "unsupported line cap style" x)))
+ #:stroke-width (stroked-path-width stroked-path)))))
;;;