xqemu/hw/xbox/nv2a/gl/gloffscreen_wgl.c

212 lines
6.2 KiB
C

/*
* Offscreen OpenGL abstraction layer - WGL (windows) specific
*
* Copyright (c) 2010 Intel
* Written by:
* Gordon Williams <gordon.williams@collabora.co.uk>
* Ian Molton <ian.molton@collabora.co.uk>
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <wingdi.h>
#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);
}