#include "BsWin32GLSupport.h" #include "BsGLTexture.h" #include "BsWin32RenderWindow.h" #include "BsGLRenderAPI.h" #include "BsWin32Context.h" #include "BsWin32VideoModeInfo.h" #include "BsException.h" #include "GL/wglew.h" #include GLenum __stdcall wglewContextInit (BansheeEngine::GLSupport *glSupport); namespace BansheeEngine { template void remove_duplicates(C& c) { std::sort(c.begin(), c.end()); typename C::iterator p = std::unique(c.begin(), c.end()); c.erase(p, c.end()); } Win32GLSupport::Win32GLSupport() : mInitialWindow(nullptr), mHasPixelFormatARB(false), mHasMultisample(false), mHasHardwareGamma(false), mHasAdvancedContext(false) { initialiseWGL(); } RenderWindowPtr Win32GLSupport::newWindow(RENDER_WINDOW_DESC& desc, UINT32 windowId, RenderWindowPtr parentWindow) { if(parentWindow != nullptr) { UINT64 hWnd; parentWindow->getCustomAttribute("WINDOW", &hWnd); desc.platformSpecific["parentWindowHandle"] = toString(hWnd); } Win32RenderWindow* window = new (bs_alloc()) Win32RenderWindow(desc, windowId, *this); return RenderWindowPtr(window, &CoreObject::_delete); } SPtr Win32GLSupport::newWindowCore(RENDER_WINDOW_DESC& desc, UINT32 windowId) { Win32RenderWindowCore* window = new (bs_alloc()) Win32RenderWindowCore(desc, windowId, *this); if (!mInitialWindow) mInitialWindow = window; return bs_shared_ptr(window); } void Win32GLSupport::start() { } void Win32GLSupport::stop() { mInitialWindow = nullptr; } void Win32GLSupport::initializeExtensions() { assert(mInitialWindow != nullptr); GLSupport::initializeExtensions(); wglewContextInit(this); // Check for W32 specific extensions probe function PFNWGLGETEXTENSIONSSTRINGARBPROC _wglGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsString"); if(_wglGetExtensionsString == nullptr) return; const char *wgl_extensions = _wglGetExtensionsString(mInitialWindow->_getHDC()); // Parse them, and add them to the main list StringStream ext; String instr; ext << wgl_extensions; while(ext >> instr) { extensionList.insert(instr); } } SPtr Win32GLSupport::createContext(HDC hdc, HGLRC externalGlrc) { GLRenderAPI* rs = static_cast(RenderAPICore::instancePtr()); // If RenderAPI has initialized a context use that, otherwise we create our own HGLRC glrc = externalGlrc; bool createdNew = false; if (!rs->_isContextInitialized()) { if (externalGlrc == 0) { if (mHasAdvancedContext) { int attribs[40]; int i = 0; attribs[i++] = WGL_CONTEXT_MAJOR_VERSION_ARB; attribs[i++] = 4; attribs[i++] = WGL_CONTEXT_MINOR_VERSION_ARB; attribs[i++] = 3; attribs[i++] = WGL_CONTEXT_PROFILE_MASK_ARB; attribs[i++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; #if BS_DEBUG_MODE attribs[i++] = WGL_CONTEXT_FLAGS_ARB; attribs[i++] = WGL_CONTEXT_DEBUG_BIT_ARB; #endif attribs[i++] = 0; glrc = wglCreateContextAttribsARB(hdc, 0, attribs); } else glrc = wglCreateContext(hdc); if (glrc == 0) BS_EXCEPT(RenderingAPIException, "wglCreateContext failed: " + translateWGLError()); createdNew = true; } } else { rs->getMainContext()->setCurrent(); glrc = wglGetCurrentContext(); } return bs_shared_ptr_new(hdc, glrc, createdNew); } void* Win32GLSupport::getProcAddress(const String& procname) { return (void*)wglGetProcAddress(procname.c_str()); } void Win32GLSupport::initialiseWGL() { // We need to create a dummy context in order to get functions // that allow us to create a more advanced context. It seems // hacky but that's the only way to do it. LPCSTR dummyText = "Dummy"; #ifdef BS_STATIC_LIB HINSTANCE hinst = GetModuleHandle(NULL); #else HINSTANCE hinst = GetModuleHandle(MODULE_NAME); #endif WNDCLASS dummyClass; memset(&dummyClass, 0, sizeof(WNDCLASS)); dummyClass.style = CS_OWNDC; dummyClass.hInstance = hinst; dummyClass.lpfnWndProc = dummyWndProc; dummyClass.lpszClassName = dummyText; RegisterClass(&dummyClass); HWND hwnd = CreateWindow(dummyText, dummyText, WS_POPUP | WS_CLIPCHILDREN, 0, 0, 32, 32, 0, 0, hinst, 0); if (hwnd == nullptr) BS_EXCEPT(RenderingAPIException, "CreateWindow() failed"); HDC hdc = GetDC(hwnd); PIXELFORMATDESCRIPTOR pfd; memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.cColorBits = 16; pfd.cDepthBits = 15; pfd.dwFlags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; int format; if ((format = ChoosePixelFormat(hdc, &pfd)) != 0) SetPixelFormat(hdc, format, &pfd); HGLRC hrc = wglCreateContext(hdc); if (hrc) { HGLRC oldrc = wglGetCurrentContext(); HDC oldhdc = wglGetCurrentDC(); // if wglMakeCurrent fails, wglGetProcAddress will return null wglMakeCurrent(hdc, hrc); PFNWGLGETEXTENSIONSSTRINGARBPROC _wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB"); PFNWGLCREATECONTEXTATTRIBSARBPROC _wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); if (_wglCreateContextAttribsARB != nullptr) { WGLEW_GET_FUN(__wglewCreateContextAttribsARB) = _wglCreateContextAttribsARB; mHasAdvancedContext = true; } // Check for pixel format and multisampling support if (_wglGetExtensionsStringARB != nullptr) { std::istringstream wglexts(_wglGetExtensionsStringARB(hdc)); String ext; while (wglexts >> ext) { if (ext == "WGL_ARB_pixel_format") mHasPixelFormatARB = true; else if (ext == "WGL_ARB_multisample") mHasMultisample = true; else if (ext == "WGL_EXT_framebuffer_sRGB") mHasHardwareGamma = true; } } if (mHasPixelFormatARB && mHasMultisample) { // Enumerate all formats w/ multisampling static const int iattr[] = { WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, WGL_SUPPORT_OPENGL_ARB, GL_TRUE, WGL_DOUBLE_BUFFER_ARB, GL_TRUE, WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, WGL_SAMPLES_ARB, 2, 0 }; int formats[256]; unsigned int count; WGLEW_GET_FUN(__wglewChoosePixelFormatARB) = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); PFNWGLGETPIXELFORMATATTRIBIVARBPROC _wglGetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)wglGetProcAddress("wglGetPixelFormatAttribivARB"); if (WGLEW_GET_FUN(__wglewChoosePixelFormatARB)(hdc, iattr, 0, 256, formats, &count)) { // determine what multisampling levels are offered int query = WGL_SAMPLES_ARB, samples; for (unsigned int i = 0; i < count; ++i) { if (_wglGetPixelFormatAttribivARB(hdc, formats[i], 0, 1, &query, &samples)) { mMultisampleLevels.push_back(samples); } } remove_duplicates(mMultisampleLevels); } } wglMakeCurrent(oldhdc, oldrc); wglDeleteContext(hrc); } // Clean up our dummy window and class DestroyWindow(hwnd); UnregisterClass(dummyText, hinst); } LRESULT Win32GLSupport::dummyWndProc(HWND hwnd, UINT umsg, WPARAM wp, LPARAM lp) { return DefWindowProc(hwnd, umsg, wp, lp); } bool Win32GLSupport::selectPixelFormat(HDC hdc, int colorDepth, int multisample, bool hwGamma, bool depthStencil) { PIXELFORMATDESCRIPTOR pfd; memset(&pfd, 0, sizeof(pfd)); pfd.nSize = sizeof(pfd); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = (colorDepth > 16)? 24 : colorDepth; pfd.cAlphaBits = (colorDepth > 16)? 8 : 0; if (depthStencil) { pfd.cDepthBits = 24; pfd.cStencilBits = 8; } int format = 0; int useHwGamma = hwGamma? GL_TRUE : GL_FALSE; if (multisample && (!mHasMultisample || !mHasPixelFormatARB)) return false; if (hwGamma && !mHasHardwareGamma) return false; if ((multisample || hwGamma) && WGLEW_GET_FUN(__wglewChoosePixelFormatARB)) { // Use WGL to test extended caps (multisample, sRGB) Vector attribList; attribList.push_back(WGL_DRAW_TO_WINDOW_ARB); attribList.push_back(GL_TRUE); attribList.push_back(WGL_SUPPORT_OPENGL_ARB); attribList.push_back(GL_TRUE); attribList.push_back(WGL_DOUBLE_BUFFER_ARB); attribList.push_back(GL_TRUE); attribList.push_back(WGL_SAMPLE_BUFFERS_ARB); attribList.push_back(GL_TRUE); attribList.push_back(WGL_ACCELERATION_ARB); attribList.push_back(WGL_FULL_ACCELERATION_ARB); attribList.push_back(WGL_COLOR_BITS_ARB); attribList.push_back(pfd.cColorBits); attribList.push_back(WGL_ALPHA_BITS_ARB); attribList.push_back(pfd.cAlphaBits); attribList.push_back(WGL_DEPTH_BITS_ARB); attribList.push_back(pfd.cDepthBits); attribList.push_back(WGL_STENCIL_BITS_ARB); attribList.push_back(pfd.cStencilBits); attribList.push_back(WGL_SAMPLES_ARB); attribList.push_back(multisample); if (useHwGamma && checkExtension("WGL_EXT_framebuffer_sRGB")) { attribList.push_back(WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT); attribList.push_back(GL_TRUE); } // terminator attribList.push_back(0); UINT nformats; if (!WGLEW_GET_FUN(__wglewChoosePixelFormatARB)(hdc, &(attribList[0]), NULL, 1, &format, &nformats) || nformats <= 0) return false; } else { format = ChoosePixelFormat(hdc, &pfd); } return (format && SetPixelFormat(hdc, format, &pfd)); } VideoModeInfoPtr Win32GLSupport::getVideoModeInfo() const { return bs_shared_ptr_new(); } String translateWGLError() { int winError = GetLastError(); char errDesc[255]; int i; // Try windows errors first i = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, winError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) errDesc, 255, NULL); return String(errDesc); } }