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
56 RegisterClassEx(&wcex);
\r
60 clientSize.left = 0;
\r
61 clientSize.right = Params.WindowSize.Width;
\r
62 clientSize.bottom = Params.WindowSize.Height;
\r
64 DWORD style = WS_POPUP;
\r
65 if (!Params.Fullscreen)
\r
66 style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
\r
68 AdjustWindowRect(&clientSize, style, FALSE);
\r
70 const s32 realWidth = clientSize.right - clientSize.left;
\r
71 const s32 realHeight = clientSize.bottom - clientSize.top;
\r
73 const s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
\r
74 const s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;
\r
76 HWND temporary_wnd=CreateWindow(ClassName, __TEXT(""), style, windowLeft,
\r
77 windowTop, realWidth, realHeight, NULL, NULL, lhInstance, NULL);
\r
81 os::Printer::log("Cannot create a temporary window.", ELL_ERROR);
\r
82 UnregisterClass(ClassName, lhInstance);
\r
86 HDC HDc = GetDC(temporary_wnd);
\r
88 // Set up pixel format descriptor with desired parameters
\r
89 PIXELFORMATDESCRIPTOR tmp_pfd = {
\r
90 sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
\r
91 1, // Version Number
\r
92 (DWORD)(PFD_DRAW_TO_WINDOW | // Format Must Support Window
\r
93 PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
\r
94 (Params.Doublebuffer?PFD_DOUBLEBUFFER:0) | // Must Support Double Buffering
\r
95 (Params.Stereobuffer?PFD_STEREO:0)), // Must Support Stereo Buffer
\r
96 PFD_TYPE_RGBA, // Request An RGBA Format
\r
97 Params.Bits, // Select Our Color Depth
\r
98 0, 0, 0, 0, 0, 0, // Color Bits Ignored
\r
99 0, // No Alpha Buffer
\r
100 0, // Shift Bit Ignored
\r
101 0, // No Accumulation Buffer
\r
102 0, 0, 0, 0, // Accumulation Bits Ignored
\r
103 Params.ZBufferBits, // Z-Buffer (Depth Buffer)
\r
104 BYTE(Params.Stencilbuffer ? 1 : 0), // Stencil Buffer Depth
\r
105 0, // No Auxiliary Buffer
\r
106 PFD_MAIN_PLANE, // Main Drawing Layer
\r
108 0, 0, 0 // Layer Masks Ignored
\r
112 for (u32 i=0; i<6; ++i)
\r
116 if (Params.Stencilbuffer)
\r
118 os::Printer::log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
\r
119 Params.Stencilbuffer = false;
\r
120 pfd.cStencilBits = 0;
\r
128 pfd.cDepthBits = 24;
\r
133 if (Params.Bits!=16)
\r
134 pfd.cDepthBits = 16;
\r
141 // try single buffer
\r
142 if (Params.Doublebuffer)
\r
143 pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
\r
150 os::Printer::log("Cannot create a GL device context", "No suitable format for temporary window.", ELL_ERROR);
\r
151 ReleaseDC(temporary_wnd, HDc);
\r
152 DestroyWindow(temporary_wnd);
\r
153 UnregisterClass(ClassName, lhInstance);
\r
157 // choose pixelformat
\r
158 PixelFormat = ChoosePixelFormat(HDc, &pfd);
\r
163 SetPixelFormat(HDc, PixelFormat, &pfd);
\r
164 os::Printer::log("Create temporary GL rendering context", ELL_DEBUG);
\r
165 HGLRC hrc=wglCreateContext(HDc);
\r
168 os::Printer::log("Cannot create a temporary GL rendering context.", ELL_ERROR);
\r
169 ReleaseDC(temporary_wnd, HDc);
\r
170 DestroyWindow(temporary_wnd);
\r
171 UnregisterClass(ClassName, lhInstance);
\r
175 CurrentContext.OpenGLWin32.HDc = HDc;
\r
176 CurrentContext.OpenGLWin32.HRc = hrc;
\r
177 CurrentContext.OpenGLWin32.HWnd = temporary_wnd;
\r
179 if (!activateContext(CurrentContext, false))
\r
181 os::Printer::log("Cannot activate a temporary GL rendering context.", ELL_ERROR);
\r
182 wglDeleteContext(hrc);
\r
183 ReleaseDC(temporary_wnd, HDc);
\r
184 DestroyWindow(temporary_wnd);
\r
185 UnregisterClass(ClassName, lhInstance);
\r
189 core::stringc wglExtensions;
\r
190 #ifdef WGL_ARB_extensions_string
\r
191 PFNWGLGETEXTENSIONSSTRINGARBPROC irrGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
\r
192 if (irrGetExtensionsString)
\r
193 wglExtensions = irrGetExtensionsString(HDc);
\r
194 #elif defined(WGL_EXT_extensions_string)
\r
195 PFNWGLGETEXTENSIONSSTRINGEXTPROC irrGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
\r
196 if (irrGetExtensionsString)
\r
197 wglExtensions = irrGetExtensionsString(HDc);
\r
199 const bool pixel_format_supported = (wglExtensions.find("WGL_ARB_pixel_format") != -1);
\r
200 const bool multi_sample_supported = ((wglExtensions.find("WGL_ARB_multisample") != -1) ||
\r
201 (wglExtensions.find("WGL_EXT_multisample") != -1) || (wglExtensions.find("WGL_3DFX_multisample") != -1) );
\r
203 os::Printer::log("WGL_extensions", wglExtensions);
\r
206 // Without a GL context we can't call wglGetProcAddress so store this for later
\r
207 FunctionPointers[0] = (void*)wglGetProcAddress("wglCreateContextAttribsARB");
\r
209 #ifdef WGL_ARB_pixel_format
\r
210 PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat_ARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
\r
211 if (pixel_format_supported && wglChoosePixelFormat_ARB)
\r
213 // This value determines the number of samples used for antialiasing
\r
214 // My experience is that 8 does not show a big
\r
215 // improvement over 4, but 4 shows a big improvement
\r
218 if (Params.AntiAlias > 32)
\r
219 Params.AntiAlias = 32;
\r
221 f32 fAttributes[] = {0.0, 0.0};
\r
222 s32 iAttributes[] =
\r
224 WGL_DRAW_TO_WINDOW_ARB,1,
\r
225 WGL_SUPPORT_OPENGL_ARB,1,
\r
226 WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
\r
227 WGL_COLOR_BITS_ARB,(Params.Bits==32) ? 24 : 15,
\r
228 WGL_ALPHA_BITS_ARB,(Params.Bits==32) ? 8 : 1,
\r
229 WGL_DEPTH_BITS_ARB,Params.ZBufferBits, // 10,11
\r
230 WGL_STENCIL_BITS_ARB,Params.Stencilbuffer ? 1 : 0,
\r
231 WGL_DOUBLE_BUFFER_ARB,Params.Doublebuffer ? 1 : 0,
\r
232 WGL_STEREO_ARB,Params.Stereobuffer ? 1 : 0,
\r
233 WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
\r
234 #ifdef WGL_ARB_multisample
\r
235 WGL_SAMPLES_ARB,Params.AntiAlias, // 20,21
\r
236 WGL_SAMPLE_BUFFERS_ARB, (Params.AntiAlias>0) ? 1 : 0,
\r
237 #elif defined(WGL_EXT_multisample)
\r
238 WGL_SAMPLES_EXT,AntiAlias, // 20,21
\r
239 WGL_SAMPLE_BUFFERS_EXT, (Params.AntiAlias>0) ? 1 : 0,
\r
240 #elif defined(WGL_3DFX_multisample)
\r
241 WGL_SAMPLES_3DFX,AntiAlias, // 20,21
\r
242 WGL_SAMPLE_BUFFERS_3DFX, (Params.AntiAlias>0) ? 1 : 0,
\r
244 #ifdef WGL_ARB_framebuffer_sRGB
\r
245 WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, Params.HandleSRGB ? 1:0,
\r
246 #elif defined(WGL_EXT_framebuffer_sRGB)
\r
247 WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT, Params.HandleSRGB ? 1:0,
\r
249 // WGL_DEPTH_FLOAT_EXT, 1,
\r
252 int iAttrSize = sizeof(iAttributes)/sizeof(int);
\r
253 const bool framebuffer_srgb_supported = ((wglExtensions.find("WGL_ARB_framebuffer_sRGB") != -1) ||
\r
254 (wglExtensions.find("WGL_EXT_framebuffer_sRGB") != -1));
\r
255 if (!framebuffer_srgb_supported)
\r
257 memmove(&iAttributes[24],&iAttributes[26],sizeof(int)*(iAttrSize-26));
\r
260 if (!multi_sample_supported)
\r
262 memmove(&iAttributes[20],&iAttributes[24],sizeof(int)*(iAttrSize-24));
\r
267 // Try to get an acceptable pixel format
\r
272 const BOOL valid = wglChoosePixelFormat_ARB(HDc,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
\r
274 if (valid && numFormats)
\r
277 iAttributes[21] -= 1;
\r
279 while(rv==0 && iAttributes[21]>1);
\r
283 Params.AntiAlias=iAttributes[21];
\r
288 Params.AntiAlias=0;
\r
290 // this only terminates the temporary HRc
\r
294 DestroyWindow(temporary_wnd);
\r
295 UnregisterClass(ClassName, lhInstance);
\r
297 // now get new window
\r
298 CurrentContext.OpenGLWin32.HWnd=videodata.OpenGLWin32.HWnd;
\r
300 if (!(CurrentContext.OpenGLWin32.HDc=GetDC((HWND)videodata.OpenGLWin32.HWnd)))
\r
302 os::Printer::log("Cannot create a GL device context.", ELL_ERROR);
\r
305 if (!PrimaryContext.OpenGLWin32.HWnd)
\r
307 PrimaryContext.OpenGLWin32.HWnd=CurrentContext.OpenGLWin32.HWnd;
\r
308 PrimaryContext.OpenGLWin32.HDc=CurrentContext.OpenGLWin32.HDc;
\r
314 void CWGLManager::terminate()
\r
316 if (CurrentContext.OpenGLWin32.HDc)
\r
317 ReleaseDC((HWND)CurrentContext.OpenGLWin32.HWnd, (HDC)CurrentContext.OpenGLWin32.HDc);
\r
318 if (PrimaryContext.OpenGLWin32.HDc && PrimaryContext.OpenGLWin32.HDc == CurrentContext.OpenGLWin32.HDc)
\r
319 memset(&PrimaryContext, 0, sizeof(PrimaryContext));
\r
320 memset(&CurrentContext, 0, sizeof(CurrentContext));
\r
322 FreeLibrary(libHandle);
\r
325 bool CWGLManager::generateSurface()
\r
327 HDC HDc = (HDC)CurrentContext.OpenGLWin32.HDc;
\r
328 // search for pixel format the simple way
\r
329 if (PixelFormat==0 || (!SetPixelFormat(HDc, PixelFormat, &pfd)))
\r
331 for (u32 i=0; i<5; ++i)
\r
335 if (Params.Stencilbuffer)
\r
337 os::Printer::log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
\r
338 Params.Stencilbuffer = false;
\r
339 pfd.cStencilBits = 0;
\r
347 pfd.cDepthBits = 24;
\r
351 if (Params.Bits!=16)
\r
352 pfd.cDepthBits = 16;
\r
359 os::Printer::log("Cannot create a GL device context", "No suitable format.", ELL_ERROR);
\r
363 // choose pixelformat
\r
364 PixelFormat = ChoosePixelFormat(HDc, &pfd);
\r
369 // set pixel format
\r
370 if (!SetPixelFormat(HDc, PixelFormat, &pfd))
\r
372 os::Printer::log("Cannot set the pixel format.", ELL_ERROR);
\r
377 if (pfd.cAlphaBits != 0)
\r
379 if (pfd.cRedBits == 8)
\r
380 ColorFormat = ECF_A8R8G8B8;
\r
382 ColorFormat = ECF_A1R5G5B5;
\r
386 if (pfd.cRedBits == 8)
\r
387 ColorFormat = ECF_R8G8B8;
\r
389 ColorFormat = ECF_R5G6B5;
\r
391 os::Printer::log("Pixel Format", core::stringc(PixelFormat).c_str(), ELL_DEBUG);
\r
395 void CWGLManager::destroySurface()
\r
399 bool CWGLManager::generateContext()
\r
401 HDC HDc=(HDC)CurrentContext.OpenGLWin32.HDc;
\r
403 // create rendering context
\r
404 #ifdef WGL_ARB_create_context
\r
405 PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs_ARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)FunctionPointers[0];
\r
406 if (wglCreateContextAttribs_ARB)
\r
408 // with 3.0 all available profiles should be usable, higher versions impose restrictions
\r
409 // we need at least 1.1
\r
410 const int iAttribs[] =
\r
412 WGL_CONTEXT_MAJOR_VERSION_ARB, 1,
\r
413 WGL_CONTEXT_MINOR_VERSION_ARB, 1,
\r
414 // WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, // enable to get a debug context (depends on driver if that does anything)
\r
417 hrc=wglCreateContextAttribs_ARB(HDc, 0, iAttribs);
\r
421 hrc=wglCreateContext(HDc);
\r
422 os::Printer::log("Irrlicht context");
\r
426 os::Printer::log("Cannot create a GL rendering context.", ELL_ERROR);
\r
430 // set exposed data
\r
431 CurrentContext.OpenGLWin32.HRc = hrc;
\r
432 if (!PrimaryContext.OpenGLWin32.HRc)
\r
433 PrimaryContext.OpenGLWin32.HRc=CurrentContext.OpenGLWin32.HRc;
\r
438 const SExposedVideoData& CWGLManager::getContext() const
\r
440 return CurrentContext;
\r
443 bool CWGLManager::activateContext(const SExposedVideoData& videoData, bool restorePrimaryOnZero)
\r
445 if (videoData.OpenGLWin32.HWnd && videoData.OpenGLWin32.HDc && videoData.OpenGLWin32.HRc)
\r
447 if (!wglMakeCurrent((HDC)videoData.OpenGLWin32.HDc, (HGLRC)videoData.OpenGLWin32.HRc))
\r
449 os::Printer::log("Render Context switch failed.");
\r
452 CurrentContext=videoData;
\r
454 else if (!restorePrimaryOnZero && !videoData.OpenGLWin32.HDc && !videoData.OpenGLWin32.HRc)
\r
456 if (!wglMakeCurrent((HDC)0, (HGLRC)0))
\r
458 os::Printer::log("Render Context reset failed.");
\r
461 CurrentContext = videoData;
\r
463 // set back to main context
\r
464 else if (!videoData.OpenGLWin32.HWnd && CurrentContext.OpenGLWin32.HDc != PrimaryContext.OpenGLWin32.HDc)
\r
466 if (!wglMakeCurrent((HDC)PrimaryContext.OpenGLWin32.HDc, (HGLRC)PrimaryContext.OpenGLWin32.HRc))
\r
468 os::Printer::log("Render Context switch failed.");
\r
471 CurrentContext=PrimaryContext;
\r
476 void CWGLManager::destroyContext()
\r
478 if (CurrentContext.OpenGLWin32.HRc)
\r
480 if (!wglMakeCurrent((HDC)CurrentContext.OpenGLWin32.HDc, 0))
\r
481 os::Printer::log("Release of render context failed.", ELL_WARNING);
\r
483 if (!wglDeleteContext((HGLRC)CurrentContext.OpenGLWin32.HRc))
\r
484 os::Printer::log("Deletion of render context failed.", ELL_WARNING);
\r
485 if (PrimaryContext.OpenGLWin32.HRc==CurrentContext.OpenGLWin32.HRc)
\r
486 PrimaryContext.OpenGLWin32.HRc=0;
\r
487 CurrentContext.OpenGLWin32.HRc=0;
\r
491 void* CWGLManager::getProcAddress(const std::string &procName)
\r
494 proc = (void*)wglGetProcAddress(procName.c_str());
\r
495 if (!proc) { // Fallback
\r
497 libHandle = LoadLibraryA("opengl32.dll");
\r
499 proc = (void*)GetProcAddress(libHandle, procName.c_str());
\r
504 bool CWGLManager::swapBuffers()
\r
506 return SwapBuffers((HDC)CurrentContext.OpenGLWin32.HDc) == TRUE;
\r