It seems to be compiling a new lambda on every call. I'll walk through my logic below:
So I am looking at how (gen-buffers) is implemented.
(defun gen-buffers (count) (with-foreign-object (buffer-array '%gl:uint count) (%gl:gen-buffers count buffer-array) (loop for i below count collecting (mem-aref buffer-array '%gl:uint i))))
%gl:gen-buffers is deifned as (defglextfun ("glGenBuffers" gen-buffers) :void (n sizei) (buffers (:pointer uint)))
which expands to: (progn (declaim (notinline gen-buffers)) (defun gen-buffers (n buffers) (generate-gl-function "glgenbuffers" 'gen-buffers ':void '((n sizei) (buffers (:pointer uint))) n buffers)) (setf (get 'gen-buffers 'proc-address-dummy) #'gen-buffers) 'gen-buffers)
and generate-gl-function (defun generate-gl-function (foreign-name lisp-name result-type body &rest args) (let ((address (gl-get-proc-address foreign-name)) (arg-list (mapcar #'first body))) (when (or (not (pointerp address)) (null-pointer-p address)) (error "Couldn't find function ~A" foreign-name)) (compile lisp-name `(lambda ,arg-list (multiple-value-prog1 (foreign-funcall-pointer ,address (:library opengl) ,@(loop for i in body collect (second i) collect (first i)) ,result-type) #-cl-opengl-no-check-error (check-error ',lisp-name)))) (apply lisp-name args)))
What is going on here? I don't believe that it is recompiling the wrapper function on every call, but I'm having issues working out how else this works. Hope someone can help me out here. Cheers Baggers
Or is it that once the lambda is compiled, it replaces the version with generate-gl-function in it? Does that mean that the wrapper functions are compiled on first call?
On 16 October 2013 13:42, Chris Bagley chris.bagley@gmail.com wrote:
It seems to be compiling a new lambda on every call. I'll walk through my logic below:
So I am looking at how (gen-buffers) is implemented.
(defun gen-buffers (count) (with-foreign-object (buffer-array '%gl:uint count) (%gl:gen-buffers count buffer-array) (loop for i below count collecting (mem-aref buffer-array '%gl:uint i))))
%gl:gen-buffers is deifned as (defglextfun ("glGenBuffers" gen-buffers) :void (n sizei) (buffers (:pointer uint)))
which expands to: (progn (declaim (notinline gen-buffers)) (defun gen-buffers (n buffers) (generate-gl-function "glgenbuffers" 'gen-buffers ':void '((n sizei) (buffers (:pointer uint))) n buffers)) (setf (get 'gen-buffers 'proc-address-dummy) #'gen-buffers) 'gen-buffers)
and generate-gl-function (defun generate-gl-function (foreign-name lisp-name result-type body &rest args) (let ((address (gl-get-proc-address foreign-name)) (arg-list (mapcar #'first body))) (when (or (not (pointerp address)) (null-pointer-p address)) (error "Couldn't find function ~A" foreign-name)) (compile lisp-name `(lambda ,arg-list (multiple-value-prog1 (foreign-funcall-pointer ,address (:library opengl) ,@(loop for i in body collect (second i) collect (first i)) ,result-type) #-cl-opengl-no-check-error (check-error ',lisp-name)))) (apply lisp-name args)))
What is going on here? I don't believe that it is recompiling the wrapper function on every call, but I'm having issues working out how else this works. Hope someone can help me out here. Cheers Baggers
On Wed, Oct 16, 2013 at 6:45 AM, Chris Bagley chris.bagley@gmail.com wrote:
Or is it that once the lambda is compiled, it replaces the version with generate-gl-function in it? Does that mean that the wrapper functions are compiled on first call?
Right, the call to COMPILE replaces the function with the result of compiling the LAMBDA, so a specialized wrapper is compiled when it is called. If I remember correctly, there is also a commented out version that uses a closure instead, if runtime compilation is a problem.
Cheers, good to know. OK going back and reading the code again, this all seems to boil down to the fact that some implementations of opengl are missing functions, which of course makes sense as there is plenty of difference between v2 and v4 (or gles etc) Does the resulting lisp program take a performance hit from having such a late compile? It's a heck of an interesting problem, I hadn't really thought about how cl-opengl handled versions before. It's a pretty cool solution! Are there any features around this area that that need implementing or improvements to code that are needed? My main part time project totally relies on cl-opengl so it would be nice to give a little back! Thanks again for the help
On 16 October 2013 14:56, Bart Botta 00003b@gmail.com wrote:
On Wed, Oct 16, 2013 at 6:45 AM, Chris Bagley chris.bagley@gmail.com wrote:
Or is it that once the lambda is compiled, it replaces the version with generate-gl-function in it? Does that mean that the wrapper functions are compiled on first call?
Right, the call to COMPILE replaces the function with the result of compiling the LAMBDA, so a specialized wrapper is compiled when it is called. If I remember correctly, there is also a commented out version that uses a closure instead, if runtime compilation is a problem.
On Wed, Oct 16, 2013 at 8:58 AM, Chris Bagley chris.bagley@gmail.com wrote:
Cheers, good to know. OK going back and reading the code again, this all seems to boil down to the fact that some implementations of opengl are missing functions
It is mainly intended for OpenGL extensions, but Windows in particular only supports gl 1.1 or 1.4 or so in the system libraries, so everything beyond that has to be loaded the same way. Other systems might be able to link more functions directly, but I'm not sure it would be worth the effort to try to figure out which ones, particularly since it might depend on drivers or hardware so needs to be checked at runtime anyway.
Does the resulting lisp program take a performance hit from having such a late compile?
I think the runtime compilation was intended to improve performance, by compiling a specific function containing the function pointer directly rather than precompiling a function that would have to look it up somewhere for every call. That way the first call is a bit expensive, but every call after that can be as fast as possible. No idea how much difference it actually makes, or if it depends on lisp implementation or platform though.
It's a heck of an interesting problem, I hadn't really thought about how cl-opengl handled versions before. It's a pretty cool solution! Are there any features around this area that that need implementing or improvements to code that are needed? My main part time project totally relies on cl-opengl so it would be nice to give a little back!
Can't think of anything in that specific area that needs work, but there is lots of room for improvement in the "high-level api" (the GL: package) part of cl-opengl, particularly with more modern style of OpenGL programming (shaders, vbos, etc).
@bart:sorry if you receive this twice. I didn’t reply-all in gmail. @all: Just resurrecting this question briefly as it has been playing on my mind. We use runtime compiling to create our wrappers in case functionality is not present. That is great, but how do more static languages that don’t have this kind of control handle multiple versions of opengl? If the answer is "go read them and find out" that is fine!
On 16 October 2013 18:44, Bart Botta 00003b@gmail.com wrote:
On Wed, Oct 16, 2013 at 8:58 AM, Chris Bagley chris.bagley@gmail.com wrote:
Cheers, good to know. OK going back and reading the code again, this all seems to boil down to the fact that some implementations of opengl are missing functions
It is mainly intended for OpenGL extensions, but Windows in particular only supports gl 1.1 or 1.4 or so in the system libraries, so everything beyond that has to be loaded the same way. Other systems might be able to link more functions directly, but I'm not sure it would be worth the effort to try to figure out which ones, particularly since it might depend on drivers or hardware so needs to be checked at runtime anyway.
Does the resulting lisp program take a performance hit from having such a late compile?
I think the runtime compilation was intended to improve performance, by compiling a specific function containing the function pointer directly rather than precompiling a function that would have to look it up somewhere for every call. That way the first call is a bit expensive, but every call after that can be as fast as possible. No idea how much difference it actually makes, or if it depends on lisp implementation or platform though.
It's a heck of an interesting problem, I hadn't really thought about how cl-opengl handled versions before. It's a pretty cool solution! Are there any features around this area that that need implementing or
improvements to
code that are needed? My main part time project totally relies on
cl-opengl
so it would be nice to give a little back!
Can't think of anything in that specific area that needs work, but there is lots of room for improvement in the "high-level api" (the GL: package) part of cl-opengl, particularly with more modern style of OpenGL programming (shaders, vbos, etc).
On Wed, Oct 30, 2013 at 6:58 AM, Chris Bagley chris.bagley@gmail.comwrote:
We use runtime compiling to create our wrappers in case functionality is not present.
Runtime compilation is more of an optimization, we could store the function pointers in a closure or global array or something instead (and in fact probably should store them differently to be handle multiple drivers on windows correctly, but making that correct/efficient would probably be harder even though not many people would need it.)
That is great, but how do more static languages that don’t have this kind
of control handle multiple versions of opengl?
In C/C++ you can call a function pointer exactly the same way as a normal
function, so they usually just define a bunch of function pointers for the extension functions and initialize them all at once. Most people probably use a library like GLEW or GLEE for that.
IIRC, GLEW basically loads as many functions as possible from a static list of names, and provides an error thunk for any names it can't load. So the short version for GLEW is basically, "They don't".
On Wed, Oct 30, 2013 at 3:47 PM, Caelan Burris caelanburris@gmail.com wrote:
IIRC, GLEW basically loads as many functions as possible from a static list of names, and provides an error thunk for any names it can't load. So the short version for GLEW is basically, "They don't".
As far as I know, GLEW generates its "static list of names" from the specifications, so GLEW handles it pretty much exactly as much as cl-opengl does (I think it uses the text of the specifications rather than the data files cl-opengl uses, but should produce approximately the same results).
cl-opengl-devel@common-lisp.net