longlene / cl-raylib

Common Lisp binding of raylib
MIT License
145 stars 21 forks source link

Consider adding finalizers to resource heavy objects #34

Open nagy opened 2 years ago

nagy commented 2 years ago

I think it might make sense to add finalizers to objects like images. Finalizers allow the garbage-collector to call a function, when an object is no longer referenced. This way, more memory can be automatically freed, for example of images. It could come in useful in situations like the following pseudocode:

(defparameter *level-images* (make-hash-table))
;; prepare level1 
(setf (gethash 1 *level-images*) (load-image "level1-image1.jpg"))
(setf (gethash 2 *level-images*) (load-image "level1-image2.jpg"))
;; load more images of level 1...
;; ... now the player enters level 2
;; all we need to do here is make a new hash table.
(setq *level-images* (make-hash-table))
;; Since the old one is unreferenced, all its contents could be eventually freed.

We do not need to iterate over the content of the container and call #'unload-image. I have had success with the following wrapper.

(defun load-image-gced (filename)
  (let* ((res (load-image filename))
         (cpy (raylib::copy-image res)))
    (sb-ext:finalize res (lambda () (unload-image cpy)))
    res))

Keep in mind, that with the #'copy-image function, the struct is not deeply copied, so it will not use much more memory. Also keep in mind, that this is only additional and you are still free to do some manual memory management where it makes sense. So this would not break existing code. With help from "trivial-garbage" we can write portable code for this.

https://trivial-garbage.common-lisp.dev/

https://www.sbcl.org/manual/#Finalization