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