Commit | Line | Data |
---|---|---|
8925f36f AW |
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 | ||
7ec693ed | 24 | (define-module (figl runtime) |
e9dbe4b7 | 25 | #:use-module (system foreign) |
7ec693ed | 26 | #:export (current-resolver |
f53f928c | 27 | define-foreign-procedure |
93f72ad8 AW |
28 | define-foreign-procedures |
29 | define-foreign-type | |
30 | define-simple-foreign-type)) | |
b59ebd7a | 31 | |
167c9635 AW |
32 | ;; |
33 | ;; OpenGL and loading. What a mess. So, in the beginning, when | |
34 | ;; Microsoft added support for OpenGL to Windows, they did so via a | |
35 | ;; trampoline DLL. This DLL had a fixed number of entry points, and it | |
36 | ;; was independent of the driver that the graphics card provided. It | |
37 | ;; also provided an extension interface, wglGetProcAddress, which could | |
38 | ;; return additional GL procedures by name. Microsoft was unwilling to | |
39 | ;; extend their trampoline DLL for whatever reason, and so on Windows | |
40 | ;; you always needed to wglGetProcAddress for almost any OpenGL | |
41 | ;; function. | |
42 | ;; | |
43 | ;; Time passed and GLX and other GL implementations started to want | |
44 | ;; extensions too. This let application vendors ship applications that | |
45 | ;; could take advantage of the capabilities of users's graphics cards | |
46 | ;; without requiring that they be present. | |
47 | ;; | |
48 | ;; There are a couple of differences between WGL and GLX, however. | |
49 | ;; Chiefly, wglGetProcAddress can only be called once you have a | |
50 | ;; context, and the resulting function can only be used in that context. | |
51 | ;; In practice it seems that it can be used also in other contexts that | |
52 | ;; end up referring to that same driver and GPU. GLX on the other hand | |
53 | ;; is context-independent, but presence of a function does not mean that | |
54 | ;; the corresponding functionality is actually available. In theory | |
55 | ;; users have to check for the presence of the GL extension or check the | |
56 | ;; core GL version, depending on whether the interface is an extension | |
57 | ;; or in GL core. | |
58 | ;; | |
59 | ;; Because of this difference between the GLX and WGL semantics, there | |
60 | ;; is no core "glGetProcAddress" function. It's terrible: each | |
61 | ;; windowing system is responsible for providing their own | |
62 | ;; function-loader interface. | |
63 | ;; | |
64 | ;; Finally, Guile needs to load up at least some interfaces using | |
65 | ;; dynamic-link / dynamic-pointer in order to be able to talk to the | |
66 | ;; library at all (and to open a context in the case of Windows), and it | |
67 | ;; happens that these interfaces also work fine for getting some of the | |
68 | ;; GL functionality! | |
69 | ;; | |
70 | ;; All of this mess really has very little place in the world of free | |
71 | ;; software, where dynamic linking is entirely sufficient to deal with | |
72 | ;; this issue, but it is how things have evolved. | |
73 | ;; | |
74 | ;; In light of all of this, we need to make some simplifications. | |
75 | ;; | |
76 | ;; One is that each low-level function will have just one foreign | |
77 | ;; function wrapper. This means that a minority of Windows | |
78 | ;; configurations won't work. Oh well. | |
79 | ;; | |
80 | ;; Another is that if dynamic-link returns a result, that it is assumed | |
81 | ;; that glXGetProcAddress (or the equivalent) would return the same | |
82 | ;; value. So we can try dynamic-link first, and only dispatch to e.g | |
83 | ;; glXGetProcAddress if that fails. | |
84 | ;; | |
85 | ;; Finally, we assume that all GL symbols may be resolved by | |
86 | ;; dynamic-pointer by looking in one library, regardless of whether they | |
87 | ;; come from the lower GL level or from the window-system-specific | |
88 | ;; level. | |
89 | ;; | |
90 | ||
91 | ;; FIXME: adapt implementation to match! | |
7ec693ed | 92 | (define (default-resolver name) |
542ee4ba AW |
93 | (dynamic-pointer name (dynamic-link))) |
94 | ||
7ec693ed AW |
95 | (define current-resolver |
96 | (make-parameter default-resolver)) | |
8925f36f | 97 | |
7ec693ed AW |
98 | (define (resolve name) |
99 | ((current-resolver) name)) | |
e9dbe4b7 AW |
100 | |
101 | (define-syntax foreign-trampoline | |
102 | (lambda (stx) | |
103 | (syntax-case stx (->) | |
104 | ((_ trampoline | |
105 | name (pname ptype) ... -> type) | |
106 | (with-syntax ((sname (symbol->string (syntax->datum #'name)))) | |
107 | #'(lambda (pname ...) | |
7ec693ed | 108 | (let ((ptr (resolve sname))) |
e9dbe4b7 | 109 | (set! trampoline |
93f72ad8 | 110 | (pointer->procedure (type) |
e9dbe4b7 | 111 | ptr |
93f72ad8 | 112 | (list (ptype) ...))) |
e9dbe4b7 AW |
113 | (trampoline pname ...)))))))) |
114 | ||
7ec693ed | 115 | (define-syntax define-foreign-procedure |
bb894c9d | 116 | (syntax-rules (->) |
f53f928c AW |
117 | ((define-foreign-procedure (name (pname ptype) ... -> type) |
118 | docstring) | |
119 | (define name | |
120 | (letrec ((trampoline | |
121 | (foreign-trampoline trampoline | |
122 | name (pname ptype) ... -> type)) | |
123 | (name (lambda (pname ...) | |
124 | docstring | |
93f72ad8 AW |
125 | (let ((pname (ptype #:unwrap pname)) |
126 | ...) | |
127 | (type #:wrap (trampoline pname ...)))))) | |
f53f928c AW |
128 | name))))) |
129 | ||
130 | (define-syntax define-foreign-procedures | |
131 | (syntax-rules () | |
132 | ((define-foreign-procedures ((name prototype ...) ...) | |
bb894c9d AW |
133 | docstring) |
134 | (begin | |
f53f928c AW |
135 | (define-foreign-procedure (name prototype ...) |
136 | docstring) | |
bb894c9d | 137 | ...)))) |
93f72ad8 AW |
138 | |
139 | (define-syntax define-foreign-type | |
140 | (syntax-rules () | |
141 | ((_ name ffi-type unwrap wrap) | |
142 | (define-syntax name | |
143 | (syntax-rules () | |
144 | ((_) ffi-type) | |
145 | ((_ #:wrap x) (wrap x)) | |
146 | ((_ #:unwrap x) (unwrap x))))))) | |
147 | ||
148 | (define-syntax define-simple-foreign-type | |
149 | (syntax-rules () | |
150 | ((_ name ffi-type) | |
151 | ;; We could dispatch through to define-foreign-type via: | |
152 | ;; | |
153 | ;; (define-foreign-type name | |
154 | ;; ffi-type | |
155 | ;; (lambda (x) x) | |
156 | ;; (lambda (x) x)) | |
157 | ;; | |
158 | ;; However the resulting wrap expression: | |
159 | ;; | |
160 | ;; ((lambda (x) x) (trampoline arg ...)) | |
161 | ;; | |
162 | ;; would not be in tail position, as the optimizer doesn't know | |
163 | ;; that the foreign function just returns one value. This hack | |
164 | ;; just passes the wrapped expression through, allowing it to be in | |
165 | ;; tail position. | |
166 | (define-syntax name | |
167 | (syntax-rules () | |
168 | ((_) ffi-type) | |
169 | ((_ #:wrap x) x) | |
170 | ((_ #:unwrap x) x)))))) |