Move SMaterial std::hash impl to its header
[minetest.git] / irr / src / CWGLManager.cpp
blob785524fbc9bb7342e8a8a493769af4167538e93e
1 // Copyright (C) 2013 Christian Stehno
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in Irrlicht.h
5 #include "CWGLManager.h"
7 #ifdef _IRR_COMPILE_WITH_WGL_MANAGER_
9 #include "os.h"
11 #include <GL/gl.h>
12 #include <GL/wglext.h>
14 namespace irr
16 namespace video
19 CWGLManager::CWGLManager() :
20 PrimaryContext(SExposedVideoData(0)), PixelFormat(0), libHandle(NULL)
22 #ifdef _DEBUG
23 setDebugName("CWGLManager");
24 #endif
25 memset(FunctionPointers, 0, sizeof(FunctionPointers));
28 CWGLManager::~CWGLManager()
32 bool CWGLManager::initialize(const SIrrlichtCreationParameters &params, const SExposedVideoData &videodata)
34 // store params, videoData is set later as it would be overwritten else
35 Params = params;
37 // Create a window to test antialiasing support
38 const fschar_t *ClassName = __TEXT("CWGLManager");
39 HINSTANCE lhInstance = GetModuleHandle(0);
41 // Register Class
42 WNDCLASSEX wcex;
43 wcex.cbSize = sizeof(WNDCLASSEX);
44 wcex.style = CS_HREDRAW | CS_VREDRAW;
45 wcex.lpfnWndProc = (WNDPROC)DefWindowProc;
46 wcex.cbClsExtra = 0;
47 wcex.cbWndExtra = 0;
48 wcex.hInstance = lhInstance;
49 wcex.hIcon = 0;
50 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
51 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
52 wcex.lpszMenuName = 0;
53 wcex.lpszClassName = ClassName;
54 wcex.hIconSm = 0;
55 RegisterClassEx(&wcex);
57 RECT clientSize;
58 clientSize.top = 0;
59 clientSize.left = 0;
60 clientSize.right = Params.WindowSize.Width;
61 clientSize.bottom = Params.WindowSize.Height;
63 DWORD style = WS_POPUP;
64 if (!Params.Fullscreen)
65 style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
67 AdjustWindowRect(&clientSize, style, FALSE);
69 const s32 realWidth = clientSize.right - clientSize.left;
70 const s32 realHeight = clientSize.bottom - clientSize.top;
72 const s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
73 const s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;
75 HWND temporary_wnd = CreateWindow(ClassName, __TEXT(""), style, windowLeft,
76 windowTop, realWidth, realHeight, NULL, NULL, lhInstance, NULL);
78 if (!temporary_wnd) {
79 os::Printer::log("Cannot create a temporary window.", ELL_ERROR);
80 UnregisterClass(ClassName, lhInstance);
81 return false;
84 HDC HDc = GetDC(temporary_wnd);
86 // Set up pixel format descriptor with desired parameters
87 PIXELFORMATDESCRIPTOR tmp_pfd = {
88 sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
89 1, // Version Number
90 (DWORD)(PFD_DRAW_TO_WINDOW | // Format Must Support Window
91 PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
92 (Params.Doublebuffer ? PFD_DOUBLEBUFFER : 0) | // Must Support Double Buffering
93 (Params.Stereobuffer ? PFD_STEREO : 0)), // Must Support Stereo Buffer
94 PFD_TYPE_RGBA, // Request An RGBA Format
95 Params.Bits, // Select Our Color Depth
96 0, 0, 0, 0, 0, 0, // Color Bits Ignored
97 0, // No Alpha Buffer
98 0, // Shift Bit Ignored
99 0, // No Accumulation Buffer
100 0, 0, 0, 0, // Accumulation Bits Ignored
101 Params.ZBufferBits, // Z-Buffer (Depth Buffer)
102 BYTE(Params.Stencilbuffer ? 1 : 0), // Stencil Buffer Depth
103 0, // No Auxiliary Buffer
104 PFD_MAIN_PLANE, // Main Drawing Layer
105 0, // Reserved
106 0, 0, 0 // Layer Masks Ignored
108 pfd = tmp_pfd;
110 for (u32 i = 0; i < 6; ++i) {
111 if (i == 1) {
112 if (Params.Stencilbuffer) {
113 os::Printer::log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
114 Params.Stencilbuffer = false;
115 pfd.cStencilBits = 0;
116 } else
117 continue;
118 } else if (i == 2) {
119 pfd.cDepthBits = 24;
120 } else if (i == 3) {
121 if (Params.Bits != 16)
122 pfd.cDepthBits = 16;
123 else
124 continue;
125 } else if (i == 4) {
126 // try single buffer
127 if (Params.Doublebuffer)
128 pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
129 else
130 continue;
131 } else if (i == 5) {
132 os::Printer::log("Cannot create a GL device context", "No suitable format for temporary window.", ELL_ERROR);
133 ReleaseDC(temporary_wnd, HDc);
134 DestroyWindow(temporary_wnd);
135 UnregisterClass(ClassName, lhInstance);
136 return false;
139 // choose pixelformat
140 PixelFormat = ChoosePixelFormat(HDc, &pfd);
141 if (PixelFormat)
142 break;
145 SetPixelFormat(HDc, PixelFormat, &pfd);
146 os::Printer::log("Create temporary GL rendering context", ELL_DEBUG);
147 HGLRC hrc = wglCreateContext(HDc);
148 if (!hrc) {
149 os::Printer::log("Cannot create a temporary GL rendering context.", ELL_ERROR);
150 ReleaseDC(temporary_wnd, HDc);
151 DestroyWindow(temporary_wnd);
152 UnregisterClass(ClassName, lhInstance);
153 return false;
156 CurrentContext.OpenGLWin32.HDc = HDc;
157 CurrentContext.OpenGLWin32.HRc = hrc;
158 CurrentContext.OpenGLWin32.HWnd = temporary_wnd;
160 if (!activateContext(CurrentContext, false)) {
161 os::Printer::log("Cannot activate a temporary GL rendering context.", ELL_ERROR);
162 wglDeleteContext(hrc);
163 ReleaseDC(temporary_wnd, HDc);
164 DestroyWindow(temporary_wnd);
165 UnregisterClass(ClassName, lhInstance);
166 return false;
169 core::stringc wglExtensions;
170 #ifdef WGL_ARB_extensions_string
171 PFNWGLGETEXTENSIONSSTRINGARBPROC irrGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
172 if (irrGetExtensionsString)
173 wglExtensions = irrGetExtensionsString(HDc);
174 #elif defined(WGL_EXT_extensions_string)
175 PFNWGLGETEXTENSIONSSTRINGEXTPROC irrGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
176 if (irrGetExtensionsString)
177 wglExtensions = irrGetExtensionsString(HDc);
178 #endif
179 const bool pixel_format_supported = (wglExtensions.find("WGL_ARB_pixel_format") != -1);
180 const bool multi_sample_supported = ((wglExtensions.find("WGL_ARB_multisample") != -1) ||
181 (wglExtensions.find("WGL_EXT_multisample") != -1) || (wglExtensions.find("WGL_3DFX_multisample") != -1));
182 if (params.DriverDebug)
183 os::Printer::log("WGL_extensions", wglExtensions);
185 // Without a GL context we can't call wglGetProcAddress so store this for later
186 FunctionPointers[0] = (void *)wglGetProcAddress("wglCreateContextAttribsARB");
188 #ifdef WGL_ARB_pixel_format
189 PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat_ARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
190 if (pixel_format_supported && wglChoosePixelFormat_ARB) {
191 // This value determines the number of samples used for antialiasing
192 // My experience is that 8 does not show a big
193 // improvement over 4, but 4 shows a big improvement
194 // over 2.
196 if (Params.AntiAlias > 32)
197 Params.AntiAlias = 32;
199 f32 fAttributes[] = {0.0, 0.0};
200 s32 iAttributes[] = {
201 WGL_DRAW_TO_WINDOW_ARB, 1,
202 WGL_SUPPORT_OPENGL_ARB, 1,
203 WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
204 WGL_COLOR_BITS_ARB, (Params.Bits == 32) ? 24 : 15,
205 WGL_ALPHA_BITS_ARB, (Params.Bits == 32) ? 8 : 1,
206 WGL_DEPTH_BITS_ARB, Params.ZBufferBits, // 10,11
207 WGL_STENCIL_BITS_ARB, Params.Stencilbuffer ? 1 : 0,
208 WGL_DOUBLE_BUFFER_ARB, Params.Doublebuffer ? 1 : 0,
209 WGL_STEREO_ARB, Params.Stereobuffer ? 1 : 0,
210 WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
211 #ifdef WGL_ARB_multisample
212 WGL_SAMPLES_ARB, Params.AntiAlias, // 20,21
213 WGL_SAMPLE_BUFFERS_ARB, (Params.AntiAlias > 0) ? 1 : 0,
214 #elif defined(WGL_EXT_multisample)
215 WGL_SAMPLES_EXT, AntiAlias, // 20,21
216 WGL_SAMPLE_BUFFERS_EXT, (Params.AntiAlias > 0) ? 1 : 0,
217 #elif defined(WGL_3DFX_multisample)
218 WGL_SAMPLES_3DFX, AntiAlias, // 20,21
219 WGL_SAMPLE_BUFFERS_3DFX, (Params.AntiAlias > 0) ? 1 : 0,
220 #endif
221 // WGL_DEPTH_FLOAT_EXT, 1,
222 0, 0, 0, 0,
224 int iAttrSize = sizeof(iAttributes) / sizeof(int);
225 if (!multi_sample_supported) {
226 memmove(&iAttributes[20], &iAttributes[24], sizeof(int) * (iAttrSize - 24));
227 iAttrSize -= 4;
230 s32 rv = 0;
231 // Try to get an acceptable pixel format
232 do {
233 int pixelFormat = 0;
234 UINT numFormats = 0;
235 const BOOL valid = wglChoosePixelFormat_ARB(HDc, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
237 if (valid && numFormats)
238 rv = pixelFormat;
239 else
240 iAttributes[21] -= 1;
241 } while (rv == 0 && iAttributes[21] > 1);
242 if (rv) {
243 PixelFormat = rv;
244 Params.AntiAlias = iAttributes[21];
246 } else
247 #endif
248 Params.AntiAlias = 0;
250 // this only terminates the temporary HRc
251 destroyContext();
252 destroySurface();
253 terminate();
254 DestroyWindow(temporary_wnd);
255 UnregisterClass(ClassName, lhInstance);
257 // now get new window
258 CurrentContext.OpenGLWin32.HWnd = videodata.OpenGLWin32.HWnd;
259 // get hdc
260 if (!(CurrentContext.OpenGLWin32.HDc = GetDC((HWND)videodata.OpenGLWin32.HWnd))) {
261 os::Printer::log("Cannot create a GL device context.", ELL_ERROR);
262 return false;
264 if (!PrimaryContext.OpenGLWin32.HWnd) {
265 PrimaryContext.OpenGLWin32.HWnd = CurrentContext.OpenGLWin32.HWnd;
266 PrimaryContext.OpenGLWin32.HDc = CurrentContext.OpenGLWin32.HDc;
269 return true;
272 void CWGLManager::terminate()
274 if (CurrentContext.OpenGLWin32.HDc)
275 ReleaseDC((HWND)CurrentContext.OpenGLWin32.HWnd, (HDC)CurrentContext.OpenGLWin32.HDc);
276 if (PrimaryContext.OpenGLWin32.HDc && PrimaryContext.OpenGLWin32.HDc == CurrentContext.OpenGLWin32.HDc)
277 memset(&PrimaryContext, 0, sizeof(PrimaryContext));
278 memset(&CurrentContext, 0, sizeof(CurrentContext));
279 if (libHandle)
280 FreeLibrary(libHandle);
283 bool CWGLManager::generateSurface()
285 HDC HDc = (HDC)CurrentContext.OpenGLWin32.HDc;
286 // search for pixel format the simple way
287 if (PixelFormat == 0 || (!SetPixelFormat(HDc, PixelFormat, &pfd))) {
288 for (u32 i = 0; i < 5; ++i) {
289 if (i == 1) {
290 if (Params.Stencilbuffer) {
291 os::Printer::log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
292 Params.Stencilbuffer = false;
293 pfd.cStencilBits = 0;
294 } else
295 continue;
296 } else if (i == 2) {
297 pfd.cDepthBits = 24;
299 if (i == 3) {
300 if (Params.Bits != 16)
301 pfd.cDepthBits = 16;
302 else
303 continue;
304 } else if (i == 4) {
305 os::Printer::log("Cannot create a GL device context", "No suitable format.", ELL_ERROR);
306 return false;
309 // choose pixelformat
310 PixelFormat = ChoosePixelFormat(HDc, &pfd);
311 if (PixelFormat)
312 break;
315 // set pixel format
316 if (!SetPixelFormat(HDc, PixelFormat, &pfd)) {
317 os::Printer::log("Cannot set the pixel format.", ELL_ERROR);
318 return false;
322 if (pfd.cAlphaBits != 0) {
323 if (pfd.cRedBits == 8)
324 ColorFormat = ECF_A8R8G8B8;
325 else
326 ColorFormat = ECF_A1R5G5B5;
327 } else {
328 if (pfd.cRedBits == 8)
329 ColorFormat = ECF_R8G8B8;
330 else
331 ColorFormat = ECF_R5G6B5;
333 os::Printer::log("Pixel Format", core::stringc(PixelFormat).c_str(), ELL_DEBUG);
334 return true;
337 void CWGLManager::destroySurface()
341 bool CWGLManager::generateContext()
343 HDC HDc = (HDC)CurrentContext.OpenGLWin32.HDc;
344 HGLRC hrc;
345 // create rendering context
346 #ifdef WGL_ARB_create_context
347 PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs_ARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)FunctionPointers[0];
348 if (wglCreateContextAttribs_ARB) {
349 // with 3.0 all available profiles should be usable, higher versions impose restrictions
350 // we need at least 1.1
351 const int iAttribs[] = {
352 WGL_CONTEXT_MAJOR_VERSION_ARB, 1,
353 WGL_CONTEXT_MINOR_VERSION_ARB, 1,
354 // WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, // enable to get a debug context (depends on driver if that does anything)
357 hrc = wglCreateContextAttribs_ARB(HDc, 0, iAttribs);
358 } else
359 #endif
360 hrc = wglCreateContext(HDc);
361 os::Printer::log("Irrlicht context");
363 if (!hrc) {
364 os::Printer::log("Cannot create a GL rendering context.", ELL_ERROR);
365 return false;
368 // set exposed data
369 CurrentContext.OpenGLWin32.HRc = hrc;
370 if (!PrimaryContext.OpenGLWin32.HRc)
371 PrimaryContext.OpenGLWin32.HRc = CurrentContext.OpenGLWin32.HRc;
373 return true;
376 const SExposedVideoData &CWGLManager::getContext() const
378 return CurrentContext;
381 bool CWGLManager::activateContext(const SExposedVideoData &videoData, bool restorePrimaryOnZero)
383 if (videoData.OpenGLWin32.HWnd && videoData.OpenGLWin32.HDc && videoData.OpenGLWin32.HRc) {
384 if (!wglMakeCurrent((HDC)videoData.OpenGLWin32.HDc, (HGLRC)videoData.OpenGLWin32.HRc)) {
385 os::Printer::log("Render Context switch failed.");
386 return false;
388 CurrentContext = videoData;
389 } else if (!restorePrimaryOnZero && !videoData.OpenGLWin32.HDc && !videoData.OpenGLWin32.HRc) {
390 if (!wglMakeCurrent((HDC)0, (HGLRC)0)) {
391 os::Printer::log("Render Context reset failed.");
392 return false;
394 CurrentContext = videoData;
396 // set back to main context
397 else if (!videoData.OpenGLWin32.HWnd && CurrentContext.OpenGLWin32.HDc != PrimaryContext.OpenGLWin32.HDc) {
398 if (!wglMakeCurrent((HDC)PrimaryContext.OpenGLWin32.HDc, (HGLRC)PrimaryContext.OpenGLWin32.HRc)) {
399 os::Printer::log("Render Context switch (back to main) failed.");
400 return false;
402 CurrentContext = PrimaryContext;
404 return true;
407 void CWGLManager::destroyContext()
409 if (CurrentContext.OpenGLWin32.HRc) {
410 if (!wglMakeCurrent((HDC)CurrentContext.OpenGLWin32.HDc, 0))
411 os::Printer::log("Release of render context failed.", ELL_WARNING);
413 if (!wglDeleteContext((HGLRC)CurrentContext.OpenGLWin32.HRc))
414 os::Printer::log("Deletion of render context failed.", ELL_WARNING);
415 if (PrimaryContext.OpenGLWin32.HRc == CurrentContext.OpenGLWin32.HRc)
416 PrimaryContext.OpenGLWin32.HRc = 0;
417 CurrentContext.OpenGLWin32.HRc = 0;
421 void *CWGLManager::getProcAddress(const std::string &procName)
423 void *proc = NULL;
424 proc = (void *)wglGetProcAddress(procName.c_str());
425 if (!proc) { // Fallback
426 if (!libHandle)
427 libHandle = LoadLibraryA("opengl32.dll");
428 if (libHandle)
429 proc = (void *)GetProcAddress(libHandle, procName.c_str());
431 return proc;
434 bool CWGLManager::swapBuffers()
436 return SwapBuffers((HDC)CurrentContext.OpenGLWin32.HDc) == TRUE;
442 #endif