1 // Copyright (C) 2013 Christian Stehno
\r
2 // This file is part of the "Irrlicht Engine".
\r
3 // For conditions of distribution and use, see copyright notice in Irrlicht.h
\r
5 #include "CWGLManager.h"
\r
7 #ifdef _IRR_COMPILE_WITH_WGL_MANAGER_
\r
12 #include <GL/wglext.h>
\r
19 CWGLManager::CWGLManager()
\r
20 : PrimaryContext(SExposedVideoData(0)), PixelFormat(0), libHandle(NULL)
\r
23 setDebugName("CWGLManager");
\r
25 memset(FunctionPointers, 0, sizeof(FunctionPointers));
\r
28 CWGLManager::~CWGLManager()
\r
32 bool CWGLManager::initialize(const SIrrlichtCreationParameters& params, const SExposedVideoData& videodata)
\r
34 // store params, videoData is set later as it would be overwritten else
\r
37 // Create a window to test antialiasing support
\r
38 const fschar_t* ClassName = __TEXT("CWGLManager");
\r
39 HINSTANCE lhInstance = GetModuleHandle(0);
\r
43 wcex.cbSize = sizeof(WNDCLASSEX);
\r
44 wcex.style = CS_HREDRAW | CS_VREDRAW;
\r
45 wcex.lpfnWndProc = (WNDPROC)DefWindowProc;
\r
46 wcex.cbClsExtra = 0;
\r
47 wcex.cbWndExtra = 0;
\r
48 wcex.hInstance = lhInstance;
\r
50 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
51 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
\r
52 wcex.lpszMenuName = 0;
\r
53 wcex.lpszClassName = ClassName;
\r
55 RegisterClassEx(&wcex);
\r
59 clientSize.left = 0;
\r
60 clientSize.right = Params.WindowSize.Width;
\r
61 clientSize.bottom = Params.WindowSize.Height;
\r
63 DWORD style = WS_POPUP;
\r
64 if (!Params.Fullscreen)
\r
65 style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
\r
67 AdjustWindowRect(&clientSize, style, FALSE);
\r
69 const s32 realWidth = clientSize.right - clientSize.left;
\r
70 const s32 realHeight = clientSize.bottom - clientSize.top;
\r
72 const s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
\r
73 const s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;
\r
75 HWND temporary_wnd=CreateWindow(ClassName, __TEXT(""), style, windowLeft,
\r
76 windowTop, realWidth, realHeight, NULL, NULL, lhInstance, NULL);
\r
80 os::Printer::log("Cannot create a temporary window.", ELL_ERROR);
\r
81 UnregisterClass(ClassName, lhInstance);
\r
85 HDC HDc = GetDC(temporary_wnd);
\r
87 // Set up pixel format descriptor with desired parameters
\r
88 PIXELFORMATDESCRIPTOR tmp_pfd = {
\r
89 sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
\r
90 1, // Version Number
\r
91 (DWORD)(PFD_DRAW_TO_WINDOW | // Format Must Support Window
\r
92 PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
\r
93 (Params.Doublebuffer?PFD_DOUBLEBUFFER:0) | // Must Support Double Buffering
\r
94 (Params.Stereobuffer?PFD_STEREO:0)), // Must Support Stereo Buffer
\r
95 PFD_TYPE_RGBA, // Request An RGBA Format
\r
96 Params.Bits, // Select Our Color Depth
\r
97 0, 0, 0, 0, 0, 0, // Color Bits Ignored
\r
98 0, // No Alpha Buffer
\r
99 0, // Shift Bit Ignored
\r
100 0, // No Accumulation Buffer
\r
101 0, 0, 0, 0, // Accumulation Bits Ignored
\r
102 Params.ZBufferBits, // Z-Buffer (Depth Buffer)
\r
103 BYTE(Params.Stencilbuffer ? 1 : 0), // Stencil Buffer Depth
\r
104 0, // No Auxiliary Buffer
\r
105 PFD_MAIN_PLANE, // Main Drawing Layer
\r
107 0, 0, 0 // Layer Masks Ignored
\r
111 for (u32 i=0; i<6; ++i)
\r
115 if (Params.Stencilbuffer)
\r
117 os::Printer::log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
\r
118 Params.Stencilbuffer = false;
\r
119 pfd.cStencilBits = 0;
\r
127 pfd.cDepthBits = 24;
\r
132 if (Params.Bits!=16)
\r
133 pfd.cDepthBits = 16;
\r
140 // try single buffer
\r
141 if (Params.Doublebuffer)
\r
142 pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
\r
149 os::Printer::log("Cannot create a GL device context", "No suitable format for temporary window.", ELL_ERROR);
\r
150 ReleaseDC(temporary_wnd, HDc);
\r
151 DestroyWindow(temporary_wnd);
\r
152 UnregisterClass(ClassName, lhInstance);
\r
156 // choose pixelformat
\r
157 PixelFormat = ChoosePixelFormat(HDc, &pfd);
\r
162 SetPixelFormat(HDc, PixelFormat, &pfd);
\r
163 os::Printer::log("Create temporary GL rendering context", ELL_DEBUG);
\r
164 HGLRC hrc=wglCreateContext(HDc);
\r
167 os::Printer::log("Cannot create a temporary GL rendering context.", ELL_ERROR);
\r
168 ReleaseDC(temporary_wnd, HDc);
\r
169 DestroyWindow(temporary_wnd);
\r
170 UnregisterClass(ClassName, lhInstance);
\r
174 CurrentContext.OpenGLWin32.HDc = HDc;
\r
175 CurrentContext.OpenGLWin32.HRc = hrc;
\r
176 CurrentContext.OpenGLWin32.HWnd = temporary_wnd;
\r
178 if (!activateContext(CurrentContext, false))
\r
180 os::Printer::log("Cannot activate a temporary GL rendering context.", ELL_ERROR);
\r
181 wglDeleteContext(hrc);
\r
182 ReleaseDC(temporary_wnd, HDc);
\r
183 DestroyWindow(temporary_wnd);
\r
184 UnregisterClass(ClassName, lhInstance);
\r
188 core::stringc wglExtensions;
\r
189 #ifdef WGL_ARB_extensions_string
\r
190 PFNWGLGETEXTENSIONSSTRINGARBPROC irrGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
\r
191 if (irrGetExtensionsString)
\r
192 wglExtensions = irrGetExtensionsString(HDc);
\r
193 #elif defined(WGL_EXT_extensions_string)
\r
194 PFNWGLGETEXTENSIONSSTRINGEXTPROC irrGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
\r
195 if (irrGetExtensionsString)
\r
196 wglExtensions = irrGetExtensionsString(HDc);
\r
198 const bool pixel_format_supported = (wglExtensions.find("WGL_ARB_pixel_format") != -1);
\r
199 const bool multi_sample_supported = ((wglExtensions.find("WGL_ARB_multisample") != -1) ||
\r
200 (wglExtensions.find("WGL_EXT_multisample") != -1) || (wglExtensions.find("WGL_3DFX_multisample") != -1) );
\r
202 os::Printer::log("WGL_extensions", wglExtensions);
\r
205 // Without a GL context we can't call wglGetProcAddress so store this for later
\r
206 FunctionPointers[0] = (void*)wglGetProcAddress("wglCreateContextAttribsARB");
\r
208 #ifdef WGL_ARB_pixel_format
\r
209 PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat_ARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
\r
210 if (pixel_format_supported && wglChoosePixelFormat_ARB)
\r
212 // This value determines the number of samples used for antialiasing
\r
213 // My experience is that 8 does not show a big
\r
214 // improvement over 4, but 4 shows a big improvement
\r
217 if (Params.AntiAlias > 32)
\r
218 Params.AntiAlias = 32;
\r
220 f32 fAttributes[] = {0.0, 0.0};
\r
221 s32 iAttributes[] =
\r
223 WGL_DRAW_TO_WINDOW_ARB,1,
\r
224 WGL_SUPPORT_OPENGL_ARB,1,
\r
225 WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
\r
226 WGL_COLOR_BITS_ARB,(Params.Bits==32) ? 24 : 15,
\r
227 WGL_ALPHA_BITS_ARB,(Params.Bits==32) ? 8 : 1,
\r
228 WGL_DEPTH_BITS_ARB,Params.ZBufferBits, // 10,11
\r
229 WGL_STENCIL_BITS_ARB,Params.Stencilbuffer ? 1 : 0,
\r
230 WGL_DOUBLE_BUFFER_ARB,Params.Doublebuffer ? 1 : 0,
\r
231 WGL_STEREO_ARB,Params.Stereobuffer ? 1 : 0,
\r
232 WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
\r
233 #ifdef WGL_ARB_multisample
\r
234 WGL_SAMPLES_ARB,Params.AntiAlias, // 20,21
\r
235 WGL_SAMPLE_BUFFERS_ARB, (Params.AntiAlias>0) ? 1 : 0,
\r
236 #elif defined(WGL_EXT_multisample)
\r
237 WGL_SAMPLES_EXT,AntiAlias, // 20,21
\r
238 WGL_SAMPLE_BUFFERS_EXT, (Params.AntiAlias>0) ? 1 : 0,
\r
239 #elif defined(WGL_3DFX_multisample)
\r
240 WGL_SAMPLES_3DFX,AntiAlias, // 20,21
\r
241 WGL_SAMPLE_BUFFERS_3DFX, (Params.AntiAlias>0) ? 1 : 0,
\r
243 #ifdef WGL_ARB_framebuffer_sRGB
\r
244 WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, Params.HandleSRGB ? 1:0,
\r
245 #elif defined(WGL_EXT_framebuffer_sRGB)
\r
246 WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT, Params.HandleSRGB ? 1:0,
\r
248 // WGL_DEPTH_FLOAT_EXT, 1,
\r
251 int iAttrSize = sizeof(iAttributes)/sizeof(int);
\r
252 const bool framebuffer_srgb_supported = ((wglExtensions.find("WGL_ARB_framebuffer_sRGB") != -1) ||
\r
253 (wglExtensions.find("WGL_EXT_framebuffer_sRGB") != -1));
\r
254 if (!framebuffer_srgb_supported)
\r
256 memmove(&iAttributes[24],&iAttributes[26],sizeof(int)*(iAttrSize-26));
\r
259 if (!multi_sample_supported)
\r
261 memmove(&iAttributes[20],&iAttributes[24],sizeof(int)*(iAttrSize-24));
\r
266 // Try to get an acceptable pixel format
\r
271 const BOOL valid = wglChoosePixelFormat_ARB(HDc,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
\r
273 if (valid && numFormats)
\r
276 iAttributes[21] -= 1;
\r
278 while(rv==0 && iAttributes[21]>1);
\r
282 Params.AntiAlias=iAttributes[21];
\r
287 Params.AntiAlias=0;
\r
289 // this only terminates the temporary HRc
\r
293 DestroyWindow(temporary_wnd);
\r
294 UnregisterClass(ClassName, lhInstance);
\r
296 // now get new window
\r
297 CurrentContext.OpenGLWin32.HWnd=videodata.OpenGLWin32.HWnd;
\r
299 if (!(CurrentContext.OpenGLWin32.HDc=GetDC((HWND)videodata.OpenGLWin32.HWnd)))
\r
301 os::Printer::log("Cannot create a GL device context.", ELL_ERROR);
\r
304 if (!PrimaryContext.OpenGLWin32.HWnd)
\r
306 PrimaryContext.OpenGLWin32.HWnd=CurrentContext.OpenGLWin32.HWnd;
\r
307 PrimaryContext.OpenGLWin32.HDc=CurrentContext.OpenGLWin32.HDc;
\r
313 void CWGLManager::terminate()
\r
315 if (CurrentContext.OpenGLWin32.HDc)
\r
316 ReleaseDC((HWND)CurrentContext.OpenGLWin32.HWnd, (HDC)CurrentContext.OpenGLWin32.HDc);
\r
317 if (PrimaryContext.OpenGLWin32.HDc && PrimaryContext.OpenGLWin32.HDc == CurrentContext.OpenGLWin32.HDc)
\r
318 memset(&PrimaryContext, 0, sizeof(PrimaryContext));
\r
319 memset(&CurrentContext, 0, sizeof(CurrentContext));
\r
321 FreeLibrary(libHandle);
\r
324 bool CWGLManager::generateSurface()
\r
326 HDC HDc = (HDC)CurrentContext.OpenGLWin32.HDc;
\r
327 // search for pixel format the simple way
\r
328 if (PixelFormat==0 || (!SetPixelFormat(HDc, PixelFormat, &pfd)))
\r
330 for (u32 i=0; i<5; ++i)
\r
334 if (Params.Stencilbuffer)
\r
336 os::Printer::log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
\r
337 Params.Stencilbuffer = false;
\r
338 pfd.cStencilBits = 0;
\r
346 pfd.cDepthBits = 24;
\r
350 if (Params.Bits!=16)
\r
351 pfd.cDepthBits = 16;
\r
358 os::Printer::log("Cannot create a GL device context", "No suitable format.", ELL_ERROR);
\r
362 // choose pixelformat
\r
363 PixelFormat = ChoosePixelFormat(HDc, &pfd);
\r
368 // set pixel format
\r
369 if (!SetPixelFormat(HDc, PixelFormat, &pfd))
\r
371 os::Printer::log("Cannot set the pixel format.", ELL_ERROR);
\r
376 if (pfd.cAlphaBits != 0)
\r
378 if (pfd.cRedBits == 8)
\r
379 ColorFormat = ECF_A8R8G8B8;
\r
381 ColorFormat = ECF_A1R5G5B5;
\r
385 if (pfd.cRedBits == 8)
\r
386 ColorFormat = ECF_R8G8B8;
\r
388 ColorFormat = ECF_R5G6B5;
\r
390 os::Printer::log("Pixel Format", core::stringc(PixelFormat).c_str(), ELL_DEBUG);
\r
394 void CWGLManager::destroySurface()
\r
398 bool CWGLManager::generateContext()
\r
400 HDC HDc=(HDC)CurrentContext.OpenGLWin32.HDc;
\r
402 // create rendering context
\r
403 #ifdef WGL_ARB_create_context
\r
404 PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs_ARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)FunctionPointers[0];
\r
405 if (wglCreateContextAttribs_ARB)
\r
407 // with 3.0 all available profiles should be usable, higher versions impose restrictions
\r
408 // we need at least 1.1
\r
409 const int iAttribs[] =
\r
411 WGL_CONTEXT_MAJOR_VERSION_ARB, 1,
\r
412 WGL_CONTEXT_MINOR_VERSION_ARB, 1,
\r
413 // WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, // enable to get a debug context (depends on driver if that does anything)
\r
416 hrc=wglCreateContextAttribs_ARB(HDc, 0, iAttribs);
\r
420 hrc=wglCreateContext(HDc);
\r
421 os::Printer::log("Irrlicht context");
\r
425 os::Printer::log("Cannot create a GL rendering context.", ELL_ERROR);
\r
429 // set exposed data
\r
430 CurrentContext.OpenGLWin32.HRc = hrc;
\r
431 if (!PrimaryContext.OpenGLWin32.HRc)
\r
432 PrimaryContext.OpenGLWin32.HRc=CurrentContext.OpenGLWin32.HRc;
\r
437 const SExposedVideoData& CWGLManager::getContext() const
\r
439 return CurrentContext;
\r
442 bool CWGLManager::activateContext(const SExposedVideoData& videoData, bool restorePrimaryOnZero)
\r
444 if (videoData.OpenGLWin32.HWnd && videoData.OpenGLWin32.HDc && videoData.OpenGLWin32.HRc)
\r
446 if (!wglMakeCurrent((HDC)videoData.OpenGLWin32.HDc, (HGLRC)videoData.OpenGLWin32.HRc))
\r
448 os::Printer::log("Render Context switch failed.");
\r
451 CurrentContext=videoData;
\r
453 else if (!restorePrimaryOnZero && !videoData.OpenGLWin32.HDc && !videoData.OpenGLWin32.HRc)
\r
455 if (!wglMakeCurrent((HDC)0, (HGLRC)0))
\r
457 os::Printer::log("Render Context reset failed.");
\r
460 CurrentContext = videoData;
\r
462 // set back to main context
\r
463 else if (!videoData.OpenGLWin32.HWnd && CurrentContext.OpenGLWin32.HDc != PrimaryContext.OpenGLWin32.HDc)
\r
465 if (!wglMakeCurrent((HDC)PrimaryContext.OpenGLWin32.HDc, (HGLRC)PrimaryContext.OpenGLWin32.HRc))
\r
467 os::Printer::log("Render Context switch failed.");
\r
470 CurrentContext=PrimaryContext;
\r
475 void CWGLManager::destroyContext()
\r
477 if (CurrentContext.OpenGLWin32.HRc)
\r
479 if (!wglMakeCurrent((HDC)CurrentContext.OpenGLWin32.HDc, 0))
\r
480 os::Printer::log("Release of render context failed.", ELL_WARNING);
\r
482 if (!wglDeleteContext((HGLRC)CurrentContext.OpenGLWin32.HRc))
\r
483 os::Printer::log("Deletion of render context failed.", ELL_WARNING);
\r
484 if (PrimaryContext.OpenGLWin32.HRc==CurrentContext.OpenGLWin32.HRc)
\r
485 PrimaryContext.OpenGLWin32.HRc=0;
\r
486 CurrentContext.OpenGLWin32.HRc=0;
\r
490 void* CWGLManager::getProcAddress(const std::string &procName)
\r
493 proc = (void*)wglGetProcAddress(procName.c_str());
\r
494 if (!proc) { // Fallback
\r
496 libHandle = LoadLibraryA("opengl32.dll");
\r
498 proc = (void*)GetProcAddress(libHandle, procName.c_str());
\r
503 bool CWGLManager::swapBuffers()
\r
505 return SwapBuffers((HDC)CurrentContext.OpenGLWin32.HDc) == TRUE;
\r