16c966ddb3be8894f9ae16b0410c5a5a39bd2847
[clinton/guile-figl.git] / figl / gl / runtime.scm
1 ;;; figl
2 ;;; Copyright (C) 2013 Andy Wingo <wingo@pobox.com>
3 ;;;
4 ;;; Figl is free software: you can redistribute it and/or modify it
5 ;;; under the terms of the GNU Lesser General Public License as
6 ;;; published by the Free Software Foundation, either version 3 of the
7 ;;; License, or (at your option) any later version.
8 ;;;
9 ;;; Figl is distributed in the hope that it will be useful, but WITHOUT
10 ;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 ;;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12 ;;; Public License for more details.
13 ;;;
14 ;;; You should have received a copy of the GNU Lesser General Public
15 ;;; License along with this program. If not, see
16 ;;; <http://www.gnu.org/licenses/>.
17
18 ;;; Commentary:
19 ;;
20 ;; figl is the Foreign Interface to GL.
21 ;;
22 ;;; Code:
23
24 (define-module (figl gl runtime)
25 #:use-module (system foreign)
26 #:use-module (figl runtime)
27 #:export (current-gl-resolver
28 current-gl-get-dynamic-object
29 define-gl-procedure
30 define-gl-procedures))
31
32 (module-use! (module-public-interface (current-module))
33 (resolve-interface '(figl runtime)))
34
35 ;;
36 ;; OpenGL and loading. What a mess. So, in the beginning, when
37 ;; Microsoft added support for OpenGL to Windows, they did so via a
38 ;; trampoline DLL. This DLL had a fixed number of entry points, and it
39 ;; was independent of the driver that the graphics card provided. It
40 ;; also provided an extension interface, wglGetProcAddress, which could
41 ;; return additional GL procedures by name. Microsoft was unwilling to
42 ;; extend their trampoline DLL for whatever reason, and so on Windows
43 ;; you always needed to wglGetProcAddress for almost any OpenGL
44 ;; function.
45 ;;
46 ;; Time passed and GLX and other GL implementations started to want
47 ;; extensions too. This let application vendors ship applications that
48 ;; could take advantage of the capabilities of users's graphics cards
49 ;; without requiring that they be present.
50 ;;
51 ;; There are a couple of differences between WGL and GLX, however.
52 ;; Chiefly, wglGetProcAddress can only be called once you have a
53 ;; context, and the resulting function can only be used in that context.
54 ;; In practice it seems that it can be used also in other contexts that
55 ;; end up referring to that same driver and GPU. GLX on the other hand
56 ;; is context-independent, but presence of a function does not mean that
57 ;; the corresponding functionality is actually available. In theory
58 ;; users have to check for the presence of the GL extension or check the
59 ;; core GL version, depending on whether the interface is an extension
60 ;; or in GL core.
61 ;;
62 ;; Because of this difference between the GLX and WGL semantics, there
63 ;; is no core "glGetProcAddress" function. It's terrible: each
64 ;; windowing system is responsible for providing their own
65 ;; function-loader interface.
66 ;;
67 ;; Finally, Guile needs to load up at least some interfaces using
68 ;; dynamic-link / dynamic-pointer in order to be able to talk to the
69 ;; library at all (and to open a context in the case of Windows), and it
70 ;; happens that these interfaces also work fine for getting some of the
71 ;; GL functionality!
72 ;;
73 ;; All of this mess really has very little place in the world of free
74 ;; software, where dynamic linking is entirely sufficient to deal with
75 ;; this issue, but it is how things have evolved.
76 ;;
77 ;; In light of all of this, we need to make some simplifications.
78 ;;
79 ;; One is that each low-level function will have just one foreign
80 ;; function wrapper. This means that a minority of Windows
81 ;; configurations won't work. Oh well.
82 ;;
83 ;; Another is that if dynamic-link returns a result, that it is assumed
84 ;; that glXGetProcAddress (or the equivalent) would return the same
85 ;; value. So we can try dynamic-link first, and only dispatch to e.g
86 ;; glXGetProcAddress if that fails.
87 ;;
88 ;; Finally, we assume that all GL symbols may be resolved by
89 ;; dynamic-pointer by looking in one library, regardless of whether they
90 ;; come from the lower GL level or from the window-system-specific
91 ;; level.
92 ;;
93
94 ;; Parameterize this with glXGetProcAddress, eglGetProcAddress, etc.
95 (define current-gl-resolver
96 (make-parameter (lambda (name) %null-pointer)))
97
98 ;; Parameterize this with a procedure that returns a dynamic object we
99 ;; can use to get libGL bindings.
100 (define current-gl-get-dynamic-object
101 (make-parameter (lambda () (dynamic-link))))
102
103 (define (resolve name)
104 (let ((ptr ((current-gl-resolver) (symbol->string name))))
105 (if (null-pointer? ptr)
106 (dynamic-pointer (symbol->string name)
107 ((current-gl-get-dynamic-object)))
108 ptr)))
109
110 (define-syntax define-gl-procedure
111 (syntax-rules (->)
112 ((define-gl-procedure (name (pname ptype) ... -> type)
113 docstring)
114 (define-foreign-procedure (name (pname ptype) ... -> type)
115 (resolve 'name)
116 docstring))))
117
118 (define-syntax define-gl-procedures
119 (syntax-rules ()
120 ((define-gl-procedures ((name prototype ...) ...)
121 docstring)
122 (begin
123 (define-gl-procedure (name prototype ...)
124 docstring)
125 ...))))