summaryrefslogtreecommitdiff
path: root/chickadee/graphics/gpu.scm
diff options
context:
space:
mode:
Diffstat (limited to 'chickadee/graphics/gpu.scm')
-rw-r--r--chickadee/graphics/gpu.scm151
1 files changed, 143 insertions, 8 deletions
diff --git a/chickadee/graphics/gpu.scm b/chickadee/graphics/gpu.scm
index cf922e3..3832256 100644
--- a/chickadee/graphics/gpu.scm
+++ b/chickadee/graphics/gpu.scm
@@ -25,6 +25,7 @@
#:use-module (chickadee math rect)
#:use-module (gl)
#:use-module (ice-9 match)
+ #:use-module (rnrs bytevectors)
#:use-module (srfi srfi-9)
#:use-module (system foreign)
#:export (blend-mode?
@@ -123,6 +124,9 @@
free-gpu-buffer
gpu-buffer?
gpu-buffer-id
+ gpu-buffer-upload
+ gpu-buffer-map
+ gpu-buffer-unmap
gpu-buffer-target
gpu-buffer:null
@@ -130,6 +134,7 @@
free-gpu-vertex-array
gpu-vertex-array?
gpu-vertex-array-id
+ gpu-vertex-array-apply-attribute
gpu-vertex-array:null
fresh-gpu-texture
@@ -208,7 +213,11 @@
set-gpu-program!
set-gpu-texture!
gpu-gc
- gpu-reset!))
+ gpu-reset!
+ gpu-draw
+ gpu-draw/indexed
+ gpu-draw/instanced
+ gpu-draw/instanced+indexed))
;;;
@@ -259,6 +268,45 @@
(->scheme (%eaccessor obj)))
...))
+(define-enum-converters begin-mode
+ symbol->begin-mode
+ begin-mode->symbol
+ (points)
+ (lines)
+ (line-loop)
+ (line-strip)
+ (triangles)
+ (triangle-strip)
+ (triangle-fan))
+
+(define-enum-converters data-type
+ symbol->data-type
+ data-type->symbol
+ (byte)
+ (unsigned-byte)
+ (short)
+ (unsigned-short)
+ (int)
+ (unsigned-int)
+ (float)
+ (double))
+
+(define (symbol->buffer-target target)
+ (match target
+ ('vertex (version-1-5 array-buffer))
+ ('index (version-1-5 element-array-buffer))))
+
+(define (symbol->buffer-usage usage)
+ (match usage
+ ('static (version-1-5 static-draw))
+ ('stream (version-1-5 stream-draw))))
+
+(define (symbol->access-mode mode)
+ (match mode
+ ('read-write (version-1-5 read-write))
+ ('read-only (version-1-5 read-only))
+ ('write-only (version-1-5 write-only))))
+
(define-enum-converters texture-min-filter
symbol->texture-min-filter
texture-min-filter->symbol
@@ -709,17 +757,20 @@
(id gpu-renderbuffer-id))
(define-gpu-type <gpu-buffer>
- (make-gpu-buffer (target)
+ (make-gpu-buffer (target usage)
(gl-generate-buffer)
- (match target
- ('index (version-1-5 element-array-buffer))
- ('vertex (version-1-5 array-buffer))))
+ (symbol->buffer-target target)
+ (symbol->buffer-usage usage)
+ (and (eq? usage 'stream) (make-hash-table)))
(free-gpu-buffer (gl-delete-buffer id))
(bind-gpu-buffer (gl-bind-buffer target id))
- (gpu-buffer:null 0 (version-1-5 array-buffer))
+ (gpu-buffer:null 0 (symbol->buffer-target 'vertex)
+ (symbol->buffer-usage 'static) #f)
gpu-buffer?
(id gpu-buffer-id)
- (target gpu-buffer-target))
+ (target gpu-buffer-target)
+ (usage gpu-buffer-usage)
+ (stream-cache gpu-buffer-stream-cache))
(define-gpu-type <gpu-vertex-array>
(make-gpu-vertex-array () (gl-generate-vertex-array))
@@ -910,7 +961,7 @@
(define-fresh fresh-gpu-framebuffer make-gpu-framebuffer)
(define-fresh fresh-gpu-renderbuffer make-gpu-renderbuffer)
-(define-fresh fresh-gpu-buffer make-gpu-buffer target)
+(define-fresh fresh-gpu-buffer make-gpu-buffer target usage)
(define-fresh fresh-gpu-vertex-array make-gpu-vertex-array)
(define-fresh fresh-gpu-texture make-gpu-texture target)
(define-fresh fresh-gpu-shader make-gpu-shader type)
@@ -1072,3 +1123,87 @@
(set-gpu-texture! gpu 0 texture)
(set-active-texture-unit! 0)
(gl-generate-mipmap (gpu-texture-target texture)))
+
+(define (gpu-buffer-upload gpu buffer usage data length offset)
+ (set-gpu-buffer! gpu buffer)
+ (gl-buffer-data (gpu-buffer-target buffer)
+ length
+ (if data
+ (bytevector->pointer data offset)
+ %null-pointer)
+ (symbol->buffer-usage usage)))
+
+;; For streaming buffers, we use buffer re-specification to achieve
+;; good throughput. However, it requires getting a new data pointer
+;; every frame and allocating a Scheme bytevector for that memory
+;; region. Allocating this bytevector every frame causes significant
+;; GC pressure. It turns out that GPU drivers tend to return the same
+;; set of pointers over and over, or at least the driver I'm using
+;; does this. So, by caching bytevectors for those memory regions we
+;; avoid bytevector allocation after a frame or two of warmup.
+(define (pointer->bytevector/cached buffer pointer length)
+ (let* ((cache (gpu-buffer-stream-cache buffer))
+ (address (pointer-address pointer))
+ (cached (hashv-ref cache address)))
+ ;; It could be that there is a cached bytevector for the address,
+ ;; but the bytevector is a different length. We must treat this
+ ;; as a cache miss and allocate a new bytevector.
+ (if (and cached (= (bytevector-length cached) length))
+ cached
+ (let ((bv (pointer->bytevector pointer length)))
+ (hashv-set! cache address bv)
+ bv))))
+
+(define (gpu-buffer-map gpu buffer length mode)
+ (let ((target (gpu-buffer-target buffer))
+ (usage (gpu-buffer-usage buffer)))
+ (set-gpu-buffer! gpu buffer)
+ (when (= usage (version-1-5 stream-draw))
+ ;; Orphan the buffer to avoid implicit synchronization.
+ ;; https://www.opengl.org/wiki/Buffer_Object_Streaming#Buffer_re-specification
+ (gl-buffer-data target length %null-pointer (gpu-buffer-usage buffer)))
+ (let ((ptr (gl-map-buffer target (symbol->access-mode mode))))
+ (pointer->bytevector/cached buffer ptr length))))
+
+(define (gpu-buffer-unmap gpu buffer)
+ (set-gpu-buffer! gpu buffer)
+ (gl-unmap-buffer (gpu-buffer-target buffer)))
+
+(define *offset-cache* (make-hash-table))
+
+(define (offset->pointer offset)
+ (or (hashv-ref *offset-cache* offset)
+ (let ((ptr (make-pointer offset)))
+ (hashv-set! *offset-cache* offset ptr)
+ ptr)))
+
+(define (gpu-vertex-array-apply-attribute gpu array buffer index size type
+ normalized? stride offset divisor)
+ (set-gpu-vertex-array! gpu array)
+ (set-gpu-buffer! gpu buffer)
+ (gl-enable-vertex-attrib-array index)
+ (gl-vertex-attrib-pointer index size (symbol->data-type type) normalized?
+ stride (offset->pointer offset))
+ (when divisor
+ (gl-vertex-attrib-divisor index divisor)))
+
+(define (gpu-draw gpu mode count offset)
+ (gl-draw-arrays (symbol->begin-mode mode) offset count))
+
+(define (gpu-draw/indexed gpu indices type mode count offset)
+ (set-gpu-buffer! gpu indices)
+ (gl-draw-elements (symbol->begin-mode mode)
+ count
+ (symbol->data-type type)
+ (offset->pointer offset)))
+
+(define (gpu-draw/instanced gpu mode count offset instances)
+ (gl-draw-arrays-instanced (symbol->begin-mode mode) offset count instances))
+
+(define (gpu-draw/instanced+indexed gpu indices type mode count offset instances)
+ (set-gpu-buffer! gpu indices)
+ (gl-draw-elements-instanced (symbol->begin-mode mode)
+ count
+ (symbol->data-type type)
+ (offset->pointer offset)
+ instances))