// // 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 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; GenericProc WINAPI GetProcAddressWithFallback(const char *name) { GenericProc proc = reinterpret_cast(gCurrentWGLGetProcAddress(name)); if (proc) { return proc; } return reinterpret_cast(GetProcAddress(gCurrentModule, name)); } bool HasExtension(const std::vector &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 GetPixelFormatAttributes(const ConfigParameters &configParams) { std::vector 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(EGLenum clientType, int majorVersion, int minorVersion, int profileMask) : GLWindowBase(clientType, majorVersion, minorVersion, profileMask), 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(glWindowingLibrary->getNative()); 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 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; LoadUtilGLES(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. LoadWGL(GetProcAddressWithFallback); if (!_wglGetExtensionsStringARB) { std::cerr << "Driver does not expose wglGetExtensionsStringARB." << std::endl; return context; } const char *extensionsString = _wglGetExtensionsStringARB(mDeviceContext); std::vector 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 compatibility and desktop GL profiles. int profileMask = 0; if (mClientType == EGL_OPENGL_ES_API) { profileMask = WGL_CONTEXT_ES2_PROFILE_BIT_EXT; } else { if ((mProfileMask & EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT) != 0) { profileMask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; } if ((mProfileMask & EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT) != 0) { profileMask |= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; } } const int createAttribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, mClientMajorVersion, WGL_CONTEXT_MINOR_VERSION_ARB, mClientMinorVersion, WGL_CONTEXT_PROFILE_MASK_ARB, profileMask, 0, 0}; context = _wglCreateContextAttribsARB(mDeviceContext, shareContext, createAttribs); 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(mWGLContext); } GLWindowContext WGLWindow::createContextGeneric(GLWindowContext share) { HGLRC shareContext = reinterpret_cast(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(newContext); } bool WGLWindow::makeCurrent() { return makeCurrent(mWGLContext); } bool WGLWindow::makeCurrentGeneric(GLWindowContext context) { HGLRC wglContext = reinterpret_cast(context); return makeCurrent(wglContext); } bool WGLWindow::makeCurrent(HGLRC context) { if (_wglMakeCurrent(mDeviceContext, context) == FALSE) { std::cerr << "Error during wglMakeCurrent.\n"; return false; } return true; } WGLWindow::Image WGLWindow::createImage(GLWindowContext context, Enum target, ClientBuffer buffer, const Attrib *attrib_list) { std::cerr << "WGLWindow::createImage not implemented.\n"; return nullptr; } WGLWindow::Image WGLWindow::createImageKHR(GLWindowContext context, Enum target, ClientBuffer buffer, const AttribKHR *attrib_list) { std::cerr << "WGLWindow::createImageKHR not implemented.\n"; return nullptr; } EGLBoolean WGLWindow::destroyImage(Image image) { std::cerr << "WGLWindow::destroyImage not implemented.\n"; return EGL_FALSE; } EGLBoolean WGLWindow::destroyImageKHR(Image image) { std::cerr << "WGLWindow::destroyImageKHR not implemented.\n"; return EGL_FALSE; } WGLWindow::Sync WGLWindow::createSync(EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list) { return nullptr; } WGLWindow::Sync WGLWindow::createSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list) { return nullptr; } EGLBoolean WGLWindow::destroySync(EGLDisplay dpy, Sync sync) { return EGL_FALSE; } EGLBoolean WGLWindow::destroySyncKHR(EGLDisplay dpy, Sync sync) { return EGL_FALSE; } EGLint WGLWindow::clientWaitSync(EGLDisplay dpy, Sync sync, EGLint flags, EGLTimeKHR timeout) { return EGL_FALSE; } EGLint WGLWindow::clientWaitSyncKHR(EGLDisplay dpy, Sync sync, EGLint flags, EGLTimeKHR timeout) { return EGL_FALSE; } EGLint WGLWindow::getEGLError() { return EGL_SUCCESS; } WGLWindow::Surface WGLWindow::createPbufferSurface(const EGLint *attrib_list) { std::cerr << "WGLWindow::createPbufferSurface not implemented.\n"; return EGL_FALSE; } EGLBoolean WGLWindow::destroySurface(Surface surface) { std::cerr << "WGLWindow::destroySurface not implemented.\n"; return EGL_FALSE; } EGLBoolean WGLWindow::bindTexImage(EGLSurface surface, EGLint buffer) { std::cerr << "WGLWindow::bindTexImage not implemented.\n"; return EGL_FALSE; } EGLBoolean WGLWindow::releaseTexImage(EGLSurface surface, EGLint buffer) { std::cerr << "WGLWindow::releaseTexImage not implemented.\n"; return EGL_FALSE; } bool WGLWindow::makeCurrent(EGLSurface draw, EGLSurface read, EGLContext context) { std::cerr << "WGLWindow::makeCurrent(draw, read, context) not implemented.\n"; return EGL_FALSE; } 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; } GenericProc WGLWindow::getProcAddress(const char *name) { return GetProcAddressWithFallback(name); } // static WGLWindow *WGLWindow::New(EGLenum clientType, int majorVersion, int minorVersion, int profileMask) { return new WGLWindow(clientType, majorVersion, minorVersion, profileMask); } // static void WGLWindow::Delete(WGLWindow **window) { delete *window; *window = nullptr; }