1 /*
2  * GStreamer
3  * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
4  * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <gst/gst.h>
27 
28 #include <gst/gl/gl.h>
29 #include <gst/gl/gstglfuncs.h>
30 
31 #include "gstglcontext_wgl.h"
32 #include <GL/wglext.h>
33 
34 #include "../utils/opengl_versions.h"
35 #include "../gstglcontext_private.h"
36 
37 struct _GstGLContextWGLPrivate
38 {
39   PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
40 
41   GstGLAPI context_api;
42 };
43 
44 #define GST_CAT_DEFAULT gst_gl_context_debug
45 
46 #define gst_gl_context_wgl_parent_class parent_class
47 G_DEFINE_TYPE_WITH_PRIVATE (GstGLContextWGL, gst_gl_context_wgl,
48     GST_TYPE_GL_CONTEXT);
49 
50 static guintptr gst_gl_context_wgl_get_gl_context (GstGLContext * context);
51 static void gst_gl_context_wgl_swap_buffers (GstGLContext * context);
52 static gboolean gst_gl_context_wgl_choose_format (GstGLContext * context,
53     GError ** error);
54 static gboolean gst_gl_context_wgl_activate (GstGLContext * context,
55     gboolean activate);
56 static gboolean gst_gl_context_wgl_create_context (GstGLContext * context,
57     GstGLAPI gl_api, GstGLContext * other_context, GError ** error);
58 static void gst_gl_context_wgl_destroy_context (GstGLContext * context);
59 GstGLAPI gst_gl_context_wgl_get_gl_api (GstGLContext * context);
60 static GstGLPlatform gst_gl_context_wgl_get_gl_platform (GstGLContext *
61     context);
62 
63 static void
gst_gl_context_wgl_class_init(GstGLContextWGLClass * klass)64 gst_gl_context_wgl_class_init (GstGLContextWGLClass * klass)
65 {
66   GstGLContextClass *context_class = (GstGLContextClass *) klass;
67 
68   context_class->get_gl_context =
69       GST_DEBUG_FUNCPTR (gst_gl_context_wgl_get_gl_context);
70   context_class->choose_format =
71       GST_DEBUG_FUNCPTR (gst_gl_context_wgl_choose_format);
72   context_class->activate = GST_DEBUG_FUNCPTR (gst_gl_context_wgl_activate);
73   context_class->create_context =
74       GST_DEBUG_FUNCPTR (gst_gl_context_wgl_create_context);
75   context_class->destroy_context =
76       GST_DEBUG_FUNCPTR (gst_gl_context_wgl_destroy_context);
77   context_class->swap_buffers =
78       GST_DEBUG_FUNCPTR (gst_gl_context_wgl_swap_buffers);
79 
80   context_class->get_proc_address =
81       GST_DEBUG_FUNCPTR (gst_gl_context_wgl_get_proc_address);
82   context_class->get_gl_api = GST_DEBUG_FUNCPTR (gst_gl_context_wgl_get_gl_api);
83   context_class->get_gl_platform =
84       GST_DEBUG_FUNCPTR (gst_gl_context_wgl_get_gl_platform);
85 }
86 
87 static void
gst_gl_context_wgl_init(GstGLContextWGL * context_wgl)88 gst_gl_context_wgl_init (GstGLContextWGL * context_wgl)
89 {
90   context_wgl->priv = gst_gl_context_wgl_get_instance_private (context_wgl);
91 
92   context_wgl->priv->context_api = GST_GL_API_OPENGL | GST_GL_API_OPENGL3;
93 }
94 
95 /* Must be called in the gl thread */
96 GstGLContextWGL *
gst_gl_context_wgl_new(GstGLDisplay * display)97 gst_gl_context_wgl_new (GstGLDisplay * display)
98 {
99   GstGLContextWGL *context;
100 
101   if ((gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_WIN32) ==
102       0)
103     /* we require an win32 display handle to create WGL contexts */
104     return NULL;
105 
106   context = g_object_new (GST_TYPE_GL_CONTEXT_WGL, NULL);
107   gst_object_ref_sink (context);
108 
109   return context;
110 }
111 
112 static HGLRC
_create_context_with_flags(GstGLContextWGL * context_wgl,HDC dpy,HGLRC share_context,gint major,gint minor,gint contextFlags,gint profileMask)113 _create_context_with_flags (GstGLContextWGL * context_wgl, HDC dpy,
114     HGLRC share_context, gint major, gint minor, gint contextFlags,
115     gint profileMask)
116 {
117   HGLRC ret;
118 #define N_ATTRIBS 20
119   gint attribs[N_ATTRIBS];
120   gint n = 0;
121 
122   if (major) {
123     attribs[n++] = WGL_CONTEXT_MAJOR_VERSION_ARB;
124     attribs[n++] = major;
125   }
126   if (minor) {
127     attribs[n++] = WGL_CONTEXT_MINOR_VERSION_ARB;
128     attribs[n++] = minor;
129   }
130   if (contextFlags) {
131     attribs[n++] = WGL_CONTEXT_FLAGS_ARB;
132     attribs[n++] = contextFlags;
133   }
134   if (profileMask) {
135     attribs[n++] = WGL_CONTEXT_PROFILE_MASK_ARB;
136     attribs[n++] = profileMask;
137   }
138   attribs[n++] = 0;
139 
140   g_assert (n < N_ATTRIBS);
141 #undef N_ATTRIBS
142 
143   ret =
144       context_wgl->priv->wglCreateContextAttribsARB (dpy, share_context,
145       attribs);
146 
147   return ret;
148 }
149 
150 static gboolean
gst_gl_context_wgl_create_context(GstGLContext * context,GstGLAPI gl_api,GstGLContext * other_context,GError ** error)151 gst_gl_context_wgl_create_context (GstGLContext * context,
152     GstGLAPI gl_api, GstGLContext * other_context, GError ** error)
153 {
154   GstGLWindow *window;
155   GstGLContextWGL *context_wgl;
156   HGLRC external_gl_context = NULL;
157   HGLRC trampoline;
158   HDC device;
159 
160   context_wgl = GST_GL_CONTEXT_WGL (context);
161   window = gst_gl_context_get_window (context);
162   device = (HDC) gst_gl_window_get_display (window);
163 
164   if (other_context) {
165     if (gst_gl_context_get_gl_platform (other_context) != GST_GL_PLATFORM_WGL) {
166       g_set_error (error, GST_GL_CONTEXT_ERROR,
167           GST_GL_CONTEXT_ERROR_WRONG_CONFIG,
168           "Cannot share context with a non-WGL context");
169       goto failure;
170     }
171     external_gl_context = (HGLRC) gst_gl_context_get_gl_context (other_context);
172   }
173 
174   trampoline = wglCreateContext (device);
175   if (trampoline)
176     GST_DEBUG ("gl context created: %" G_GUINTPTR_FORMAT,
177         (guintptr) trampoline);
178   else {
179     g_set_error (error, GST_GL_CONTEXT_ERROR,
180         GST_GL_CONTEXT_ERROR_CREATE_CONTEXT, "failed to create glcontext:0x%x",
181         (unsigned int) GetLastError ());
182     goto failure;
183   }
184   g_assert (trampoline);
185 
186   /* get extension functions */
187   wglMakeCurrent (device, trampoline);
188 
189   context_wgl->priv->wglCreateContextAttribsARB =
190       (PFNWGLCREATECONTEXTATTRIBSARBPROC)
191       wglGetProcAddress ("wglCreateContextAttribsARB");
192 
193   wglMakeCurrent (device, 0);
194   wglDeleteContext (trampoline);
195   trampoline = NULL;
196 
197   if (context_wgl->priv->wglCreateContextAttribsARB != NULL
198       && gl_api & GST_GL_API_OPENGL3) {
199     gint i;
200 
201     for (i = 0; i < G_N_ELEMENTS (opengl_versions); i++) {
202       gint profileMask = 0;
203       gint contextFlags = 0;
204 
205       if ((opengl_versions[i].major > 3
206               || (opengl_versions[i].major == 3
207                   && opengl_versions[i].minor >= 2))) {
208         profileMask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
209         contextFlags |= WGL_CONTEXT_DEBUG_BIT_ARB;
210       } else {
211         break;
212       }
213 
214       GST_DEBUG_OBJECT (context, "trying to create a GL %d.%d context",
215           opengl_versions[i].major, opengl_versions[i].minor);
216 
217       context_wgl->wgl_context = _create_context_with_flags (context_wgl,
218           device, external_gl_context, opengl_versions[i].major,
219           opengl_versions[i].minor, contextFlags, profileMask);
220 
221       if (context_wgl->wgl_context) {
222         context_wgl->priv->context_api = GST_GL_API_OPENGL3;
223         break;
224       }
225     }
226   }
227 
228   if (!context_wgl->wgl_context) {
229 
230     if (context_wgl->priv->wglCreateContextAttribsARB && external_gl_context) {
231       context_wgl->wgl_context =
232           context_wgl->priv->wglCreateContextAttribsARB (device,
233           external_gl_context, 0);
234     }
235 
236 
237     if (!context_wgl->wgl_context) {
238 
239       context_wgl->wgl_context = wglCreateContext (device);
240 
241       if (!context_wgl->wgl_context) {
242         g_set_error (error, GST_GL_CONTEXT_ERROR,
243             GST_GL_CONTEXT_ERROR_CREATE_CONTEXT,
244             "Failed to create WGL context 0x%x",
245             (unsigned int) GetLastError ());
246         goto failure;
247       }
248 
249       if (external_gl_context) {
250         if (!wglShareLists (external_gl_context, context_wgl->wgl_context)) {
251           g_set_error (error, GST_GL_CONTEXT_ERROR,
252               GST_GL_CONTEXT_ERROR_CREATE_CONTEXT,
253               "failed to share contexts through wglShareLists 0x%x",
254               (unsigned int) GetLastError ());
255           goto failure;
256         }
257       }
258     }
259 
260     context_wgl->priv->context_api = GST_GL_API_OPENGL;
261   }
262 
263   GST_LOG ("gl context id: %" G_GUINTPTR_FORMAT,
264       (guintptr) context_wgl->wgl_context);
265 
266   gst_object_unref (window);
267 
268   return TRUE;
269 
270 failure:
271   gst_object_unref (window);
272 
273   return FALSE;
274 }
275 
276 static void
gst_gl_context_wgl_destroy_context(GstGLContext * context)277 gst_gl_context_wgl_destroy_context (GstGLContext * context)
278 {
279   GstGLContextWGL *context_wgl;
280 
281   context_wgl = GST_GL_CONTEXT_WGL (context);
282 
283   if (context_wgl->wgl_context)
284     wglDeleteContext (context_wgl->wgl_context);
285   context_wgl->wgl_context = NULL;
286 }
287 
288 static gboolean
gst_gl_context_wgl_choose_format(GstGLContext * context,GError ** error)289 gst_gl_context_wgl_choose_format (GstGLContext * context, GError ** error)
290 {
291   GstGLWindow *window;
292   PIXELFORMATDESCRIPTOR pfd;
293   gint pixelformat = 0;
294   gboolean res = FALSE;
295   HDC device;
296 
297   window = gst_gl_context_get_window (context);
298   gst_gl_window_win32_create_window (GST_GL_WINDOW_WIN32 (window), error);
299   device = (HDC) gst_gl_window_get_display (window);
300   gst_object_unref (window);
301 
302   pfd.nSize = sizeof (PIXELFORMATDESCRIPTOR);
303   pfd.nVersion = 1;
304   pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
305   pfd.iPixelType = PFD_TYPE_RGBA;
306   pfd.cColorBits = 24;
307   pfd.cRedBits = 8;
308   pfd.cRedShift = 0;
309   pfd.cGreenBits = 8;
310   pfd.cGreenShift = 0;
311   pfd.cBlueBits = 8;
312   pfd.cBlueShift = 0;
313   pfd.cAlphaBits = 0;
314   pfd.cAlphaShift = 0;
315   pfd.cAccumBits = 0;
316   pfd.cAccumRedBits = 0;
317   pfd.cAccumGreenBits = 0;
318   pfd.cAccumBlueBits = 0;
319   pfd.cAccumAlphaBits = 0;
320   pfd.cDepthBits = 24;
321   pfd.cStencilBits = 8;
322   pfd.cAuxBuffers = 0;
323   pfd.iLayerType = PFD_MAIN_PLANE;
324   pfd.bReserved = 0;
325   pfd.dwLayerMask = 0;
326   pfd.dwVisibleMask = 0;
327   pfd.dwDamageMask = 0;
328 
329   pfd.cColorBits = (BYTE) GetDeviceCaps (device, BITSPIXEL);
330 
331   pixelformat = ChoosePixelFormat (device, &pfd);
332 
333   if (!pixelformat) {
334     g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
335         "Failed to choose a pixel format");
336     return FALSE;
337   }
338 
339   res = SetPixelFormat (device, pixelformat, &pfd);
340 
341   return res;
342 }
343 
344 static void
gst_gl_context_wgl_swap_buffers(GstGLContext * context)345 gst_gl_context_wgl_swap_buffers (GstGLContext * context)
346 {
347   GstGLWindow *window = gst_gl_context_get_window (context);
348   HDC device = (HDC) gst_gl_window_get_display (window);
349 
350   SwapBuffers (device);
351 
352   gst_object_unref (window);
353 }
354 
355 static guintptr
gst_gl_context_wgl_get_gl_context(GstGLContext * context)356 gst_gl_context_wgl_get_gl_context (GstGLContext * context)
357 {
358   return (guintptr) GST_GL_CONTEXT_WGL (context)->wgl_context;
359 }
360 
361 static gboolean
gst_gl_context_wgl_activate(GstGLContext * context,gboolean activate)362 gst_gl_context_wgl_activate (GstGLContext * context, gboolean activate)
363 {
364   GstGLWindow *window;
365   GstGLContextWGL *context_wgl;
366   HDC device;
367   gboolean result;
368 
369   window = gst_gl_context_get_window (context);
370   context_wgl = GST_GL_CONTEXT_WGL (context);
371   device = (HDC) gst_gl_window_get_display (window);
372 
373   if (activate) {
374     result = wglMakeCurrent (device, context_wgl->wgl_context);
375   } else {
376     result = wglMakeCurrent (NULL, NULL);
377   }
378 
379   gst_object_unref (window);
380 
381   return result;
382 }
383 
384 GstGLAPI
gst_gl_context_wgl_get_gl_api(GstGLContext * context)385 gst_gl_context_wgl_get_gl_api (GstGLContext * context)
386 {
387   GstGLContextWGL *context_wgl = GST_GL_CONTEXT_WGL (context);
388 
389   return context_wgl->priv->context_api;
390 }
391 
392 static GstGLPlatform
gst_gl_context_wgl_get_gl_platform(GstGLContext * context)393 gst_gl_context_wgl_get_gl_platform (GstGLContext * context)
394 {
395   return GST_GL_PLATFORM_WGL;
396 }
397 
398 gpointer
gst_gl_context_wgl_get_proc_address(GstGLAPI gl_api,const gchar * name)399 gst_gl_context_wgl_get_proc_address (GstGLAPI gl_api, const gchar * name)
400 {
401   gpointer result;
402 
403   if (!(result = gst_gl_context_default_get_proc_address (gl_api, name))) {
404     result = wglGetProcAddress ((LPCSTR) name);
405   }
406 
407   return result;
408 }
409 
410 guintptr
gst_gl_context_wgl_get_current_context(void)411 gst_gl_context_wgl_get_current_context (void)
412 {
413   return (guintptr) wglGetCurrentContext ();
414 }
415