1 //========================================================================
2 // GLFW 3.5 OSMesa - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2016 Google Inc.
5 // Copyright (c) 2016-2017 Camilla Löwy <elmindreda@glfw.org>
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
28 #include "internal.h"
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <assert.h>
33
makeContextCurrentOSMesa(_GLFWwindow * window)34 static void makeContextCurrentOSMesa(_GLFWwindow* window)
35 {
36 if (window)
37 {
38 int width, height;
39 _glfw.platform.getFramebufferSize(window, &width, &height);
40
41 // Check to see if we need to allocate a new buffer
42 if ((window->context.osmesa.buffer == NULL) ||
43 (width != window->context.osmesa.width) ||
44 (height != window->context.osmesa.height))
45 {
46 _glfw_free(window->context.osmesa.buffer);
47
48 // Allocate the new buffer (width * height * 8-bit RGBA)
49 window->context.osmesa.buffer = _glfw_calloc(4, (size_t) width * height);
50 window->context.osmesa.width = width;
51 window->context.osmesa.height = height;
52 }
53
54 if (!OSMesaMakeCurrent(window->context.osmesa.handle,
55 window->context.osmesa.buffer,
56 GL_UNSIGNED_BYTE,
57 width, height))
58 {
59 _glfwInputError(GLFW_PLATFORM_ERROR,
60 "OSMesa: Failed to make context current");
61 return;
62 }
63 }
64
65 _glfwPlatformSetTls(&_glfw.contextSlot, window);
66 }
67
getProcAddressOSMesa(const char * procname)68 static GLFWglproc getProcAddressOSMesa(const char* procname)
69 {
70 return (GLFWglproc) OSMesaGetProcAddress(procname);
71 }
72
destroyContextOSMesa(_GLFWwindow * window)73 static void destroyContextOSMesa(_GLFWwindow* window)
74 {
75 if (window->context.osmesa.handle)
76 {
77 OSMesaDestroyContext(window->context.osmesa.handle);
78 window->context.osmesa.handle = NULL;
79 }
80
81 if (window->context.osmesa.buffer)
82 {
83 _glfw_free(window->context.osmesa.buffer);
84 window->context.osmesa.width = 0;
85 window->context.osmesa.height = 0;
86 }
87 }
88
swapBuffersOSMesa(_GLFWwindow * window)89 static void swapBuffersOSMesa(_GLFWwindow* window)
90 {
91 // No double buffering on OSMesa
92 }
93
swapIntervalOSMesa(int interval)94 static void swapIntervalOSMesa(int interval)
95 {
96 // No swap interval on OSMesa
97 }
98
extensionSupportedOSMesa(const char * extension)99 static int extensionSupportedOSMesa(const char* extension)
100 {
101 // OSMesa does not have extensions
102 return GLFW_FALSE;
103 }
104
105
106 //////////////////////////////////////////////////////////////////////////
107 ////// GLFW internal API //////
108 //////////////////////////////////////////////////////////////////////////
109
_glfwInitOSMesa(void)110 GLFWbool _glfwInitOSMesa(void)
111 {
112 int i;
113 const char* sonames[] =
114 {
115 #if defined(_GLFW_OSMESA_LIBRARY)
116 _GLFW_OSMESA_LIBRARY,
117 #elif defined(_WIN32)
118 "libOSMesa.dll",
119 "OSMesa.dll",
120 #elif defined(__APPLE__)
121 "libOSMesa.8.dylib",
122 #elif defined(__CYGWIN__)
123 "libOSMesa-8.so",
124 #elif defined(__OpenBSD__) || defined(__NetBSD__)
125 "libOSMesa.so",
126 #else
127 "libOSMesa.so.8",
128 "libOSMesa.so.6",
129 #endif
130 NULL
131 };
132
133 if (_glfw.osmesa.handle)
134 return GLFW_TRUE;
135
136 for (i = 0; sonames[i]; i++)
137 {
138 _glfw.osmesa.handle = _glfwPlatformLoadModule(sonames[i]);
139 if (_glfw.osmesa.handle)
140 break;
141 }
142
143 if (!_glfw.osmesa.handle)
144 {
145 _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found");
146 return GLFW_FALSE;
147 }
148
149 _glfw.osmesa.CreateContextExt = (PFN_OSMesaCreateContextExt)
150 _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaCreateContextExt");
151 _glfw.osmesa.CreateContextAttribs = (PFN_OSMesaCreateContextAttribs)
152 _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaCreateContextAttribs");
153 _glfw.osmesa.DestroyContext = (PFN_OSMesaDestroyContext)
154 _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaDestroyContext");
155 _glfw.osmesa.MakeCurrent = (PFN_OSMesaMakeCurrent)
156 _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaMakeCurrent");
157 _glfw.osmesa.GetColorBuffer = (PFN_OSMesaGetColorBuffer)
158 _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaGetColorBuffer");
159 _glfw.osmesa.GetDepthBuffer = (PFN_OSMesaGetDepthBuffer)
160 _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaGetDepthBuffer");
161 _glfw.osmesa.GetProcAddress = (PFN_OSMesaGetProcAddress)
162 _glfwPlatformGetModuleSymbol(_glfw.osmesa.handle, "OSMesaGetProcAddress");
163
164 if (!_glfw.osmesa.CreateContextExt ||
165 !_glfw.osmesa.DestroyContext ||
166 !_glfw.osmesa.MakeCurrent ||
167 !_glfw.osmesa.GetColorBuffer ||
168 !_glfw.osmesa.GetDepthBuffer ||
169 !_glfw.osmesa.GetProcAddress)
170 {
171 _glfwInputError(GLFW_PLATFORM_ERROR,
172 "OSMesa: Failed to load required entry points");
173
174 _glfwTerminateOSMesa();
175 return GLFW_FALSE;
176 }
177
178 return GLFW_TRUE;
179 }
180
_glfwTerminateOSMesa(void)181 void _glfwTerminateOSMesa(void)
182 {
183 if (_glfw.osmesa.handle)
184 {
185 _glfwPlatformFreeModule(_glfw.osmesa.handle);
186 _glfw.osmesa.handle = NULL;
187 }
188 }
189
190 #define SET_ATTRIB(a, v) \
191 { \
192 assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
193 attribs[index++] = a; \
194 attribs[index++] = v; \
195 }
196
_glfwCreateContextOSMesa(_GLFWwindow * window,const _GLFWctxconfig * ctxconfig,const _GLFWfbconfig * fbconfig)197 GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
198 const _GLFWctxconfig* ctxconfig,
199 const _GLFWfbconfig* fbconfig)
200 {
201 OSMesaContext share = NULL;
202 const int accumBits = fbconfig->accumRedBits +
203 fbconfig->accumGreenBits +
204 fbconfig->accumBlueBits +
205 fbconfig->accumAlphaBits;
206
207 if (ctxconfig->client == GLFW_OPENGL_ES_API)
208 {
209 _glfwInputError(GLFW_API_UNAVAILABLE,
210 "OSMesa: OpenGL ES is not available on OSMesa");
211 return GLFW_FALSE;
212 }
213
214 if (ctxconfig->share)
215 share = ctxconfig->share->context.osmesa.handle;
216
217 if (OSMesaCreateContextAttribs)
218 {
219 int index = 0, attribs[40];
220
221 SET_ATTRIB(OSMESA_FORMAT, OSMESA_RGBA);
222 SET_ATTRIB(OSMESA_DEPTH_BITS, fbconfig->depthBits);
223 SET_ATTRIB(OSMESA_STENCIL_BITS, fbconfig->stencilBits);
224 SET_ATTRIB(OSMESA_ACCUM_BITS, accumBits);
225
226 if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE)
227 {
228 SET_ATTRIB(OSMESA_PROFILE, OSMESA_CORE_PROFILE);
229 }
230 else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE)
231 {
232 SET_ATTRIB(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE);
233 }
234
235 if (ctxconfig->major != 1 || ctxconfig->minor != 0)
236 {
237 SET_ATTRIB(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major);
238 SET_ATTRIB(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor);
239 }
240
241 if (ctxconfig->forward)
242 {
243 _glfwInputError(GLFW_VERSION_UNAVAILABLE,
244 "OSMesa: Forward-compatible contexts not supported");
245 return GLFW_FALSE;
246 }
247
248 SET_ATTRIB(0, 0);
249
250 window->context.osmesa.handle =
251 OSMesaCreateContextAttribs(attribs, share);
252 }
253 else
254 {
255 if (ctxconfig->profile)
256 {
257 _glfwInputError(GLFW_VERSION_UNAVAILABLE,
258 "OSMesa: OpenGL profiles unavailable");
259 return GLFW_FALSE;
260 }
261
262 window->context.osmesa.handle =
263 OSMesaCreateContextExt(OSMESA_RGBA,
264 fbconfig->depthBits,
265 fbconfig->stencilBits,
266 accumBits,
267 share);
268 }
269
270 if (window->context.osmesa.handle == NULL)
271 {
272 _glfwInputError(GLFW_VERSION_UNAVAILABLE,
273 "OSMesa: Failed to create context");
274 return GLFW_FALSE;
275 }
276
277 window->context.makeCurrent = makeContextCurrentOSMesa;
278 window->context.swapBuffers = swapBuffersOSMesa;
279 window->context.swapInterval = swapIntervalOSMesa;
280 window->context.extensionSupported = extensionSupportedOSMesa;
281 window->context.getProcAddress = getProcAddressOSMesa;
282 window->context.destroy = destroyContextOSMesa;
283
284 return GLFW_TRUE;
285 }
286
287 #undef SET_ATTRIB
288
289
290 //////////////////////////////////////////////////////////////////////////
291 ////// GLFW native API //////
292 //////////////////////////////////////////////////////////////////////////
293
glfwGetOSMesaColorBuffer(GLFWwindow * handle,int * width,int * height,int * format,void ** buffer)294 GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width,
295 int* height, int* format, void** buffer)
296 {
297 void* mesaBuffer;
298 GLint mesaWidth, mesaHeight, mesaFormat;
299
300 _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
301
302 _GLFWwindow* window = (_GLFWwindow*) handle;
303 assert(window != NULL);
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
glfwGetOSMesaDepthBuffer(GLFWwindow * handle,int * width,int * height,int * bytesPerValue,void ** buffer)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
340 _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
341
342 _GLFWwindow* window = (_GLFWwindow*) handle;
343 assert(window != NULL);
344
345 if (window->context.source != GLFW_OSMESA_CONTEXT_API)
346 {
347 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
348 return GLFW_FALSE;
349 }
350
351 if (!OSMesaGetDepthBuffer(window->context.osmesa.handle,
352 &mesaWidth, &mesaHeight,
353 &mesaBytes, &mesaBuffer))
354 {
355 _glfwInputError(GLFW_PLATFORM_ERROR,
356 "OSMesa: Failed to retrieve depth buffer");
357 return GLFW_FALSE;
358 }
359
360 if (width)
361 *width = mesaWidth;
362 if (height)
363 *height = mesaHeight;
364 if (bytesPerValue)
365 *bytesPerValue = mesaBytes;
366 if (buffer)
367 *buffer = mesaBuffer;
368
369 return GLFW_TRUE;
370 }
371
glfwGetOSMesaContext(GLFWwindow * handle)372 GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle)
373 {
374 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
375
376 _GLFWwindow* window = (_GLFWwindow*) handle;
377 assert(window != NULL);
378
379 if (window->context.source != GLFW_OSMESA_CONTEXT_API)
380 {
381 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
382 return NULL;
383 }
384
385 return window->context.osmesa.handle;
386 }
387
388