Table of Contents
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. resultmay 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)normalisesvin 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.