changeset 20543:9b23c51de4ae draft

-Codechange: [Win32] Try to get an OpenGL 3.2+ context if possible.
author Michael Lutz <michi@icosahedron.de>
date Tue, 28 May 2013 00:03:42 +0200
parents b6097f4fe3be
children ef2b5b14c3bc
files src/video/opengl.cpp src/video/opengl.h src/video/win32_v.cpp
diffstat 3 files changed, 103 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/src/video/opengl.cpp
+++ b/src/video/opengl.cpp
@@ -83,7 +83,7 @@
  * @param substring Substring to find.
  * @return Pointer to the start of the match or NULL if the substring is not present.
  */
-static const char *FindStringInExtensionList(const char *string, const char *substring)
+const char *FindStringInExtensionList(const char *string, const char *substring)
 {
 	while (1) {
 		/* Is the extension string present at all? */
--- a/src/video/opengl.h
+++ b/src/video/opengl.h
@@ -16,6 +16,7 @@
 #include "../core/geometry_type.hpp"
 
 bool IsOpenGLVersionAtLeast(byte major, byte minor);
+const char *FindStringInExtensionList(const char *string, const char *substring);
 void *GetOGLProcAddress(const char *proc);
 
 /** Platform-independent back-end class for OpenGL video drivers. */
--- a/src/video/win32_v.cpp
+++ b/src/video/win32_v.cpp
@@ -1114,6 +1114,7 @@
 #ifdef WITH_OPENGL
 
 #include <GL/gl.h>
+#include <GL/wglext.h>
 #include "opengl.h"
 
 #ifdef _MSC_VER
@@ -1124,6 +1125,82 @@
 #define PFD_SUPPORT_COMPOSITION 0x00008000
 #endif
 
+static PFNWGLCREATECONTEXTATTRIBSARBPROC _wglCreateContextAttribsARB = NULL;
+static bool _hasWGLARBCreateContextProfile = false; ///< Is WGL_ARB_create_context_profile supported?
+
+/**
+ * Set the pixel format of a window-
+ * @param dc Device context to set the pixel format of.
+ * @param fullscreen Should the pixel format be used for fullscreen drawing?
+ * @return NULL on success, error message otherwise.
+ */
+static const char *SelectPixelFormat(HDC dc, bool fullscreen)
+{
+	PIXELFORMATDESCRIPTOR pfd = {
+		sizeof(PIXELFORMATDESCRIPTOR), // Size of this struct.
+		1,                             // Version of this struct.
+		PFD_DRAW_TO_WINDOW |           // Require window support.
+		PFD_SUPPORT_OPENGL |           // Require OpenGL support.
+		PFD_DEPTH_DONTCARE,
+		PFD_TYPE_RGBA,                 // Request RGBA format.
+		24,                            // 24 bpp (excluding alpha).
+		0, 0, 0, 0, 0, 0, 0, 0,        // Colour bits and shift ignored.
+		0, 0, 0, 0, 0,                 // No accumulation buffer.
+		0, 0,                          // No depth/stencil buffer.
+		0,                             // No aux buffers.
+		PFD_MAIN_PLANE,                // Main layer.
+		0, 0, 0, 0                     // Ignored/reserved.
+	};
+
+	if (fullscreen) pfd.dwFlags |= PFD_DOUBLEBUFFER;
+	if (IsVistaOrLater()) pfd.dwFlags |= PFD_SUPPORT_COMPOSITION; // Make OpenTTD compatible with Aero.
+
+	/* Choose a suitable pixel format. */
+	int format = ChoosePixelFormat(dc, &pfd);
+	if (format == 0) return "No suitable pixel format found";
+	if (!SetPixelFormat(dc, format, &pfd)) return "Can't set pixel format";
+
+	return NULL;
+}
+
+/** Bind all WGL extension functions we need. */
+static void LoadWGLExtensions()
+{
+	/* Querying the supported WGL extensions and loading the matching
+	 * functions requires a valid context, even for the extensions
+	 * regarding context creation. To get around this, we create
+	 * a dummy window with a dummy context. The extension functions
+	 * remain valid even after this context is destroyed. */
+	HWND wnd = CreateWindow(_T("STATIC"), _T("dummy"), WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), NULL);
+	HDC dc = GetDC(wnd);
+
+	/* Set pixel format of the window. */
+	if (SelectPixelFormat(dc, false) == NULL) {
+		/* Create rendering context. */
+		HGLRC rc = wglCreateContext(dc);
+		if (rc != NULL) {
+			wglMakeCurrent(dc, rc);
+
+			/* Get list of WGL extensions. */
+			PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
+			if (wglGetExtensionsStringARB != NULL) {
+				const char *wgl_exts = wglGetExtensionsStringARB(dc);
+				/* Bind supported functions. */
+				if (FindStringInExtensionList(wgl_exts, "WGL_ARB_create_context") != NULL) {
+					_wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
+				}
+				_hasWGLARBCreateContextProfile = FindStringInExtensionList(wgl_exts, "WGL_ARB_create_context_profile") != NULL;
+			}
+
+			wglMakeCurrent(NULL, NULL);
+			wglDeleteContext(rc);
+		}
+	}
+
+	ReleaseDC(wnd, dc);
+	DestroyWindow(wnd);
+}
+
 static FVideoDriver_Win32OpenGL iFVideoDriver_Win32OpenGL;
 
 const char *VideoDriver_Win32OpenGL::Start(const char * const *param)
@@ -1131,6 +1208,8 @@
 	/* Check for a supported blitter. */
 	if (BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() != 32) return "Only 32bpp blitters supported";
 
+	LoadWGLExtensions();
+
 	this->Initialize(32); // OpenGL is always 32 bpp.
 	this->MakeWindow(_fullscreen);
 
@@ -1173,37 +1252,32 @@
 
 const char *VideoDriver_Win32OpenGL::AllocateContext()
 {
-	PIXELFORMATDESCRIPTOR pfd = {
-		sizeof(PIXELFORMATDESCRIPTOR), // Size of this struct.
-		1,                             // Version of this struct.
-		PFD_DRAW_TO_WINDOW |           // Require window support.
-		PFD_SUPPORT_OPENGL |           // Require OpenGL support.
-		PFD_DEPTH_DONTCARE,
-		PFD_TYPE_RGBA,                 // Request RGBA format.
-		24,                            // 24 bpp (excluding alpha).
-		0, 0, 0, 0, 0, 0, 0, 0,        // Colour bits and shift ignored.
-		0, 0, 0, 0, 0,                 // No accumulation buffer.
-		0, 0,                          // No depth/stencil buffer.
-		0,                             // No aux buffers.
-		PFD_MAIN_PLANE,                // Main layer.
-		0, 0, 0, 0                     // Ignored/reserved.
-	};
-
-	if (_wnd.fullscreen) pfd.dwFlags |= PFD_DOUBLEBUFFER;
-	if (IsVistaOrLater()) pfd.dwFlags |= PFD_SUPPORT_COMPOSITION; // Make OpenTTD compatible with Aero.
-
 	this->dc = GetDC(this->main_wnd);
 
-	/* Choose a suitable pixel format. */
-	int format = ChoosePixelFormat(this->dc, &pfd);
-	if (format == 0) return "No suitable pixel format found";
-	if (!SetPixelFormat(this->dc, format, &pfd)) return "Can't set pixel format";
+	const char *err = SelectPixelFormat(this->dc, _wnd.fullscreen);
+	if (err != NULL) return err;
+
+	HGLRC rc = NULL;
 
-	/* Create OpenGL device context. */
-	this->gl_rc = wglCreateContext(this->dc);
-	if (this->gl_rc == 0) return "Can't create OpenGL context";
-	if (!wglMakeCurrent(this->dc, this->gl_rc)) return "Can't active GL context";
+	/* Create OpenGL device context. Try to get an 3.2+ context if possible. */
+	if (_wglCreateContextAttribsARB != NULL) {
+		int attribs[] = {
+			WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
+			WGL_CONTEXT_MINOR_VERSION_ARB, 2,
+			_hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, // Terminate list if WGL_ARB_create_context_profile isn't supported.
+			0
+		};
+		rc = _wglCreateContextAttribsARB(this->dc, NULL, attribs);
+	}
 
+	if (rc == NULL) {
+		/* Old OpenGL or old driver, let's hope for the best. */
+		rc = wglCreateContext(this->dc);
+		if (rc == NULL) return "Can't create OpenGL context";
+	}
+	if (!wglMakeCurrent(this->dc, rc)) return "Can't active GL context";
+
+	this->gl_rc = rc;
 	this->ogl_backend = new OpenGLBackend;
 
 	return this->ogl_backend->Init();