/* * Offscreen OpenGL abstraction layer - WGL (windows) specific * * Copyright (c) 2010 Intel * Written by: * Gordon Williams * Ian Molton * Copyright (c) 2013 Wayo * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include "gloffscreen.h" /* In Windows, you must create a window *before* you can create a pbuffer or * get a context. So we create a hidden Window on startup(see glo_init/GloMain). * * Also, you can't share contexts that have different pixel formats, so we can't * just create a new context from the window. We must create a whole new PBuffer * just for a context :( */ struct GloMain { HINSTANCE hInstance; HDC hDC; HWND hWnd; /* Our hidden window */ HGLRC hContext; }; struct GloMain glo; int glo_inited = 0; struct _GloContext { /* Pixel format returned by wglChoosePixelFormat */ int wglPixelFormat; /* We need a pbuffer to make a context of the right pixelformat :( */ HPBUFFERARB hPBuffer; HDC hDC; HGLRC hContext; }; #define GLO_WINDOW_CLASS "QEmuGLClass" /* Initialise gloffscreen */ static void glo_init(void) { WNDCLASSEX wcx; PIXELFORMATDESCRIPTOR pfd; if (glo_inited) { fprintf(stderr, "gloffscreen already inited\n"); abort(); } /* Grab An Instance For Our Window */ glo.hInstance = GetModuleHandle(NULL); wcx.cbSize = sizeof(wcx); wcx.style = 0; wcx.lpfnWndProc = DefWindowProc; wcx.cbClsExtra = 0; wcx.cbWndExtra = 0; wcx.hInstance = glo.hInstance; wcx.hIcon = NULL; wcx.hCursor = NULL; wcx.hbrBackground = NULL; wcx.lpszMenuName = NULL; wcx.lpszClassName = GLO_WINDOW_CLASS; wcx.hIconSm = NULL; RegisterClassEx(&wcx); glo.hWnd = CreateWindow( GLO_WINDOW_CLASS, "QEmuGL", 0,0,0,0,0, (HWND)NULL, (HMENU)NULL, glo.hInstance, (LPVOID) NULL); if (!glo.hWnd) { fprintf(stderr, "Unable to create window\n"); abort(); } glo.hDC = GetDC(glo.hWnd); /* Create a pixel format */ memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; pfd.iLayerType = PFD_MAIN_PLANE; unsigned int pixelFormat = ChoosePixelFormat(glo.hDC, &pfd); DescribePixelFormat(glo.hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd); if (!SetPixelFormat(glo.hDC, pixelFormat, &pfd)) return; /* Create a tempoary OpenGL 2 context */ glo.hContext = wglCreateContext(glo.hDC); if (glo.hContext == NULL) { fprintf(stderr, "Unable to create GL context\n"); abort(); } wglMakeCurrent(glo.hDC, glo.hContext); if (!epoxy_has_wgl_extension(glo.hDC, "ARB_create_context") || !epoxy_has_wgl_extension(glo.hDC, "ARB_pixel_format") || !epoxy_has_wgl_extension(glo.hDC, "ARB_pbuffer")) { fprintf(stderr, "Unable to load the required WGL extensions\n"); abort(); } glo_inited = 1; } /* Uninitialise gloffscreen */ static void glo_kill(void) { if (glo.hContext) { wglMakeCurrent(NULL, NULL); wglDeleteContext(glo.hContext); glo.hContext = NULL; } if (glo.hDC) { ReleaseDC(glo.hWnd, glo.hDC); glo.hDC = NULL; } if (glo.hWnd) { DestroyWindow(glo.hWnd); glo.hWnd = NULL; } UnregisterClass(GLO_WINDOW_CLASS, glo.hInstance); } GloContext *glo_context_create(void) { if (!glo_inited) glo_init(); GloContext *context = (GloContext *)malloc(sizeof(GloContext)); memset(context, 0, sizeof(GloContext)); /* Create the context proper */ const int ctx_attri[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 3, WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0 }; context->hDC = glo.hDC; context->hContext = wglCreateContextAttribsARB(context->hDC, 0, ctx_attri); if (context->hContext == NULL) { printf("Unable to create GL context\n"); exit(EXIT_FAILURE); } glo_set_current(context); return context; } /* Set current context */ void glo_set_current(GloContext *context) { epoxy_handle_external_wglMakeCurrent(); if (context == NULL) { wglMakeCurrent(NULL, NULL); } else { wglMakeCurrent(context->hDC, context->hContext); } } /* Destroy a previously created OpenGL context */ void glo_context_destroy(GloContext *context) { if (!context) return; wglMakeCurrent(NULL, NULL); if (context->hPBuffer != NULL) { wglReleasePbufferDCARB(context->hPBuffer, context->hDC); wglDestroyPbufferARB(context->hPBuffer); } if (context->hDC != NULL) { ReleaseDC(glo.hWnd, context->hDC); } if (context->hContext) { wglDeleteContext(context->hContext); } free(context); }