diff options
Diffstat (limited to 'chickadee/graphics/gpu.scm')
-rw-r--r-- | chickadee/graphics/gpu.scm | 151 |
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)) |