| // |
| // Copyright 2018 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // WGLWindow: |
| // Implements initializing a WGL rendering context. |
| // |
| |
| #include "util/windows/WGLWindow.h" |
| |
| #include "common/string_utils.h" |
| #include "common/system_utils.h" |
| #include "util/OSWindow.h" |
| |
| #include <iostream> |
| |
| namespace |
| { |
| constexpr int kColorBits = 24; |
| constexpr int kAlphaBits = 8; |
| constexpr int kDepthBits = 24; |
| constexpr int kStencilBits = 8; |
| |
| PIXELFORMATDESCRIPTOR GetDefaultPixelFormatDescriptor() |
| { |
| PIXELFORMATDESCRIPTOR pixelFormatDescriptor = {}; |
| pixelFormatDescriptor.nSize = sizeof(pixelFormatDescriptor); |
| pixelFormatDescriptor.nVersion = 1; |
| pixelFormatDescriptor.dwFlags = |
| PFD_DRAW_TO_WINDOW | PFD_GENERIC_ACCELERATED | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; |
| pixelFormatDescriptor.iPixelType = PFD_TYPE_RGBA; |
| pixelFormatDescriptor.cColorBits = kColorBits; |
| pixelFormatDescriptor.cAlphaBits = kAlphaBits; |
| pixelFormatDescriptor.cDepthBits = kDepthBits; |
| pixelFormatDescriptor.cStencilBits = kStencilBits; |
| pixelFormatDescriptor.iLayerType = PFD_MAIN_PLANE; |
| |
| return pixelFormatDescriptor; |
| } |
| |
| PFNWGLGETPROCADDRESSPROC gCurrentWGLGetProcAddress = nullptr; |
| HMODULE gCurrentModule = nullptr; |
| |
| angle::GenericProc WINAPI GetProcAddressWithFallback(const char *name) |
| { |
| angle::GenericProc proc = reinterpret_cast<angle::GenericProc>(gCurrentWGLGetProcAddress(name)); |
| if (proc) |
| { |
| return proc; |
| } |
| |
| return reinterpret_cast<angle::GenericProc>(GetProcAddress(gCurrentModule, name)); |
| } |
| |
| bool HasExtension(const std::vector<std::string> &extensions, const char *ext) |
| { |
| return std::find(extensions.begin(), extensions.end(), ext) != extensions.end(); |
| } |
| |
| void DumpLastWindowsError() |
| { |
| std::cerr << "Last Windows error code: 0x" << std::hex << GetLastError() << std::endl; |
| } |
| |
| // Based on GetDefaultPixelFormatAttributes from wgl_utils.cpp |
| std::vector<int> GetPixelFormatAttributes(const ConfigParameters &configParams) |
| { |
| std::vector<int> attribs; |
| attribs.push_back(WGL_DRAW_TO_WINDOW_ARB); |
| attribs.push_back(TRUE); |
| |
| attribs.push_back(WGL_ACCELERATION_ARB); |
| attribs.push_back(WGL_FULL_ACCELERATION_ARB); |
| |
| attribs.push_back(WGL_SUPPORT_OPENGL_ARB); |
| attribs.push_back(TRUE); |
| |
| attribs.push_back(WGL_DOUBLE_BUFFER_ARB); |
| attribs.push_back(TRUE); |
| |
| attribs.push_back(WGL_PIXEL_TYPE_ARB); |
| attribs.push_back(WGL_TYPE_RGBA_ARB); |
| |
| attribs.push_back(WGL_COLOR_BITS_ARB); |
| attribs.push_back(kColorBits); |
| |
| attribs.push_back(WGL_ALPHA_BITS_ARB); |
| attribs.push_back(kAlphaBits); |
| |
| attribs.push_back(WGL_DEPTH_BITS_ARB); |
| attribs.push_back(kDepthBits); |
| |
| attribs.push_back(WGL_STENCIL_BITS_ARB); |
| attribs.push_back(kStencilBits); |
| |
| attribs.push_back(WGL_SWAP_METHOD_ARB); |
| attribs.push_back(WGL_SWAP_UNDEFINED_ARB); |
| |
| attribs.push_back(WGL_COLORSPACE_EXT); |
| if (configParams.colorSpace == EGL_COLORSPACE_sRGB) |
| { |
| attribs.push_back(WGL_COLORSPACE_SRGB_EXT); |
| } |
| else |
| { |
| attribs.push_back(WGL_COLORSPACE_LINEAR_EXT); |
| } |
| |
| attribs.push_back(0); |
| |
| return attribs; |
| } |
| |
| } // namespace |
| |
| WGLWindow::WGLWindow(int glesMajorVersion, int glesMinorVersion) |
| : GLWindowBase(glesMajorVersion, glesMinorVersion), |
| mDeviceContext(nullptr), |
| mWGLContext(nullptr), |
| mWindow(nullptr) |
| {} |
| |
| WGLWindow::~WGLWindow() {} |
| |
| // Internally initializes GL resources. |
| GLWindowResult WGLWindow::initializeGLWithResult(OSWindow *osWindow, |
| angle::Library *glWindowingLibrary, |
| angle::GLESDriverType driverType, |
| const EGLPlatformParameters &platformParams, |
| const ConfigParameters &configParams) |
| { |
| if (driverType != angle::GLESDriverType::SystemWGL) |
| { |
| std::cerr << "WGLWindow requires angle::GLESDriverType::SystemWGL.\n"; |
| return GLWindowResult::Error; |
| } |
| |
| glWindowingLibrary->getAs("wglGetProcAddress", &gCurrentWGLGetProcAddress); |
| |
| if (!gCurrentWGLGetProcAddress) |
| { |
| std::cerr << "Error loading wglGetProcAddress." << std::endl; |
| return GLWindowResult::Error; |
| } |
| |
| gCurrentModule = reinterpret_cast<HMODULE>(glWindowingLibrary->getNative()); |
| angle::LoadWGL(GetProcAddressWithFallback); |
| |
| mWindow = osWindow->getNativeWindow(); |
| mDeviceContext = GetDC(mWindow); |
| const PIXELFORMATDESCRIPTOR pixelFormatDescriptor = GetDefaultPixelFormatDescriptor(); |
| |
| int pixelFormat = 0; |
| |
| if (!_wglChoosePixelFormatARB) |
| { |
| std::cout << "Driver does not expose wglChoosePixelFormatARB." << std::endl; |
| } |
| else |
| { |
| std::vector<int> pixelFormatAttribs = GetPixelFormatAttributes(configParams); |
| |
| UINT matchingFormats = 0; |
| _wglChoosePixelFormatARB(mDeviceContext, &pixelFormatAttribs[0], nullptr, 1u, &pixelFormat, |
| &matchingFormats); |
| } |
| |
| if (pixelFormat == 0 && configParams.colorSpace != EGL_COLORSPACE_LINEAR) |
| { |
| std::cerr << "Could not find a compatible pixel format for a non-linear color space." |
| << std::endl; |
| return GLWindowResult::NoColorspaceSupport; |
| } |
| |
| if (pixelFormat == 0) |
| { |
| pixelFormat = ChoosePixelFormat(mDeviceContext, &pixelFormatDescriptor); |
| } |
| |
| if (pixelFormat == 0) |
| { |
| std::cerr << "Could not find a compatible pixel format." << std::endl; |
| DumpLastWindowsError(); |
| return GLWindowResult::Error; |
| } |
| |
| // According to the Windows docs, it is an error to set a pixel format twice. |
| int currentPixelFormat = GetPixelFormat(mDeviceContext); |
| if (currentPixelFormat != pixelFormat) |
| { |
| if (SetPixelFormat(mDeviceContext, pixelFormat, &pixelFormatDescriptor) != TRUE) |
| { |
| std::cerr << "Failed to set the pixel format." << std::endl; |
| DumpLastWindowsError(); |
| return GLWindowResult::Error; |
| } |
| } |
| |
| mWGLContext = createContext(configParams, nullptr); |
| if (mWGLContext == nullptr) |
| { |
| return GLWindowResult::Error; |
| } |
| |
| if (!makeCurrent()) |
| { |
| return GLWindowResult::Error; |
| } |
| |
| mPlatform = platformParams; |
| mConfigParams = configParams; |
| |
| angle::LoadGLES(GetProcAddressWithFallback); |
| return GLWindowResult::NoError; |
| } |
| |
| bool WGLWindow::initializeGL(OSWindow *osWindow, |
| angle::Library *glWindowingLibrary, |
| angle::GLESDriverType driverType, |
| const EGLPlatformParameters &platformParams, |
| const ConfigParameters &configParams) |
| { |
| return initializeGLWithResult(osWindow, glWindowingLibrary, driverType, platformParams, |
| configParams) == GLWindowResult::NoError; |
| } |
| |
| HGLRC WGLWindow::createContext(const ConfigParameters &configParams, HGLRC shareContext) |
| { |
| HGLRC context = _wglCreateContext(mDeviceContext); |
| if (!context) |
| { |
| std::cerr << "Failed to create a WGL context." << std::endl; |
| return context; |
| } |
| |
| if (!makeCurrent(context)) |
| { |
| std::cerr << "Failed to make WGL context current." << std::endl; |
| return context; |
| } |
| |
| // Reload entry points to capture extensions. |
| angle::LoadWGL(GetProcAddressWithFallback); |
| |
| if (!_wglGetExtensionsStringARB) |
| { |
| std::cerr << "Driver does not expose wglGetExtensionsStringARB." << std::endl; |
| return context; |
| } |
| |
| const char *extensionsString = _wglGetExtensionsStringARB(mDeviceContext); |
| |
| std::vector<std::string> extensions; |
| angle::SplitStringAlongWhitespace(extensionsString, &extensions); |
| |
| if (!HasExtension(extensions, "WGL_EXT_create_context_es2_profile")) |
| { |
| std::cerr << "Driver does not expose WGL_EXT_create_context_es2_profile." << std::endl; |
| return context; |
| } |
| |
| if (mConfigParams.webGLCompatibility.valid() || mConfigParams.robustResourceInit.valid()) |
| { |
| std::cerr << "WGLWindow does not support the requested feature set." << std::endl; |
| return context; |
| } |
| |
| // Tear down the context and create another with ES2 compatibility. |
| _wglDeleteContext(context); |
| |
| // This could be extended to cover ES1 compatiblity. |
| int kCreateAttribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, |
| mClientMajorVersion, |
| WGL_CONTEXT_MINOR_VERSION_ARB, |
| mClientMinorVersion, |
| WGL_CONTEXT_PROFILE_MASK_ARB, |
| WGL_CONTEXT_ES2_PROFILE_BIT_EXT, |
| 0, |
| 0}; |
| |
| context = _wglCreateContextAttribsARB(mDeviceContext, shareContext, kCreateAttribs); |
| if (!context) |
| { |
| std::cerr << "Failed to create an ES2 compatible WGL context." << std::endl; |
| return context; |
| } |
| |
| return context; |
| } |
| |
| void WGLWindow::destroyGL() |
| { |
| if (mWGLContext) |
| { |
| _wglDeleteContext(mWGLContext); |
| mWGLContext = nullptr; |
| } |
| |
| if (mDeviceContext) |
| { |
| ReleaseDC(mWindow, mDeviceContext); |
| mDeviceContext = nullptr; |
| } |
| } |
| |
| bool WGLWindow::isGLInitialized() const |
| { |
| return mWGLContext != nullptr; |
| } |
| |
| GLWindowContext WGLWindow::getCurrentContextGeneric() |
| { |
| return reinterpret_cast<GLWindowContext>(mWGLContext); |
| } |
| |
| GLWindowContext WGLWindow::createContextGeneric(GLWindowContext share) |
| { |
| HGLRC shareContext = reinterpret_cast<HGLRC>(share); |
| HGLRC newContext = createContext(mConfigParams, shareContext); |
| |
| // createContext() calls makeCurrent(newContext), so we need to restore the current context. |
| if (!makeCurrent()) |
| { |
| return nullptr; |
| } |
| |
| return reinterpret_cast<GLWindowContext>(newContext); |
| } |
| |
| bool WGLWindow::makeCurrent() |
| { |
| return makeCurrent(mWGLContext); |
| } |
| |
| bool WGLWindow::makeCurrentGeneric(GLWindowContext context) |
| { |
| HGLRC wglContext = reinterpret_cast<HGLRC>(context); |
| return makeCurrent(wglContext); |
| } |
| |
| bool WGLWindow::makeCurrent(HGLRC context) |
| { |
| if (_wglMakeCurrent(mDeviceContext, context) == FALSE) |
| { |
| std::cerr << "Error during wglMakeCurrent.\n"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool WGLWindow::setSwapInterval(EGLint swapInterval) |
| { |
| if (!_wglSwapIntervalEXT || _wglSwapIntervalEXT(swapInterval) == FALSE) |
| { |
| std::cerr << "Error during wglSwapIntervalEXT.\n"; |
| return false; |
| } |
| return true; |
| } |
| |
| void WGLWindow::swap() |
| { |
| if (SwapBuffers(mDeviceContext) == FALSE) |
| { |
| std::cerr << "Error during SwapBuffers.\n"; |
| } |
| } |
| |
| bool WGLWindow::hasError() const |
| { |
| return GetLastError() != S_OK; |
| } |
| |
| angle::GenericProc WGLWindow::getProcAddress(const char *name) |
| { |
| return GetProcAddressWithFallback(name); |
| } |
| |
| // static |
| WGLWindow *WGLWindow::New(int glesMajorVersion, int glesMinorVersion) |
| { |
| return new WGLWindow(glesMajorVersion, glesMinorVersion); |
| } |
| |
| // static |
| void WGLWindow::Delete(WGLWindow **window) |
| { |
| delete *window; |
| *window = nullptr; |
| } |