Monorepo for Aesthetic.Computer aesthetic.computer
at main 83 lines 2.8 kB view raw
1;;; Utility functions — logging, timing 2 3(in-package :ac-native.util) 4 5(defvar *log-stream* *error-output* 6 "Where ac-log writes. Rebound to a file stream when USB is mounted.") 7 8(defvar *log-file* nil "Open file stream for USB log, or nil.") 9 10(defun ac-log (fmt &rest args) 11 "Log a message to stderr and optionally to USB log file." 12 (let ((msg (apply #'format nil fmt args))) 13 (write-string msg *error-output*) 14 (force-output *error-output*) 15 (when *log-file* 16 (write-string msg *log-file*) 17 (force-output *log-file*)))) 18 19(defun open-usb-log (path) 20 "Open a log file on the USB drive (append mode)." 21 (setf *log-file* (open path :direction :output 22 :if-exists :append 23 :if-does-not-exist :create))) 24 25(defun close-usb-log () 26 (when *log-file* 27 (close *log-file*) 28 (setf *log-file* nil))) 29 30;;; Monotonic clock 31 32(cffi:defcstruct timespec 33 (tv-sec :long) 34 (tv-nsec :long)) 35 36(cffi:defcfun ("clock_gettime" %clock-gettime) :int 37 (clk-id :int) 38 (tp :pointer)) 39 40(defconstant +clock-monotonic+ 1) 41 42(defun monotonic-time-ms () 43 "Return monotonic time in milliseconds as a double-float." 44 (cffi:with-foreign-object (ts '(:struct timespec)) 45 (%clock-gettime +clock-monotonic+ ts) 46 (let ((sec (cffi:foreign-slot-value ts '(:struct timespec) 'tv-sec)) 47 (nsec (cffi:foreign-slot-value ts '(:struct timespec) 'tv-nsec))) 48 (+ (* sec 1000.0d0) (/ nsec 1000000.0d0))))) 49 50(cffi:defcfun ("clock_nanosleep" %clock-nanosleep) :int 51 (clk-id :int) 52 (flags :int) 53 (request :pointer) 54 (remain :pointer)) 55 56(defconstant +timer-abstime+ 1) 57 58(defvar *frame-target-ns* (floor 1000000000 60) 59 "Nanoseconds per frame at 60fps.") 60 61(defvar *next-frame-sec* 0) 62(defvar *next-frame-nsec* 0) 63 64(defun frame-sync-60fps () 65 "Sleep until the next 60fps frame boundary. Call once per frame." 66 (incf *next-frame-nsec* *frame-target-ns*) 67 (when (>= *next-frame-nsec* 1000000000) 68 (decf *next-frame-nsec* 1000000000) 69 (incf *next-frame-sec*)) 70 ;; Initialize on first call 71 (when (zerop *next-frame-sec*) 72 (cffi:with-foreign-object (ts '(:struct timespec)) 73 (%clock-gettime +clock-monotonic+ ts) 74 (setf *next-frame-sec* 75 (cffi:foreign-slot-value ts '(:struct timespec) 'tv-sec)) 76 (setf *next-frame-nsec* 77 (cffi:foreign-slot-value ts '(:struct timespec) 'tv-nsec)) 78 (return-from frame-sync-60fps))) 79 ;; Sleep until target time 80 (cffi:with-foreign-object (ts '(:struct timespec)) 81 (setf (cffi:foreign-slot-value ts '(:struct timespec) 'tv-sec) *next-frame-sec*) 82 (setf (cffi:foreign-slot-value ts '(:struct timespec) 'tv-nsec) *next-frame-nsec*) 83 (%clock-nanosleep +clock-monotonic+ +timer-abstime+ ts (cffi:null-pointer))))