3 Results and Allocation
dxtr edited this page 2026-10-17 04:43:21 +02:00

Results and Allocation

The result argument

Every function that returns a vector, matrix or quaternion takes an optional last argument, result, of the type it returns:

  • Without it, the function returns a new value.
  • With it, the function writes the value into result, returns it, and allocates nothing.
  • result may be one of the arguments, where the types match: every part of the value is computed before any is stored. So (gm:vec3-normalize v v) normalises v in place.
(let ((world (gm:mat4-identity))
      (position (gm:make-vec3)))
  (gm:mat4-compose (gm:make-vec3 :x 1 :y 2 :z 3) (gm:make-quat) (gm:make-vec3 :x 1 :y 1 :z 1)
                   world)                                   ; into WORLD
  (gm:mat4-transform-point world (gm:make-vec3) position)   ; into POSITION
  (gm:vec3-normalize position position))                    ; in place
;; => #(0.26726124 0.5345225 0.8017837)

Functions that return a number, such as vec3-length or mat4-determinant, return a single float, which allocates nothing either. Nor do the functions of doubles (see Doubles).

Code that runs every frame

Code that runs every frame should allocate nothing once it is running, so that the garbage collector has nothing to collect. Keep the values it works on in the objects it works for, made once, and pass every result:

(defstruct (body (:constructor make-body ()))
  "A moving body: where it is, how it moves, and its world matrix."
  (position (gm:make-vec3) :type gm:vec3)
  (velocity (gm:make-vec3 :z -1) :type gm:vec3)
  (rotation (gm:make-quat) :type gm:quat)
  (scale (gm:make-vec3 :x 1 :y 1 :z 1) :type gm:vec3)
  (step (gm:make-vec3) :type gm:vec3)   ; scratch, for UPDATE-BODY
  (world (gm:mat4-identity) :type gm:mat4))

(defun update-body (body dt)
  "Move BODY by its velocity for DT seconds, and make its world matrix."
  (let ((step (body-step body)))
    (gm:vec3-scale (body-velocity body) dt step)
    (gm:vec3+ (body-position body) step (body-position body))
    (gm:mat4-compose (body-position body) (body-rotation body) (body-scale body)
                     (body-world body))))

(let ((body (make-body)))
  (dotimes (frame 60)
    (update-body body 1/60))
  (body-position body))
;; => #(0.0 0.0 -0.9999997)

update-body allocates nothing.

Constant values made inside such code, such as (gm:make-vec3 :y 1) for up, are made again on every call. Make them once: in an object, as body's slots are, or with load-time-value for a value that is never written.

Threads

Functions keep no state. A value may be read from several threads at once; a value being written, as a result or with setf, belongs to one thread. Batches split their work over a job system's threads by range; see Batches and SIMD.