150 lines
6.2 KiB
Common Lisp
150 lines
6.2 KiB
Common Lisp
#|
|
|
This file is a part of stoe project.
|
|
Copyright (c) 2015 Renaud Casenave-Péré (renaud@casenave-pere.fr)
|
|
|#
|
|
|
|
(uiop:define-package :stoe/shader/compiler
|
|
(:use :cl
|
|
:stoe/core/utils
|
|
:stoe/engine/gl-utils
|
|
:stoe/engine/viewport
|
|
:stoe/shader/shader
|
|
:stoe/shader/glsl)
|
|
(:export #:defshader
|
|
#:defprogram
|
|
#:compile-all-shaders
|
|
#:destroy-all-shaders))
|
|
(in-package :stoe/shader/compiler)
|
|
|
|
(defvar *shaders-table* (make-hash-table))
|
|
(defvar *programs-table* (make-hash-table))
|
|
|
|
(defun %defshader (lambda-list body)
|
|
(glsl-compile lambda-list body))
|
|
|
|
(defmacro defshader (name lambda-list &body body)
|
|
"Define a shader defining function.
|
|
The newly created shader will be put in a special package: `%stoe.shaders'."
|
|
`(progn
|
|
(defparameter ,name (%defshader ',lambda-list ',body))
|
|
(when (not (null (glsl-version)))
|
|
(mapc (lambda (program)
|
|
(delete-program program)
|
|
(compile-program program)) (gethash ',name *shaders-table*)))))
|
|
|
|
(defun %defprogram (lambda-list body)
|
|
(make-program lambda-list body))
|
|
|
|
(defun clean-dep (name shader-list)
|
|
(loop for shader in (cdr shader-list) by #'cddr
|
|
do (setf (gethash shader *shaders-table*)
|
|
(delete name (gethash shader *shaders-table*)))))
|
|
|
|
(defun add-dep (name shader-list)
|
|
(loop for shader in (cdr shader-list) by #'cddr
|
|
do (pushnew name (gethash shader *shaders-table*))))
|
|
|
|
(defmacro defprogram (name lambda-list &body body)
|
|
"Define a new program comprised of all the specified shaders."
|
|
`(progn
|
|
(when (gethash ',name *programs-table*)
|
|
(clean-dep ',name (gethash ',name *programs-table*)))
|
|
(defparameter ,name (%defprogram ',lambda-list ',body))
|
|
(setf (gethash ',name *programs-table*) ',body)
|
|
(add-dep ',name ',body)
|
|
(when (not (null (glsl-version)))
|
|
(compile-program ',name))))
|
|
|
|
(defun compile-shader (type shader)
|
|
"Compile the shader into opengl."
|
|
(let ((shader-obj (gl-assert (gl:create-shader type))))
|
|
(when (= shader-obj 0)
|
|
(error "Couldn't create shader object."))
|
|
(handler-case
|
|
(progn
|
|
(gl-assert (gl:shader-source shader-obj (glsl-print shader))
|
|
(gl:compile-shader shader-obj))
|
|
(unless (gl:get-shader shader-obj :compile-status)
|
|
(error "Compile failure in ~(~s~) shader:~%~a~%~2i~a~%" type
|
|
(glsl-print shader) (gl:get-shader-info-log shader-obj)))
|
|
shader-obj)
|
|
(error (condition)
|
|
(gl:delete-shader shader-obj)
|
|
(error condition)))))
|
|
|
|
(defun initialize-program (program)
|
|
"Initialize the program.
|
|
Retrieve the attributes and uniform locations."
|
|
(flet ((retrieve-location (var)
|
|
(unless (member :location (var-qualifiers var))
|
|
(let ((target (var-target var)))
|
|
(setf (var-qualifiers var)
|
|
(append (var-qualifiers var)
|
|
(list :location (funcall (cond
|
|
((eq target :in) #'gl:get-attrib-location)
|
|
((eq target :uniform) #'gl:get-uniform-location))
|
|
(program-id program)
|
|
(var-name var)))))))))
|
|
(mapc #'retrieve-location (program-vars program))))
|
|
|
|
(defun compile-program (symbol)
|
|
"Compile and link the program."
|
|
(loop
|
|
while (restart-case
|
|
(let ((program (symbol-value symbol))
|
|
compiled-shaders)
|
|
(unless program
|
|
(error "The program ~s is undefined." symbol))
|
|
(unwind-protect
|
|
(progn
|
|
(loop for type in (program-stages program) by #'cddr
|
|
for shader in (cdr (program-stages program)) by #'cddr
|
|
do (push (compile-shader type (symbol-value shader)) compiled-shaders))
|
|
(let ((prog-id (gl-assert (gl:create-program))))
|
|
(when (= prog-id 0)
|
|
(error "Couldn't create program object."))
|
|
(unwind-protect
|
|
(progn
|
|
(mapc (lambda (shader) (gl-assert (gl:attach-shader prog-id shader)))
|
|
compiled-shaders)
|
|
(gl-assert (gl:link-program prog-id))
|
|
(unless (gl:get-program prog-id :link-status)
|
|
(error "Link failure in shader program ~s:~%~2i~a~%" symbol
|
|
(gl:get-program-info-log prog-id)))
|
|
(setf (program-id program) prog-id)
|
|
(initialize-program program))
|
|
(mapc (lambda (shader) (gl:detach-shader prog-id shader))
|
|
(gl:get-attached-shaders prog-id)))))
|
|
(mapc (lambda (shader) (gl:delete-shader shader)) compiled-shaders))
|
|
nil)
|
|
(retry ()
|
|
:report (lambda (stream) (format stream "Retry compiling the shader program ~a."
|
|
symbol))
|
|
t)
|
|
(give-up ()
|
|
:report (lambda (stream) (format stream "Give up on the shader program ~a."
|
|
symbol))
|
|
nil))))
|
|
|
|
(defun delete-program (symbol)
|
|
"Delete the program."
|
|
(let ((program (symbol-value symbol)))
|
|
(when (program-id program)
|
|
(gl:delete-program (program-id program))
|
|
(setf (program-id program) nil))))
|
|
|
|
(defun compile-all-shaders ()
|
|
"Compile and link all the shaders into opengl."
|
|
(loop-with-progress "Compiling shaders"
|
|
for symbol being the hash-key in *programs-table*
|
|
do (progn
|
|
(compile-program symbol)
|
|
progress-step)))
|
|
|
|
(defun destroy-all-shaders ()
|
|
"Destroy the programs registered in opengl."
|
|
(loop-with-progress "Deleting shaders"
|
|
for symbol being the hash-key in *programs-table*
|
|
do (progn
|
|
(delete-program symbol)
|
|
progress-step)))
|