3 (defmacro with-new-list
(list mode
&body forms
)
4 "New/End-List wrapper."
6 (gl:new-list
,list
,mode
)
7 (unwind-protect (progn ,@forms
)
10 (defmacro with-push-name
(name &body forms
)
11 "Select name push/pop wrapper"
14 (unwind-protect (progn ,@forms
)
17 (defmacro with-begin
(mode &body forms
)
18 "Immediate mode block wrapper."
21 (unwind-protect (progn ,@forms
)
24 (defmacro with-push-attrib
((&rest attrib-bits
) &body forms
)
25 "Pushes the bitwise or of attrib-bits, executing forms and clean up with pop-attrib."
27 (gl:push-attrib
(logior ,@attrib-bits
))
28 (unwind-protect (progn ,@forms
)
31 (defmacro with-push-matrix
(&body forms
)
32 "Pushes the current matrix onto the stack, executes forms and clean up with pop-matrix."
35 (unwind-protect (progn ,@forms
)
38 (defmacro with-setup-projection
(&body forms
)
39 "Switch to projection mode, load identity, execute forms and return to modelview matrix mode."
41 (gl:matrix-mode gl
:+projection
+)
43 (unwind-protect (progn ,@forms
)
44 (gl:matrix-mode gl
:+modelview
+))))
46 (defmacro with-projection-matrix
((&body projection-matrix-setup-forms
)
48 "Temporarily change the projection matrix:
49 Saves the current projection matrix,
50 loads identity in the projection matrix,
51 runs projection-matrix-setup-forms in projection matrix-mode,
52 runs forms in modelview matrix-mode,
53 restores the saved projection-matrix
54 and leaves in modelview matrix-mode."
56 (gl:matrix-mode gl
:+projection
+)
61 ,@projection-matrix-setup-forms
62 (gl:matrix-mode gl
:+modelview
+)
64 (gl:matrix-mode gl
:+projection
+)
66 (gl:matrix-mode gl
:+modelview
+))))
70 (defmacro with-push-client-attrib
((&rest attrib-bits
) &body forms
)
71 "Pushes the bitwise or of the client attrib-bits, executing forms and clean up with pop-client-attrib."
73 (gl:push-client-attrib
(logior ,@attrib-bits
))
74 (unwind-protect (progn ,@forms
)
75 (gl:pop-client-attrib
))))
78 (defmacro with-begin-query
((target id
) &body forms
)
80 (gl:begin-query
,target
,id
)
81 (unwind-protect (progn ,@forms
)
84 (defmacro with-map-buffer
((target access
) &body forms
)
85 "Executes forms with VBO buffer mapped as with gl:map-buffer, unmapping cleanly afterwards."
86 (let ((ntarget (gensym "TARGET-")))
87 `(let ((,ntarget
,target
))
88 (gl:map-buffer
,ntarget
,access
)
89 (unwind-protect (progn ,@forms
)
90 (gl:unmap-buffer
,ntarget
)))))
92 (defmacro with-bind-buffer
((target buffer
) &body forms
)
93 "Executes forms with the VBO buffer bound with gl:bind-buffer, cleanly restoring to previous state afterwards."
94 (let ((ntarget (gensym "TARGET-"))
95 (saved-buffer-v (gensym "SAVED-BUFFER-V-")))
96 `(let ((,ntarget
,target
))
97 (cffi:with-foreign-object
(,saved-buffer-v
'gl
:int
)
99 ((eql ,ntarget gl
:+array-buffer
+) (gl:get-integerv gl
:+array-buffer-binding
+ ,saved-buffer-v
))
100 ((eql ,ntarget gl
:+element-array-buffer
+) (gl:get-integerv gl
:+element-array-buffer-binding
+ ,saved-buffer-v
))
101 ((eql ,ntarget gl
:+pixel-pack-buffer
+) (gl:get-integerv gl
:+pixel-pack-buffer-binding
+ ,saved-buffer-v
))
102 ((eql ,ntarget gl
:+pixel-unpack-buffer
+) (gl:get-integerv gl
:+pixel-unpack-buffer-binding
+ ,saved-buffer-v
)))
103 (gl:bind-buffer
,ntarget
,buffer
)
104 (unwind-protect (progn ,@forms
)
105 (gl:bind-buffer
,ntarget
(cffi:mem-ref
,saved-buffer-v
'gl
:int
)))))))
107 ;; ARB_vertex_buffer_object
109 (defmacro with-map-buffer-arb
((target access
) &body forms
)
110 "Executes forms with VBO buffer mapped as with gl:map-buffer-arb, unmapping cleanly afterwards."
111 (let ((ntarget (gensym "TARGET-")))
112 `(let ((,ntarget
,target
))
113 (gl:map-buffer-arb
,ntarget
,access
)
114 (unwind-protect (progn ,@forms
)
115 (gl:unmap-buffer-arb
,ntarget
)))))
117 (defmacro with-bind-buffer-arb
((target buffer
) &body forms
)
118 "Executes forms with the VBO buffer bound with gl:bind-buffer-arb, cleanly restoring to previous state afterwards."
119 (let ((ntarget (gensym "TARGET-"))
120 (saved-buffer-v (gensym "SAVED-BUFFER-V-")))
121 `(let ((,ntarget
,target
))
122 (cffi:with-foreign-object
(,saved-buffer-v
'gl
:int
)
124 ((eql ,ntarget gl
:+array-buffer-arb
+) (gl:get-integerv gl
:+array-buffer-binding-arb
+ ,saved-buffer-v
))
125 ((eql ,ntarget gl
:+element-array-buffer-arb
+) (gl:get-integerv gl
:+element-array-buffer-binding-arb
+ ,saved-buffer-v
))
126 ((eql ,ntarget gl
:+pixel-pack-buffer-arb
+) (gl:get-integerv gl
:+pixel-pack-buffer-binding-arb
+ ,saved-buffer-v
))
127 ((eql ,ntarget gl
:+pixel-unpack-buffer-arb
+) (gl:get-integerv gl
:+pixel-unpack-buffer-binding-arb
+ ,saved-buffer-v
)))
128 (gl:bind-buffer-arb
,ntarget
,buffer
)
129 (unwind-protect (progn ,@forms
)
130 (gl:bind-buffer-arb
,ntarget
(cffi:mem-ref
,saved-buffer-v
'gl
:int
)))))))
133 ;; common (between ARB and 2.0) shader stuff
137 (define-condition shader-object-error
(error)
138 ((object-handle :initarg
:object-handle
:reader object-handle
)
139 (info-log :initarg
:info-log
:reader info-log
))
140 (:report
(lambda (condition stream
)
141 (format stream
"~a Error with ~a:~%~a~%"
143 (object-handle condition
)
144 (info-log condition
)))))
145 (define-condition shader-compile-error
(shader-object-error) ())
146 (define-condition program-link-error
(shader-object-error) ())
148 ;; 2.0 and ARB_shader_objects/ARB_vertex_shader/ARB_fragment_shader conveniences
150 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
151 (defmacro define-arb-and-non-arb
(&body form
)
152 "Expands out code with (?extension with-arb without-arb) markers in it.
153 Care should be taken that it doesn't interfere with code with ` , markers in it."
154 (let ((extension nil
))
155 (declare (special extension
))
156 (labels ((expand-extension (form)
158 (if (eql (first form
) '?extension
)
159 (if extension
(second form
) (third form
))
160 (mapcar #'expand-extension form
))
163 ,@(let ((extension t
))
164 (declare (special extension
))
165 (expand-extension form
)) ; undefined function: EXPAND-EXTENSION
166 ,@(expand-extension form
))))))
168 (define-arb-and-non-arb
169 (defun (?extension shader-source-from-stream-arb shader-source-from-stream
) (handle in
)
170 (declare (type stream in
))
171 (let* ((lines (loop for line
= (read-line in nil
) while line collecting line
))
172 (c-lines (cffi:foreign-alloc
:string
:initial-contents lines
)))
173 ((?extension gl
:shader-source-arb gl
:shader-source
) handle
(length lines
) c-lines
(cffi:null-pointer
))))
175 (defun (?extension check-compiled-shader-arb check-compiled-shader
) (handle)
176 "Given a shader handle that has been compiled, checks and flags in a lisp-friendly manner
177 their compile status. Errors are signalled using an error with the shader log, success will
178 just warn with the contents of the program-log, if present."
179 (cffi:with-foreign-objects
((log-length 'gl
:sizei
)
180 (compile-status 'gl
:int
))
182 (gl:get-object-parameter-iv-arb handle gl
:+object-info-log-length-arb
+ log-length
)
183 (gl:get-shader-iv handle gl
:+info-log-length
+ log-length
))
185 (gl:get-object-parameter-iv-arb handle gl
:+object-compile-status-arb
+ compile-status
)
186 (gl:get-shader-iv handle gl
:+compile-status
+ compile-status
))
187 (let ((shader-log (when (> (cffi:mem-ref log-length
'gl
:sizei
) 1)
188 (cffi:with-foreign-pointer-as-string
(str 4096)
189 ((?extension gl
:get-info-log-arb gl
:get-shader-info-log
) handle
(cffi:mem-ref log-length
'gl
:sizei
) (cffi:null-pointer
) str
)
190 (cffi:foreign-string-to-lisp str
(cffi:mem-ref log-length
'gl
:sizei
))))))
191 (if (= (cffi:mem-ref compile-status
'gl
:int
)
193 (when shader-log
(warn shader-log
))
194 (error 'shader-compile-error
:info-log shader-log
:object-handle handle
)))))
196 (defun (?extension make-shader-arb make-shader
) (type source
)
197 "Convenience function to create a shader of type given from source, which may either be a
198 pathname of a file to load from, or a string of the shader source directly. Returns handle of the new shader."
199 (let ((handle ((?extension gl
:create-shader-object-arb gl
:create-shader
) type
)))
200 (if (typep source
'pathname
)
201 (with-open-file (in source
:direction
:input
)
202 ((?extension shader-source-from-stream-arb shader-source-from-stream
) handle in
))
203 (with-input-from-string (in source
)
204 ((?extension shader-source-from-stream-arb shader-source-from-stream
) handle in
)))
205 ((?extension gl
:compile-shader-arb gl
:compile-shader
) handle
)
206 ((?extension check-compiled-shader-arb check-compiled-shader
) handle
)
209 (defun (?extension check-linked-program-arb check-linked-program
) (handle)
210 "Given a program handle that has been linked, checks and flags in a lisp-friendly manner
211 their link status. Errors are signalled using an error with the program log, success will
212 just warn with the contents of the program-log, if present."
213 (cffi:with-foreign-objects
((log-length 'gl
:sizei
)
214 (link-status 'gl
:int
))
216 (gl:get-object-parameter-iv-arb handle gl
:+object-info-log-length-arb
+ log-length
)
217 (gl:get-program-iv handle gl
:+info-log-length
+ log-length
))
219 (gl:get-object-parameter-iv-arb handle gl
:+object-link-status-arb
+ link-status
)
220 (gl:get-program-iv handle gl
:+link-status
+ link-status
))
221 (let ((program-log (when (> (cffi:mem-ref log-length
'gl
:sizei
) 1)
222 (cffi:with-foreign-pointer-as-string
(str 4096)
223 ((?extension gl
:get-info-log-arb gl
:get-program-info-log
) handle
(cffi:mem-ref log-length
'gl
:sizei
) (cffi:null-pointer
) str
)
224 (cffi:foreign-string-to-lisp str
(cffi:mem-ref log-length
'gl
:sizei
))))))
225 (if (= (cffi:mem-ref link-status
'gl
:int
)
227 (when program-log
(warn program-log
))
228 (error 'program-link-error
:info-log program-log
:object-handle handle
)))))
230 (defun (?extension make-program-arb make-program
) (&rest shader-handles
)
231 "Given shader handles, creates a program, attaches any shaders given and links the program."
232 (let ((handle ((?extension gl
:create-program-object-arb gl
:create-program
))))
233 (dolist (shader-handle shader-handles
)
234 ((?extension gl
:attach-object-arb gl
:attach-shader
) handle shader-handle
))
236 ((?extension gl
:link-program-arb gl
:link-program
) handle
)
237 ((?extension check-linked-program-arb check-linked-program
) handle
))
240 (defparameter (?extension
*fallback-synchronizing-program-arb
* *fallback-synchronizing-program
*) nil
)
242 (defun (?extension fallback-synchronizing-program-arb fallback-synchronizing-program
) ()
243 (or (?extension
*fallback-synchronizing-program-arb
* *fallback-synchronizing-program
*)
244 (setf (?extension
*fallback-synchronizing-program-arb
* *fallback-synchronizing-program
*)
245 ((?extension make-program-arb make-program
)
246 ((?extension make-shader-arb make-shader
) (?extension gl
:+vertex-shader-arb
+ gl
:+vertex-shader
+)
247 "varying float position;
250 gl_Position=ftransform();
251 position=gl_Position.x+gl_Position.y+gl_Position.z;
254 ((?extension make-shader-arb make-shader
) (?extension gl
:+fragment-shader-arb
+ gl
:+fragment-shader
+)
255 "varying float position;
257 float intensity=mod(position*4.0,1.0) > 0.5 ? 1.0 : 0.1;
258 gl_FragColor=vec4(intensity,intensity,0.0,1.0);
261 (defun (?extension synchronizing-program-arb synchronizing-program
) (program-name &rest shader-type-sources
)
262 "Creates a managed shader program that will poll shader files on disk
263 and load them automatically if their write-times change. This can be used for
264 easily developing shader code while the program is running. If the shader code
265 does not compile for any reason, errors will be displayed on the *error-output*
266 and a program specified by gl:*fallback-synchronizing-program* will be returned
267 instead (a black and yellow striped static pattern).
269 For an example, please see examples/synchronized-shader.lisp."
270 (defvar *synchronizing-shader-programs
* (make-hash-table :test
'equal
))
271 ;; structure of an entry:
272 ;; program-name: (program-object-handle shader-entry+ ... )
273 ;; shader-entry: (shader-object-handle source-file last-compile-time)
276 (when (not (nth-value 1 (gethash program-name
*synchronizing-shader-programs
*)))
277 (let* ((program ((?extension create-program-object-arb create-program
)))
278 (shaders (mapcar #'(lambda (type-source)
280 ((?extension create-shader-object-arb create-shader
) (first type-source
))))
281 ((?extension attach-object-arb attach-shader
) program shader
)
283 shader-type-sources
)))
284 (setf (gethash program-name
*synchronizing-shader-programs
*)
285 (list program
(mapcar #'(lambda (shader type-source
)
286 (list shader
(second type-source
) 0))
288 shader-type-sources
)))))
289 (let ((program-spec (gethash program-name
*synchronizing-shader-programs
*)))
290 ;; compile out-of-date shaders
291 (when (some #'(lambda (shader-entry)
292 (destructuring-bind (shader source last-time
) shader-entry
293 (let ((now-time (file-write-date source
)))
294 (when (> now-time last-time
)
295 (with-open-file (in source
:direction
:input
)
296 ((?extension shader-source-from-stream-arb shader-source-from-stream
) shader in
))
297 ((?extension gl
:compile-shader-arb gl
:compile-shader
) shader
)
299 ((?extension check-compiled-shader-arb check-compiled-shader
) shader
)
300 (shader-object-error (e)
301 (let ((*print-escape
* nil
))
302 (print-object e
*error-output
*))))
303 (setf (third shader-entry
) now-time
)
305 (second program-spec
))
306 ;; re-link if anything was out of date
307 ((?extension gl
:link-program-arb gl
:link-program
) (first program-spec
))
309 ((?extension check-linked-program-arb check-linked-program
) (first program-spec
))
310 (shader-object-error (e)
311 (let ((*print-escape
* nil
))
312 (print-object e
*error-output
*)))))
314 ;; check link status: return or fallback
315 (if (cffi:with-foreign-object
(link-status 'gl
:int
)
316 ((?extension gl
:get-object-parameter-iv-arb gl
:get-program-iv
)
318 (?extension gl
:+object-link-status-arb
+ gl
:+link-status
+)
320 (eql (cffi:mem-ref link-status
'gl
:int
) gl
:+true
+))
322 ((?extension fallback-synchronizing-program-arb fallback-synchronizing-program
)))))
324 (defmacro (?extension with-use-program-arb with-use-program
) (name &body forms
)
325 "Executes forms using the shader program named. And cleanly use no-program afterwards."
326 (let ((current-program (gensym "CURRENT-PROGRAM-")))
327 `(let ((,current-program
(?extension
328 (gl:get-handle-arb gl
:+program-object-arb
+)
329 (cffi:with-foreign-object
(current-program-v 'gl
:int
)
330 (gl:get-integerv gl
:+current-program
+ current-program-v
)
331 (cffi:mem-ref current-program-v
'gl
:int
)))))
332 ((?extension gl
:use-program-object-arb gl
:use-program
) ,name
)
333 (unwind-protect (progn ,@forms
)
334 ((?extension gl
:use-program-object-arb gl
:use-program
) ,current-program
))))))
337 (defun clear-synchronizing-shaders ()
338 (makunbound '#:*synchronizing-shader-programs
*))
343 (export '(with-new-list with-push-name with-begin with-push-attrib with-push-matrix with-setup-projection with-projection-matrix
345 with-push-client-attrib
346 ;; ARB_vertex_buffer_object
347 with-map-buffer-arb with-bind-buffer-arb
350 with-map-buffer with-bind-buffer
351 ;; ARB_vertex_shader/ARB_fragment_shader/ARB_shader_objects
352 shader-source-from-stream-arb check-compiled-shader-arb make-shader-arb synchronizing-shader-arb
353 check-linked-program-arb make-program-arb
354 with-use-program-arb synchronizing-program-arb
355 fallback-synchronizing-program-arb
356 *fallback-synchronizing-program-arb
*
358 shader-source-from-stream check-compiled-shader make-shader synchronizing-shader
359 check-linked-program make-program
360 with-use-program synchronizing-program
361 fallback-synchronizing-program
362 *fallback-synchronizing-program
*
364 clear-synchronizing-shaders
))