jak-project/goal_src/engine/dma/dma-bucket.gc
2021-08-14 23:15:10 -04:00

90 lines
3.5 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
;; name: dma-bucket.gc
;; name in dgo: dma-bucket
;; dgos: GAME, ENGINE
;; A dma-bucket is used to organize dma data.
;; When an object is drawn, it may add data to multiple buckets.
;; When the dma data is transferred, it is transferred bucket by bucket.
;; A dma-bucket is a 16 byte thing that lives in the dma-buffer.
;; buckets live consecutively in the dma-buffer, and can mark the start of a DMA chain
;; location anywhere.
;; The bucket doesn't own the data, but rather is a beginning of a linked list of DMA data associated with that bucket
;; that's stored somewhere else.
;; At the end, all the buckets are spliced together.
;; The typical process is:
;; - empty buckets are allocated with add-buckets
;; - tags are put somewhere and added to the appropriate bucket with insert-tag, updating last as needed.
;; - buckets are patched to link to each other with dma-buffer-patch-buckets.
;; the idea here is that you can build the buckets in whatever order you want, but the buckets
;; will be DMAd in the bucket allocation order.
;; Each bucket contains:
;; a tag, (64-bits), to point to the chain of the bucket
;; last, a pointer to the last tag of this bucket, so that the bucket can be patched to point to the next.
(defun dma-buffer-add-buckets ((dma-buf dma-buffer) (count int))
"Add count buckets. Each bucket is initialized as empty and won't transfer anything."
(let* ((initial-bucket (the-as dma-bucket (-> dma-buf base)))
(current-bucket initial-bucket))
;;(let ((current-bucket (the-as dma-bucket (-> dma-buf base))))
(dotimes (i count)
;; set the DMA tag to next, with a qwc of zero.
;; the address is set to the next bucket.
;; By default, this will do no transfer and just move on in the dma-buf.
;; Data will be added to the bucket later.
(set! (-> current-bucket tag)
(new 'static 'dma-tag
:id (dma-tag-id next)
:addr (the-as int (&+ (the-as pointer current-bucket) 16))
)
)
;; Set the last pointer to point to this tag (this lives in the 8 byte gap)
(set! (-> current-bucket last) (the-as (pointer dma-tag) current-bucket))
;; Advance to next bucket.
(&+! current-bucket 16)
)
;; update base ptr of dma-buffer to point after the buckets.
(set! (-> dma-buf base) (the-as pointer current-bucket))
(the (inline-array dma-bucket) initial-bucket)
)
)
(defun dma-buffer-patch-buckets ((bucket (inline-array dma-bucket)) (count int))
"After adding all data to buckets, call this to stitch together the chains for
count consecutive buckets"
(when (nonzero? bucket)
(dotimes (i count)
;; set last tag's address to the next bucket.
(set! (-> bucket 0 last 0 addr) (the-as int (the-as pointer (-> bucket 1))))
;; clear last pointer.
(set! (-> bucket 0 last) (the-as (pointer dma-tag) 0))
;; next bucket
(set! bucket (the-as (inline-array dma-bucket) (-> bucket 1)))
)
)
bucket
)
(defun dma-bucket-insert-tag ((base (inline-array dma-bucket)) (idx bucket-id) (tag-start pointer) (tag-end (pointer dma-tag)))
"Add a DMA chain to the bucket"
;; find the bucket
(let ((bucket (-> base idx)))
;; update our last bucket to point to this one.
;; this is abusing the dma-bucket type to set the "addr" field of the dma-tag.
(set! (-> (the-as dma-bucket (-> bucket last)) next) (the-as uint tag-start))
;; remember this as the last tag in the bucket.
(set! (-> bucket last) tag-end)
)
tag-start
)