blob: be2745687e9ee7fa924f8132024d3ec9411860d3 [file] [log] [blame]
//
// 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(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<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 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<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;
}
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::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;
}
angle::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;
}