1 | //======================================================================== |
2 | // GLFW 3.4 OSMesa - www.glfw.org |
3 | //------------------------------------------------------------------------ |
4 | // Copyright (c) 2016 Google Inc. |
5 | // Copyright (c) 2016-2017 Camilla Löwy <[email protected]> |
6 | // |
7 | // This software is provided 'as-is', without any express or implied |
8 | // warranty. In no event will the authors be held liable for any damages |
9 | // arising from the use of this software. |
10 | // |
11 | // Permission is granted to anyone to use this software for any purpose, |
12 | // including commercial applications, and to alter it and redistribute it |
13 | // freely, subject to the following restrictions: |
14 | // |
15 | // 1. The origin of this software must not be misrepresented; you must not |
16 | // claim that you wrote the original software. If you use this software |
17 | // in a product, an acknowledgment in the product documentation would |
18 | // be appreciated but is not required. |
19 | // |
20 | // 2. Altered source versions must be plainly marked as such, and must not |
21 | // be misrepresented as being the original software. |
22 | // |
23 | // 3. This notice may not be removed or altered from any source |
24 | // distribution. |
25 | // |
26 | //======================================================================== |
27 | // Please use C89 style variable declarations in this file because VS 2010 |
28 | //======================================================================== |
29 | |
30 | #include <stdlib.h> |
31 | #include <string.h> |
32 | #include <assert.h> |
33 | |
34 | #include "internal.h" |
35 | |
36 | |
37 | static void makeContextCurrentOSMesa(_GLFWwindow* window) |
38 | { |
39 | if (window) |
40 | { |
41 | int width, height; |
42 | _glfw.platform.getFramebufferSize(window, &width, &height); |
43 | |
44 | // Check to see if we need to allocate a new buffer |
45 | if ((window->context.osmesa.buffer == NULL) || |
46 | (width != window->context.osmesa.width) || |
47 | (height != window->context.osmesa.height)) |
48 | { |
49 | _glfw_free(window->context.osmesa.buffer); |
50 | |
51 | // Allocate the new buffer (width * height * 8-bit RGBA) |
52 | window->context.osmesa.buffer = _glfw_calloc(4, (size_t) width * height); |
53 | window->context.osmesa.width = width; |
54 | window->context.osmesa.height = height; |
55 | } |
56 | |
57 | if (!OSMesaMakeCurrent(window->context.osmesa.handle, |
58 | window->context.osmesa.buffer, |
59 | GL_UNSIGNED_BYTE, |
60 | width, height)) |
61 | { |
62 | _glfwInputError(GLFW_PLATFORM_ERROR, |
63 | "OSMesa: Failed to make context current" ); |
64 | return; |
65 | } |
66 | } |
67 | |
68 | _glfwPlatformSetTls(&_glfw.contextSlot, window); |
69 | } |
70 | |
71 | static GLFWglproc getProcAddressOSMesa(const char* procname) |
72 | { |
73 | return (GLFWglproc) OSMesaGetProcAddress(procname); |
74 | } |
75 | |
76 | static void destroyContextOSMesa(_GLFWwindow* window) |
77 | { |
78 | if (window->context.osmesa.handle) |
79 | { |
80 | OSMesaDestroyContext(window->context.osmesa.handle); |
81 | window->context.osmesa.handle = NULL; |
82 | } |
83 | |
84 | if (window->context.osmesa.buffer) |
85 | { |
86 | _glfw_free(window->context.osmesa.buffer); |
87 | window->context.osmesa.width = 0; |
88 | window->context.osmesa.height = 0; |
89 | } |
90 | } |
91 | |
92 | static void swapBuffersOSMesa(_GLFWwindow* window) |
93 | { |
94 | // No double buffering on OSMesa |
95 | } |
96 | |
97 | static void swapIntervalOSMesa(int interval) |
98 | { |
99 | // No swap interval on OSMesa |
100 | } |
101 | |
102 | static int extensionSupportedOSMesa(const char* extension) |
103 | { |
104 | // OSMesa does not have extensions |
105 | return GLFW_FALSE; |
106 | } |
107 | |
108 | |
109 | ////////////////////////////////////////////////////////////////////////// |
110 | ////// GLFW internal API ////// |
111 | ////////////////////////////////////////////////////////////////////////// |
112 | |
113 | GLFWbool _glfwInitOSMesa(void) |
114 | { |
115 | int i; |
116 | const char* sonames[] = |
117 | { |
118 | #if defined(_GLFW_OSMESA_LIBRARY) |
119 | _GLFW_OSMESA_LIBRARY, |
120 | #elif defined(_WIN32) |
121 | "libOSMesa.dll" , |
122 | "OSMesa.dll" , |
123 | #elif defined(__APPLE__) |
124 | "libOSMesa.8.dylib" , |
125 | #elif defined(__CYGWIN__) |
126 | "libOSMesa-8.so" , |
127 | #else |
128 | "libOSMesa.so.8" , |
129 | "libOSMesa.so.6" , |
130 | #endif |
131 | NULL |
132 | }; |
133 | |
134 | if (_glfw.osmesa.handle) |
135 | return GLFW_TRUE; |
136 | |
137 | for (i = 0; sonames[i]; i++) |
138 | { |
139 | _glfw.osmesa.handle = _glfwPlatformLoadModule(sonames[i]); |
140 | if (_glfw.osmesa.handle) |
141 | break; |
142 | } |
143 | |
144 | if (!_glfw.osmesa.handle) |
145 | { |
146 | _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found" ); |
147 | return GLFW_FALSE; |
148 | } |
149 | |
150 | _glfw.osmesa.CreateContextExt = (PFN_OSMesaCreateContextExt) |
151 | _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaCreateContextExt" ); |
152 | _glfw.osmesa.CreateContextAttribs = (PFN_OSMesaCreateContextAttribs) |
153 | _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaCreateContextAttribs" ); |
154 | _glfw.osmesa.DestroyContext = (PFN_OSMesaDestroyContext) |
155 | _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaDestroyContext" ); |
156 | _glfw.osmesa.MakeCurrent = (PFN_OSMesaMakeCurrent) |
157 | _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaMakeCurrent" ); |
158 | _glfw.osmesa.GetColorBuffer = (PFN_OSMesaGetColorBuffer) |
159 | _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaGetColorBuffer" ); |
160 | _glfw.osmesa.GetDepthBuffer = (PFN_OSMesaGetDepthBuffer) |
161 | _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaGetDepthBuffer" ); |
162 | _glfw.osmesa.GetProcAddress = (PFN_OSMesaGetProcAddress) |
163 | _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaGetProcAddress" ); |
164 | |
165 | if (!_glfw.osmesa.CreateContextExt || |
166 | !_glfw.osmesa.DestroyContext || |
167 | !_glfw.osmesa.MakeCurrent || |
168 | !_glfw.osmesa.GetColorBuffer || |
169 | !_glfw.osmesa.GetDepthBuffer || |
170 | !_glfw.osmesa.GetProcAddress) |
171 | { |
172 | _glfwInputError(GLFW_PLATFORM_ERROR, |
173 | "OSMesa: Failed to load required entry points" ); |
174 | |
175 | _glfwTerminateOSMesa(); |
176 | return GLFW_FALSE; |
177 | } |
178 | |
179 | return GLFW_TRUE; |
180 | } |
181 | |
182 | void _glfwTerminateOSMesa(void) |
183 | { |
184 | if (_glfw.osmesa.handle) |
185 | { |
186 | _glfwPlatformFreeModule(_glfw.osmesa.handle); |
187 | _glfw.osmesa.handle = NULL; |
188 | } |
189 | } |
190 | |
191 | #define setAttrib(a, v) \ |
192 | { \ |
193 | assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ |
194 | attribs[index++] = a; \ |
195 | attribs[index++] = v; \ |
196 | } |
197 | |
198 | GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, |
199 | const _GLFWctxconfig* ctxconfig, |
200 | const _GLFWfbconfig* fbconfig) |
201 | { |
202 | OSMesaContext share = NULL; |
203 | const int accumBits = fbconfig->accumRedBits + |
204 | fbconfig->accumGreenBits + |
205 | fbconfig->accumBlueBits + |
206 | fbconfig->accumAlphaBits; |
207 | |
208 | if (ctxconfig->client == GLFW_OPENGL_ES_API) |
209 | { |
210 | _glfwInputError(GLFW_API_UNAVAILABLE, |
211 | "OSMesa: OpenGL ES is not available on OSMesa" ); |
212 | return GLFW_FALSE; |
213 | } |
214 | |
215 | if (ctxconfig->share) |
216 | share = ctxconfig->share->context.osmesa.handle; |
217 | |
218 | if (OSMesaCreateContextAttribs) |
219 | { |
220 | int index = 0, attribs[40]; |
221 | |
222 | setAttrib(OSMESA_FORMAT, OSMESA_RGBA); |
223 | setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); |
224 | setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); |
225 | setAttrib(OSMESA_ACCUM_BITS, accumBits); |
226 | |
227 | if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) |
228 | { |
229 | setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); |
230 | } |
231 | else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) |
232 | { |
233 | setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); |
234 | } |
235 | |
236 | if (ctxconfig->major != 1 || ctxconfig->minor != 0) |
237 | { |
238 | setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); |
239 | setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); |
240 | } |
241 | |
242 | if (ctxconfig->forward) |
243 | { |
244 | _glfwInputError(GLFW_VERSION_UNAVAILABLE, |
245 | "OSMesa: Forward-compatible contexts not supported" ); |
246 | return GLFW_FALSE; |
247 | } |
248 | |
249 | setAttrib(0, 0); |
250 | |
251 | window->context.osmesa.handle = |
252 | OSMesaCreateContextAttribs(attribs, share); |
253 | } |
254 | else |
255 | { |
256 | if (ctxconfig->profile) |
257 | { |
258 | _glfwInputError(GLFW_VERSION_UNAVAILABLE, |
259 | "OSMesa: OpenGL profiles unavailable" ); |
260 | return GLFW_FALSE; |
261 | } |
262 | |
263 | window->context.osmesa.handle = |
264 | OSMesaCreateContextExt(OSMESA_RGBA, |
265 | fbconfig->depthBits, |
266 | fbconfig->stencilBits, |
267 | accumBits, |
268 | share); |
269 | } |
270 | |
271 | if (window->context.osmesa.handle == NULL) |
272 | { |
273 | _glfwInputError(GLFW_VERSION_UNAVAILABLE, |
274 | "OSMesa: Failed to create context" ); |
275 | return GLFW_FALSE; |
276 | } |
277 | |
278 | window->context.makeCurrent = makeContextCurrentOSMesa; |
279 | window->context.swapBuffers = swapBuffersOSMesa; |
280 | window->context.swapInterval = swapIntervalOSMesa; |
281 | window->context.extensionSupported = extensionSupportedOSMesa; |
282 | window->context.getProcAddress = getProcAddressOSMesa; |
283 | window->context.destroy = destroyContextOSMesa; |
284 | |
285 | return GLFW_TRUE; |
286 | } |
287 | |
288 | #undef setAttrib |
289 | |
290 | |
291 | ////////////////////////////////////////////////////////////////////////// |
292 | ////// GLFW native API ////// |
293 | ////////////////////////////////////////////////////////////////////////// |
294 | |
295 | GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width, |
296 | int* height, int* format, void** buffer) |
297 | { |
298 | void* mesaBuffer; |
299 | GLint mesaWidth, mesaHeight, mesaFormat; |
300 | _GLFWwindow* window = (_GLFWwindow*) handle; |
301 | assert(window != NULL); |
302 | |
303 | _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); |
304 | |
305 | if (window->context.source != GLFW_OSMESA_CONTEXT_API) |
306 | { |
307 | _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); |
308 | return GLFW_FALSE; |
309 | } |
310 | |
311 | if (!OSMesaGetColorBuffer(window->context.osmesa.handle, |
312 | &mesaWidth, &mesaHeight, |
313 | &mesaFormat, &mesaBuffer)) |
314 | { |
315 | _glfwInputError(GLFW_PLATFORM_ERROR, |
316 | "OSMesa: Failed to retrieve color buffer" ); |
317 | return GLFW_FALSE; |
318 | } |
319 | |
320 | if (width) |
321 | *width = mesaWidth; |
322 | if (height) |
323 | *height = mesaHeight; |
324 | if (format) |
325 | *format = mesaFormat; |
326 | if (buffer) |
327 | *buffer = mesaBuffer; |
328 | |
329 | return GLFW_TRUE; |
330 | } |
331 | |
332 | GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle, |
333 | int* width, int* height, |
334 | int* bytesPerValue, |
335 | void** buffer) |
336 | { |
337 | void* mesaBuffer; |
338 | GLint mesaWidth, mesaHeight, mesaBytes; |
339 | _GLFWwindow* window = (_GLFWwindow*) handle; |
340 | assert(window != NULL); |
341 | |
342 | _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); |
343 | |
344 | if (window->context.source != GLFW_OSMESA_CONTEXT_API) |
345 | { |
346 | _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); |
347 | return GLFW_FALSE; |
348 | } |
349 | |
350 | if (!OSMesaGetDepthBuffer(window->context.osmesa.handle, |
351 | &mesaWidth, &mesaHeight, |
352 | &mesaBytes, &mesaBuffer)) |
353 | { |
354 | _glfwInputError(GLFW_PLATFORM_ERROR, |
355 | "OSMesa: Failed to retrieve depth buffer" ); |
356 | return GLFW_FALSE; |
357 | } |
358 | |
359 | if (width) |
360 | *width = mesaWidth; |
361 | if (height) |
362 | *height = mesaHeight; |
363 | if (bytesPerValue) |
364 | *bytesPerValue = mesaBytes; |
365 | if (buffer) |
366 | *buffer = mesaBuffer; |
367 | |
368 | return GLFW_TRUE; |
369 | } |
370 | |
371 | GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle) |
372 | { |
373 | _GLFWwindow* window = (_GLFWwindow*) handle; |
374 | _GLFW_REQUIRE_INIT_OR_RETURN(NULL); |
375 | |
376 | if (window->context.source != GLFW_OSMESA_CONTEXT_API) |
377 | { |
378 | _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); |
379 | return NULL; |
380 | } |
381 | |
382 | return window->context.osmesa.handle; |
383 | } |
384 | |
385 | |